Programming languages Bison/Flex、reduce/reduce、不同生产中的标识符
我正在用bison/flex做一个解析器 这是我代码的一部分: 我想实现赋值产品,因此标识符可以是布尔表达式或表达式,其类型将由符号表检查。 因此,它允许类似于: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,解决这个问题的任何解决方案?本质上,您试图做的是将语义规则(类型信息)注入到语法中。这是可能的,但并不容易。更重要的是,这很少是个好主意。如果能很好地描述语法和语义,几乎
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);
}