Часть 4: Создание парсера для присвоения переменных и печати

Эта статья является продолжением статьи: Часть 3: Создание парсера для арифметических операций

Теперь, когда мы выполнили синтаксический анализ арифметического выражения. Мы напишем парсер для операторов на языке программирования. Начнем с присвоения переменной. Как только вы узнаете, как выполнять синтаксический анализ одного оператора, остальные будут такими же.

Позвольте заявление

Мы используем операторы let для присваивания переменных. Его можно использовать только один раз, после чего мы можем продолжать обновлять значение с помощью оператора присваивания, который будет кратко обсуждаться позже. Мы можем резюмировать оператор let в следующей форме: let <identifier> = <expresssion>;. Здесь есть два значения, которые нас интересуют identifier и expresssion. Выражение, которое мы уже определили, немного изменим для накопления идентификатора. Идентификатор просто хранит имя переменной. Его можно представить ниже.

Storage — это диктофон, в котором мы храним все значения. Когда мы вызываем идентификатор в каком-либо выражении, мы читаем значение из dict. Если это не так, мы поднимем и исключение. Теперь, когда этот идентификатор убран, давайте объявим узел для оператора Let.

Здесь name, в котором хранится информация об идентификаторе. И value для выражения, которое дает значение. Теперь, когда узлы определены, нам нужно создать парсер в классе Parse.

Простой не так ли? Если текущий токен пуст, это идентификатор. Мы проверим это после проверки типа данных в выражении разбора.

Теперь мы можем определить идентификатор. Мы можем построить наш класс let поверх этого. Разбор грамматики — это прямой процесс. Если мы видим ключевое слово let, это указывает на то, что это оператор let. Затем мы переходим к следующему шагу и проверяем, следует ли за его идентификатором (ID) токен назначения (ASSIGN). В этом случае, если мы его найдем, мы продолжим поиск выражения, чтобы получить значение. В конце мы проверим, что в конце токен (SEMICOLON) существует.

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

Узел похож на оператор let с одним отличием. Мы будем обновлять переменную только в том случае, если она уже определена с помощью let. Разбор оператора assign также полностью аналогичен, за исключением того, что мы не начинаем с let. Мы начинаем с узла Token.ID и возвращаем узел AssignStatement.

Выписки для печати

В нашем языке программирования есть два типа операторов печати: print и prinln. Формальный не добавляет новую строку в конце, в отличие от последнего. Давайте определим узел в операторах печати в nodes.py. Значение хранит информацию, которая должна быть сохранена. Пока состояние решает, выбираем ли мы print или println.

Наш оператор печати требует круглых скобок до и после выражения. Проверяем то же самое в его парсере.

Единственное, что расширяется, — это интеграция оператора Let и оператора Print в синтаксический анализатор. parse_statement — это функция, которая определяет, какой тип оператора мы создаем. Звучит сложно!. Не просто смотреть на код.

Мы помещаем анализатор арифметических выражений из последнего скрипта в parse_expression_statement, так что у нас может быть красивое выражение или в parse_statement.

Оценщик

Оценка является последним и последним шагом интерпретатора. Мы уже проделали тяжелую работу при создании парсера. Функция eval(), которую мы создали в каждом узле, автоматически обходит дерево. Все, что нам нужно сделать, это вызвать eval.

Мы уже создали REPL для запуска терминала и скрипт для чтения файла в предыдущих статьях. Я только что интегрировал его так, что он будет запускать интерпретатор, когда с терминала не будет указано имя файла. Когда файл задан, он будет анализировать файл.

В папке примера у нас есть файл с именем variable_declaration.nmi, который содержит следующий код.

Теперь момент истины.

$ python3 src/repl.py example/variable_declaration.nmi

вывод:

“integer: “10
“float: “4.0
“string: “”Hello, World!”
“boolean: “True

Неплохо. Но для строки отображается . Очевидно, будут и другие ошибки. Но мы справимся.

Ссылка

  1. Часть 1: Создание лексера с использованием регулярных выражений
  2. Часть 2: Создание лексера с использованием манипуляций со строками
  3. Часть 3: Создание парсера для арифметических операций
  4. Часть 4: Создание парсера для присвоения переменных и печати
  5. Часть 5: Создание синтаксического анализатора для цикла и ветвления
  6. Написание интерпретатора на ходу Торстена Болла
  7. "Исходный код"

Дополнительные материалы на plainenglish.io