Parsing 在解析器组合器中组合lexer和parser

Parsing 在解析器组合器中组合lexer和parser,parsing,haskell,parser-combinators,uu-parsinglib,Parsing,Haskell,Parser Combinators,Uu Parsinglib,我使用的是uu parsinglib,但我认为下面的问题是parser combinator generic 我们考虑下面的例子: 我有一个lexer,带有一个combinatorpLex,它生成一个令牌列表(类型为MyToken)。现在我想编写一个解析器,它将使用令牌并构建一个AST 连接lexer和解析器的最佳方式是什么?现在我有一个lex函数: lex s = parse ( (,) <$> pLex <*> pEnd) (createStr (LineColPos

我使用的是
uu parsinglib
,但我认为下面的问题是parser combinator generic

我们考虑下面的例子:

我有一个lexer,带有一个combinator
pLex
,它生成一个令牌列表(类型为
MyToken
)。现在我想编写一个解析器,它将使用令牌并构建一个
AST

连接lexer和解析器的最佳方式是什么?现在我有一个
lex
函数:

lex s = parse ( (,) <$> pLex <*> pEnd) (createStr (LineColPos 0 0 0) s)
lex s=parse((,)pLex pEnd)(createStr(LineColPos 0)s)

我应该创建一个函数
parse p=…
?如果是,我如何构造它来跟踪lexer中的列和行?或者我应该创建一个
parsercompbinator
,以某种方式使用
pLex
组合器吗?

基于表的解析器需要分离词法分析和解析,因为它们的前瞻能力有限。展望未来,将词法分析结合到解析器中会使状态空间爆炸

基于组合器的方法通常不会遇到这个问题,因为它们通常会进行递归下降解析。除非图书馆作者另有说明,否则组合这些阶段是无害的,分离它们也不会有什么好处

尽管uu parsinglib提供了
Str
类来对不同的字符串类输入进行抽象,但查看其定义表明,它仍然假设您最终读取的是字符序列,无论它们来自字符串、ByteString、文本、,因此,试图让它解析MyToken流似乎很困难。如果您觉得有必要这样做,那么Parsec可能是一个更好的选择

至于您关于字符串实现的问题,组合器接受包含语法结构的类似字符串的输入,并返回相应的语义值(如果匹配)。在combinator内部,您可以通过直接从输入流中提取并组合您调用的子combinator的语义值,从您解析的内容中构建语义值

因此,您的示例中的“字符串匹配”组合器在其作用域中将有一个令牌列表,这要感谢它所做的解析。您可以使用Haskell的全部功能,以任何对您的语言有意义的方式将这些标记组合成一个MyString值:可能是一个“拼接字符串”类型,表示要将哪些值切片到其中


字符串组合器可能由“表达式”组合器调用,该组合器将能够将MyString值与其他解析值组合成MyExpression值。这是组合器返回语义值的过程

我认为uu parsinglib中没有任何东西可以阻止您使用与文本不同的输入。只是对于文本(和朋友),我们提供了一些您可能需要的功能。如果您查看较旧的uulib解析器组合器,您将发现一种基于扫描程序的方法,它同样可以用于较新的uu parsinglib


如果你想处理大量的数据,最好有单独的扫描阶段。错误消息往往更具信息性。在uulib中,您会发现一些对编写扫描仪的支持(大多数语言对词汇结构有一些特殊的限制/要求,很多工具将(失败/需要调整)在我的手机上创建扫描仪(例如越位规则)

,所以我现在只想指出:在BasicInstances模块中有Str type和createStr,可以用来创建令牌流。其他解析组合器库有时也包含词法分析。有关这一点,请参见Parsec或parsers中的令牌解析模块。通常,这就是使用解析器组合器的方向。但是你总是可以把你杠杆的输出像[MyToken]一样输入一个令牌解析器。@J.Abrahamson:我有很大的语法,所以我不知道使用lexer和解析器是否是一个好主意(但也许使用解析器组合器这是一种方式?)我有一个问题,因为我的组合器“pString”返回令牌列表(因为在我的语言中,stirngs cna有一些变量,如
“test:$name”
。所以通常我会编写一些“end”combinators,它产生一系列标记,然后我将它们相加。这是一种糟糕的方法吗?@J.Abrahamson:如果您有一些必须返回标记列表的combinators,请告诉我如何设计解析器,我将非常感激(如上面的字符串或pNewline,它应该返回
Newline
Indent Int
(新行缩进)标记)。标记流可以工作,但可能有更好的方法?您可以在主解析期间生成的每个标记流上触发子parser。因此如果
tokenizeSegment::P string[MyToken]
parseSegment::P[MyToken]SegmentAST
然后执行类似于
parseSegment-tokenizeSegment::P-String的操作(ParseError-SegmentAST)
。您也可以通过解构
并将
ParseError
传递给主解析器来处理该错误。感谢您的回答-似乎我不应该将解析步骤分为词法分析和解析。我一直觉得创建“lexer/toeniser”和“parser”非常“纯粹”,但现在我正在慢慢改变主意:)将它们进行一定程度的划分仍然是有意义的,将标记匹配组合符放在一个模块中,将解析组合符放在另一个模块中。但是,将解析器构建到词素级别的有趣之处在于,您可以根据解析上下文进行不同的词法分析。例如,您的特殊字符串可以使用一组不同的组合词对字符串内部的拼接进行词法分析。谢谢:)顺便问一下,你知道在
uu parsingli中是否有创建“状态”的简单方法吗