kvs/kvs_lexer.py

192 lines
6.1 KiB
Python
Raw Normal View History

2026-05-02 23:04:46 +03:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Лексер КВС
Принимает: исходный .квс файл
Выдаёт: текстовый файл с токенами (по одному токену на строку)
Формат вывода: ТИП:ЗНАЧЕНИЕ
Поддерживает сложную адресацию:
- [ ] - квадратные скобки
- + - плюс
- - - минус
- * - звёздочка (умножение/масштаб)
- NUMBER - числа (десятичные, шестнадцатеричные, отрицательные)
2026-05-02 23:04:46 +03:00
"""
import sys
import re
2026-05-02 23:04:46 +03:00
def error_unterminated_string(line_num):
return f"Незакрытая кавычка в строке на строке {line_num}"
def is_hex_number(s):
"""Проверяет, является ли строка шестнадцатеричным числом (0x... или 0X...)"""
return re.match(r'^0[xX][0-9a-fA-F]+$', s) is not None
def is_dec_number(s):
"""Проверяет, является ли строка десятичным числом (возможно с минусом)"""
if s.startswith('-'):
s = s[1:]
return s.isdigit()
2026-05-02 23:04:46 +03:00
def tokenize_line(line, line_num):
"""Разбирает строку в токены"""
# Удаляем комментарии
2026-05-02 23:04:46 +03:00
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
tokens = []
i = 0
n = len(line)
2026-05-02 23:04:46 +03:00
while i < n:
ch = line[i]
# Пропускаем пробелы
2026-05-02 23:04:46 +03:00
if ch.isspace():
i += 1
continue
# Строковые литералы
2026-05-02 23:04:46 +03:00
if ch == '"':
i += 1
s = ''
while i < n and line[i] != '"':
if line[i] == '\\' and i + 1 < n:
s += '\\' + line[i + 1]
i += 2
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string(line_num))
i += 1
tokens.append(('STRING', s))
continue
# Одиночные символы-разделители
2026-05-02 23:04:46 +03:00
if ch == ',':
tokens.append(('COMMA', ','))
i += 1
continue
if ch == ':':
tokens.append(('COLON', ':'))
i += 1
continue
# НОВЫЕ ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ
if ch == '[':
tokens.append(('LBRACKET', '['))
i += 1
continue
if ch == ']':
tokens.append(('RBRACKET', ']'))
i += 1
continue
if ch == '+':
tokens.append(('PLUS', '+'))
i += 1
continue
if ch == '-':
# Проверяем, не является ли это началом отрицательного числа
# Смотрим следующий символ
if i + 1 < n and (line[i + 1].isdigit() or line[i + 1] == '0'):
# Возможно отрицательное число, дадим разобраться дальше
# Но для простоты сохраняем как MINUS, а число будет отдельным токеном
pass
tokens.append(('MINUS', '-'))
i += 1
continue
if ch == '*':
tokens.append(('STAR', '*'))
i += 1
continue
# Слова, числа, метки
2026-05-02 23:04:46 +03:00
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;[]+*-'):
2026-05-02 23:04:46 +03:00
j += 1
word = line[i:j]
2026-05-02 23:04:46 +03:00
if word:
# Определяем тип токена
if is_hex_number(word):
tokens.append(('NUMBER', word))
elif is_dec_number(word):
tokens.append(('NUMBER', word))
else:
tokens.append(('WORD', word))
2026-05-02 23:04:46 +03:00
i = j
return tokens
def lex_source(source_text):
"""Лексирует весь исходный текст"""
lines = source_text.split('\n')
all_tokens = []
for line_num, line in enumerate(lines, start=1):
try:
tokens = tokenize_line(line, line_num)
if tokens:
all_tokens.append(('NEWLINE', str(line_num)))
for tok_type, tok_value in tokens:
all_tokens.append((tok_type, tok_value))
except ValueError as e:
print(f"Ошибка лексера: {e}", file=sys.stderr)
sys.exit(1)
return all_tokens
def write_tokens(tokens, output_file):
"""Записывает токены в файл"""
with open(output_file, 'w', encoding='utf-8') as f:
for tok_type, tok_value in tokens:
f.write(f"{tok_type}:{tok_value}\n")
def read_tokens(input_file):
"""Читает токены из файла"""
tokens = []
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
colon_pos = line.find(':')
if colon_pos == -1:
continue
tok_type = line[:colon_pos]
tok_value = line[colon_pos + 1:]
tokens.append((tok_type, tok_value))
return tokens
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Использование: python kvs_lexer.py <вход.квс> <выход.токены>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
try:
with open(input_file, 'r', encoding='utf-8') as f:
source_text = f.read()
except FileNotFoundError:
print(f"Ошибка: файл '{input_file}' не найден.", file=sys.stderr)
sys.exit(1)
tokens = lex_source(source_text)
write_tokens(tokens, output_file)
print(f"Лексер: {len(tokens)} токенов записано в {output_file}")