Я начинаю с хорошо сформированной (и хорошо работающей) грамматики для языка , Переменные, К этой грамматике я хотел бы добавить то, что я ...
Я начинаю с хорошо сформированной (и хорошо работающей) грамматики для языка. Переменные, К этой грамматике я хотел бы добавить то, что я ...
Я начинаю с хорошо сформированной (и хорошо работающей) грамматики для языка. Переменные,
бинарные операторы, вызовы функций, списки, циклы, условные выражения и т. д. К этой грамматике я хотел бы добавить то, что я называю конструкцией объекта
:
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, поскольку они не добавляются в таблицу символов, и с ними ничего не делается во время компиляции, кроме как перевести запрос имени игрока в трехадресный код.
Итак, я потратил разумное количество времени на изучение грамматики (и вывода bison) и не увидел, что здесь явно не так. Не имея средств для его выполнения, я не могу легко выяснить, что происходит, экспериментально. Поэтому вот несколько конкретных шагов, которые я обычно выполняю при отладке грамматик. Надеюсь, вы сможете сделать что-либо из того, что еще не сделали, а затем, возможно, опубликуете последующие действия (или отредактируете свой вопрос) с любыми результатами, которые могут быть показательными:
и правила more_object
, к которым присоединяется СТРЕЛКА. Работает ли это так, как вы ожидаете? объект
другим очень простым rule (используя некоторые токены, не встречающиеся где-либо еще) и посмотрите, можете ли вы включить эти токены, не нарушая все остальное. - report = all
. Проверьте вывод, чтобы попытаться отследить правила вы добавили и состояния, на которые они влияют. Попробуйте удалить эти правила и повторите процесс - что изменилось? Часто это занимает очень много времени и вызывает огромную боль, но это хорошее последнее средство. Я рекомендую карандаш и немного бумага.Если посмотреть на структуру вывода ошибки - '+' распознается как токен идентификатора и, следовательно, рассматривается как символ. Возможно, стоит проверить ваш лексер, чтобы увидеть, как он обрабатывает токены идентификаторов. Вы могли просто случайно схватить слишком много. В качестве дополнительного метода отладки вы можете рассмотреть возможность преобразования некоторых из этих литералов токенов (например, '+', '{' и т. Д.) В настоящие токены, чтобы отчеты об ошибках Bison могли немного помочь вам.
РЕДАКТИРОВАТЬ: Хорошо, чем больше я копался в этом, тем больше убеждаюсь, что лексер не обязательно работает так, как должен. Я бы дважды проверил, что поток токенов, который вы получаете от yylex (), соответствует вашим ожиданиям, прежде чем продолжить. В частности, похоже, что набор символов, которые вы считаете особенными (например, '+' и '{'), захватываются некоторыми из ваших регулярных выражений или, по крайней мере, им разрешено передавать идентификаторы.
Похоже, вы делаете классическую ошибку при использовании прямых строк в исходном файле yacc. Поскольку вы используете лексер, вы можете использовать имена токенов только в исходных файлах yacc. Подробнее об этом здесь
Я думаю, ваша основная проблема в том, что вы не смогли определить конструктор поддерева в субграмматике вашего объекта. (РЕДАКТИРОВАТЬ: 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). Это решение является леворекурсивным и не может нарушить конкретную ловушку.
Вы не получаете конфликтов сдвига / уменьшения, потому что ваши правила, использующие имя_объекта
и more_objects
, являются праворекурсивными, а не леворекурсивными правилами, которые Yacc (Bison) обрабатывает больше всего естественно.
В классическом Yacc вы обнаружите, что у вас может закончиться стековое пространство при достаточно глубоком вложении нотации ' объект-> имя-> что-> не
». Bison расширяет свой стек во время выполнения, поэтому вам придется исчерпать память, что в наши дни намного сложнее, чем когда у машин было несколько мегабайт памяти (или меньше).
Одним из результатов правой рекурсии является то, что никаких сокращений не происходит, пока вы не прочитаете последнее из имен объектов в цепочке (или, точнее, один символ после этого). Я вижу, что вы также использовали правую рекурсию со своим правилом statement_list
- и в ряде других мест тоже.
id_lookup : IDENTIFIER
формально идентичен
имя_объекта : IDENTIFIER
и object_name будут принимать все, что id_lookup не принимает, поэтому assertLookup (yytext); вероятно, работает на всем, что может выглядеть как IDENTIFIER и не принимается другим правилом, просто чтобы выбрать между 2, а затем object_name не может принять, потому что одиночный просмотр запрещает это.
Для сумеречной зоны два символа, для которых вы получили ошибки, не объявляются как токены с открытием зоны неопределенного поведения и могут сбить синтаксический анализатор с попыткой обработать их как потенциальные идентификаторы, когда грамматика расшатывается.
Я только что попробовал запустить 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!