kvs/kvs_lexer.py

204 lines
6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Лексер КВС
Принимает: исходный .квс файл
Выдаёт: текстовый файл с токенами (по одному токену на строку)
Формат вывода: ТИП:ЗНАЧЕНИЕ
Поддерживает сложную адресацию:
- [ ] - квадратные скобки
- + - плюс
- - - минус
- * - звёздочка (умножение/масштаб)
- NUMBER - числа (десятичные, шестнадцатеричные, отрицательные)
"""
import sys
def error_unterminated_string(line_num):
return f"Незакрытая кавычка в строке на строке {line_num}"
def is_hex_number(s):
"""Проверяет, является ли строка шестнадцатеричным числом (0x... или 0X...)"""
if len(s) < 3:
return False
if not (s[0] == '0' and (s[1] == 'x' or s[1] == 'X')):
return False
for ch in s[2:]:
if not (('0' <= ch <= '9') or ('a' <= ch <= 'f') or ('A' <= ch <= 'F')):
return False
return True
def is_dec_number(s):
"""Проверяет, является ли строка десятичным числом (возможно с минусом)"""
if not s:
return False
start = 0
if s[0] == '-':
if len(s) == 1:
return False
start = 1
for ch in s[start:]:
if ch < '0' or ch > '9':
return False
return True
def tokenize_line(line, line_num):
"""Разбирает строку в токены"""
# Удаляем комментарии
semi = -1
for idx in range(len(line)):
if line[idx] == ';':
semi = idx
break
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
tokens = []
i = 0
n = len(line)
while i < n:
ch = line[i]
# Пропускаем пробелы
if ch == ' ' or ch == '\t':
i += 1
continue
# Строковые литералы
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
# Одиночные символы-разделители
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 == '-':
tokens.append(('MINUS', '-'))
i += 1
continue
if ch == '*':
tokens.append(('STAR', '*'))
i += 1
continue
# Слова, числа, метки
j = i
while j < n and not (line[j] in ' \t,;:[]+*-'):
j += 1
word = line[i:j]
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))
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}")