针对不同数据类型的 Yacc
Yacc for different data types
我正在尝试编写一个语法,让用户使用他们习惯的操作符号进行多次计算。比如A+B,其中A和B是矩阵,或者是数字。
这是语法的相关部分:
q_term: fraction
| q_term '+' fraction {$$ = q_add(,);}
| q_term '-' fraction {$$ = q_sub(,);}
| q_term '*' fraction {$$ = q_mul(,);}
| q_term '/' fraction {$$ = q_div(,);}
;
qm_term: q_matrix
| qm_term '+' q_matrix {$$ = qm_add(,);}
| qm_term '-' q_matrix {$$ = qm_sub(,);}
| qm_term '*' q_matrix {$$ = qm_mul(,);}
;
它给了我一堆 shift/reduce 错误。我认为那是因为它在不止一个地方看到了操作字符。
如何解决班次减少错误?
编辑:
下面是解析器如何区分矩阵和标量
q_term: fraction
| q_term '+' fraction {$$ = q_add(,);}
| q_term '-' fraction {$$ = q_sub(,);}
| q_term '*' fraction {$$ = q_mul(,);}
| q_term '/' fraction {$$ = q_div(,);}
;
q_matrix: '[' q_term {qm_temp = qm_create(); qm_append(qm_temp,,'c');}/* new q_matrix */
| q_matrix ',' q_term {qm_append(qm_temp,,'c');} /* add a number to the current q_matrix row */
| q_matrix ';' q_term {qm_append(qm_temp,,'r');} /* add a new row */
| q_matrix ']' {qm_finish(qm_temp); $$ = qm_copy_matrix(qm_temp);} /* close the list */
;
fraction: INTEGER {$$ = q_new(,1);} /* this converts a lone integer into a fraction */
| INTEGER '|' INTEGER {$$ = q_new(,);}
在没有变量的语言中(例如,一个简单的计算器),可以在解析期间区分不同类型的表达式,前提是不可能自动将一种类型强制(转换)为另一种类型。
但实际上,每次都重复完整地输入矩阵会很麻烦。您和您的其他用户将很快需要某种方法来将矩阵常量保存为命名对象。如果命名对象也可以是标量,那么你要么必须坚持对象的名称以某种方式表示类型(例如,矩阵可能必须用大写字母或类似的字母书写),或者更可能的是你'在解析过程中,我们最终将不知道名称是标量表达式还是矩阵表达式。到那时,您为在解析过程中尝试区分两种类型的表达式而构建的任何复杂语法都会突然变得毫无意义。
所以我的建议是让自己避免恶化。初始解析应该只构建某种形式的 AST,然后您可以遍历树以执行您需要的任何语义分析,包括解析多态运算符和插入自动强制转换(如果有)。
可忽略的附录
虽然q_matrix
的语法没有问题,但我觉得有点尴尬,因为它并没有真正代表矩阵常量的句法结构。我会稍微不同地写它(也使用语义值来存储中间结果而不是全局变量):
q_matrix: '[' q_row_list ']' { $$ = ; }
q_rows : q_row { $$ = qm_create();
qm_append_row($$, ); }
| q_rows ';' q_row { /* $$ = ; */
/* ensure .cols() == .cols */
qm_append_row($$, ); }
q_row : q_term { $$ = qr_create();
qr_append_val($$, ); }
| q_row ',' q_term { /* $$ = ; */
qr_append_val($$, ); }
在上面,我注释掉了 $$ = ;
的两个实例,因为在 C 语言 bison 生成的解析器的情况下,该复制已经在执行任何操作之前完成。如果您更改为另一种语言,例如 C++,您可能需要包含显式副本。
该代码假定您同时具有矩阵和行(或向量)对象。 (当然,一个向量对象可以是一个只有一行的矩阵对象,如果你不想麻烦地实现两种不同的类型的话。)在上面的代码中,一行在添加到矩阵之前完成;此时,很容易检查以确保追加的行具有与累积矩阵相同的列数。我用评论指出了这个测试,而不是试图建议如果测试失败应该采取什么行动。
我正在尝试编写一个语法,让用户使用他们习惯的操作符号进行多次计算。比如A+B,其中A和B是矩阵,或者是数字。
这是语法的相关部分:
q_term: fraction
| q_term '+' fraction {$$ = q_add(,);}
| q_term '-' fraction {$$ = q_sub(,);}
| q_term '*' fraction {$$ = q_mul(,);}
| q_term '/' fraction {$$ = q_div(,);}
;
qm_term: q_matrix
| qm_term '+' q_matrix {$$ = qm_add(,);}
| qm_term '-' q_matrix {$$ = qm_sub(,);}
| qm_term '*' q_matrix {$$ = qm_mul(,);}
;
它给了我一堆 shift/reduce 错误。我认为那是因为它在不止一个地方看到了操作字符。
如何解决班次减少错误?
编辑:
下面是解析器如何区分矩阵和标量
q_term: fraction
| q_term '+' fraction {$$ = q_add(,);}
| q_term '-' fraction {$$ = q_sub(,);}
| q_term '*' fraction {$$ = q_mul(,);}
| q_term '/' fraction {$$ = q_div(,);}
;
q_matrix: '[' q_term {qm_temp = qm_create(); qm_append(qm_temp,,'c');}/* new q_matrix */
| q_matrix ',' q_term {qm_append(qm_temp,,'c');} /* add a number to the current q_matrix row */
| q_matrix ';' q_term {qm_append(qm_temp,,'r');} /* add a new row */
| q_matrix ']' {qm_finish(qm_temp); $$ = qm_copy_matrix(qm_temp);} /* close the list */
;
fraction: INTEGER {$$ = q_new(,1);} /* this converts a lone integer into a fraction */
| INTEGER '|' INTEGER {$$ = q_new(,);}
在没有变量的语言中(例如,一个简单的计算器),可以在解析期间区分不同类型的表达式,前提是不可能自动将一种类型强制(转换)为另一种类型。
但实际上,每次都重复完整地输入矩阵会很麻烦。您和您的其他用户将很快需要某种方法来将矩阵常量保存为命名对象。如果命名对象也可以是标量,那么你要么必须坚持对象的名称以某种方式表示类型(例如,矩阵可能必须用大写字母或类似的字母书写),或者更可能的是你'在解析过程中,我们最终将不知道名称是标量表达式还是矩阵表达式。到那时,您为在解析过程中尝试区分两种类型的表达式而构建的任何复杂语法都会突然变得毫无意义。
所以我的建议是让自己避免恶化。初始解析应该只构建某种形式的 AST,然后您可以遍历树以执行您需要的任何语义分析,包括解析多态运算符和插入自动强制转换(如果有)。
可忽略的附录
虽然q_matrix
的语法没有问题,但我觉得有点尴尬,因为它并没有真正代表矩阵常量的句法结构。我会稍微不同地写它(也使用语义值来存储中间结果而不是全局变量):
q_matrix: '[' q_row_list ']' { $$ = ; }
q_rows : q_row { $$ = qm_create();
qm_append_row($$, ); }
| q_rows ';' q_row { /* $$ = ; */
/* ensure .cols() == .cols */
qm_append_row($$, ); }
q_row : q_term { $$ = qr_create();
qr_append_val($$, ); }
| q_row ',' q_term { /* $$ = ; */
qr_append_val($$, ); }
在上面,我注释掉了 $$ = ;
的两个实例,因为在 C 语言 bison 生成的解析器的情况下,该复制已经在执行任何操作之前完成。如果您更改为另一种语言,例如 C++,您可能需要包含显式副本。
该代码假定您同时具有矩阵和行(或向量)对象。 (当然,一个向量对象可以是一个只有一行的矩阵对象,如果你不想麻烦地实现两种不同的类型的话。)在上面的代码中,一行在添加到矩阵之前完成;此时,很容易检查以确保追加的行具有与累积矩阵相同的列数。我用评论指出了这个测试,而不是试图建议如果测试失败应该采取什么行动。