2012-12-28 69 views
3

我正在开发一个领域特定的语言。部分语言完全像C表达式解析语义,如精度和符号。如何处理用于两件事情的相同符号柠檬解析器

我正在使用柠檬分析器。我遇到了一个同样的问题,用于两种不同的事情,我无法分辨词法分析器中的差异。 &符号(&)符号用于“按位”和“地址”。

起初我认为这是一个微不足道的问题,直到我意识到它们没有相同的关联性。

我该如何给出两个不同的关联性?我应该只使用AMP(如&符号),并使地址和位和规则使用AMP,或者我应该使用不同的标记(例如ADDRESSOF和BITWISE_AND)。如果我使用单独的符号,我该如何知道词法分析器中的哪一个(它不能不知道,而不是解析器本身!)。

+0

+1为了补偿疼痛,因为你必须亲手写这个。 – 2012-12-28 21:40:47

+0

我不确定从哪里开始。我应该尝试在语法树级别解析它,还是应该尝试在解析器中检测它(例如,通过窥视最近的令牌流)。 – doug65536

+0

在解析器中。 AST必须是明确的。解析器是什么数学和逻辑。 – 2012-12-28 21:57:09

回答

3

如果你打算写规则明确地说,对于每个“优先级”级别使用不同的非终端,那么根本不需要声明优先级,而且您也不应该这样做。

柠檬和所有yacc衍生物一样,使用优先声明来消除模糊语法中的歧义。特定的模糊语法是这样的:

expression: expression '+' expression 
      | expression '*' expression 
      | '&' expression 
      | ... etc, etc. 

在这种情况下,每一个替代方案都会导致转换减少冲突。如果您的解析器发电机没有优先规则,或者你想成为精确,你必须写,作为一个明确的语法(这是你做了什么):

term: ID | NUMBER | '(' expression ')' ; 
postfix_expr:  term | term '[' expression '] | ... ; 
unary_expr:   postfix_expr | '&' unary_expr | '*' unary_expr | ... ; 
multiplicative_expr: unary_expr | multiplicative_expr '*' postfix_expr | ... ; 
additive_expr:  multiplicative_expr | additive_expr '+' multiplicative_expr | ... ; 
... 
assignment_expr:  conditional_expr | unary_expr '=' assignment_expr | ...; 
expression:   assignment_expr ; 
[1] 

注意,明确语法甚至显示了左结合(上面的乘法和加法)和右结合(赋值,尽管有点奇怪,见下文)。所以确实没有含糊之处。

现在,优先声明(%left,%right等)是只有用于消除歧义。如果没有歧义,则声明忽略。解析器生成器甚至不检查它们是否反映语法。 (实际上,许多语法不能表示为这种优先关系。)

因此,如果语法是明确的,那么包含优先声明是一个非常糟糕的主意。他们可能是完全错误的,并误导任何读过语法的人。改变它们不会影响语言解析的方式,这可能会误导任何想编辑语法的人。

至少有一些问题,最好是使用具有优先规则的模糊语法,还是使用明确的语法。在类似于C的语言中,其语法不能用简单的优先级列表表示,因此使用明确的语法可能会更好。然而,明确的语法有更多的状态,并且可能会使语法分析稍微慢一些,除非语法分析器生成器能够优化单位减少量(上述语法中的所有第一个替代方法,其中每个表达式类型可能只是前一个表达式类型,但不影响AST;这些生产中的每一个都需要减少,尽管它大部分是空操作,并且许多解析器生成器会插入一些代码。)

原因C不能简单地表示为优先关系恰好是赋值运算符。试想一下:

a = 4 + b = c + 4; 

这并不是因为assignment-expression分析,赋值运算符可以在左侧仅适用于unary-expression。这并不反映+=之间可能的数字优先顺序。 [2]

如果+较高优先级的比=,表达式将作为解析:

a = ((4 + b) = (c + 4)); 

并且如果+较低优先级,这将解析为

(a = 4) + (b = (c + 4)); 

[1]我刚刚意识到我遗漏了cast_expression,但我不能投入重演;你明白了)

[2]描述固定。

+0

自从我生成解析器(并且它是flex/bison)以来已经有数年了。谢谢,这是一个很好的回复。 – doug65536

+0

GCC拒绝'a = 4 + b = c + 4;',错误值为左值,作为赋值的左操作数。你必须明确地引入圆括号才能将赋值赋予“b”(最低限度:a = 4 +(b = c + 4);')。 –

+0

@JonathanLeffler:非常正确(并且规则在C++中是不同的)。 – rici

0

后来我意识到在解引用(*)和乘法(*)之间有相同的含义。

柠檬提供了一种方法来为规则指定一个精度,使用周期后的方括号中的关联声明(%左/右/非关联)中使用的名称。

我没有验证这正常工作,但我认为你可以做到这一点(注意:在方括号中接近尾声的东西):

. 
. 
. 

%left COMMA. 
%right QUESTION ASSIGN 
    ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN 
    LSH_ASSIGN RSH_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN. 
%left LOGICAL_OR. 
%left LOGICAL_AND. 
%left BITWISE_OR. 
%left BITWISE_XOR. 
%left BITWISE_AND. 
%left EQ NE. 
%left LT LE GT GE. 
%left LSHIFT RSHIFT. 
%left PLUS MINUS. 
%left TIMES DIVIDE MOD. 
//%left MEMBER_INDIRECT ->* .* 
%right INCREMENT DECREMENT CALL INDEX DOT INDIRECT ADDRESSOF DEREFERENCE. 

. 
. 
. 

multiplicative_expr ::= cast_expr. 
multiplicative_expr(A) ::= multiplicative_expr(B) STAR cast_expr(C). [TIMES] 
    { A = Node_2_Op(Op_Mul, B, C); } 
. 
. 
. 
unary_expr(A) ::= STAR unary_expr(B). [DEREFERENCE] 
    { A = Node_1_Op(Op_Dereference, B); }