Parsing 是什么导致Happy抛出解析错误?
我已经用Alex编写了一个lexer,我正在尝试将它连接到用Happy编写的解析器。我将尽最大努力总结我的问题,而不粘贴大量代码 我从我的lexer的单元测试中知道字符串Parsing 是什么导致Happy抛出解析错误?,parsing,haskell,happy,alex,Parsing,Haskell,Happy,Alex,我已经用Alex编写了一个lexer,我正在尝试将它连接到用Happy编写的解析器。我将尽最大努力总结我的问题,而不粘贴大量代码 我从我的lexer的单元测试中知道字符串“\x7”被lexed为: [TokenNonPrint '\x7', TokenEOF] 我的令牌类型(由lexer吐出)是token。如前所述,我定义了lexWrap和alexEOF,这为我提供了以下头和令牌声明: %name parseTokens %tokentype { Token } %lexer { lexWr
“\x7”
被lexed为:
[TokenNonPrint '\x7', TokenEOF]
我的令牌类型(由lexer吐出)是token
。如前所述,我定义了lexWrap
和alexEOF
,这为我提供了以下头和令牌声明:
%name parseTokens
%tokentype { Token }
%lexer { lexWrap } { alexEOF }
%monad { Alex }
%error { parseError }
%token
NONPRINT {TokenNonPrint $$}
PLAIN { TokenPlain $$ }
我使用以下命令调用parser+lexer组合:
parseExpr :: String -> Either String [Expr]
parseExpr s = runAlex s parseTokens
%lexer { lexWrap } { TokenEOF }
以下是我的前几部作品:
exprs :: { [Expr] }
exprs
: {- empty -} { trace "exprs 30" [] }
| exprs expr { trace "exprs 31" $ $2 : $1 }
nonprint :: { Cmd }
: NONPRINT { NonPrint $ parseNonPrint $1}
expr :: { Expr }
expr
: nonprint {trace "expr 44" $ Cmd $ $1}
| PLAIN { trace "expr 37" $ Plain $1 }
我将省略Expr
和NonPrint
的数据类型声明,因为它们很长,只有构造函数Cmd
和NonPrint
在这里起作用。函数parseNonPrint
在Parse.y的底部定义为:
parseNonPrint :: Char -> NonPrint
parseNonPrint '\x7' = Bell
此外,我的错误处理函数如下所示:
parseError :: Token -> Alex a
parseError tokens = error ("Error processing token: " ++ show tokens)
这样写,我希望以下hspec测试能够通过:
parseExpr "\x7" `shouldBe` Right [Cmd (NonPrint Bell)]
但是相反,我看到“exprs 30”
打印一次(即使我正在运行5个不同的单元测试),并且parsexpr
的所有测试都返回Right[]
。我不明白为什么会出现这种情况,但我更改了exprs
产品以防止出现这种情况:
exprs :: { [Expr] }
exprs
: expr { trace "exprs 30" [$1] }
| exprs expr { trace "exprs 31" $ $2 : $1 }
现在,我的所有测试在它们命中的第一个令牌上失败---parsexpr“\x7”失败,原因是:
uncaught exception: ErrorCall (Error processing token: TokenNonPrint '\a')
我完全搞糊涂了,因为我希望解析器采用路径exprs->expr->nonprint->nonprint->nonprint
并成功。我不明白为什么这个输入会使解析器处于错误状态。没有任何跟踪
语句被命中(优化了?)
我做错了什么?事实证明,造成此错误的原因是无害的线路
%lexer { lexWrap } { alexEOF }
这是关于将Alex与Happy一起使用的链接问题(不幸的是,“将Alex用作Happy的一元词汇量”等查询的谷歌最佳结果之一)推荐的。修复方法是将其更改为以下内容:
parseExpr :: String -> Either String [Expr]
parseExpr s = runAlex s parseTokens
%lexer { lexWrap } { TokenEOF }
我必须深入研究生成的代码以发现问题。这是由派生自%tokens
指令的代码引起的,该指令如下所示(我在试图跟踪错误时注释掉了我所有的令牌声明,除了TokenNonPrint
):
显然,Happy将%tokens
指令的每一行转换为模式匹配的一个分支。它还为%lexer
指令中标识为EOF令牌的任何内容插入一个分支
通过插入值的名称alexEOF
,而不是数据构造函数TokenEOF
,case语句的此分支可以将名称alexEOF
重新绑定到传递到lexWrap
的任何令牌,隐藏原始绑定并短路case语句,使其命中每次都是EOF规则,这会导致Happy进入错误状态
由于标识符
alexEOF
(或TokenEOF
),因此类型系统不会捕捉到错误不会出现在生成的代码中的任何其他地方。像这样误用%lexer
指令会导致GHC发出警告,但是,由于警告出现在生成的代码中,因此无法将其与代码抛出的所有其他无害警告区分开来。您能给我们指出代码吗?即github repo?@user5402有所有的代码。目前有点草率,尤其是测试的标签。