May 21, 2023

Пишем интерпретатор на PascalABC.NET. Лексический анализатор. Часть 1

Общие термины

Компилятор или интерпретатор языка программирования состоит из нескольких подсистем.

Лексический анализатор (лексер) разбивает текст программы на так называемые лексемы или токены - неделимые элементы языка. К ним относятся ключевые слова, числовые и строковые константы, знаки препинания, знаки операций (которые могут быть односимвольными или многосимвольными как например := в Паскале или != в языке C). На этапе лексического анализа также пропускаются комментарии языка, которые могут быть либо однострочными (как правило до конца строки), либо многострочными (начинающимися и заканчивающимися как правило разными лексемами).

Синтаксический анализатор (другое название парсер)строит из лексем правильные языковые конструкции: операторы, выражения, списки и т.п. Синтаксические конструкции описываются грамматикой языка, которая представляет набор правил - по нескольку правил на каждую синтаксическую конструкцию. Грамматика языка обычно рекурсивна: например, условный оператор является разновидностью оператора и по каждой ветке (then и else) также содержит оператор.

Семантический анализатор проверяет правильную синтаксически программу на наличие так называемых семантических ошибок. К числу таких ошибок относятся ошибки, связанные с одинаковыми именами (в одном блоке нельзя описать две переменные с одним именем), а также ошибки, связанные с неверной типизацией (целой переменной нельзя присвоить строковое значение). Часто семантический анализатор называют Type Checkerом, подчеркивая, что проверка типов до выполнения программы - важнейшая задача.

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

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

Мы будем нацелены в итоге на создание эффективного интерпретатора для языка со статической типизацией, то есть языка, в котором тип переменной не меняется после её первичного описания или инициализации.

Лексический анализатор. Основная программа

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

Приведем пример итоговой программы:

Как мы видим, программа написана на одной строке - этой строкой инициализируется конструктор класса Lexer. Важнейшей функцией лексера является метод NextToken, возвращающий следующую лексему. Мы перебираем таким образом все лексемы в цикле и выводим их пока не будет достигнута лексема TokenType.Eof, обозначающая конец программы.

На скриншоте видно, что лексер разбирает все лексемы и выводит их, разделяя одним пробелом. Лексема в конце - это Eof - признак конца программы.

Полный код модуля LexUnit и класса Lexer будет приведен в следующей статье.