Инициирование правила короткого замыкания в Bison для & amp; и || операции

Я программирую простой калькулятор в Bison & Flex с использованием C / C ++ (логика выполнена в Bison, а часть C / C ++ отвечает за структуры данных, например, STL и другие).

У меня есть следующая проблема:

В моем калькуляторе знак доллара $ означает i ++ и ++ i (как префикс, так и постфикс), например :

int y = 3;
-> $y = 4
-> y$ = 4

Когда пользователь нажимает: int_expression1 && int_expression2 , если int_expression1 оценивается в 0 (то есть ложь), тогда я не хочу, чтобы бизоны оценивали int_expression2 !

Например :

int a = 0 ;
int x = 2 ;

и пользователь нажимает: int z = a&&x$

Итак, переменная a оценивается в 0 следовательно, я не хочу оценивать x однако он все равно увеличивается на 1 … вот код бизона / c ++:

%union
{
int int_value;
double double_value;
char* string_value;
}

%type <int_value> int_expr
%type <double_value> double_expr
%type <double_value> cmp_expr

int_expr:

| int_expr '&&' int_expr    { /* And operation between two integers */
if ($1 == 0)
$$ = 0;
else  // calc
$$ = $1 && $3;
}

Как я могу сказать bison не оценивать второе выражение, если первое уже было оценено как ложное (т.е. 0)

4

Решение

Преобразование обширного комментария в ответ:

Как я могу сказать Бизону не оценивать второе выражение, если первое уже было оценено как ложное?

Это ваш код, который выполняет оценку, а не Bison; положить «вину», где он принадлежит.

Вы должны обнаружить, что вы имеете дело с && правило, прежде чем RHS оценивается. Скорее всего, вам нужно вставить некоторый код после && и перед вторым int_expr что приостанавливает оценку, если первый int_expr оценивает до 0. Вам также нужно изменить весь другой код оценки, чтобы проверить и выполнить флаг «не оценивать».

В качестве альтернативы, у вас есть Bison, который выполняет синтаксический анализ и создает программу, которую вы выполняете, когда анализ завершен, а не оценивается во время анализа. Это гораздо больший набор изменений.

Вы уверены, что хотите поместить код перед вторым int_expr? Я не могу найти правдоподобный способ сделать это. Это хороший трюк, но я не могу найти способ на самом деле сказать Бизону не оценивать второе int_exprбез ущерба для всей оценки.

Вы должны написать свой код, чтобы он не оценивался, когда он не должен оцениваться. Синтаксис Bison:

| int_expr '&&' {...code 1...} int_expr {...code 2...}

«Код 1» проверит $1 и организовать прекращение оценки (установить глобальную переменную или что-то подобное). «Код 2» будет условно оценивать $4 (4 потому что «код 1» теперь 3 доллара). Весь код оценки должен подчиняться указаниям «кода 1» — он не должен оценивать, если «код 1» говорит «не оценивать». Или вы можете сделать то, что я предложил и aselle предложенный; разбирать и оценивать отдельно.

Второе предложение Асель о Среда программирования UNIX. Там есть целая глава о разработке калькулятора (они называют это hoc для калькулятора более высокого порядка), который стоит прочитать. Имейте в виду, однако, что книга была опубликована в 1984 году и предшествует стандарту Си с хорошим отрывом. В коде C нет прототипов, и (по современным стандартам) это занимает несколько свобод. я должен hoc6 (последняя версия hoc они описывают; также версии 1-3) в современном C — свяжитесь со мной, если хотите (см. мой профиль).

Вот в чем проблема: я не могу перестать оценивать в середине правила, так как не могу использовать return (Я могу, но бесполезно; это приводит к выходу программы). | intExpr '&&' { if ($1 == 0) {/* turn off a flag */ } } intExpr { /* code */} После выхода $3 $4 оценивается автоматически.

Вы можете прекратить оценку в середине правила, но вы должны кодировать свой блок кода оценки выражения, чтобы учесть эту возможность. И когда я сказал «прекратить оценку», я имел в виду «прекратить делать вычисления», а не «остановить анализатор на его пути». Разбор должен продолжаться; Ваш код, который вычисляет значения, должен оценивать только тогда, когда требуется оценка, а не когда оценка не требуется. Это может быть (тьфу!) Глобальный флаг, или у вас может быть какой-то другой механизм.

Вероятно, лучше всего преобразовать ваш анализатор в генератор кода и выполнить код после того, как вы его проанализировали. Это своего рода осложнение, поэтому это хорошая стратегия.

@JonathanLeffler: Ты действительно король! Это должен быть ответ !!!

Теперь это ответ.

2

Другие решения

Вы почти наверняка захотите сгенерировать какое-то другое представление перед оценкой в ​​своем калькуляторе Дерево разбора или ast — это классические методы, но простой стековый компьютер также популярен. Есть много замечательных примеров того, как это сделать, но мой любимый
http://www.amazon.com/Unix-Programming-Environment-Prentice-Hall-Software/dp/013937681X
Это показывает, как взять простой инструмент прямой оценки, подобный тому, который вы создали в yacc (старый бизон), и перейти на язык программирования, который почти такой же мощный, как BASIC. Все в очень немногих страницах. Это очень старая книга, но она того стоит.

Вы также можете посмотреть на SeExpr http://www.disneyanimation.com/technology/seexpr.html
который является простым калькулятором языка выражений для скаляров и 3 векторов. Если вы посмотрите на https://github.com/wdas/SeExpr/blob/master/src/SeExpr/SeExprNode.cpp
на линии 313 вы увидите && реализация функции eval ():

void
SeExprAndNode::eval(SeVec3d& result) const
{
// operands and result must be scalar
SeVec3d a, b;
child(0)->eval(a);
if (!a[0]) {
result[0] = 0;
} else {
child(1)->eval(b);
result[0] = (b[0] != 0.0);
}
}

Этот файл содержит все объекты, которые представляют операции в дереве разбора. Эти объекты генерируются при разборе кода (это действия в yacc). Надеюсь это поможет.

1