Всем привет! Сегодня мы начнём писать язык программирования на 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/
Добавить комментарий