9

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

4

3 回答 3

22

通过 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);
}}
于 2012-09-23T03:29:43.657 回答
7

请参阅Flex 手册中有关与 YACC 连接的部分

15 与 Yacc 的接口

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

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

     %%

     [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;
于 2009-12-05T11:09:06.280 回答
3

设置上下文

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

  1. 标记化,由 lex 或 flex 等工具完成,带有接口 yylex()) 和
  2. 解析步骤 1 中生成的令牌流(根据用户指定的语法),这是由带有接口 yyparse() 的 bison/yacc 等工具完成的。

在执行阶段 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 world 中某个令牌(与 lex world 相关)的 yytext 值,请使用该老朋友 VALUE,如下所示:

  1. 增加 VALUE 的联合类型以添加​​另一个字段,例如 char* lex_token_str
  2. 在 lex 规则中,执行 yylval.lex_token_str = strdup(yytext)
  3. 然后在 yacc world 中使用适当的 $n 访问它。
  4. 如果您想要访问的不仅仅是单个令牌值(例如,对于 lex 标识的令牌 ID,解析器可能希望同时访问名称和符号表指针),然后使用 VALUE 增加联合类型结构成员,包含 char*(用于名称)和 int*(用于 symtab 指针)。
于 2016-04-19T14:51:38.573 回答