非终端发生JISON错误



我正在为一个类编写一个 JISON 文件,并尝试使用非终端代替声明运算符的关联性,但我完全不知道错误的真正含义,因为这是一个类的一次性赋值,我还没有找到在这个用例中使用非终端的惊人例子。

我的JISON代码:

/* lexical grammar */
%lex
%%
s+                   /* skip whitespace */
[0-9]+("."[0-9]+)?b  return 'NUMBER'
"*"                   return '*'
"/"                   return '/'
"-"                   return '-'
"+"                   return '+'
"^"                   return '^'
"!"                   return '!'
"%"                   return '%'
"("                   return '('
")"                   return ')'
"PI"                  return 'PI'
"E"                   return 'E'
<<EOF>>               return 'EOF'
.                     return 'INVALID'
/lex
%start expressions
%% /* language grammar */
expressions
: e EOF
{ typeof console !== 'undefined' ? console.log($1) : print($1);
return $1; }
;
e
: NegExp
{$$ = $1;}
| MulExp
{$$ = $1;}
| PowExp
{$$ = $1;}
| UnaryExp
{$$ = $1;}
| RootExp
{$$ = $1;}
;

RootExp
: ’(’ RootExp ’)’
{$$ = ’(’ + $2 + ’)’;}
| NUMBER
{$$ = Number(yytext);}
| E
{$$ = ’E’;}
| PI
{$$ = ’PI’;}
;
UnaryExp
: UnaryExp '!'
{$$ = '(' + $1 + '!' + ')';}
| UnaryExp '%'
{$$ = '(' + $1 + '%' + ')';}
| '-' UnaryExp
{$$ = '(-' + $2 + ')';}
;
NegExp
: NegExp '+' e
{$$ = '(' + $1 + ' + ' + $3 + ')';}
| NegExp '-' e
{$$ = '(' + $1 + ' - ' + $3 + ')';}
;
MulExp
: MulExp '*' e
{$$ = '(' + $1 + ' * ' + $3 + ')';}
| MulExp '/' e
{$$ = '(' + $1 + ' / ' + $3 + ')';}
;
PowExp
: e '^' PowExp
{$$ = '(' + $1 + ' ^ ' + $3 + ')';}
;

当我运行jison filename.jison时,我收到一系列错误,例如:

Conflict in grammar: multiple actions possible when lookahead token is ^ in state 26
- reduce by rule: MulExp -> MulExp / e
- shift token (then go to state 13)

和:

States with conflicts:
State 3
e -> NegExp . #lookaheads= EOF ^ + - * /
NegExp -> NegExp .+ e #lookaheads= EOF + - ^ / *
NegExp -> NegExp .- e #lookaheads= EOF + - ^ / *

同样,我不是在寻找人为我做功课的人,但关于去哪里或做什么来帮助调试的指示将不胜感激。

这是真的;要找到不使用优先级声明来解决歧义的表达式语法的例子并不容易。这可能是因为在这个特定的用例中,优先级声明非常方便,并且可能比编写明确的语法更具可读性。生成的解析器通常也稍微高效一些,因为它避免了通常明确的语法风格所强加的单位减少链。

这种便利的另一面是,它不能帮助学生理解语法的实际工作原理,如果没有这种理解,就很难将优先声明应用于不太明确的应用程序。因此,引起这个问题的练习当然是值得的。

你会发现明确的表达式语法的一个地方是在(某些)编程语言的规范中,因为明确的语法不依赖于解析器生成器用于解决解析冲突的算法的精确性质。但是,这些示例往往相当复杂,因为真正的编程语言通常有很多运算符。即便如此,jison 示例目录中的示例 C 语法确实显示了算术表达式语法的标准模型。以下摘录经过了极大的简化,但大多数作品只是从原版复制粘贴而来的。(我删除了许多运算符,大多数优先级级别,以及一些处理转换表达式和特殊逗号运算符之类的复杂情况,这些在这里肯定无关紧要。

primary_expression
: IDENTIFIER
| CONSTANT
| '(' expression ')'
;
postfix_expression
: primary_expression
| postfix_expression INC_OP
| postfix_expression DEC_OP
;
unary_expression
: postfix_expression
| '-' unary_expression
| INC_OP unary_expression
| DEC_OP unary_expression
;
/* I added this for explanatory purposes. DO NOT USE IT. See the text. */
exponential_expression
: unary_expression
| unary_expression '^' exponential_expression    
multiplicative_expression
: exponential_expression
| multiplicative_expression '*' exponential_expression
| multiplicative_expression '/' exponential_expression
| multiplicative_expression '%' exponential_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
expression
: additive_expression
;

C 没有幂运算符,所以我添加了一个具有正确关联性和比乘法更高的优先级的运算符,这将用于解释目的。但是,您的作业可能也希望它具有比一元否定更高的优先级,我没有这样做。所以我不建议直接使用上述内容。

在上面的模型中需要注意的一件事是,每个优先级都对应于一个非终端。这些非终端使用单元生产链接到有序链中。 我们可以看到这个序列:

表达式⇒ additive_expression ⇒ multiplicative_expression ⇒ exponential_expression ⇒ unary_expression ⇒ postfix_expression ⇒ primary_expression

这确实对应于此语法的优先顺序。

语法的另一个有趣的方面是,左关联运算符(除幂之外的所有运算符)都是用左递归生产实现的,而右关联运算符是用右递归生产实现的。这不是巧合。

这是基本模型,但值得花几分钟来尝试了解它的实际工作原理,因为它非常简单。让我们看一下乘法的一个生产,看看我们是否可以理解为什么它意味着幂绑定更紧密,加法绑定不那么紧密:

multiplicative_expression: multiplicative_expression '*' exponential_expression

这个作品是说multiplicative_expression由一个*组成,左边有一个multiplicative_expression,右边有一个exponential_expression

现在,这对2 + 3 * 4 ^ 2意味着什么?2 + 3是一个additive_expression,但我们可以从单位生产的链中看到,multiplicative_expression不生产additive_expression。因此,语法不包括2 + 3*左侧匹配的短语的可能性。然而,3(一个CONSTANT,它是一个primary_expression)被解析为乘法的左操作数是完全合法的。

同时,4 ^ 2是一个exponential_expression,我们的生产清楚地表明exponential_expression可以在*的右侧匹配。

一个类似的论证,检查加法和指数表达式产生,将表明3 * 4 ^ 2(multiplicative_expression)可以在+运算符的右侧,而2 + 3 * 4(additive_expression)和3 * 4(multiplicative_expression)都不能在幂运算符的左侧。

换句话说,这种简单的语法精确而明确地定义了表达式必须如何分解。只有一个可能的解析树:

expression
|
add
|
+--------+----------------+
|        |                |
|        |              mult
|        |                |
|        |        +-------+---------------+
|        |        |       |               |
|        |        |       |             power
|        |        |       |               |
|        |        |       |       +-------+-------+ 
|        |        |       |       |       |       |
add       |      mult      |     unary     |     power
...       |       ...      |      ...      |      ...
|        |        |       |       |       |       |
primary     |     primary    |    primary    |    primary
|        |        |       |       |       |       |
2        +        3       *       4       ^       2

我希望这有所帮助。

相关内容

  • 没有找到相关文章

最新更新