Parsing 创建一个单独的;布尔表达式;动态语言的规则
我正在Bison中为一种简单的动态类型语言创建语法。我有一个“通用的”Parsing 创建一个单独的;布尔表达式;动态语言的规则,parsing,compiler-construction,grammar,bison,formal-languages,Parsing,Compiler Construction,Grammar,Bison,Formal Languages,我正在Bison中为一种简单的动态类型语言创建语法。我有一个“通用的”表达式规则,它有点类似于C中右值的概念;表达式出现在赋值的右侧,也可以作为参数等发送到函数。该规则的简化版本如下: constantExpression : TOK_INTEGER_CONSTANT | TOK_FLOAT_CONSTANT | stringLiteral ; expression : constantExpression | identifier |
表达式
规则,它有点类似于C中右值的概念;表达式出现在赋值的右侧,也可以作为参数等发送到函数。该规则的简化版本如下:
constantExpression
: TOK_INTEGER_CONSTANT
| TOK_FLOAT_CONSTANT
| stringLiteral
;
expression
: constantExpression
| identifier
| booleanExpression
| booleanExpression TOK_QUESTION_MARK expression TOK_COLON expression
| TOK_LPAREN expression TOK_RPAREN
| expression TOK_PLUS expression
| expression TOK_MINUS expression
;
我还有一个专用的布尔表达式规则;布尔表达式在if
语句中最常用,但任何其他需要二进制真值的上下文也可以:
booleanExpression
: identifier
| expression '<' expression
| expression '<=' expression
| expression '==' expression
| expression '!=' expression
| expression '>' expression
| expression '>=' expression
| booleanExpression '&&' booleanExpression
| booleanExpression '||' booleanExpression
| '!' booleanExpression
| 'true'
| 'false'
;
我知道与运算符优先级相关的shift-reduce冲突,这在这里不是问题,我已经使用运算符的%left
规则修复了它
program : stmt_list
stmt_list:%empty
| stmt_list stmt
stmt : assign
| call
| empty
| while
| '{' stmt_list '}'
assign : IDENTIFIER '=' expr ';'
call : expr '(' expr_list ')' ';'
| expr '(' ')' ';'
empty : ';'
while : "while" '(' boolean_expr ')' stmt
expr_list
: expr
| expr_list ',' expr
boolean_expr
: boolean_term
| boolean_expr "or" boolean_expr
| expr '<' expr
boolean_term
: "true" | "false"
| expr { /* insert conversion from expr to boolean */ }
expr : term
| expr '+' expr
term : INTEGER
| IDENTIFIER
| '(' expr ')'
我的问题:这个问题的最佳解决方案是什么?我的想法是
布尔表达式
规则并移动
这一切都取决于表达式
-可以,但需要在
语义分析阶段;在我的语言中,字符串不是隐式的
可转换为布尔值,因此像if(“foo”)
这样的代码是不合法的,但是
将被解析器接受上面哪一个是最好的主意?还有其他我没有考虑过的可能性吗?传统智慧是:不要试图在语法中进行语义分析 首先,它使语法复杂化,即使这是可能的,正如您所看到的。相比之下,在AST上的树遍历中执行类型检查规则时非常简单 第二,这是不可能的。因为您的语言是动态的,所以您不知道任何变量的类型。因此编译时检查可能会导致三种情况,而不是两种:好、坏和未知。这在语法上会更加复杂,但在语义分析上只会稍微复杂一些 然而,根据语言的确切性质,可能会选择一个中间立场。通常,一些运算符(布尔运算符和比较运算符)肯定返回布尔值,而某些上下文肯定需要布尔值。因此,您可以添加一个
boolean\u表达式
non-terminal,用于指示哪里的结果肯定是布尔值,哪里的值必须是布尔值。然后你可以在语法中插入一个单一的生产单元
boolean_expression: expression
使用语义操作将运行时检查节点插入AST
在语义分析过程中,如果确定该检查将始终成功,则可以消除该检查;如果确定该检查将始终失败,则会产生错误。否则,最终将发出代码来执行检查
此解决方案的优点是,语法随后显示了需要布尔值的上下文,而不需要进行完全强制要求所需的拜占庭式修改
(在下面的示例中,我只展示了一个布尔运算符、一个比较运算符和一个算术运算符。很明显,一种真正的语言会有更多的布尔运算符、一个比较运算符和一个算术运算符,但它根本不会改变表示形式。我也不关心序言,序言中必须包含运算符的优先级声明。)
如果你认为上面的内容太复杂,那么就按照传统的观点去做,避免在语法中穿插语义。另一方面,如果你觉得它有解释性的价值,并且你的语言可以接受这些限制,那么就根据你的目的调整它
笔记:
truthy\u或\u falsy\u expr
似乎太长了,boolish\u expr
似乎太奇怪了。欢迎提出建议
如果它是动态类型的,那么像
If(“notABool”)
这样的东西不应该是运行时错误吗?那么someVar=“notABool”呢;如果(someVar)
,您当前的语法已经允许使用哪一个?您是如何处理的?@sepp2k是的,您给出的第二个示例确实是一个运行时错误if(“string”)
可能已经在解析阶段被拒绝,或者我们可以说string文本创建了一个不能在布尔上下文中使用的对象,并在运行时抛出一个错误。我真的不知道哪种方法更好,这也是我问这个问题的部分原因。通常动态类型语言不会静态地产生任何类型错误,即使在很容易检测到的情况下也是如此。但是,它们可能会产生警告(但是,大多数情况下,这些警告是由外部工具处理的,而不是语言实现本身)。无论是执行静态类型检查(无论是完整的还是不完整的,错误还是警告),这都是语义分析阶段的工作,而不是PAR。
program : stmt_list
stmt_list:%empty
| stmt_list stmt
stmt : assign
| call
| empty
| while
| '{' stmt_list '}'
assign : IDENTIFIER '=' expr ';'
call : expr '(' expr_list ')' ';'
| expr '(' ')' ';'
empty : ';'
while : "while" '(' boolean_expr ')' stmt
expr_list
: expr
| expr_list ',' expr
boolean_expr
: boolean_term
| boolean_expr "or" boolean_expr
| expr '<' expr
boolean_term
: "true" | "false"
| expr { /* insert conversion from expr to boolean */ }
expr : term
| expr '+' expr
term : INTEGER
| IDENTIFIER
| '(' expr ')'
program : stmt_list
stmt_list:%empty
| stmt_list stmt
stmt : assign
| call
| empty
| while
| '{' stmt_list '}'
assign : IDENTIFIER '=' either_expr ';'
call : expr '(' expr_list ')' ';'
| expr '(' ')' ';'
empty : ';'
while : "while" '(' truthy_expr ')' stmt
expr_list
: either_expr
| expr_list ',' either_expr
truthy_expr
: boolean_expr
| expr { /* insert conversion from expr to boolean */ }
either_expr
: boolean_expr
| expr
boolean_expr
: boolean_term
| truthy_expr "or" truthy_expr
| expr '<' expr
boolean_term
: "true"
| "false"
| '(' boolean_expr ')'
expr : term
| expr '+' expr
term : INTEGER
| IDENTIFIER
| '(' expr ')'