在 Happy 和 Alex 中推回令牌

Pushing back tokens in Happy and Alex

我正在解析一种同时具有 <<< 的语言。在我的亚历克斯定义中,我得到了包含类似

的东西
tokens :-

"<"             { token Lt }
"<<"            { token (BinOp Shl) }

所以每当我遇到 << 时,它都会被标记为左移, 而不是 小于的。这通常是一件好事,因为我最终在标记化后扔掉了空格并想区分 1 < < 21 << 2。但是,有时我希望 << 被读作两个 <。例如,我有

<<A>::B> 

我想这样读

< < A > :: B >

显然,我可以尝试调整我的 Happy 解析器规则以适应额外的情况,但这很难扩展。在其他命令式解析器生成器中,我可能会尝试做一些事情,比如推回令牌的 "part"(当我遇到 << 但我只需要 < 时,就像 push_back("<") 一样)。

有没有其他人遇到过这样的问题,如果有,你是如何处理的?兴欣有没有"pushing back"代币的获取方式?我是否应该尝试保留一个空白标记(我实际上倾向于最后一个选择 - 尽管非常头疼,它会让我通过确保两者之间没有空白来处理 << <).

我不知道如何在 Happy 中表达这一点,但你不需要单独的“空白”标记。您可以将 <> 解析为一个独特的“尖括号”标记,当输入中紧跟一个运算符符号时,中间没有空格。

然后,当您想要解析一个运算符时,您可以将一系列角度和运算符连接到一个标记中。当你想把它们当作括号时,你只需照常单独处理即可。

所以 a << b 将被标记为:

identifier "a"
left angle      -- joined with following operator
operator "<"
identifier "b"

解析运算符时,将角度标记与以下运算符标记连接起来,生成单个 operator "<<" 标记。

<<A>::B> 将被标记为:

left angle
operator "<"    -- accepted as bracket
identifier "A"
right angle
operator "::"
identifier "B"
operator ">"    -- accepted as bracket

解析尖括号术语时,您接受尖角标记和 </> 运算符。

这取决于您的语法没有歧义。您是否应该解析运算符名称或括号中的内容。

虽然我最初接受了@Jon 的回答,但我最终 运行 陷入了各种与优先级相关的问题(考虑 expr < exprexpr << expr 之间的优先级),这给我带来了很多麻烦头痛。我最近(成功地)回到了词法 << 作为一个标记。解决方案有两个:

  1. 我硬着头皮为 << 添加了额外的规则(之前我只有 < 的规则)。对于问题 (<<A>::B>) 中的示例,我的规则来自

    ty_qual_path
      : '<' ty_sum '>' '::' ident
    

    ty_qual_path
      : '<' ty_sum '>' '::' ident
      | '<<' ty_sum '>' '::' ident '>' '::' ident
    

    (实际规则实际上有点复杂,但这不是这个答案)。

  2. 我找到了一种巧妙的方法来处理以 > 开头的令牌(这会导致 vector<i32,vector<i32>> 之类的问题,其中最后一个 >> 是令牌):使用 threaded lexer (section 2.5.2), exploit the {%% ... } RHS of rules which lets you reconsider the lookahead token, and add a pushToken facility to my parser monad (this turned out to be quite simple - here is exactly what I did)。然后我添加了一个虚拟规则 - 比如

    gt :: { () }
      : {- empty -}   {%% \tok ->
          case tok of
            Tok ">>"  -> pushToken (Tok ">")  *> pushToken (Tok ">")
            Tok ">="  -> pushToken (Tok "=")  *> pushToken (Tok ">")
            Tok ">>=" -> pushToken (Tok ">=") *> pushToken (Tok ">")
            _         -> pushToken tok
        }
    

    每次在其他规则中我都期望 > 但也可能有任何其他以 > 开头的标记,我会在 > 标记之前加上 gt.这具有向前看可能以 > 开头而不是 > 的下一个标记的效果,并尝试将该标记转换为一个 > 标记和 [= 的另一个标记50=] 的初始令牌。