【问题标题】:Do I have a bug in my grammar, or the parser-generation tool?我的语法或解析器生成工具有错误吗?
【发布时间】:2015-03-14 02:32:46
【问题描述】:

以下是我试图为其生成解析器的 EBNF 格式(主要是 - 实际语法记录在 here)语法:

expr = lambda_expr_list $;

lambda_expr_list = [ lambda_expr_list "," ] lambda_expr;

lambda_expr = conditional_expr [ "->" lambda_expr ];

conditional_expr = boolean_or_expr [ "if" conditional_expr "else" conditional_expr ];

boolean_or_expr = [ boolean_or_expr "or" ] boolean_xor_expr;

boolean_xor_expr = [ boolean_xor_expr "xor" ] boolean_and_expr;

boolean_and_expr = [ boolean_and_expr "and" ] boolean_not_expr;

boolean_not_expr = [ "not" ] relation;

relation = [ relation ( "=="
                      | "!="
                      | ">"
                      | "<="
                      | "<"
                      | ">="
                      | [ "not" ] "in"
                      | "is" [ "not" ] ) ] bitwise_or_expr;

bitwise_or_expr = [ bitwise_or_expr "|" ] bitwise_xor_expr;

bitwise_xor_expr = [ bitwise_xor_expr "^" ] bitwise_and_expr;

bitwise_and_expr = [ bitwise_and_expr "&" ] bitwise_shift_expr;

bitwise_shift_expr = [ bitwise_shift_expr ( "<<"
                                          | ">>" ) ] subtraction_expr;

subtraction_expr = [ subtraction_expr "-" ] addition_expr;

addition_expr = [ addition_expr "+" ] division_expr;

division_expr = [ division_expr ( "/"
                                | "\\" ) ] multiplication_expr;

multiplication_expr = [ multiplication_expr ( "*"
                                            | "%" ) ] negative_expr;

negative_expr = [ "-" ] positive_expr;

positive_expr = [ "+" ] bitwise_not_expr;

bitwise_not_expr = [ "~" ] power_expr;

power_expr = slice_expr [ "**" power_expr ];

slice_expr = member_access_expr { subscript };

subscript = "[" slice_defn_list "]";

slice_defn_list = [ slice_defn_list "," ] slice_defn;

slice_defn = lambda_expr
           | [ lambda_expr ] ":" [ [ lambda_expr ] ":" [ lambda_expr ] ];

member_access_expr = [ member_access_expr "." ] function_call_expr;

function_call_expr = atom { parameter_list };

parameter_list = "(" [ lambda_expr_list ] ")";

atom = identifier
     | scalar_literal
     | nary_literal;

identifier = /[_A-Za-z][_A-Za-z0-9]*/;

scalar_literal = float_literal
               | integer_literal
               | boolean_literal;

float_literal = point_float_literal
              | exponent_float_literal;

point_float_literal = /[0-9]+?\.[0-9]+|[0-9]+\./;

exponent_float_literal = /([0-9]+|[0-9]+?\.[0-9]+|[0-9]+\.)[eE][+-]?[0-9]+/;

integer_literal = dec_integer_literal
                | oct_integer_literal
                | hex_integer_literal
                | bin_integer_literal;

dec_integer_literal = /[1-9][0-9]*|0+/;

oct_integer_literal = /0[oO][0-7]+/;

hex_integer_literal = /0[xX][0-9a-fA-F]+/;

bin_integer_literal = /0[bB][01]+/;

boolean_literal = "true"
                | "false";

nary_literal = tuple_literal
             | list_literal
             | dict_literal
             | string_literal
             | byte_string_literal;

tuple_literal = "(" [ lambda_expr_list ] ")";

list_literal = "[" [ ( lambda_expr_list
                     | list_comprehension ) ] "]";

list_comprehension = lambda_expr "for" lambda_expr_list "in" lambda_expr [ "if" lambda_expr ];

dict_literal = "{" [ ( dict_element_list
                     | dict_comprehension ) ] "}";

