Странная проблема с контекстно-свободной грамматикой

Я начинаю с хорошо сформированной (и хорошо работающей) грамматики для языка , Переменные, К этой грамматике я хотел бы добавить то, что я ...

Я начинаю с хорошо сформированной (и хорошо работающей) грамматики для языка. Переменные, К этой грамматике я хотел бы добавить то, что я ...

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

object
  : object_name ARROW more_objects
  ;

more_objects
  : object_name
  | object_name ARROW more_objects
  ;

object_name
  : IDENTIFIER
  ;

Суть в том, чтобы иметь возможность доступа к скалярам вложенный в объекты. Например:

car->color
monster->weapon->damage
pc->tower->motherboard->socket_type

Я добавляю объект как primary_expression :

primary_expression
  : id_lookup
  | constant_value
  | '(' expression ')'
  | list_initialization
  | function_call
  | object
  ;

Теперь вот пример сценария:

const list = [ 1, 2, 3, 4 ];
for var x in list {
  send "foo " + x + "!";
}
send "Done!";

Перед добавлением нетерминального объекта как primary_expression все это солнце и щенки. Даже после того, как я добавлю это, Бизон не жалуется. Не сообщается о сдвиге и / или уменьшении конфликтов. И сгенерированный код компилируется без звука. Но когда я пытаюсь запустить приведенный выше пример сценария, в строке 2 появляется сообщение об ошибке : Попытка использовать неопределенный символ '{' в строке 2.

Если я изменю сценарий на:

var list = 0;
for var x in [ 1, 2, 3, 4 ] {
  send "foo " + x + "!";
}
send "Done!";

Затем я получаю ошибку в строке 3: Попытка использовать неопределенный символ '+' в строке 3.

Очевидно, что присутствие объекта в грамматике портит поведение синтаксического анализатора [SOMEhow], и я чувствую, что игнорирую довольно простой принцип теории языка, который исправил бы это в одно мгновение, но тот факт, что нет никаких конфликтов сдвига / уменьшения, оставил меня в замешательстве.

лучший способ (грамматически) написать эти правила? Что мне не хватает? Почему нет никаких конфликтов?

(А вот полный файл грамматики , если это помогает)


ОБНОВЛЕНИЕ: Чтобы пояснить, этот язык, который компилируется в код, выполняемый виртуальная машина, встроенная в другую систему - игру, в частности. У него есть скаляры и списки, и нет сложных типов данных. Когда я говорю, что хочу добавить object s к языку, это на самом деле неправильно. Я не добавляю поддержку пользовательских типов в свой язык.

Объекты, к которым осуществляется доступ с помощью конструкции object , на самом деле являются объектами из игры, к которым я предоставляю доступ языковому процессору через промежуточный слой, который соединяет виртуальную машину с игровым движком. Этот слой предназначен для максимально возможного отделения определения языка и механики виртуальной машины от реализации и деталей игрового движка.

Так что, когда на моем языке я пишу:

player->name

Это кодифицируется только компилятором , «player» и «name» не являются традиционными идентификаторами s, поскольку они не добавляются в таблицу символов, и с ними ничего не делается во время компиляции, кроме как перевести запрос имени игрока в трехадресный код.

9
задан Chris Tonkinson 6 August 2010 в 07:39
поделиться

6 ответов

