问题描述
我正在尝试实现一种允许通过并置进行乘法的语法。 这是用于解析 CAS 的多项式输入。
据我所知,它工作得很好,除了少数边缘情况。 我发现有两个问题:
- 与其他规则冲突,例如,
a^2 b
被(错误地)解析为(^ a (* 2 b))
,而不是(* (^ a 2) b)
。 - yacc(bison) 报告
28 shift/reduce conflicts
和8 reduce/reduce conflicts
。
我很确定正确解决第一个问题也会解决第二个问题,但到目前为止我还没有成功。
以下是我正在使用的语法要点:
%start prgm
%union {
double num;
char *var;
ASTNode *node;
}
%token <num> NUM
%token <var> VAR
%type <node> expr
%left '+' '-'
%left '*' '/'
%right '^'
%%
prgm: // nothing
| prgm '\n'
| prgm expr '\n'
;
expr: NUM
| VAR
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| expr '^' expr
| expr expr %prec '*'
| '-' expr
| '(' expr ')'
;
%%
删除并列规则 (expr expr %prec '*'
) 解决了 shift/reduce 和 reduce/reduce 警告。
请注意,我语法中的 ab
应表示 (* a b)
。
多字符变量前面应该有引号('
);这在 lex
文件中已经处理得很好。
词法分析器完全忽略空格 (
) 和制表符 (\t
)。
我知道 this question,但此处并列的使用似乎并不表示乘法。
任何意见或帮助将不胜感激!
附言如果有帮助,this is the link to the entire project.
解决方法
如您链接的问题的答案所示,很难指定并列的运算符优先级,因为没有要移位的运算符。 (在您的代码中,您可以指定产生式 duplicated_numbers = ...
list_of_duplicates = []
for item in list_of_items:
if item[0] in duplicated_numbers:
list_of_duplicates.append(item)
的优先级。但是这种减少将与什么前瞻标记进行比较?将 FIRST(expr) 中的每个标记添加到您的优先级声明中不是很可扩展,并且可能导致不需要的优先解决方案。
优先级解决方案的另一个问题是一元减运算符的行为(链接问题中未解决的问题),因为如所写,您的语法允许将 expr: expr expr
解析为减法或a - b
和 a
的并列乘法。 (并注意 -b
在 FIRST(expr) 中,导致我上面提到的可能不需要的分辨率之一。)
因此,如链接问题中所建议的,最佳解决方案是使用具有显式优先级的语法,例如以下内容:(此处,我使用 -
作为非终结符的名称,而不是juxt
):
expr_sequence
这个语法可能不是你想要的:
- 对一元减号的处理相当简单,但有几个问题。我认为将
%start prgm %token NUM %token VAR %left '+' '-' %left '*' '/' %right '^' %% prgm: // nothing | prgm '\n' | prgm expr '\n' expr: juxt | '-' juxt | expr '+' expr | expr '-' expr | expr '*' expr | expr '/' expr | expr '^' expr juxt: atom | juxt atom atom: NUM | VAR | '(' expr ')'
解析为-xy
而不是-(xy)
没有问题,但这并不理想。此外,它不允许(-x)y
(也可能不是问题但不理想)。最后,它不会将--x
解析为-x^y
,而是将其解析为-(x^y)
,这与常见做法相反。 - 此外,它错误地将并列绑定得太紧。您可能会也可能不会认为
(-x)^y
解析为a/xy
是一个问题,但您可能会反对将a/(xy)
解析为2x^7
。
避免这些问题的最简单方法是使用一种语法,其中运算符优先级通过明确的语法规则统一实现。
这是一个实现标准优先规则的示例(幂运算优先于一元减法;并列乘法与显式乘法具有相同的优先级)。值得花几分钟仔细查看哪个非终结符出现在哪个产生式中,并考虑它与所需的优先级规则有何关联。
(2x)^7