如何在yacc中使用yylval和字符串

时间:2009-12-05 10:35:05

标签: yacc bison lexical-analysis

我想传递令牌的实际字符串。如果我有一个名为ID的令牌,那么我希望我的yacc文件实际知道调用了什么ID。我必须使用yylval将字符串传递给flex文件中的yacc文件。我该怎么做?

3 个答案:

答案 0 :(得分:16)

通过yylval返回字符串或任何复杂类型的关键是yacc在y.tab.h文件中创建的YYSTYPE联合。 YYSTYPE是一个联合,其中包含yacc源文件中定义的每种令牌类型的成员。例如,要返回与yacc源文件中的SYMBOL标记关联的字符串,请使用yacc源文件中的%union 声明此YYSTYPE联合:

/*** Yacc's YYSTYPE Union ***/

/* The yacc parser maintains a stack (array) of token values while
   it is parsing.  This union defines all the possible values tokens
   may have.  Yacc creates a typedef of YYSTYPE for this union. All
   token types (see %type declarations below) are taken from
   the field names of this union.  The global variable yylval which lex
   uses to return token values is declared as a YYSTYPE union.
 */

    %union {
        long int4;              /* Constant integer value */
        float fp;               /* Constant floating point value */
        char *str;              /* Ptr to constant string (strings are malloc'd) */
        exprT expr;             /* Expression -  constant or address */
        operatorT *operatorP;   /* Pointer to run-time expression operator */
    };

%type <str> SYMBOL

然后在LEX源文件中有一个与SYMBOL令牌匹配的模式。与该规则关联的代码负责返回表示SYMBOL的实际字符串。您不能只是将指针传递给yytext缓冲区,因为它是一个静态缓冲区,可以为匹配的每个标记重用。要返回匹配的文本,必须使用_strdup()在堆上复制静态yytext缓冲区,并通过yyval.str传递指向此字符串的指针。然后是yacc规则,它与SYMBOL令牌的责任相匹配,即当它完成时释放堆分配的字符串。

[A-Za-z_][A-Za-z0-9_]*  {{
    int i;

    /*
    * condition letter followed by zero or more letters
    * digits or underscores
    *      Convert matched text to uppercase
    *      Search keyword table
    *      if found
    *          return <keyword>
    *      endif
    * 
    *      set lexical value string to matched text
    *      return <SYMBOL>
    */

    /*** KEYWORDS and SYMBOLS ***/
    /* Here we match a keywords or SYMBOL as a letter
    * followed by zero or more letters, digits or 
    * underscores.
    */

    /* Convert the matched input text to uppercase */
    _strupr(yytext);         /* Convert to uppercase */

    /* First we search the keyword table */
    for (i = 0; i<NITEMS(keytable); i++) {
        if (strcmp(keytable[i].name, yytext)==0)
            return (keytable[i].token);
    }

    /* Return a SYMBOL since we did not match a keyword */
    yylval.str=_strdup(yytext);
    return (SYMBOL);
}}

答案 1 :(得分:7)

请参阅the Flex manual section on Interfacing with YACC

  

15与Yacc接口

     

flex的主要用途之一是作为a   yacc的伴侣   分析器生成。 yacc解析器期望   调用名为yylex()的例程   找到下一个输入令牌。例程   应该返回的类型   下一个令牌以及任何   全局yylval中的关联值。   要使用flex和yacc,可以指定   yacc的`-d'选项来指示它   生成文件y.tab.h   包含所有的定义   yacc输入中出现的%令牌。   然后,此文件包含在flex中   扫描器。例如,如果其中之一   令牌是TOK_NUMBER,是其中的一部分   扫描仪可能看起来像:

     %{
     #include "y.tab.h"
     %}

     %%

     [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;

答案 2 :(得分:3)

设置背景

语法分析(检查输入文本是否遵循指定的语法)包含两个阶段:

  1. 标记化,由lex或flex等工具完成,界面为yylex())和
  2. 解析在步骤1中生成的令牌流(根据用户指定的语法),这是通过bison / yacc等工具和yyparse()接口完成的。
  3. 在执行阶段1 时,给定输入流,每次调用yylex()都会标识一个标记(一个字符串),yytext指向该字符串的第一个字符。例如:使用输入流“int x = 10;”并且使用符合C语言的标记化的lex规则,然后对yylex()的前5次调用将识别以下5个标记“int”,“x”,“=”,“10”,“;”每次yytext都指向返回令牌的第一个字符。

    阶段2 ,解析器(您提到的yacc)是一个程序,每次调用此yylex函数获取一个令牌并使用这些令牌来查看它是否符合规则一个语法。这些对yylex的调用将返回令牌作为一些整数代码。例如,在前面的示例中,对yylex()的前5次调用可能会将以下整数返回给解析器:TYPE,ID,EQ_OPERATOR和INTEGER(其实际整数值在某些头文件中定义)。

    现在所有解析器都可以看到那些整数代码,这些代码有时可能没用。例如,在运行示例中,您可能希望将TYPE与int,ID关联到某个符号表指针,将INTEGER关联到十进制10.为方便起见,yylex返回的每个标记与另一个默认类型为int的VALUE关联,但是你可能有自定义类型。在lex环境中,此VALUE作为yylval访问。

    例如,再次根据运行示例,yylex可能具有以下规则来标识10

    [0-9]+   {  yylval.intval = atoi(yytext); return INTEGER; }
    

    ,然后确定x

    [a-zA-Z][a-zA-Z0-9]*   {yylval.sym_tab_ptr = SYM_TABLE(yytext); return ID;}
    

    请注意,我在这里定义了VALUE(或yylval)类型作为包含int(intval)和int *指针(sym_tab_ptr)的联合。

    但是在yacc世界中,这个VALUE被识别/访问为$ n。例如,请考虑以下yacc规则来标识特定的赋值语句

    TYPE ID '=' VAL:  { //In this action part of the yacc rule, use $2 to get the symbol table pointer associated with ID, use $4 to get decimal 10.}
    

    回答您的问题

    如果您想在yacc世界中访问某个令牌(与lex世界相关)的yytext值,请使用该旧朋友VALUE如下:

    1. 增加VALUE的联合类型以添加​​另一个字段,例如char * lex_token_str
    2. 在lex规则中,执行yylval.lex_token_str = strdup(yytext)
    3. 然后在yacc world中使用适当的$ n。
    4. 访问它
    5. 如果您想要访问更多的令牌的单个值,(例如对于lex识别的令牌ID,解析器可能想要访问名称和符号表指针),那么增加联合类型VALUE与结构成员,包含char *(用于名称)和int *(用于symtab指针)。