Итак, я потратил разумное количество времени на изучение грамматики (и вывода bison) и не увидел, что здесь явно не так. Не имея средств для его выполнения, я не могу легко выяснить, что происходит, экспериментально. Поэтому вот несколько конкретных шагов, которые я обычно выполняю при отладке грамматик. Надеюсь, вы сможете сделать что-либо из того, что еще не сделали, а затем, возможно, опубликуете последующие действия (или отредактируете свой вопрос) с любыми результатами, которые могут быть показательными:

  1. Найдите наименьшее (с точки зрения количества токенов) возможное рабочее ввод и минимально возможные нерабочие вводы на основе правил, которые вы ожидаете применить.
  2. Создайте копию файла грамматики, включающую только сложные правила и как можно меньше других вспомогательных правил (т. Е. Вам нужен язык, который позволяет создавать только последовательности, состоящие из объекта и правила more_object , к которым присоединяется СТРЕЛКА. Работает ли это так, как вы ожидаете?
  3. Правило, в котором оно вложено, работает так, как вы ожидаете? Попробуйте заменить объект другим очень простым rule (используя некоторые токены, не встречающиеся где-либо еще) и посмотрите, можете ли вы включить эти токены, не нарушая все остальное.
  4. Запустите bison с помощью - report = all . Проверьте вывод, чтобы попытаться отследить правила вы добавили и состояния, на которые они влияют. Попробуйте удалить эти правила и повторите процесс - что изменилось? Часто это занимает очень много времени и вызывает огромную боль, но это хорошее последнее средство. Я рекомендую карандаш и немного бумага.

Если посмотреть на структуру вывода ошибки - '+' распознается как токен идентификатора и, следовательно, рассматривается как символ. Возможно, стоит проверить ваш лексер, чтобы увидеть, как он обрабатывает токены идентификаторов. Вы могли просто случайно схватить слишком много. В качестве дополнительного метода отладки вы можете рассмотреть возможность преобразования некоторых из этих литералов токенов (например, '+', '{' и т. Д.) В настоящие токены, чтобы отчеты об ошибках Bison могли немного помочь вам.

РЕДАКТИРОВАТЬ: Хорошо, чем больше я копался в этом, тем больше убеждаюсь, что лексер не обязательно работает так, как должен. Я бы дважды проверил, что поток токенов, который вы получаете от yylex (), соответствует вашим ожиданиям, прежде чем продолжить. В частности, похоже, что набор символов, которые вы считаете особенными (например, '+' и '{'), захватываются некоторыми из ваших регулярных выражений или, по крайней мере, им разрешено передавать идентификаторы.

1
ответ дан 3 November 2019 в 07:11
поделиться

Похоже, вы делаете классическую ошибку при использовании прямых строк в исходном файле yacc. Поскольку вы используете лексер, вы можете использовать имена токенов только в исходных файлах yacc. Подробнее об этом здесь

2
ответ дан 3 November 2019 в 07:11
поделиться

Я думаю, ваша основная проблема в том, что вы не смогли определить конструктор поддерева в субграмматике вашего объекта. (РЕДАКТИРОВАТЬ: OP говорит, что он оставил семантические действия для объект из его примера текста. Это не меняет следующего ответа).

Вам, вероятно, также придется искать объекты в том порядке, в котором они встречаются. Возможно, вы намеревались:

primary_expression 
   : constant_value                                        { $$ = $1; } 
   | '(' expression ')'                                    { $$ = $2; } 
   | list_initialization                                   { $$ = $1; } 
   | function_call                                         { $$ = $1; } 
   | object                                                { $$ = $1; } 
   ; 



object
   : IDENTIFIER    { $$ = LookupVariableOrObject( yytext ); } 
   |  object ARROW IDENTIFIER  { $$ = LookupSubobject( $1, yytext ); } 
   ; 

Я предполагаю, что если кто-то встречает идентификатор X сам по себе, ваша интерпретация по умолчанию в том, что это имя переменной. Но если вы встретите X -> Y, то даже если X - имя переменной, вам нужен объект X с подобъектом Y.

LookupVarOrObject выполняет поиск по самому левому обнаруженному идентификатору , чтобы узнать, является ли он переменной (и возвращать по существу то же значение, что и idlookup, который должен создавать узел AST типа AST_VAR), или посмотрите, является ли это допустимым именем объекта, и верните узел AST, помеченный как AST_OBJ, или пожаловаться, если идентификатор не один из них.

LookupSuboject проверяет свой левый операнд, чтобы убедиться, что это AST_OBJ. (или AST_VAR, имя которого совпадает с именем объекта). и пожаловаться, если это не так. Если это так, то он ищет дочерний объект yytext для названный AST_OBJ.

РЕДАКТИРОВАТЬ: на основе комментариев обсуждения в другом ответе, правая рекурсия в оригинале OP грамматика может быть проблематичной, если семантические проверки OP проверяют состояние глобального лексера (yytext). Это решение является леворекурсивным и не может нарушить конкретную ловушку.

1
ответ дан 3 November 2019 в 07:11
поделиться

Вы не получаете конфликтов сдвига / уменьшения, потому что ваши правила, использующие имя_объекта и more_objects , являются праворекурсивными, а не леворекурсивными правилами, которые Yacc (Bison) обрабатывает больше всего естественно.

В классическом Yacc вы обнаружите, что у вас может закончиться стековое пространство при достаточно глубоком вложении нотации ' объект-> имя-> что-> не ». Bison расширяет свой стек во время выполнения, поэтому вам придется исчерпать память, что в наши дни намного сложнее, чем когда у машин было несколько мегабайт памяти (или меньше).

Одним из результатов правой рекурсии является то, что никаких сокращений не происходит, пока вы не прочитаете последнее из имен объектов в цепочке (или, точнее, один символ после этого). Я вижу, что вы также использовали правую рекурсию со своим правилом statement_list - и в ряде других мест тоже.

1
ответ дан 3 November 2019 в 07:11
поделиться

id_lookup : IDENTIFIER

формально идентичен

имя_объекта : IDENTIFIER

и object_name будут принимать все, что id_lookup не принимает, поэтому assertLookup (yytext); вероятно, работает на всем, что может выглядеть как IDENTIFIER и не принимается другим правилом, просто чтобы выбрать между 2, а затем object_name не может принять, потому что одиночный просмотр запрещает это.

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

0
ответ дан 3 November 2019 в 07:11
поделиться

Я только что попробовал запустить muscl в Ubuntu 10.04, используя bison 2.4.1, и я смог запустить оба ваших примера без синтаксических ошибок. Я предполагаю, что у вас есть ошибка в вашей версии bison. Дайте мне знать, если я как-то неправильно запускаю ваш парсер. Ниже приведен результат первого примера, который вы привели.

./muscle < ./test1.m (это был ваш первый тест)

\-statement list
  |-declaration (constant)
  | |-symbol reference
  | | \-list (constant)
  | \-list
  |   |-value
  |   | \-1
  |   |-value
  |   | \-2
  |   |-value
  |   | \-3
  |   \-value
  |     \-4
  |-loop (for-in)
  | |-symbol reference
  | | \-x (variable)
  | |-symbol reference
  | | \-list (constant)
  | \-statement list
  |   \-send statement
  |     \-binary op (addition)
  |       |-binary op (addition)
  |       | |-value
  |       | | \-foo 
  |       | \-symbol reference
  |       |   \-x (variable)
  |       \-value
  |         \-!
  \-send statement
    \-value
      \-Done!

 +-----+----------+-----------------------+-----------------------+
 |   1 | VALUE    | 1                     |                       |
 |   2 | ELMT     | @1                    |                       |
 |   3 | VALUE    | 2                     |                       |
 |   4 | ELMT     | @3                    |                       |
 |   5 | VALUE    | 3                     |                       |
 |   6 | ELMT     | @5                    |                       |
 |   7 | VALUE    | 4                     |                       |
 |   8 | ELMT     | @7                    |                       |
 |   9 | LIST     |                       |                       |
 |  10 | CONST    | @10                   | @9                    |
 |  11 | ITER_NEW | @11                   | @10                   |
 |  12 | BRA      | @14                   |                       |
 |  13 | ITER_INC | @11                   |                       |
 |  14 | ITER_END | @11                   |                       |
 |  15 | BRT      | @22                   |                       |
 |  16 | VALUE    | foo                   |                       |
 |  17 | ADD      | @16                   | @11                   |
 |  18 | VALUE    | !                     |                       |
 |  19 | ADD      | @17                   | @18                   |
 |  20 | SEND     | @19                   |                       |
 |  21 | BRA      | @13                   |                       |
 |  22 | VALUE    | Done!                 |                       |
 |  23 | SEND     | @22                   |                       |
 |  24 | HALT     |                       |                       |
 +-----+----------+-----------------------+-----------------------+
foo 1!
foo 2!
foo 3!
foo 4!
Done!
0
ответ дан 3 November 2019 в 07:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: