Как использовать yylval со строками в yacc - PullRequest
5 голосов
/ 05 декабря 2009

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

Ответы [ 3 ]

16 голосов
/ 23 сентября 2012

Ключом к возвращению строки или любого сложного типа через 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);
}}
7 голосов
/ 05 декабря 2009

См. раздел руководства 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;
3 голосов
/ 19 апреля 2016

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

Синтаксический анализ (чтобы проверить, соответствует ли входной текст указанной грамматике) состоит из двух фаз:

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

При выполнении фаза 1 при заданном входном потоке при каждом вызове yylex () идентифицируется токен (строка char), а yytext указывает на первый символ этой строки. Например: при вводе поток "int x = 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 к этому VALUE обращаются как yylval.

Например, снова в соответствии с текущим примером, yylex может иметь следующее правило для идентификации 10

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

и следующие для идентификации х

[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, используйте это старое значение VALUE в качестве следующего:

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