Parsing 解决LALR歧义
我最近对LALR的研究已经足够深入,可以编写一个语法,我正试图为它构建一个java或c风格的语法(其开头是指定的) 我知道编写解析器生成器需要额外的努力,比如重新发明轮子(为什么不使用Antlr?),但我的目标是引导一个业余操作系统,它可以在不依赖第三方工具链的情况下自行编译。我的问题不是生成器,而是语法 我在语句和表达式中遇到了reduce/reduce歧义 我知道如何解决某些类型的歧义,例如悬挂else,但这几个对我来说不是直观的,它们让我感到困惑 解决这些问题的最佳方法是什么?此外,是否有一个原型工具,我可以用来帮助可视化的解决方案?或者,我应该回到原点,尝试为语法实现一个GLR解析器生成器吗 这些声明是合法的:Parsing 解决LALR歧义,parsing,compiler-construction,grammar,lalr,ambiguous-grammar,Parsing,Compiler Construction,Grammar,Lalr,Ambiguous Grammar,我最近对LALR的研究已经足够深入,可以编写一个语法,我正试图为它构建一个java或c风格的语法(其开头是指定的) 我知道编写解析器生成器需要额外的努力,比如重新发明轮子(为什么不使用Antlr?),但我的目标是引导一个业余操作系统,它可以在不依赖第三方工具链的情况下自行编译。我的问题不是生成器,而是语法 我在语句和表达式中遇到了reduce/reduce歧义 我知道如何解决某些类型的歧义,例如悬挂else,但这几个对我来说不是直观的,它们让我感到困惑 解决这些问题的最佳方法是什么?此外,是否有
Generic.List<int> myVar1 = x + 4, myVar2; // stmt -> var-decl ;
// var-decl -> type-name var-decl-list
t = 99; // simple-stmt -> assign
i++; // simple-stmt -> incr-decr
// incr-decr -> primary-expr ++
json.deserialize<list<int>>(obj); // simple-stmt -> call
// call -> primary-expr ( params )
// ... -> primary-expr . basic-name ( params )
// ... -> basic-name . basic-name ( params )
当标识符移位时,下一个状态为:
basic-name -> ident *
basic-name -> ident * < type-list >
潜在冲突。在父状态下,lookaheads没有帮助,因为嵌套名称
和主表达式
中有一个点。哦,太好了,让我们试着用嵌套名称来减少:
type-name -> nested-name *
nested-name -> nested-name * . basic-name
这里没什么可看的。。。
现在,用primary expr
来降低成本怎么样:
unary-expr -> primary-expr *
primary-expr -> primary-expr * . basic-name
primary-expr -> primary-expr * ++
call -> primary-expr * ( params )
incr-decr -> primary-expr * ++
...
现在,当我们使用shift++时,我们得到:
primary-expr -> primary-expr ++ *
incr-decr -> primary-expr ++ *
…另一种方法是减少冲突
让我们尝试移动(
而不是标识
:
primary-expr -> ( * expr )
unary-op -> ( * type-name )
expr -> * assign-expr
assign-expr -> * assign
assign-expr -> * cond-expr
assign -> * unary-expr assign-op expr
unary-expr -> * unary-op primary-expr
unary-expr -> * primary-expr
unary-op -> * ( typename )
unary-op -> * ! | ~ | ...
primary-expr -> * call
primary-expr -> * primary-expr . basic-name
primary-expr -> * primary-expr ++
primary-expr -> * basic-name
primary-expr -> * ( expr )
call -> * primary-expr ( params )
cond-expr -> * ...
...
rel-expr -> * rel-expr < shift-expr
rel-expr -> * shift-expr
...
type-name -> * nested-name
type-name -> * basic-type
nested-name -> * nested-name . basic-name
nested-name -> * basic-name
basic-name -> * ident < type-list >
basic-name -> * ident
primary expr->(*expr)
一元运算->(*类型名称)
expr->*分配expr
分配表达式->*分配
分配expr->*条件expr
赋值->*一元表达式赋值运算表达式
一元表达式->*一元运算主表达式
一元表达式->*主表达式
一元运算->*(类型名称)
一元运算->*!| ~|。。。
主expr->*呼叫
主表达式->*主表达式基本名称
主表达式->*主表达式++
主表达式->*基本名称
主表达式->*(表达式)
调用->*主表达式(参数)
条件表达式->*。。。
...
rel expr->*rel expr*移位expr
...
类型名称->*嵌套名称
类型名称->*基本类型
嵌套名称->*嵌套名称。基本名称
嵌套名称->*基本名称
基本名称->*标识<类型列表>
基本名称->*标识
将标识
或(
移动到堆栈上时也会出现同样的问题
这些就是我到目前为止遇到的问题。由于basic name
优先于rel expr
,我假设x
之类的东西会被解释为basic name->ident
,如果它实际上是一个关系表达式,就会出错
我的大脑已经到了需要帮助的地步。你的帖子中有几个问题,这让我觉得不是很理想。但我会尽力为每个问题提供一些想法。在我看来,你有三个问题:
rel expr
在这么多步骤中派生出basic name
,我是说它有更高的优先级。这对我来说没有任何意义。事实上,a派生出B对LR算法的移位或缩减决定没有影响。@rici可能我还不完全理解。因为lookahe只有一个符号ad,为了移动rel expr的。如果我们这样做,即使完整输入是rel expr*
x()
,我们已经减少到rel-expr
:rel-expr*()
,我们无法从接下来的3个令牌中获取
,因为要做到这一点,我们必须在堆栈上获取基本名称->标识*
,而不是基本名称->rel expr*
。这当然是一个移位-减少冲突。甚至可能是一个歧义。
unary-expr -> primary-expr *
primary-expr -> primary-expr * . basic-name
primary-expr -> primary-expr * ++
call -> primary-expr * ( params )
incr-decr -> primary-expr * ++
...
primary-expr -> primary-expr ++ *
incr-decr -> primary-expr ++ *
primary-expr -> ( * expr )
unary-op -> ( * type-name )
expr -> * assign-expr
assign-expr -> * assign
assign-expr -> * cond-expr
assign -> * unary-expr assign-op expr
unary-expr -> * unary-op primary-expr
unary-expr -> * primary-expr
unary-op -> * ( typename )
unary-op -> * ! | ~ | ...
primary-expr -> * call
primary-expr -> * primary-expr . basic-name
primary-expr -> * primary-expr ++
primary-expr -> * basic-name
primary-expr -> * ( expr )
call -> * primary-expr ( params )
cond-expr -> * ...
...
rel-expr -> * rel-expr < shift-expr
rel-expr -> * shift-expr
...
type-name -> * nested-name
type-name -> * basic-type
nested-name -> * nested-name . basic-name
nested-name -> * basic-name
basic-name -> * ident < type-list >
basic-name -> * ident
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
Expression:
LambdaExpression
AssignmentExpression
AssignmentExpression:
ConditionalExpression
Assignment
Assignment:
LeftHandSide AssignmentOperator Expression
...
UnaryExpression:
PreIncrementExpression
+ UnaryExpression
UnaryExpressionNotPlusMinus
PreIncrementExpression:
++ UnaryExpression
UnaryExpressionNotPlusMinus:
PostfixExpression
~ UnaryExpression
PostfixExpression:
Primary
ExpressionName
PostIncrementExpression
PostIncrementExpress:
PostfixExpression ++
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
primary-expr : field-access-expr
| nested-name
non-field-access-expr:
call
| post-increment-expression // was primary-expr ++
| ( expr )
...
field-access-expr :
non-field-access-expr
| field-access-expr . basic-name
ConstructorDeclarator:
[TypeParameters] SimpleTypeName ( [FormalParameterList] )
MethodInvocation:
Primary . [TypeArguments] Identifier ( [ArgumentList] )