应用解析I:建立基础

应用解析I:建立基础

上周,我们通过阅读Gherkin语法的基础知识为解析做准备。 在本文和下一篇文章中,我们将使用可应用的解析库来解析该语法。 本周,我们将重点介绍该库的基础知识,并建立组合使用的词汇表。 我们将大量使用Applicative类型类。 如果您需要对此进行复习,请查看本文 当我们开始编码时,您也可以在Github上跟随示例! 这里的大多数代码在Parser.hs中

在接下来的几周中,我们还将看到其他几个解析库。 如果您想获得关于这些的更多想法,请下载我们的生产清单 它总结了许多其他有用的库,可用于编写更高级别的Haskell。

如果您从未开始编写Haskell,那么现在就是您的机会! 获取免费的初学者清单,并学习入门基础!

入门

因此,为了开始解析,让我们对输入格式进行一些注释。 首先,我们将输入要素文档视为单个字符串。 我们将删除所有空行,然后修剪每行的开头和结尾空格。

parseFeatureFromFile :: FilePath -> IO Feature
parseFeatureFromFile inputFile = do
fileContents <- lines <$> readFile inputFile
let nonEmptyLines = filter (not . isEmpty) fileContents
let trimmedLines = map trim nonEmptyLines
let finalString = unlines trimmedLines
case parseFeature finalString of
...

isEmpty :: String -> Bool
isEmpty = all isSpace
trim :: String -> String
trim input = reverse flippedTrimmed
where
trimStart = dropWhile isSpace input
flipped = reverse trimStart
flippedTrimmed = dropWhile isSpace flipped

这对于我们的语法意味着几件事。 首先,我们不在乎缩进。 第二,我们忽略多余的行。 这意味着我们的解析器可能会允许某些我们不想要的格式。 但这没关系,因为我们正在尝试使事情保持简单。

RE类型

通过基于应用程序的解析,我们将使用的主要数据类型称为RE ,用于正则表达式。 它代表一个解析器,并通过两种类型进行参数化:

data RE sa = ...

s类型是指我们将要解析的基本单位。 由于我们将输入解析为单个String ,因此将为Char 那么a类型是解析元素的结果。 各个解析器对此有所不同。 我们可以使用的最基本的组合器是sym 这将解析您选择的单个符号:

sym :: s - > RE ss
parseLowercaseA :: RE Char Char
parseLowercaseA = sym 'a'

要使用RE解析器,我们将match函数或其等效的等价字符=~ 如果我们可以匹配整个输入字符串,这些将返回Just值,否则返回Nothing

>> match parseLowercaseA “a”
Just 'a'
>> “b” =~ parseLowercaseA
Nothing
>> “ab” =~ parseLowercaseA
Nothing -- (Needs to parse entire input)

谓词和字符串

自然,我们需要一些更复杂的功能。 除了解析单个输入字符外,我们还可以使用psym解析任何适合特定谓词的psym 因此,如果我们想读取不是换行符的任何字符,则可以执行以下操作:

parseNonNewline :: RE Char Char
parseNonNewline = psym (/= '\n')

string组合器允许我们匹配特定的完整字符串,然后返回它:

readFeatureWord :: RE Char String
readFeatureWord = string “Feature”

尽管经常会最终放弃“结果”,但我们将使用它来解析关键字。

适用组合器

现在, RE类型适用。 这意味着我们可以在其上应用各种应用组合器。 其中之一是many ,这使我们可以多次应用一个解析器。 这是一个我们会经常使用的组合器。 它允许我们读取所有内容,直到换行符并返回结果字符串:

readUntilEndOfLine :: RE Char String
readUntilEndOfLine = many (psym (/= '\n'))

除此之外,我们还要利用适用的<*>运算符组合不同的解析器。 我们还可以使用<$>在这些函数之上应用纯函数(或构造函数)。 假设我们有一个存储两个字符的数据类型。 这是我们可以为其构建解析器的方法:

data TwoChars = TwoChars Char Char
parseTwoChars :: RE Char TwoChars
parseTwoChars = TwoChars <$> parseNonNewline <*> parseNonNewline
...
>> match parseTwoChars “ab”
Just (TwoChars 'a' 'b')

我们还可以使用<**> ,它们是主要应用运算符的近亲。 第一个将解析,但随后忽略右侧的解析结果。 第二个丢弃左侧结果。

parseFirst :: RE Char Char
parseFirst = parseNonNewline <* parseNonNewline
parseSecond :: RE Char Char
parseSecond = parseNonNewline *> parseNonnewline
>> match parseFirst “ab”
Just 'a'
>> match parseSecond “ab”
Just 'b'
>> match parseFirst “a”
Nothing

注意最后一个失败,因为解析器需要两个输入! 我们将在一秒钟内回到失败的想法。 但是,既然我们知道这种技术,我们就可以编写另外两个有用的解析器:

readThroughEndOfLine :: RE Char String
readThroughEndOfLine = readUntilEndOfLine <* sym '\n'
readThroughBar :: RE Char String
readThroughBar = readUntilBar <* sym '|'
readUntilBar :: RE Char String
readUntilBar = many (psym (\c -> c /= '|' && c /= '\n'))

第一个将解析行的其余部分,然后使用换行符本身。 其他解析器完成相同的任务,但竖线字符除外。 下周解析“ Examples部分时将需要这些。

替代方案:处理解析失败

我们在上面介绍了解析器“失败”的概念。 当然,当解析器失败时,我们需要能够提供替代方案! 否则,我们的语言在结构上将非常有限。 幸运的是, RE类型还实现了Alternative 这意味着当一个失败时,我们可以使用<|>运算符来确定另一个解析器。 让我们看一下实际情况:

parseFeatureTitle :: RE Char String
parseFeatureTitle = string “Feature: “ *> readThroughEndOfLine
parseScenarioTitle :: RE Char String
parseScenarioTitle = string “Scenario: “ *> readThroughEndOfLine
parseEither :: RE Char String
parseEither = parseFeatureTitle <|> parseScenarioTitle
>> match parseFeatureTitle “Feature: Login\n”
Just “Login”
>> match parseFeatureTitle “Scenario: Login\n”
Nothing
>> match parseEither “Scenario: Login\n”
Just “Login”

当然,如果所有选项都失败,那么解析器仍然会失败!

>> match parseEither “Random: Login\n”
Nothing

我们将需要它来将某些级别的选择引入我们的解析系统。 例如,由用户决定是否将Background作为其功能的一部分。 因此,我们需要能够读取背景(如果有的话),否则就可以解析场景。

结论

到此结束了对应用程序解析的基本组合器的介绍。 下周,我们将介绍在这里开发的所有内容,并将它们用于Gherkin语法本身。 到目前为止,一切似乎都很小。 但是,我们将看到,一旦具备基本要素,我们就能非常迅速地建立结果!

如果您想查看更多对重要的Haskell任务有用的库,请查看我们的生产清单 它将向您介绍一些用于解析的库,数据库,API等等!

如果您是Haskell的新手,那就没有更好的开始时间了! 下载我们的免费初学者清单 它将帮助您下载正确的工具并开始学习该语言。

From: https://hackernoon.com/applicative-parsing-i-building-the-foundation-23e8a9c2fb5d