应用解析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