%token <sym> NUMBER PRINT VAR BLTIN UNDEF WHILE IF ELSE
%type <inst> stmt asgn expr stmtlist cond while if end
%right '='
%left OR
%left AND
%left GT GE LT LE EQ NE
%left '+'
%left '*' '/'
%left UNARYMINUS NOT
%right '^'
%%
list: /* nothing */
| list '\n'
| list asgn '\n' { code2(pop, STOP); return 1; }
| list stmt '\n' { code(STOP); return 1; }
| list expr '\n' { code2(print, STOP); return 1; }
| list error '\n' { yyerrok; }
;
asgn: VAR '=' expr { $$=$3; code3(varpush, (Inst)$1, assign); }
;
stmt: expr { code(pop); }
| PRINT expr { code(prexpr); $$ = $2; }
| while cond stmt end {
($1)[1] = (Inst)$3; /* body of loop */
($1)[2] = (Inst)$4; } /* end, if cond fails */
| if cond stmt end { /* else-less if */
($1)[1] = (Inst)$3; /* thenpart */
($1)[3] = (Inst)$4; } /* end, if cond fails */
| if cond stmt end ELSE stmt end { /* if with else */
($1)[1] = (Inst)$3; /* thenpart */
($1)[2] = (Inst)$6; /* elsepart */
($1)[3] = (Inst)$7; } /* end, if cond fails */
| '{' stmtlist '}' { $$ = $2; }
;
cond: '(' expr ')' { code(STOP); $$ = $2; }
;
while: WHILE { $$ = code3(whilecode, STOP, STOP); }
;
if: IF { $$=code(ifcode); code3(STOP, STOP, STOP); }
;
end: /* nothing */ { code(STOP); $$ = progp; }
;
stmtlist: /* nothing */ { $$ = progp; }
| stmtlist '\n'
| stmtlist stmt
;
expr: NUMBER { $$ = code2(constpush, (Inst)$1); }
| VAR { $$ = code3(varpush, (Inst)$1, eval); }
| asgn
| BLTIN '(' expr ')'
{ $$ = $3; code2(bltin, (Inst)$1->u.ptr); }
| '(' expr ')' { $$ = $2; }
| expr '+' expr { code(add); }
| expr '-' expr { code(sub); }
| expr '*' expr { code(mul); }
| expr '/' expr { code(div); }
| expr '^' expr { code (power); }
| '-' expr %prec UNARYMINUS { $$ = $2; code(negate); }
| expr GT expr { code(gt); }
| expr GE expr { code(ge); }
| expr LT expr { code(lt); }
| expr LE expr { code(le); }
| expr EQ expr { code(eq); }
| expr NE expr { code(ne); }
| expr AND expr { code(and); }
| expr OR expr { code(or); }
| NOT expr { $$ = $2; code(not); }
%%
/* end of grammar */
В грамматике есть пять случаев неоднозначности типа сдвиг/свертка, подобных упомянутой в грамматике для
hoc3
.
Обратите внимание на то, что команды
STOP
, завершающие последовательность операторов, теперь порождаются в нескольких местах. Как и прежде,
progp
здесь представляет собой адрес очередной создаваемой команды. При выполнении все команды
STOP
служат для завершения цикла в функции
execute
. Правило для понятия все по сути является подпрограммой, вызываемой из нескольких мест, именно оно порождает команду
STOP
и возвращает адрес следующей за ней команды.
Команды, создаваемые для операторов
if
и
while
, требуют особого рассмотрения. Когда встречается ключевое слово
while
, порождается операция
whilecode
, и адрес этой команды возвращается в качестве значения правила
пока: WHILE
Но в то же самое время резервируются два следующих машинных слова, которые будут определены ниже. Далее создаются команды для выражения, которое образует условие в операторе
while
. Значение, возвращаемое правилом
cond
, является адресом начала команд для условия. После распознавания всего оператора
while
в два зарезервированных слова, вводимых за командой
whilecode
, заносится адрес начала тела цикла и адрес оператора, следующего за циклом. (Команда по этому адресу будет создана позднее.)
| пока усл опер все {
($1)[1] = (Inst) $3 /* тело цикла */
($1)[2] = (Inst) $4 /* все, если условие неверно */
}
Здесь
$1
обозначает адрес команды
whilecode
, таким образом,
($1)[1]
и
($1)[2]
обозначают два следующих слова.
Рисунок, приведенный ниже, может прояснить ситуацию:
С оператором
if
дело обстоит аналогично, но резервируются три слова: для частей
then
и
else
, а также для оператора, следующего за
if
. Мы вскоре рассмотрим этот случай.