Пишем свой язык программирования на Python. Часть 1. Лексер

от автора

Всем привет! Сегодня мы начнём писать язык программирования на Python!

Структура языка программирования.

Все мы знаем что наш язык программирования как то работает. Но как? Вот структура:

  • Лексер делает токены

  • Парсер строит AST

  • Интерпретатор интерпретирует AST

Лексер возвращает токены. Вот пример:

# Строка: a = 5; b = 5; # Вывод:  [('a', 'ID'), ('=', 'RESERVED'), ('5', 'INT'), ...]

Как можно заметить, структура токена выглядит так:

Кортеж где первая часть это какое то слово (число или ключевое слово) а вторая часть — его тег.

Да. Так и есть. Возвращается список таких кортежей (токенов).

Парсер строит AST. Давайте представим, что AST — это дерево. В нём много веток. Ветка 1 это будет присваивание. 1-ая ветка первой ветки в AST, это имя переменной. А 2-ая ветка первой ветки это значение.

Так работает парсер.

Интерпретатор наше дерево выполняет. Больше нечего интересного.

Правила.

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

Вот правила которые я придумал:

  • Цикл while и do-while.

  • Условие if-else.

  • Лишь один типа данных (int).

  • И присваивание (имя = значение;).

Вот пример кода (он си-подобный):

f = 1; n = 5; while (n > 1) {   f = f * n;   n = n - 1; }

Ещё один (с условием if-else):

f = 120; if (f == 120) {   f = 5; } else {   f = 120; }

И последний (с do-while):

a = 5; do {   a = a - 1; } while (a > 10);

У «a» будет значение 4. Но у данного цикла будут баги.

И наш язык программирования называется AlmostC (Почти C).

Библиотека lexer.py.

В данной библиотеке хранится лексер.

Вот код библиотеки лексера:

def lex(characters, tokens_regex):     pos = 0     tokens = []     while pos < len(characters):         match = None         for token_expr in token_exprs:             pattern, tag = token_expr             regex = re.compile(pattern)             match = regex.match(characters, pos)             if match:                 text = match.group(0)                 if tag:                     token = (text, tag)                     tokens.append(token)                 break         if not match:             raise SyntaxError('Illegal character: %s' % characters[pos])         else:             pos = match.end(0)     return tokens

Мы проходимся по регуляркам из списка token_exprs и находим нужное совпадение. Иначе ошибка.

В конце возвращаем токены.

Достаточно легко.

Файл almostc_lexer.py

В данном файле очень мало строчек кода.

Только список регулярок и функция для функции lex из файла lexer.py.

Вот код файла:

from lexer import * import re  RESERVED = 'RESERVED' ID = 'ID' INT = 'INT'  token_exprs = [     (r'[ \n\t]+', None),     (r'\(', RESERVED),     (r'=', RESERVED),     (r'while', RESERVED),     (r'if', RESERVED),     (r'do', RESERVED),     (r'else', RESERVED),     (r'\{', RESERVED),     (r'\}', RESERVED),     (r'\)', RESERVED),     (r';', RESERVED),     (r'\+', RESERVED),     (r'-', RESERVED),     (r'\*', RESERVED),     (r'/', RESERVED),     (r'<=', RESERVED),     (r'<', RESERVED),     (r'>=', RESERVED),     (r'>', RESERVED),     (r'!=', RESERVED),     (r'==', RESERVED),     (r',', RESERVED),     (r'[0-9]+', INT),     (r'[a-zA-Z][a-zA-Z0-9_]*', ID) ]  def almostc_lexer(chars):   return lex(chars, token_exprs)

Всего три тега. RESERVED — ключевые слова (к примеру while), ID — идентификатор (имя переменной, к примеру: x) и INT — числа (0, 5, 93).

Дальше token_exprs — кортежи регулярок, схема такого кортежа: регулярка, тег.

1 — true.

0 — false.

Функция almostc_lexer(программа) — вернуть токены функции lex.

Итог.

Сейчас мы с вами написали лексер. В следующей части будем делать парсер нашего языка.


ссылка на оригинал статьи https://habr.com/ru/articles/905582/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *