首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是什么导致快乐抛出一个解析错误?

是什么导致快乐抛出一个解析错误?
EN

Stack Overflow用户
提问于 2015-08-13 19:06:56
回答 1查看 878关注 0票数 2

我用Alex编写了一个lexer,并试图将它连接到一个用“快乐”编写的解析器。我将尽力总结我的问题,而不粘贴大量代码块。

从我对lexer的单元测试中,我知道字符串"\x7"是按以下顺序排列的:

代码语言:javascript
复制
[TokenNonPrint '\x7', TokenEOF]

我的令牌类型(由lexer发出)是Token。我已经将lexWrapalexEOF定义为描述了here,它为我提供了以下头部和令牌声明:

代码语言:javascript
复制
%name parseTokens 
%tokentype { Token }
%lexer { lexWrap } { alexEOF }
%monad { Alex }
%error { parseError }

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

我使用以下方法调用parser+lexer组合:

代码语言:javascript
复制
parseExpr :: String -> Either String [Expr]
parseExpr s = runAlex s parseTokens

以下是我的第一批作品:

代码语言:javascript
复制
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 }

我将省略ExprNonPrint的数据类型声明,因为它们是长的,只有构造函数CmdNonPrint才重要。函数parseNonPrint在Parse.y的底部定义为:

代码语言:javascript
复制
parseNonPrint :: Char -> NonPrint
parseNonPrint '\x7' = Bell

此外,我的错误处理函数看起来类似于:

代码语言:javascript
复制
parseError :: Token -> Alex a
parseError tokens = error ("Error processing token: " ++ show tokens)

这样编写,我希望下面的hspec测试能够通过:

代码语言:javascript
复制
parseExpr "\x7" `shouldBe` Right [Cmd (NonPrint Bell)]

但是,我只看到了一次"exprs 30"打印(尽管我正在运行5个不同的单元测试)和parseExpr返回Right []的所有测试。我不明白为什么会出现这种情况,但我更改了exprs产品以防止这种情况发生:

代码语言:javascript
复制
exprs :: { [Expr] }
exprs
  : expr { trace "exprs 30" [$1] }
  | exprs expr { trace "exprs 31" $ $2 : $1 }

现在,我所有的测试都在第一个标记上失败-- parseExpr "\x7"失败了:

代码语言:javascript
复制
uncaught exception: ErrorCall (Error processing token: TokenNonPrint '\a')

我完全糊涂了,因为我希望解析器走上exprs -> expr -> nonprint -> NONPRINT的道路并成功。我不明白为什么这个输入会使解析器处于错误状态。没有一个trace语句被击中(优化离开?)。

我做错了什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-08-14 06:19:12

事实证明,这个错误的原因是无伤大雅的线条。

代码语言:javascript
复制
%lexer { lexWrap } { alexEOF }

这是由关于在快乐中使用Alex的链接问题推荐的(不幸的是,Google的查询结果之一是“使用Alex作为带有快乐的一元使用的词汇”)。

代码语言:javascript
复制
%lexer { lexWrap } { TokenEOF }

我不得不深入研究生成的代码来发现这个问题。这是由%tokens指令派生的代码引起的,如下所示(除了TokenNonPrint之外,我注释掉了所有令牌声明,同时试图跟踪错误):

代码语言:javascript
复制
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指令的每一行转换为模式匹配的一个分支。它还插入一个分支,用于在%lexer指令中标识为EOF令牌的任何内容。

通过插入值alexEOF的名称,而不是数据构造函数TokenEOF,case语句的这个分支具有将名称alexEOF重新绑定到传递给lexWrap的任何令牌的效果,从而隐藏原来的绑定并短路case语句,使其每次都命中EOF规则,从而在某种程度上导致愉快进入错误状态。

类型系统不会捕捉到错误,因为在生成的代码中,标识符alexEOF (或TokenEOF)没有出现在任何其他地方。这样误用%lexer指令会导致GHC发出警告,但是,由于该警告出现在生成的代码中,因此不可能将其与代码抛出的所有其他无害警告区分开来。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31996489

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档