2015-08-13 67 views
2

我已经在Alex编写了一个词法分析器,我试图将它连接到一个用Happy编写的解析器。我会尽我所能在不粘贴大量代码的情况下总结我的问题。是什么原因导致Happy抛出解析错误?

从我的词法分析器的我的单元测试,字符串"\x7"被lexed给我知道:

[TokenNonPrint '\x7', TokenEOF] 

我的令牌类型(词法分析器吐出),是Token。我定义lexWrapalexEOF描述here,这给了我下面的头和令牌声明:

%name parseTokens 
%tokentype { Token } 
%lexer { lexWrap } { alexEOF } 
%monad { Alex } 
%error { parseError } 

%token 
    NONPRINT {TokenNonPrint $$} 
    PLAIN { TokenPlain $$ } 

我调用与以下解析器+词法组合:

parseExpr :: String -> Either String [Expr] 
parseExpr s = runAlex s parseTokens 

这里是我的前几个作品:

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和的数据类型声明,因为它们很长,只有构造函数CmdNonPrint这里很重要。该功能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不同单吨测试)和我所有的测试parseExpr返回Right []。我不明白为什么会是这样,但我改变了exprs产量,以阻止它:

exprs :: { [Expr] } 
exprs 
    : expr { trace "exprs 30" [$1] } 
    | exprs expr { trace "exprs 31" $ $2 : $1 } 

现在我所有的测试失败对他们碰到的第一个令牌--- parseExpr "\x7"失败:

uncaught exception: ErrorCall (Error processing token: TokenNonPrint '\a') 

而且我很困惑,因为我期望解析器能够取得路径exprs -> expr -> nonprint -> NONPRINT并成功。我不明白为什么这个输入会使解析器处于错误状态。没有任何trace语句被击中(优化掉?)。

我在做什么错?

+0

你能指点我们的代码 - 即github回购? – ErikR

+0

@ user5402 https://github.com/pscollins/ansi-parser拥有所有代码。目前有点马虎,特别是测试标签。 –

回答

0

事实证明这个错误的原因是这是推荐的链接的问题有关使用亚历克斯与快乐(无害线

%lexer { lexWrap } { alexEOF } 

不幸的是,前谷歌搜索结果的查询之一,如“使用亚历。与开心单子词法分析器)的解决方法是将其更改为以下:

%lexer { lexWrap } { TokenEOF } 

我不得不掏生成的代码,以发现问题。它是由从%tokens指令,它看起来如下衍生的代码引起的(我评论了我所有的令牌声明除了TokenNonPrint试图追查错误):

happyNewToken action sts stk 
    = lexWrap(\tk -> 
    let cont i = happyDoAction i tk action sts stk in 
    case tk of { 
    alexEOF -> happyDoAction 2# tk action sts stk; -- !!!! 
    TokenNonPrint happy_dollar_dollar -> cont 1#; 
    _ -> happyError' tk 
    }) 

显然,快乐将每个行%tokens指令中的模式匹配的一个分支。它插入一个分支作为EOF标记在%lexer指令。

通过插入值,alexEOF的名称,而不是数据构造,TokenEOF,case语句的这个分支有效果再结合名alexEOF到任何令牌中lexWrap过去了,阴影原绑定和短路case语句,以便它每次都符合EOF规则,这会以某种方式导致Happy进入错误状态。

由于标识码alexEOF(或TokenEOF)未出现在生成的代码中的任何其他位置,错误未被类型系统捕获。以这种方式滥用%lexer指令将导致GHC发出警告,但是,由于警告出现在生成的代码中,因此不可能将其与代码抛出的所有其他无害警告区分开来。

相关问题