【问题标题】:Parser: Distinguish parenthesis and function declaration in JavaScript like grammar解析器:像语法一样区分 JavaScript 中的括号和函数声明
【发布时间】:2018-07-06 04:25:32
【问题描述】:

我正在开发一个使用 OCaml 和 Menhir 作为解析器和词法分析器的编译器。当我编写像语法这样的 JavaScript 时,使用 (a, b) => a + b 像 lambda 函数定义以及带有括号优先子表达式的算术 (a + b) * c,我写了

expr
: x = ID { EId(x) }
...
| LPAREN; e = expr; RPAREN; { e }
...
| LPAREN; args = separated_list(COMMA, ID); RPAREN; ARROW; body = expr; { EFunction(args, body) }

parser.mly

只需添加更多上下文,我的lexer.mll 如下:

let letter = ['a'-'z' 'A'-'Z']
let lud = ['a'-'z' 'A'-'Z' '_' '0'-'9']
let id = letter lud*

rule read = parse
    ...
    | "(" { LPAREN }
    | ")" { RPAREN }
    | "," { COMMA }
    | "=>" { ARROW }
    ...
    | id { ID (Lexing.lexeme lexbuf) }
    ...

但这会在编译时出现reduce/reduce错误:

Error: do not know how to resolve a reduce/reduce conflict between the following two productions:
expr -> ID
separated_nonempty_list(COMMA,ID) -> ID

我知道问题可能是由这两者之间的歧义引起的:(a)(a) => a(one-arg-function)。但是我仍然不明白为什么它仍然会产生这个错误,因为对我来说,非常清楚的是括号后面跟着一个粗箭头 => 将是一个函数......

有人可以帮我解决这个问题吗?

【问题讨论】:

    标签: parsing ocaml grammar menhir


    【解决方案1】:

    但我仍然无法理解为什么它仍然会产生这个错误,因为对我来说非常清楚的是括号后面跟着一个粗箭头 => 将成为一个函数......

    是的,非常清晰。语法是完全明确的。但是您不限于一次查看输入一个标记,而 LR(1) 解析器则可以。当解析器试图决定如何处理(a) 中的a 时,它还看不到胖箭头,它必须在它之前做出决定。也就是说,在使用 ) 之前,解析器需要确定它之前的内容是 expr 还是 separated_nonempty_list

    可能值得注意的是,语法实际上是 LR(2):多了一个前瞻标记,冲突可以解决。这并不是什么安慰,因为 Menhir 没有提供任何增加前瞻的机制,但这确实意味着存在解决方案,因为一种语言的 LR(k) 语法的存在意味着相同语言的 LR(1) 语法的存在语言;甚至还有一种生成 LR(1) 语法的机械算法。

    除了转换整个语法之外,唯一有点混乱的解决方案是隔离 '(a)` 的情况,这可以通过一对明显冗余的规则来完成:

    expr: LPAREN ID RPAREN ARROW expr
        | LPAREN ID RPAREN
    

    第二个产生式显然与LPAREN expr RPAREN 冲突,但它是移位/归约冲突而不是归约/归约冲突,它可以通过赋予RPARENID 更高的优先级来解决,以便强制解决赞成转移RPAREN

    这是对优先级声明的完全颠倒,而且随着您的语法变得越来越复杂,它很可能会出现问题。一个理论上更合理的解决方案是定义expr_other_than_identifier。您可以找到 here 的示例,这是一个非常相似的语法,并且在其他一些类似问题的 SO 答案中。

    特别是,Yacc 自己的语法是 LR(2)(在您看到启动下一个规则的非终结符之后的 : 之前,您无法判断规则已经结束)。该语法存在类似的解决方案,但大多数类似 yacc 的解析器生成器通过将额外的前瞻推入词法分析器来解决问题。例如,您可以将 ) => 识别为单个标记(包括内部空格),或者如果下一个标记是粗箭头,您可以发出不同的右括号标记。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-06
      • 1970-01-01
      • 1970-01-01
      • 2020-12-15
      • 2012-04-07
      • 2022-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多