我在Bison编写的ASN.1编译器中遇到了问题。
A OCTET STRING (CONTAINING B)
编译器忽略CONTAINING B并将数据引用为OCTET STRING 而不是将其称为B. 以下是当前规则:
OctetStringType :
OCTET STRING Constraint { $$ = new OctetString($3); } |
OCTET STRING '{' NamedOctetList '}' Constraint { $$ = new OctetString($6); }
;
我尝试创建一条新规则:
OctetStringType :
OCTET STRING '('ContentsConstraint')' {}|
OCTET STRING Constraint { $$ = new OctetString($3); } |
OCTET STRING '{' NamedOctetList '}' Constraint { $$ = new OctetString($6); }
;
ContentsConstraint :
CONTAINING Type { }
;
当我尝试打印结果时:
OCTET STRING '('ContentsConstraint')' {printf("$$: %s\n",$$);}
我得到A. 如何访问B?我是否必须修改这些规则才能访问B?
答案 0 :(得分:1)
(我假设您正在修改现有的ASN.1语法,而不是您自己编写的语法。)
$$
是由语义动作计算的语义值。所以
OCTET STRING '('ContentsConstraint')' {printf("$$: %s\n",$$);}
毫无意义;您尚未为$$
分配值,因此必须将其视为未定义的值。
实际上,bison / yacc解析器在执行操作之前已经有效地执行了赋值$$ = $1;
(这很有用,因为这意味着当你想要的时候你不需要写它在行动中做)。因此,在这种情况下,您将打印生产中第一个符号的语义值(这是$1
的含义),即终端OCTET
。但是,令牌OCTET
不太可能具有语义值;在大多数解析器中,从不使用关键字终端的语义值,因此分配它没有意义。
大多数bison / yacc衍生物都会遇到麻烦,以确保将每个语义值初始化为某些以防止编译器警告(旧版本没有这样做),但是从未指定过,应将其视为未初始化。简而言之,您的代码显示未定义的行为,并且可以打印任何内容。
我想你想要打印ContentsConstraint
非终端的语义值。假设您定义非终端的产品的语义操作都正确地为其分配了值,您可以将其作为$4
访问,因为ContentsConstraint
是规则中的第四个标记。这意味着您至少需要修改规则
ContentsConstraint : CONTAINING Type { }
到
ContentsConstraint : CONTAINING Type { $$ = $2; }
因为否则ContentsConstraint
的值将是默认操作的结果,即$1
,在这种情况下没有语义值,如上所述。
我建议阅读bison manual,至少在前几页“Semantic Actions”中参考可能使概念更清晰的示例。 (阅读整本手册不应该消耗太多时间,而且会更有用,但我知道阅读手册最近被视为passé。)