在 Bison 中访问一个产品的价值以及从另一个产品分配类型

Accessing The Value Of A Production In Bison As Well As Assigning Types From Another Production

我正在与 bison 一起工作,目前遇到了一个问题,并且对所有这些工作原理还比较陌生,我需要能够判断某个特定的作品是 > 或 + 还是 >= 但我不知道存储和检索此值的最佳方法是以下代码:

%union 
{
  char* text;
  TYPE_INFO typeInfo;
};

N_ARITHLOGIC_EXPR   : N_UN_OP N_EXPR
            {
                if(.type == FUNCTION){
                    yyerror("Arg 1 cannot be function");
                }
                $$.type = BOOL;
                $$.numParams = NOT_APPLICABLE;
                $$.returnType = NOT_APPLICABLE;

            }
            | N_BIN_OP N_EXPR N_EXPR
            {
                if(T_LT || T_GT || T_LE || T_GE || T_EQ || T_NE || T_NOT){
                    if(!((.type == INT && .type == INT) || (.type == STR && .type == STR))){
                        yyerror("Arg n must be integer or string");
                    }
                    else{
                        $$.type = BOOL;
                        $$.numParams = NOT_APPLICABLE;
                        $$.returnType = NOT_APPLICABLE;
                    }
                }
                else if(T_AND || T_OR){
                    if((.type == INT && .type == FUNCTION) || (.type == STR && .type == FUNCTION) || (.type == BOOL && .type == FUNCTION) || (.type == FUNCTION && .type == FUNCTION)){
                        yyerror("Arg n cannot be a function");
                    }
                    else{
                        $$.type = BOOL;
                        $$.numParams = NOT_APPLICABLE;
                        $$.returnType = NOT_APPLICABLE;                         
                    }
                }
                else if (T_ADD || T_SUB || T_MULT || T_DIV){
                    if(!(.type == INT && .type == INT)){
                        yyerror("Arg n must be integer");
                    }
                    else{
                        $$.type = INT;
                        $$.numParams = NOT_APPLICABLE;
                        $$.returnType = NOT_APPLICABLE;
                    }
                }
            }
            ;

if 语句现在显然不起作用,但我只需要能够查看它是有理运算符还是算术运算符等

另外,稍后我需要能够使用一个作品,所以说以下内容:

N_PROGN_OR_USERFUNCTCALL : N_FUNCT_NAME N_ACTUAL_PARAMS
            {

            }
            | T_LPAREN N_LAMBDA_EXPR T_RPAREN N_ACTUAL_PARAMS
            {

            }
            ;
N_FUNCT_NAME        : T_PROGN
            {

                //Change type of N_PROGN_OR_USERFUNCTCALL based off of the function return type of T_PROGN

            }

根据 T_PROGN 的 return 类型,我需要能够更改 N_PROGN_OR_USERFUNCTCALL 的类型,最好的方法是什么?谢谢!

关键字(一般意义上,包括><=等运算符)最常见的样式是使每个关键字成为唯一的终结符。在某些情况下,这会导致语法中出现一定程度的重复,但它避免了扫描器和解析器之间的过度依赖。所以这有点平衡。

如果你想将两个具有不同语义的关键字合并到一个终端中,你可以通过使终端的语义类型成为 enum(或道德等价物),并在词法分析器中设置它来实现.但您也可以在语法中组合两个关键字。

以下所有都有它们的用例,它们真的没有什么不同:

合并终端:

 // Scanner patterns
"<="       yylval.op = OP_LE; return T_BINOP;
"<"        yylval.op = OP_LT; return T_BINOP;
"+"        yylval.op = OP_PLUS; return T_BINOP;
 // etc.

 // grammar
%token <opcode> T_BINOP

%%

expr: T_BIN_OP expr expr {
        switch () {
          case OP_LT: case OP_LE: case OP_EQ: ... {
            if (check_compare(, )) {
              $$ = (TypeInfo){ .type = BOOL,
                               .numParams = NOT_APPLICABLE,
                               .returnType = NOT_APPLICABLE };
            else {
              yyerror(...);
            }
            break
          case OP_PLUS: case OP_MINUS: case OP_TIMES: ...
            if (check_arith(, )) { 
              // ...

个别终端机:

 // Scanner patterns
"<="       return OP_LE;
"<"        return OP_LT;
"+"        return OP_PLUS;
 // etc.

 // grammar
%token OP_LE "<=" OP_LT "<" 
       OP_PLUS "+"
       ...

%%

expr: "<=" expr expr {
        if (check_compare(, )) {
          $$ = (TypeInfo){ .type = BOOL,
                           .numParams = NOT_APPLICABLE,
                           .returnType = NOT_APPLICABLE };
        else {
          yyerror(...);
        }
    | "+" expr expr {
        if (check_arith(, )) {
          $$ = (TypeInfo){ .type = BOOL,
                           .numParams = NOT_APPLICABLE,
                           .returnType = NOT_APPLICABLE };
        else {
          yyerror(...);
        }
    // ...

或者,替代语法:

%token OP_LE "<=" OP_LT "<" 
       OP_PLUS "+" OP_TIMES "*"
       ...
%type <opcode> cmp_op arith_op ...
%%
cmp_op: "<="  { $$ = OP_LE; }
      | "<"   { $$ = OP_LT; }
      // ...
arith_op: "+" { $$ = OP_PLUS; }
        | "*" { $$ = OP_TIMES; }
        // ...
expr: cmp_op expr expr {
        if (check_compare(, )) {
          $$ = (TypeInfo){ .type = BOOL,
                           .numParams = NOT_APPLICABLE,
                           .returnType = NOT_APPLICABLE };
        else {
          yyerror(...);
        }
    | arith_op expr expr {
        if (check_arith(, )) {
          $$ = (TypeInfo){ .type = BOOL,
                           .numParams = NOT_APPLICABLE,
                           .returnType = NOT_APPLICABLE };
        else {
          yyerror(...);
        }
    // ...

注意:上面的 None 实际上保存了仔细计算的操作码,也没有保存参数。但是,问题中的代码也没有。但是 none 版本之间存在显着差异。


老实说,我不确定你的第二个问题是什么意思。什么是“N_PROGN_OR_USERFUNCTCALL 的类型”?你是指你设置为TYPE_INFO的type成员值的枚举吗?还是您的意思是您可能希望 N_PROGN_OR_USERFUNCTCALL 成为不止一种可能的语义类型?在后一种情况下,您需要重新考虑您的设计。 Bison/yacc 在这方面与 C 具有完全相同的语义;变量的类型就是变量的类型,您无法在运行时决定 xint 还是 double。非终结符在语法上等同于变量,每个非终结符都有一个预先声明的类型。如果您需要更多选择,您可以使用可区分联合(或 std::variant,在 C++ 中),就像您可以使用底层实现语言一样。 (可区分联合是 struct 包含类型枚举和 union 不同类型的值。)