Programming languages Bison/Flex、reduce/reduce、不同生产中的标识符

Programming languages Bison/Flex、reduce/reduce、不同生产中的标识符,programming-languages,bison,flex-lexer,Programming Languages,Bison,Flex Lexer,我正在用bison/flex做一个解析器 这是我代码的一部分: 我想实现赋值产品,因此标识符可以是布尔表达式或表达式,其类型将由符号表检查。 因此,它允许类似于: int a = 1; boolean b = true; if(b) ... 然而,如果我在术语和布尔表达式中都包含标识符,那么它就是reduce/reduce,解决这个问题的任何解决方案?本质上,您试图做的是将语义规则(类型信息)注入到语法中。这是可能的,但并不容易。更重要的是,这很少是个好主意。如果能很好地描述语法和语义,几乎

我正在用bison/flex做一个解析器

这是我代码的一部分:

我想实现赋值产品,因此标识符可以是布尔表达式或表达式,其类型将由符号表检查。 因此,它允许类似于:

int a = 1;
boolean b = true;
if(b) ...

然而,如果我在术语和布尔表达式中都包含标识符,那么它就是reduce/reduce,解决这个问题的任何解决方案?

本质上,您试图做的是将语义规则(类型信息)注入到语法中。这是可能的,但并不容易。更重要的是,这很少是个好主意。如果能很好地描述语法和语义,几乎总是最好的

尽管如此,正如前面介绍的那样,您的语法是明确的,
LALR(1)
。然而,后一个特性是脆弱的,当您完成语法时,您将很难维护它

例如,您的问题中没有包含赋值语法,但它会

assignment: identifier '=' expr
          | identifier '=' boolean_expr
          ;
与所示语法部分的其余部分不同,该结果是不明确的,因为:

x = y
在不知道任何有关
y
的信息的情况下,
y
可以简化为
术语
布尔表达式

一个可能更有趣的例子是在语法中添加括号。显而易见的方法是添加两个产品:

term: '(' expr ')'

boolean_expr: '(' boolean_expr ')'
生成的语法没有歧义,但不再是
LALR(1)
。考虑下面两个声明:

boolean x = (y) < 7
boolean x = (y)
因此,对于任何
k
,得到的明确语法不是
LALR(k)


解决此问题的一种方法是在词法级别注入类型信息,方法是允许扫描器访问符号表。然后,扫描器可以在符号表中查看扫描的标识符标记,并使用符号表中的信息来决定三种标记类型之一(或更多,如果您有更多数据类型):
未定义的\u变量
整数\u变量
,和
布尔\u变量
。然后你会有,例如:

declaration: "int" undefined_variable '=' expr
           | "boolean" undefined_variable '=' boolean_expr
           ;

term: integer_variable
    | ...
    ;

boolean_expr: boolean_variable
            | ...
            ; 
这会起作用,但很明显,这是不可伸缩的:每次添加类型时,都必须扩展语法和词汇描述,因为现在语义不仅与语法混淆,甚至与词汇分析混淆在一起。一旦你把语义从盒子里放出来,它就会污染一切

对于某些语言来说,这确实是最方便的解决方案:例如,如果区分typedef名称和标识符名称,那么C解析就容易得多,这样您就可以知道
(t)*x
是强制转换还是乘法。(但是C++的工作并不是那么容易,它有更复杂的名字查找规则,而且需要更多的语义分析来找到正确的解析。) 但是,老实说,我建议你不要使用C——更不用说C++——作为如何设计语言的一个模型。编译器很难解析的语言,人类也很难解析。继续成为C++新手的一个经常性的疼痛源,甚至有时会让有经验的程序员跳出:

class X {
  public:
    X(int n = 0) : data_is_available_(n) {}
    operator bool() const { return data_is_available_; }
    // ...
  private:
    bool data_is_available_;
    // ...
};

X my_x_object();
// ...
if (!x) {
  // This code is unreachable. Can you see why?
}
简而言之,您最好使用一种可以解析为AST的语言,而不需要任何语义信息。一旦解析器生成了AST,您就可以在单独的过程中进行语义分析,其中一个过程将检查类型约束。这绝对是最干净的解决方案。没有显式键入,语法会稍微简化,因为
expr
现在可以是任何
expr

 expr:        conjunction | expr "or" conjunction ;
 conjunction: comparison  | conjunction "and" comparison ;
 comparison:  product     | product '<' product ;
 product:     factor      | product '*' factor ;
 factor:      term        | factor '+' term ;
 term:        identifier
     |        constant
     |        '(' expr ')'
     ;

非常感谢。事实上,我对野牛很陌生,两周前就开始学野牛了。我有一个符号表来检查类型,但我看不出它如何帮助解决这个问题。在对reduce进行语义检查之前,它必须将identifier简化为expr,对吗?类似地,如果标识符的类型为布尔型,则必须将其缩减为布尔型表达式。我只是制作了一个只有3种类型的简单解析器:整数、布尔和点,因此我认为如果我为每种类型的变量添加前缀,就像整数变量以“I_uu”?@user2716189开头一样,那么就很容易了:无需为变量添加前缀,因为你的lexer可以在符号表中查找变量的类型(假设它可以访问符号表)。未声明的变量只能用作声明的目标,并且声明的变量不能重新声明(这是一个假设,但似乎是合理的),因此扫描程序可以为这三种情况返回三种令牌类型中的一种。我试图在回答中更明确地说明这一点。
 expr:        conjunction | expr "or" conjunction ;
 conjunction: comparison  | conjunction "and" comparison ;
 comparison:  product     | product '<' product ;
 product:     factor      | product '*' factor ;
 factor:      term        | factor '+' term ;
 term:        identifier
     |        constant
     |        '(' expr ')'
     ;
expr : expr '+' expr { CheckArithmeticCompatibility($1, $3);
                       $$ = NewArithmeticNode('+', $1, $3);
                     }