dict_element_list = [ dict_element_list "," ] dict_element;

dict_element = lambda_expr ":" lambda_expr;

dict_comprehension = dict_element "for" lambda_expr_list "in" lambda_expr [ "if" lambda_expr ];

string_literal = /[uU]?[rR]?(\u0027(\\.|[^\\\r\n\u0027])*\u0027|\u0022(\\.|[^\\\r\n\u0022])*\u0022)/;

byte_string_literal = /[bB][rR]?(\u0027(\\[\u0000-\u007F]|[\u0000-\u0009\u000B-\u000C\u000E-\u0026\u0028-\u005B\u005D-\u007F])*\u0027|\u0022(\\[\u0000-\u007F]|[\u0000-\u0009\u000B-\u000C\u000E-\u0021\u0023-\u005B\u005D-\u007F])*\u0022)/;

我用来生成解析器的工具是Grako,它生成了一个修改过的Packrat解析器,声称支持直接和间接左递归。

当我在这个字符串上运行生成的解析器时:

input.filter(e -> e[0] in ['t', 'T']).map(e -> (e.len().str(), e)).map(e -> '(Line length: ' + e[0] + ') ' + e[1]).list()

我收到以下错误:

grako.exceptions.FailedParse: (1:13) Expecting end of text. :
input.filter(e -> e[0] in ['t', 'T']).map(e -> (e.len().str(), e)).map(e -> '(Line length: ' + e[0] + ') ' + e[1]).list()
            ^
expr

调试表明解析器似乎到达了第一个 e[0] 的末尾,然后永远不会回溯到/到达尝试匹配 in 令牌的点。

我的语法是否存在一些问题,以至于支持左递归的 Packrat 解析器会失败?还是我应该在 Grako 问题跟踪器上提交问题?

【问题讨论】:

  • 你能简化语法和语句来重现问题吗?

标签: python parsing grammar grako


【解决方案1】:

这可能是语法错误,但错误消息并没有告诉您它实际发生在哪里。完成语法后,我总是在其中嵌入 cut (~) 元素(在 if、运算符、左括号等关键字之后,在任何看起来合理的地方) .

cut 元素使 Grako 生成的解析器提交到解析树中最接近的选项中的选项。这样,解析器不会在 if 开始时失败,而是会在实际上无法解析的表达式处报告失败。

语法中的一些错误很难发现,为此我只是通过解析跟踪来找出解析器在输入中走了多远,以及为什么它决定不能走得更远。

我不会将 PEG 解析器上的左递归用于专业工作,尽管它可能适用于更简单的学术工作。

boolean_or_expr = boolean_xor_expr {"or" boolean_xor_expr};

然后可以在语义操作中处理关联性。

另请参阅issue 49 下针对 Grako 的讨论。它表示用于支持左递归的算法并不总是在结果 AST 中产生预期的关联性。

【讨论】:

  • 感谢有关剪切元素的提示。我怀疑这可能也会加快解析速度,因为现在它很慢。
  • 也就是说,如果我想保持语法规则左递归,我可能会使用自下而上的解析器。关于您关于关联性的观点 - 我不禁想知道如果不保留规则的关联性,让 Packrat 支持左递归有什么意义。直接说“你需要一个自下而上的解析器”不是更简单吗?
  • @Trey 左递归使得为某些语言编写语法变得更容易。缺乏关于关联性的保证不是设计的,而是所使用的算法的结果,以及研究的主题。另请注意,您想要的关联性可能会针对您的特定语法保留。
  • 对此延迟表示歉意;我一直在忙于Uni。感谢您的澄清。很高兴知道。 :) 我重构了语法,按照您的建议添加了剪切元素(新语法可在 pastebin.com/eykU3V9K 获得)。
  • @Tray 您能否将此作为问题发布到Bitbucket。我会看看下一次通过 Grako 的通道。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 2011-11-15
  • 1970-01-01
  • 2016-02-09
相关资源
最近更新 更多