1

我正在尝试为一种编程语言实现一个解释器,最后偶然发现了一个我需要回溯的情况,但是我的解析器生成器(ply,一个用 Python 编写的 lex&yacc 克隆)不允许这是涉及的规则:

'var_access_start : super'
'var_access_start : NAME'
'var_access_name : DOT NAME'
'var_access_idx : OPSQR expression CLSQR'
'''callargs : callargs COMMA expression
            | expression
            | '''
'var_access_metcall : DOT NAME LPAREN callargs RPAREN'
'''var_access_token : var_access_name
                    | var_access_idx
                    | var_access_metcall'''
'''var_access_tokens : var_access_tokens var_access_token
                     | var_access_token'''
'''fornew_var_access_tokens : var_access_tokens var_access_name
                            | var_access_tokens var_access_idx
                            | var_access_name
                            | var_access_idx'''
'type_varref : var_access_start fornew_var_access_tokens'
'hard_varref : var_access_start var_access_tokens'
'easy_varref : var_access_start'
'varref : easy_varref'
'varref : hard_varref'
'typereference : NAME'
'typereference : type_varref'
'''expression : new typereference LPAREN callargs RPAREN'''

'var_decl_empty : NAME'
'var_decl_value : NAME EQUALS expression'
'''var_decl : var_decl_empty
            | var_decl_value'''
'''var_decls : var_decls COMMA var_decl
             | var_decl'''
    'statement : var var_decls SEMIC'

错误发生在表单的语句中

var x = new SomeGuy.SomeOtherGuy();

其中 SomeGuy.SomeOtherGuy 将是存储类型的有效变量(类型是第一类对象)-并且该类型具有不带参数的构造函数

解析该表达式时发生的情况是解析器构造了一个 var_access_start = SomeGuy var_access_metcall = 。SomeOtherGuy ( ) 然后找到一个分号并以错误状态结束 - 我显然希望解析器回溯,并尝试构造一个表达式 = new typereference(SomeGuy .SomeOtherGuy) LPAREN empty_list RPAREN 然后事情会起作用,因为 ; 可以匹配 var 语句语法

但是,鉴于 PLY 不支持回溯,而且我绝对没有足够的解析器生成器经验来自己实际实现它——我可以对我的语法进行任何更改来解决这个问题吗?

我考虑过使用 -> 而不是 . 作为“方法调用”运算符,但我宁愿不改变语言只是为了安抚解析器。此外,我将方法作为“变量引用”的一种形式,因此您可以执行 myObject.someMethod().aChildOfTheResult[0].doSomeOtherThing(1,2,3).helloWorld() 但如果可以修改语法以实现同样的效果,这也对我有用

谢谢!

4

1 回答 1

2

我假设您的语言包含的表达方式与您在摘录中包含的表达方式不同。我还将假设 ,new实际上supervar终端。

以下只是粗略的概述。为了便于阅读,我使用bison带引号的文字的语法,但我认为您在转换时不会遇到任何问题。

您说“类型是一等值”,但您的语法明确禁止使用方法调用来返回类型。事实上,它似乎也排除了返回函数的方法调用,但这似乎很奇怪,因为它暗示方法不是一等值,即使类型是。因此,我通过允许以下表达式简化了语法:

 new foo.returns_method_which_returns_type()()()

重新添加限制很容易,但它使说明更难遵循。

基本思想是避免强迫解析器做出过早的决定;一旦new遇到,只能从前瞻令牌中区分方法调用和构造函数调用。所以我们需要确保到那时为止使用相同的归约,这意味着当遇到开括号时,我们仍然必须保留这两种可能性。

primary: NAME
       | "super"
       ;

postfixed: primary
         | postfixed '.' NAME
         | postfixed '[' expression ']'
         | postfixed '(' call_args ')'            /* PRODUCTION 1 */
         ;

expression: postfixed
          | "new" postfixed '(' call_args ')'     /* PRODUCTION 2 */
       /* | other stuff not relevant here */
          ;

 /* Your callargs allows (,,,3). This one doesn't */
call_args : /* EMPTY */
          | expression_list
          ;
expression_list: expression
               | expression_list ',' expression
               ;

/* Another slightly simplified production */
var_decl: NAME
        | NAME '=' expression
        ;

var_decl_list: var_decl
             | var_decl_list ',' var_decl
             ;

statement: "var" var_decl_list ';'
      /* | other stuff not relevant here */
         ;

现在,看看PRODUCTION 1PRODUCTION 2,它们非常相似。(标有注释。)这些基本上是您寻求回溯的模棱两可。但是,在这种语法中,没有问题,因为一旦new遇到 a ,PRODUCTION 2就只能在前瞻记号为,或时进行归约;,而PRODUCTION 1只能用前瞻记号.(进行归约[

(用野牛测试语法,只是为了确保没有冲突。)

于 2014-02-09T03:49:32.420 回答