Это не тот ответ, на который вы надеетесь. Я думаю, причина, по которой вы не видели примеров того, чего вы хотите, заключается в том, что нецелесообразно применять правила набора текста в файле грамматики (.y); скорее, разработчики выполняют это в процедурном коде .c или .cpp. Как правило, в любом случае вы будете проводить анализ анализируемого ввода, поэтому это побочный продукт для обеспечения соблюдения семантических правил, как вы это делаете.
В стороне, я не совсем понимаю, как вы разбираете выражения, учитывая фрагмент вашей грамматики, который вы воспроизводите в своем вопросе.
Вот почему я утверждаю, что это непрактично. (1) Ваша информация о типе должна просачиваться через не-терминалы грамматики. (2) Хуже того, это должно быть отражено в именах переменных.
Рассмотрим этот игрушечный пример анализа простых операторов присваивания, которые могут использовать идентификаторы, числовые константы и четыре оператора настольного калькулятора. Маркер NUMBER может быть целым числом, равным 42, или плавающим, как 3.14. И скажем, что IDENTIFIER - это одна буква, A-Z.
%token IDENTIFIER NUMBER
%%
stmt : IDENTIFIER '=' expr
;
expr : expr '+' term
| expr '-' term
| term
;
term : term '*' factor
| term '/' factor
| factor
;
factor : '(' expr ')'
| '-' factor
| NUMBER
| IDENTIFIER
;
Теперь давайте попробуем ввести правила набора текста. Мы разделим токен NUMBER в FLT_NUMBER и INT_NUMBER.Наши expr
, term
и factor
нетерминалы разделить на две части, а также:
%token IDENTIFIER FLT_NUMBER INT_NUMBER
stmt : IDENTIFIER '=' int_expr
| IDENTIFIER '=' flt_expr
;
int_expr : int_expr '+' int_term
| int_expr '-' int_term
| int_term
;
flt_expr : flt_expr '+' flt_term
| flt_expr '-' flt_term
| flt_term
;
int_term : int_term '*' int_factor
| int_term '/' int_factor
| int_factor
;
flt_term : flt_term '*' flt_factor
| flt_term '/' flt_factor
| flt_factor
;
int_factor : '(' int_expr ')'
| '-' int_factor
| INT_NUMBER
| int_identifier
;
flt_factor : '(' flt_expr ')'
| '-' flt_factor
| FLT_NUMBER
| flt_identifier
;
int_identifier : IDENTIFIER ;
flt_identifier : IDENTIFIER ;
Поскольку наша грамматика стоит в этой точке, возникает конфликт: синтаксический анализатор не может сказать, следует ли признавать IDENTIFIER как int_identifier
или a flt_identifier
. Поэтому он не знает, следует ли уменьшить A = B
как IDENTIFIER = int_expr
или IDENTIFIER = flt_expr
.
(Здесь мое понимание Ruby немного мягкое :) Ruby (как и большинство языков) не предоставляет способ на лексическом уровне для определения числового типа идентификатора. Сравните это со старой школой BASIC, где A обозначает число, а A $ обозначает строку. Другими словами, если вы изобрели язык, где, скажем, A # обозначает целое число, а A @ обозначает float, то вы можете сделать эту работу.
Если вы хотите разрешить ограниченные выражения смешанного типа, например int_term '*' flt_factor
, то ваша грамматика станет еще сложнее.
Могут быть способы обойти эти проблемы. Парсер, построенный по технологии, отличной от yacc/bison, может упростить работу. По крайней мере, возможно, мой эскиз даст вам несколько идей, чтобы продолжить.
Вы неправильно поняли цель типа%. Он предназначен для управления типами собственных грамматических терминалов и нетерминалов для использования в YYUNION, поэтому вам не нужно писать typecasts для ваших $$, $ 1 и т. Д .: не позволять вам управлять семантическими типами в программе, которую вы разбираете. – EJP