Умножение путем сопоставления в yacc

Я пытаюсь реализовать грамматику, которая позволяет умножать путем сопоставления. Это для анализа полиномиальных входных данных для CAS.

Насколько мне известно, он работает довольно хорошо, за исключением нескольких крайних случаев. Я выделил две проблемы:

  1. Конфликт с другими правилами, например, a^2 b (ошибочно) интерпретируется как (^ a (* 2 b)), а не как (* (^ a 2) b).
  2. yacc(bison) сообщает 28 shift/reduce conflicts и 8 reduce/reduce conflicts.

Я почти уверен, что правильное решение первой проблемы решит и вторую, но пока мне это не удалось.

Ниже приводится суть грамматики, с которой я работаю:

%start  prgm
%union {
    double  num;
    char    *var;
    ASTNode *node;
}
%token  <num>   NUM
%token  <var>   VAR
%type   <node>  expr

%left   '+' '-'
%left   '*' '/'
%right  '^'
%%
prgm:     // nothing
    | prgm '\n'
    | prgm expr '\n'
    ;
expr:     NUM
    | VAR
    | expr '+' expr
    | expr '-' expr
    | expr '*' expr
    | expr '/' expr
    | expr '^' expr
    | expr expr %prec '*'
    | '-' expr
    | '(' expr ')'
    ;
%%

Удаление правила сопоставления (expr expr %prec '*') устраняет предупреждения о сдвиге/уменьшении и уменьшении/уменьшении.

Обратите внимание, что ab в моей грамматике должно означать (* a b). Перед многосимвольными переменными должна стоять кавычка ('); это уже хорошо обработано в файле lex. Лексер полностью игнорирует пробелы ( ) и табуляции (\t).

Мне известен этот вопрос, но использование сопоставления здесь, похоже, не указывает на умножение.

Любые комментарии или помощь будут очень признательны!


P.S. Если это поможет, это ссылка на весь проект.


person Jay Lee    schedule 27.03.2021    source источник
comment
Семантика сопоставленного оператора не имеет абсолютно никакого отношения к синтаксису. Семантика даже не является частью грамматики. Насколько я вижу, связанный вопрос является точной копией. (Во всяком случае, семантика там тоже умножение.)   -  person rici    schedule 27.03.2021
comment
@rici Спасибо за комментарий. Я попытался применить решение, указанное в связанном вопросе, заменив все мои expr на expr_sequence и добавив новый нетерминал expr_sequence: expr | expr_sequence expr, который еще не потерпел неудачу. Не могли бы вы подробнее рассказать о том, где заменить мои expr на expr_sequence?   -  person Jay Lee    schedule 27.03.2021


Ответы (1)


Как указано в ответе на вопрос, который вы связали, трудно указать приоритет оператора сопоставления, потому что нет оператора для сдвига. (Как и в вашем коде, вы можете указать приоритет производственного expr: expr expr. Но с каким упреждающим токеном будет сравниваться это сокращение? Добавление каждого токена в FIRST(expr) к вашим объявлениям приоритета не очень масштабируемо и может привести к нежелательному приоритету резолюции.

Дополнительная проблема с решением приоритета заключается в поведении унарного оператора минус (проблема, не рассматриваемая в связанном вопросе), потому что, как написано, ваша грамматика позволяет анализировать a - b либо как вычитание, либо как сопоставленное умножение a и -b. (И обратите внимание, что - находится в FIRST(expr), что приводит к одному из возможных нежелательных разрешений, о которых я упоминал выше.)

Таким образом, лучшие решения, как рекомендуется в связанном вопросе, - это использовать грамматику с явным приоритетом, например следующую: (Здесь я использовал juxt в качестве имени нетерминала, а не expr_sequence):

%start  prgm
%token  NUM
%token  VAR

%left   '+' '-'
%left   '*' '/'
%right  '^'
%%
prgm:     // nothing
    | prgm '\n'
    | prgm expr '\n'
expr: juxt
    | '-' juxt
    | expr '+' expr
    | expr '-' expr
    | expr '*' expr
    | expr '/' expr
    | expr '^' expr
juxt: atom
    | juxt atom
atom: NUM
    | VAR
    | '(' expr ')'

Эта грамматика может быть не тем, что вам нужно:

  • это довольно простое обращение с унарным минусом имеет несколько проблем. Я не думаю, что проблематично то, что он анализирует -xy в -(xy) вместо (-x)y, но это не идеально. Кроме того, он не позволяет --x (тоже, вероятно, не проблема, но не идеально). Наконец, он анализирует -x^y не как -(x^y), а как (-x)^y, что противоречит частой практике.
  • Кроме того, он неправильно связывает сопоставление слишком сильно. Вы можете или не можете считать проблемой то, что a/xy анализируется как a/(xy), но вы, вероятно, будете возражать против того, чтобы 2x^7 анализировалось как (2x)^7.

Самый простой способ избежать этих проблем — использовать грамматику, в которой приоритет операций единообразно реализован с помощью однозначных правил грамматики.

Вот пример, который реализует стандартные правила приоритета (возведение в степень имеет приоритет над унарным минусом; сопоставление умножения имеет тот же приоритет, что и явное умножение). Стоит потратить несколько минут, чтобы внимательно посмотреть, какой нетерминал появляется в какой продукции, и подумать о том, как это коррелирует с желаемыми правилами приоритета.

%union {
    double  num;
    char    *var;
    ASTNode *node;
}
%token  <num>   NUM
%token  <var>   VAR
%type   <node>  expr mult neg expt atom

%%
prgm:     // nothing
    | prgm '\n'
    | prgm error '\n'
    | prgm expr '\n'
expr: mult
    | expr '+' mult
    | expr '-' mult
mult: neg
    | mult '*' neg
    | mult '/' neg
    | mult expt
neg : expt
    | '-' neg
expt: atom
    | atom '^' neg
atom: NUM
    | VAR
    | '(' expr ')'
person rici    schedule 27.03.2021
comment
Спасибо за исчерпывающий ответ! Предоставленная грамматика действительно решает вторую проблему. Как вы упомянули в посте, сопоставление действительно тесно связано и понимает a^2 b как a^(2b), чего я не хотел. Есть ли способ исправить эту ситуацию, или это ограничение парсера yacc/LALR? - person Jay Lee; 28.03.2021
comment
@JayLee: Нет, вы можете заставить бизона анализировать что угодно, если это однозначно. Но expr: expr '+' expr не однозначен, и вы полагаетесь на объявления приоритета для устранения неоднозначности. Это работает до тех пор, пока ваша грамматика не соответствует модели приоритета. Сопоставление умножения является такой точкой. В этот момент вам нужно написать однозначную грамматику. - person rici; 28.03.2021
comment
Мало того, что я мог реализовать грамматику так, как я предполагал, ваш ответ очень помог мне понять, как yacc работает с грамматикой. Теперь я лучше разбираюсь в построении однозначной грамматики с точки зрения явных объявлений приоритета и введения новых нетерминалов. Еще раз спасибо за помощь! - person Jay Lee; 28.03.2021