Как использовать yylval со строками в yacc

Я хочу передать фактическую строку токена. Если у меня есть токен с именем ID, то я хочу, чтобы мой файл yacc действительно знал, как называется ID. Я думаю, что мне нужно передать строку, используя yylval, в файл yacc из файла flex. Как я могу это сделать?


person neuromancer    schedule 05.12.2009    source источник
comment
Пожалуйста, хотя бы посмотрите документацию, прежде чем задавать вопрос здесь.   -  person    schedule 06.12.2009


Ответы (3)


См. раздел руководства Flex, посвященный взаимодействию с YACC.

15 Взаимодействие с Yacc

Одним из основных применений flex является его дополнение к парсеру-генератору yacc. Синтаксические анализаторы yacc ожидают вызова подпрограммы с именем yylex() для поиска следующей входной лексемы. Предполагается, что подпрограмма возвращает тип следующего токена, а также помещает любое связанное значение в глобальный yylval. Чтобы использовать flex с yacc, нужно указать опцию `-d' для yacc, чтобы указать ему сгенерировать файл y.tab.h, содержащий определения всех %токенов, появляющихся во входных данных yacc. Затем этот файл включается в сканер flex. Например, если один из токенов — TOK_NUMBER, часть сканера может выглядеть так:

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

     %%

     [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;
person Community    schedule 05.12.2009
comment
Это совсем не объясняет, как вернуть строку, поскольку YYSTYPE (здесь не упоминается) по умолчанию имеет значение int. - person JonnyJD; 11.02.2014

Ключом к возврату строки или любого сложного типа через yylval является объединение YYSTYPE, созданное yacc в файле y.tab.h. YYSTYPE — это объединение с членом для каждого типа токена, определенного в исходном файле yacc. Например, чтобы вернуть строку, связанную с токеном SYMBOL в исходном файле yacc, вы объявляете это объединение YYSTYPE, используя %union в исходном файле yacc:

/*** 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, потому что это статический буфер, который повторно используется для каждого сопоставленного токена. Чтобы вернуть совпадающий текст, статический буфер yytext должен быть реплицирован в куче с помощью _strdup(), а указатель на эту строку передается через 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);
}}
person JonN    schedule 23.09.2012

Настройка контекста

Синтаксический анализ (для проверки соответствия входного текста заданной грамматике) состоит из двух этапов:

  1. токенизация, которая выполняется такими инструментами, как lex или flex, с интерфейсом yylex()) и
  2. анализ потока токена, сгенерированного на шаге 1 (в соответствии с заданной пользователем грамматикой), который выполняется такими инструментами, как bison/yacc с интерфейсом yyparse()).

При выполнении этапа 1 при заданном входном потоке каждый вызов yylex() идентифицирует токен (строку символов), а yytext указывает на первый символ этой строки. Например: при входном потоке "целое число х = 10;" и с правилами lex для токенизации, соответствующими языку C, то первые 5 вызовов yylex() будут идентифицировать следующие 5 токенов "int", "x", "=", "10", ";" и каждый раз yytext будет указывать на первый символ возвращаемого токена.

Фаза 2. Анализатор (который вы упомянули как yacc ) — это программа, которая каждый раз вызывает эту функцию yylex для получения токена и использует эти токены, чтобы проверить, соответствует ли он правилам грамматики. Эти вызовы yylex вернут токены в виде некоторых целочисленных кодов. Например, в предыдущем примере первые 5 вызовов yylex() могут возвращать синтаксическому анализатору следующие целые числа: TYPE, ID, EQ_OPERATOR и INTEGER (действительные целые значения которых определены в некотором заголовочном файле).

Теперь парсер может видеть только эти целочисленные коды, которые иногда могут быть бесполезны. Например, в работающем примере вы можете связать TYPE с int, ID с некоторым указателем таблицы символов и INTEGER с десятичным числом 10. Чтобы облегчить это, каждый токен, возвращаемый yylex, связан с другим VALUE, тип которого по умолчанию — int, но у вас могут быть пользовательские типы для этого. В среде lex это ЗНАЧЕНИЕ доступно как 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 это ЗНАЧЕНИЕ идентифицируется/доступно как $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.}

Отвечаю на ваш вопрос

Если вы хотите получить доступ к значению yytext определенного токена (который связан с миром lex) в мире yacc, используйте это старое знакомое ЗНАЧЕНИЕ следующим образом:

  1. Увеличьте тип объединения VALUE, чтобы добавить еще одно поле, скажем, char* lex_token_str
  2. В правиле lex выполните yylval.lex_token_str = strdup(yytext)
  3. Затем в мире yacc получите к нему доступ, используя соответствующий $n.
  4. Если вы хотите получить доступ к более чем одному значению токена (например, для идентификатора токена, идентифицированного lex, синтаксический анализатор может захотеть получить доступ как к имени, так и к указателю таблицы символов), затем дополните тип объединения VALUE с помощью член структуры, содержащий char* (для имени) и int* (для указателя symtab).
person Sandeep Dasgupta    schedule 19.04.2016