Как получить всю входную строку в Lex и Yacc?

Итак, вот в чем дело.

На моем языке у меня есть несколько команд, скажем

XYZ 3 5
GGB 8 9
HDH 8783 33

И в моем файле Lex

XYZ { return XYZ; }
GGB { return GGB; }
HDH { return HDH; }
[0-9]+ { yylval.ival = atoi(yytext); return NUMBER; }
\n  { return EOL; }

В моем файле yacc

start : commands
    ;

commands : command
         | command EOL commands
    ;

    command : xyz
            | ggb
            | hdh
    ;

    xyz : XYZ NUMBER NUMBER { /* Do something with the numbers */ }
       ;

    etc. etc. etc. etc.

Мой вопрос в том, как я могу получить весь текст

XYZ 3 5
GGB 8 9
HDH 8783 33

В команды, все еще возвращая ЦИФРЫ?

Также, когда мой Lex возвращает STRING [0-9a-zA-Z]+, и я хочу проверить его длину, должен ли я сделать это как

rule: STRING STRING { if (strlen($1) < 5 ) /* Do some shit else error */ }

или на самом деле у меня есть токен в моем Lex, который возвращает разные токены в зависимости от длины?


person DevDevDev    schedule 05.08.2009    source источник


Ответы (3)


Если я правильно понял ваш первый вопрос, у вас могут быть семантические действия, такие как

{ $$ = makeXYZ($2, $3); }

что позволит вам построить значение команды по своему усмотрению.

Что касается вашего второго вопроса, границы между лексическим анализом и грамматическим анализом, а также между грамматическим анализом и семантическим анализом не являются жесткими и четко установленными. Их перемещение — это компромисс между такими факторами, как простота описания, ясность сообщений об ошибках и надежность при наличии ошибок. Учитывая проверку длины строки, вероятность возникновения ошибки довольно высока, и сообщение об ошибке, если оно обрабатывается путем возврата разных терминалов для другой длины, вероятно, будет неясным. Поэтому, если это возможно — это зависит от грамматики — я бы обработал это на этапе семантического анализа, когда сообщение можно легко адаптировать.

person AProgrammer    schedule 06.08.2009

Если вы сделаете так, чтобы ваш лексический анализатор (yylex()) сохранял всю строку в какой-либо переменной, тогда ваш код сможет получить к ней доступ. Связь с собственно синтаксическим анализатором будет осуществляться через обычные механизмы, но ничто не говорит о том, что вы не можете также скрывать другую переменную (вероятно, статическую переменную файла, но остерегайтесь многопоточности), которая хранит всю входную строку до того, как она будет разобрана.

person Jonathan Leffler    schedule 06.08.2009
comment
Я думаю, может быть, я не был так ясен, но у меня есть что-то вроде БЛОК ЗАГОЛОВКА1 МАТЕРИАЛЫ БЛОК1END БЛОК2 МАТЕРИАЛЫ БЛОК2END Где БЛОК1 и БЛОК2 соответствуют одному и тому же правилу. Что мне нужно, так это весь текст BLOCK1 на тот момент, когда я сопоставляю BLOCK1. Самый простой, но раздражающий способ — это просто сделать так, чтобы все правила имели значение строки, т. е. из моего примера %type ‹sval› xyz Тогда для каждого правила мне пришлось бы поставить xyz: XYZ NUMBER NUMBER { $$ = XYZ + 2 доллара + 3 доллара; } Что сверхурочно может стать очень раздражающим. - person DevDevDev; 06.08.2009

Поскольку вы используете yylval.ival, у вас уже есть union с полем ival в исходном коде YACC, например:

%union {
    int ival;
}

Теперь вы указываете тип токена, например:

%token <ival> NUMBER

Итак, теперь вы можете получить доступ к полю ival просто для токена NUMBER как $1 в ваших правилах, например

xyz : XYZ NUMBER NUMBER { printf("XYZ %d %d", $2, $3); }

Для вашего второго вопроса я бы определил союз следующим образом:

%union {
    char*   strval;
    int     ival;
}

и в вашем источнике LEX укажите типы токенов

%token <strval> STRING;
%token <ival> NUMBER;

Итак, теперь вы можете делать такие вещи, как

foo : STRING NUMBER { printf("%s (len %d) %d", $1, strlen($1), $2); }
person qrdl    schedule 06.08.2009
comment
Это дает segfault при совпадении со строкой. - person Nathan Schwermann; 14.11.2012