5

Я создаю компилятор с Lex и YACC (на самом деле Flex и Bison). Язык позволяет неограниченно пересылать ссылки на любой символ (например, C#). Проблема в том, что невозможно разобрать язык, не зная, что такое идентификатор.Как реализовать прямые ссылки в компиляторе?

Единственное решение, о котором я знаю, это лекс всего источника, а затем выполнить синтаксический анализ «в ширину», поэтому элементы более высокого уровня, такие как объявления классов и объявления функций, анализируются перед функциями, которые их используют. Однако для больших файлов потребуется большой объем памяти, и с YACC было бы сложно справиться (мне пришлось бы создавать отдельные грамматики для каждого типа объявления/тела). Мне также пришлось бы вручную написать lexer (что не так уж и много).

Меня не интересует эффективность (хотя это все еще важно), потому что я собираюсь переписать сам компилятор, как только закончу его, но я хочу, чтобы эта версия была быстрой (так что если любые быстрые общие методы, которые не могут быть выполнены в Lex/YACC, но могут быть сделаны вручную, пожалуйста, предложите их также). Таким образом, сейчас самым важным фактором является легкость развития.

Есть ли хорошие решения этой проблемы? Как это обычно делается в компиляторах для таких языков, как C# или Java?

ответ

7

Это вполне возможно проанализировать. Несмотря на двусмысленность между идентификаторами и ключевыми словами, lex с удовольствием справится с этим, указав приоритет ключевых слов.

Я не вижу, какие другие проблемы есть. Вам не нужно определять, действительно ли идентификаторы действительны во время этапа синтаксического анализа. Вы создаете либо дерево синтаксического анализа, либо абстрактное синтаксическое дерево (различие является тонким, но неуместным для целей этого обсуждения) при анализе. После этого вы создаете свои вложенные структуры таблиц символов, выполняя проход по AST, который вы создали во время разбора. Затем вы выполняете другой проход по AST, чтобы проверить, что используемые идентификаторы действительны. Следуйте этому с помощью одного или нескольких дополнительных анализов по AST для генерации выходного кода или какой-либо другой промежуточной структуры данных, и все готово!

EDIT: Если вы хотите посмотреть, как это делается, проверьте исходный код для компилятора Mono C#. Это фактически написано на C#, а не на C или C++, но оно использует порт .NET Jay, который очень похож на yacc.

+0

Это не имеет ничего общего с ключевыми словами. Это больше похоже на: ABC (пакет AB). (Класс C), (пакет A) (класс B). (Поле C) или (заданный A). (Поле B). (Поле C) и т. Д. – Zifre

+1

Затем применяется второй абзац моего ответа. Вам не нужно разбираться в этом. Рассматривать '.' как оператор в вашей грамматике. В ваших тестах AST вы можете проверить их на таблице символов. – U62

+0

Ну, мне кажется, мне нужно просто создать дерево разбора, а не АСТ. Как вы сказали, они разные. Если никто другой не придумает лучшего ответа, я соглашусь с этим, но я бы не хотел этого делать так ... – Zifre

1

Один из вариантов заключается в том, чтобы иметь дело с прямыми ссылками, просто сканируя и кэшируя токены, пока вы не нажмете что-то, что вы знаете, как с реальным (вроде как восстановление «panic-mode»). После того, как вы запустили весь файл, вернитесь назад и попробуйте повторно разбить биты, которые раньше не разбирались.

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

Как сделать несколько грамматик, немного позабавиться с препроцессором на файл YACC, и вы должны быть в состоянии сделать их все из того же исходного источника

+0

Я не очень беспокоюсь о том, чтобы вручную писать лексер, это не так сложно (на самом деле это может быть немного легче, так как мой язык имеет Python-подобный отступ).Использование препроцессора с YACC звучит так, как будто оно может работать, но есть ли способ изменить символ начала? – Zifre

+0

Репроцессор с yacc, это как раз идея. определите полную грамматику без явного определения стартового символа и затем замените небольшой бит файла (через что-то вроде #include или #define), чтобы выбрать начальную точку. Один из способов сделать это - иметь правило начала формы «Root :: = MacroRule;» и замените MacroRule тем, что вы хотите для этой версии. – BCS

Смежные вопросы