为 Bison 使用字符串 return 值的正确方法是什么?

What is the proper way to use string return value for Bison?

从构成 Bison 找到的规则的一个或多个标记中获取结果字符串的正确方法是什么?

timer:
    TILDE amount  {
        printf("Timer: Amount:%s\n", );
        $$ = ;
      }

  | TILDE WORD amount { 
        printf("Timer: %s Amount:%s\n",,  );
        // make timer string
        $$ = malloc(strlen() + strlen() + 10);
        sprintf($$, "%s %s", , );
        free();
        free();
      }

  | TILDE MULTIWORD amount  { 
        printf("Timer: %s Amount:%s\n",,  );
        // make timer string
        $$ = malloc(strlen() + strlen() + 10);
        sprintf($$, "%s %s", , );
        free();
      }
  ;

time、amount、WORD都是string类型,MULTIWORD也是。

例如,如果第一个规则匹配,意味着有一个波浪线“~”和一个数量,通常是字符串格式的数字,如“3”,那么获取结果值的最佳方式是什么,即'timer' 是否只有金额的值?

如果第二条规则匹配,我将如何将 WORD 和金额添加到一个字符串中并将它们放入 $$ 值中。 我在 Bison 文档中找不到任何关于此的内容。

编辑:忘记提及问题。打印出来的字符串不仅仅是我想要传递的内容。例如,金额定义为:

amount:
    // an empty amount - for one word timers
    LCURL RCURL {
      $$ = malloc(5);
      $$[0] = ' ';
      $$[1] = '[=12=]';
    }

  | LCURL NUMBER RCURL  { 
      // get string for amount
      $$ = malloc(100);
      sprintf($$, "%.3lf", );
    }

  | LCURL NUMBER UNIT RCURL { 
      // get string for amount
      // remove % from unit 
      $$ = malloc(100 + strlen() + 5);
      sprintf($$, "%.3lf %s", , ); 
    }

  | LCURL WORD RCURL  {
      $$ = ;
    }

  | LCURL WORD UNIT RCURL {
      $$ = malloc(strlen() + strlen() + 5);
      sprintf($$, "%s%s", , );
    }

  | LCURL MULTIWORD RCURL {
      $$ = ;
    }

  | LCURL MULTIWORD UNIT RCURL {
      $$ = malloc(strlen() + strlen() + 5);
      sprintf($$, "%s%s", , );
    }
  ;

当我将 $2 放入字符串中时,它还会在其后面包含 RCURL。这会导致大量随机字符串和不正确的解析。

完整的词法分析器文件:

%{
  #include "Cooklang.tab.h"
  #include <stdlib.h>
  void showError();

%}


SYMB_CHAR             "$"|"="|"+"|"-"|"_"|"*"|"`"
PUNC_CHAR             "!"|"?"|","|"."|"/"|"&"|"("|")"|":"
NEW_LINE              "\n"
WHITE_SPACE           " "|"\t"
ALPHA_CHAR            [a-zA-Z]
COOKLANG_CHAR         ">"|"|"|"~"|"@"|"#"|":"|"{"|"}"|"%"



ZERO                  "0" 
NON_ZERO_DIGIT        [0-9]


DIGIT                 ({ZERO}|{NON_ZERO_DIGIT}){WHITE_SPACE}*
INTEGER               ({ZERO}|({NON_ZERO_DIGIT}{DIGIT}*)){WHITE_SPACE}*
DECIMAL               {INTEGER}"."{INTEGER}{WHITE_SPACE}*
FRACTIONAL            {INTEGER}{WHITE_SPACE}*"/"{WHITE_SPACE}*{INTEGER}{WHITE_SPACE}*


WORD                  ({ALPHA_CHAR}|{DIGIT}|{SYMB_CHAR})+{WHITE_SPACE}*

HWORD                 "#"{WORD}
ATWORD                "@"{WORD}

MULTIWORD             {WORD}{2,}


UNIT                  "%"{WHITE_SPACE}*({MULTIWORD}*|{PUNC_CHAR}*)?



%%

[ \t]

"{"                   {return LCURL;}
"}"                   {return RCURL;}
"~"                   {return TILDE;}

{ATWORD}              { yytext++;
                        yylval.string = yytext;
                        return ATWORD;
}

{HWORD}               { yytext++;
                        yylval.string = yytext;
                        return HWORD;
                      }

                      
{UNIT}                { yytext++;
                        yylval.string = yytext;
                        return UNIT;}

{DIGIT}               { yylval.number = strtod(yytext, NULL); return NUMBER;}
{INTEGER}             { yylval.number = strtod(yytext, NULL); return NUMBER;}
{DECIMAL}             { yylval.number = strtod(yytext, NULL); return NUMBER;}
{FRACTIONAL}          { char * tok = strtok(yytext, "/");

                        double first = strtod(tok, NULL);

                        tok = strtok(NULL, "/");

                        double second = strtod(tok, NULL);

                        double final = first/second;

                        yylval.number = final;

                        return NUMBER;}

{WORD}                { yylval.string = yytext;
                        return WORD;
                      }
{MULTIWORD}           { yylval.string = yytext;
                        return MULTIWORD;
                      }



{PUNC_CHAR}           { yylval.character = yytext[0];
                        printf("char: |%c|", yytext[0]);
                        return PUNC_CHAR; }

{NEW_LINE}            {return NL;}




%%

int main( int argc, char ** argv ){
  ++argv;
  --argc;

  if( argc > 0 ){
    yyin = fopen(argv[0], "r");
  } else {
    yyin  = stdin;
  }

  yyparse();

  yylex();
  
  printf("\n");
  
  return 0;
}

此词法分析器操作不正确:

yylval.string = yytext;  /* NEVER do this */

yytext 是指向词法分析器拥有的临时存储缓冲区的指针。它的内容仅在下一次调用词法分析器之前有效;在那之后,指针可能不再有效(如果缓冲区被重新分配)或者内容将发生变化。

所以你必须复制yytext指向的字符串,如果你需要它比词法分析器动作更持久(如果你想传递字符串,你当然需要到解析器)。

如果你有strdup(或包含一个实现),你可以只使用yylval.string = strdup(yytext),或者你可以自己动态分配存储;如果您选择后一种解决方案,请记住 yyleng 包含文本的长度,因此不需要 strlen:

    /* +1 for the null terminator */
yylval.string = malloc(yyleng + 1);  
    /* should check for allocation failure */
memcpy(yylval.string, yytext, yyleng + 1);

当您不再需要时,请不要忘记free()副本。