русификация лексера

This commit is contained in:
mzhoot 2026-06-20 04:16:50 +03:00
parent d445f40ac4
commit 5bc6436c07
2 changed files with 70 additions and 83 deletions

View file

@ -2,11 +2,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Лексер КВС Лексер КВС (русская версия)
Принимает: исходный .квс файл Принимает: исходный .квс файл
Выдаёт: текстовый файл с токенами (по одному токену на строку) Выдаёт: текстовый файл с токенами (по одному токену на строку)
Формат вывода: ТИП:ЗНАЧЕНИЕ Формат вывода: ТИП:ЗНАЧЕНИЕ
Все типы токенов переведены на русский язык:
- СТРОКА → строковый литерал
- СЛОВО → идентификатор
- ЧИСЛО → числовой литерал
- ЗАПЯТАЯ → разделитель
- ДВОЕТОЧИЕ → для меток
- СК_ОТКР → [
- СК_ЗАКР → ]
- ПЛЮС → +
- МИНУС → -
- ЗВЕЗДА → *
- НОВСТР → маркер конца строки
Поддерживает сложную адресацию: Поддерживает сложную адресацию:
- [ ] - квадратные скобки - [ ] - квадратные скобки
- + - плюс - + - плюс
@ -46,7 +59,7 @@ def is_dec_number(s):
return True return True
def tokenize_line(line, line_num): def tokenize_line(line, line_num):
"""Разбирает строку в токены""" """Разбирает строку в токены (русские названия типов)"""
# Удаляем комментарии # Удаляем комментарии
semi = -1 semi = -1
for idx in range(len(line)): for idx in range(len(line)):
@ -85,43 +98,53 @@ def tokenize_line(line, line_num):
if i >= n: if i >= n:
raise ValueError(error_unterminated_string(line_num)) raise ValueError(error_unterminated_string(line_num))
i += 1 i += 1
tokens.append(('STRING', s)) tokens.append(('СТРОКА', s))
continue continue
# Одиночные символы-разделители # Одиночные символы-разделители
if ch == ',': if ch == ',':
tokens.append(('COMMA', ',')) tokens.append(('ЗАПЯТАЯ', ','))
i += 1 i += 1
continue continue
if ch == ':': if ch == ':':
tokens.append(('COLON', ':')) tokens.append(('ДВОЕТОЧИЕ', ':'))
i += 1 i += 1
continue continue
# ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ # Токены для сложной адресации
if ch == '[': if ch == '[':
tokens.append(('LBRACKET', '[')) tokens.append(('СК_ОТКР', '['))
i += 1 i += 1
continue continue
if ch == ']': if ch == ']':
tokens.append(('RBRACKET', ']')) tokens.append(('СК_ЗАКР', ']'))
i += 1 i += 1
continue continue
if ch == '+': if ch == '+':
tokens.append(('PLUS', '+')) tokens.append(('ПЛЮС', '+'))
i += 1 i += 1
continue continue
if ch == '-': if ch == '-':
tokens.append(('MINUS', '-')) # Проверяем, не является ли минус частью отрицательного числа
if i + 1 < n and (line[i+1].isdigit() or line[i+1] == '0'):
j = i + 1
while j < n and (line[j].isdigit() or line[j] in 'xXabcdefABCDEF'):
j += 1
word = line[i:j]
if is_hex_number(word) or is_dec_number(word):
tokens.append(('ЧИСЛО', word))
i = j
continue
tokens.append(('МИНУС', '-'))
i += 1 i += 1
continue continue
if ch == '*': if ch == '*':
tokens.append(('STAR', '*')) tokens.append(('ЗВЕЗДА', '*'))
i += 1 i += 1
continue continue
@ -134,11 +157,11 @@ def tokenize_line(line, line_num):
if word: if word:
# Определяем тип токена # Определяем тип токена
if is_hex_number(word): if is_hex_number(word):
tokens.append(('NUMBER', word)) tokens.append(('ЧИСЛО', word))
elif is_dec_number(word): elif is_dec_number(word):
tokens.append(('NUMBER', word)) tokens.append(('ЧИСЛО', word))
else: else:
tokens.append(('WORD', word)) tokens.append(('СЛОВО', word))
i = j i = j
@ -153,7 +176,7 @@ def lex_source(source_text):
try: try:
tokens = tokenize_line(line, line_num) tokens = tokenize_line(line, line_num)
if tokens: if tokens:
all_tokens.append(('NEWLINE', str(line_num))) all_tokens.append(('НОВСТР', str(line_num)))
for tok_type, tok_value in tokens: for tok_type, tok_value in tokens:
all_tokens.append((tok_type, tok_value)) all_tokens.append((tok_type, tok_value))
except ValueError as e: except ValueError as e:

View file

@ -2,8 +2,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Парсер КВС Парсер КВС (адаптирован под русские токены)
Принимает: файл токенов от лексера Принимает: файл токенов от лексера (русские названия типов)
Выдаёт: текстовый файл с AST-структурой Выдаёт: текстовый файл с AST-структурой
Поддерживает сложную адресацию: Поддерживает сложную адресацию:
@ -35,14 +35,13 @@ def memory_operand_to_ast_string(mem_op):
elif addr_type == 'register_indirect': elif addr_type == 'register_indirect':
return f"MEM:reg_indirect:{mem_op[1]}" return f"MEM:reg_indirect:{mem_op[1]}"
elif addr_type == 'complex': elif addr_type == 'complex':
# Пока не поддерживается, но зарезервировано
return f"MEM:complex:{mem_op[1]}+{mem_op[2]}*{mem_op[3]}+{mem_op[4]}" return f"MEM:complex:{mem_op[1]}+{mem_op[2]}*{mem_op[3]}+{mem_op[4]}"
else: else:
return f"MEM:unknown" return f"MEM:unknown"
def read_tokens(input_file): def read_tokens(input_file):
"""Читает токены из файла""" """Читает токены из файла (русские названия типов)"""
tokens = [] tokens = []
with open(input_file, 'r', encoding='utf-8') as f: with open(input_file, 'r', encoding='utf-8') as f:
for line in f: for line in f:
@ -94,21 +93,22 @@ class Parser:
if tok is None: if tok is None:
return False return False
if tok[0] == 'NEWLINE': # Пропускаем маркер конца строки
if tok[0] == 'НОВСТР':
self.next_token() self.next_token()
tok = self.peek() tok = self.peek()
if tok is None: if tok is None:
return False return False
if tok[0] == 'WORD': # Проверяем, есть ли метка (СЛОВО за которым следует ДВОЕТОЧИЕ)
# Проверяем, есть ли после слова двоеточие (метка) if tok[0] == 'СЛОВО':
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'COLON': if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'ДВОЕТОЧИЕ':
label_name = self.next_token()[1] label_name = self.next_token()[1]
self.next_token() # COLON self.next_token() # ДВОЕТОЧИЕ
self.parsed_lines.append(f"LABEL:{label_name}:{self.current_section}") self.parsed_lines.append(f"LABEL:{label_name}:{self.current_section}")
next_tok = self.peek() next_tok = self.peek()
if next_tok and next_tok[0] != 'NEWLINE': if next_tok and next_tok[0] != 'НОВСТР':
return self.parse_instruction_or_directive() return self.parse_instruction_or_directive()
return True return True
@ -116,10 +116,10 @@ class Parser:
def parse_instruction_or_directive(self): def parse_instruction_or_directive(self):
tok = self.peek() tok = self.peek()
if tok is None or tok[0] == 'NEWLINE': if tok is None or tok[0] == 'НОВСТР':
return True return True
if tok[0] != 'WORD': if tok[0] != 'СЛОВО':
raise ValueError(f"Ожидалось слово, получено: {tok}") raise ValueError(f"Ожидалось слово, получено: {tok}")
word = self.next_token()[1] word = self.next_token()[1]
@ -151,7 +151,7 @@ class Parser:
# === Глобальные метки === # === Глобальные метки ===
elif word == '.глобал': elif word == '.глобал':
label = self.expect('WORD')[1] label = self.expect('СЛОВО')[1]
self.parsed_lines.append(f"DIRECTIVE:{word}:{label}") self.parsed_lines.append(f"DIRECTIVE:{word}:{label}")
# === Строковые директивы (запрещены в .бнд) === # === Строковые директивы (запрещены в .бнд) ===
@ -161,7 +161,7 @@ class Parser:
"Ошибка: секция .бнд не поддерживает инициализированные данные " "Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)" "(используйте .резб, .резс, .рездс, .резкс)"
) )
string_tok = self.expect('STRING') string_tok = self.expect('СТРОКА')
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}") self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}")
# === Константы (запрещены в .бнд) === # === Константы (запрещены в .бнд) ===
@ -171,13 +171,11 @@ class Parser:
"Ошибка: секция .бнд не поддерживает инициализированные данные " "Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)" "(используйте .резб, .резс, .рездс, .резкс)"
) )
name = self.expect('WORD')[1] name = self.expect('СЛОВО')[1]
# Пропускаем '=', если есть
eq_tok = self.peek() eq_tok = self.peek()
if eq_tok and eq_tok[0] == 'WORD' and eq_tok[1] == '=': if eq_tok and eq_tok[0] == 'СЛОВО' and eq_tok[1] == '=':
self.next_token() self.next_token()
# Значение может быть WORD или NUMBER value_tok = self.expect_one_of(['СЛОВО', 'ЧИСЛО'])
value_tok = self.expect_one_of(['WORD', 'NUMBER'])
self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}") self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}")
# === Байты (запрещены в .бнд) === # === Байты (запрещены в .бнд) ===
@ -190,14 +188,13 @@ class Parser:
bytes_list = [] bytes_list = []
while True: while True:
tok = self.peek() tok = self.peek()
if tok is None or tok[0] == 'NEWLINE' or tok[0] == 'COMMA': if tok is None or tok[0] == 'НОВСТР' or tok[0] == 'ЗАПЯТАЯ':
if tok and tok[0] == 'COMMA': if tok and tok[0] == 'ЗАПЯТАЯ':
self.next_token() self.next_token()
else: else:
break break
else: else:
# Может быть WORD (метка) или NUMBER (число) val_tok = self.expect_one_of(['СЛОВО', 'ЧИСЛО', 'СТРОКА'])
val_tok = self.expect_one_of(['WORD', 'NUMBER', 'STRING'])
bytes_list.append(val_tok[1]) bytes_list.append(val_tok[1])
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list)) self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list))
@ -207,7 +204,7 @@ class Parser:
raise ValueError( raise ValueError(
f"Ошибка: директива {word} допустима только внутри секции .бнд" f"Ошибка: директива {word} допустима только внутри секции .бнд"
) )
count_tok = self.expect('NUMBER') count_tok = self.expect('ЧИСЛО')
count = int(count_tok[1]) count = int(count_tok[1])
if count <= 0: if count <= 0:
raise ValueError( raise ValueError(
@ -221,76 +218,46 @@ class Parser:
raise ValueError(f"Неизвестная директива: {word}") raise ValueError(f"Неизвестная директива: {word}")
def parse_operand(self): def parse_operand(self):
""" """Разбирает один операнд"""
Разбирает один операнд.
Возвращает строку (регистр, число, метка, строка, старый формат памяти)
или кортеж (новый формат памяти).
Поддерживает:
- регистры
- числа
- метки
- строки
- адресацию в квадратных скобках
"""
tok = self.peek() tok = self.peek()
if tok is None: if tok is None:
return None return None
if tok[0] == 'LBRACKET': if tok[0] == 'СК_ОТКР':
self.next_token() # съедаем '[' self.next_token()
return self.parse_memory_operand() return self.parse_memory_operand()
elif tok[0] == 'NUMBER': elif tok[0] == 'ЧИСЛО':
return self.next_token()[1] return self.next_token()[1]
elif tok[0] == 'STRING': elif tok[0] == 'СТРОКА':
return self.next_token()[1] return self.next_token()[1]
elif tok[0] == 'WORD': elif tok[0] == 'СЛОВО':
return self.next_token()[1] return self.next_token()[1]
else: else:
raise ValueError(f"Неожиданный токен в операнде: {tok}") raise ValueError(f"Неожиданный токен в операнде: {tok}")
def parse_memory_operand(self): def parse_memory_operand(self):
""" """Разбирает операнд памяти"""
Разбирает операнд памяти.
Открывающая скобка уже прочитана в parse_operand.
Возвращает:
- строку "[выражение]" для абсолютной адресации (старый формат)
- кортеж (addr_type, base_reg, displacement, label) для косвенной
Поддерживает:
[регистр] → косвенная адресация (кортеж)
[число] → абсолютная (строка)
[метка] → абсолютная (строка)
[регистр + ...] → пока не поддерживается (строка)
"""
parts = [] parts = []
# Разбираем содержимое до закрывающей скобки
while True: while True:
tok = self.peek() tok = self.peek()
if tok is None: if tok is None:
raise ValueError("Незакрытая квадратная скобка") raise ValueError("Незакрытая квадратная скобка")
if tok[0] == 'RBRACKET': if tok[0] == 'СК_ЗАКР':
self.next_token() self.next_token()
break break
if tok[0] in ('WORD', 'NUMBER', 'PLUS', 'MINUS', 'STAR'): if tok[0] in ('СЛОВО', 'ЧИСЛО', 'ПЛЮС', 'МИНУС', 'ЗВЕЗДА'):
parts.append(self.next_token()[1]) parts.append(self.next_token()[1])
else: else:
raise ValueError(f"Неожиданный токен в адресации: {tok}") raise ValueError(f"Неожиданный токен в адресации: {tok}")
# Анализируем собранные части
if len(parts) == 1: if len(parts) == 1:
part = parts[0] part = parts[0]
# Проверяем, регистр ли это
if part in REGISTERS: if part in REGISTERS:
# Косвенная адресация: [регистр]
return make_memory_operand('register_indirect', base_reg=part) return make_memory_operand('register_indirect', base_reg=part)
# Иначе: [число] или [метка] — возвращаем строку (старый формат)
# Старое поведение для всего остального
# (абсолютная адресация, комплексная - пока не поддерживается)
expr = ' '.join(parts) expr = ' '.join(parts)
return f"[{expr}]" return f"[{expr}]"
@ -301,21 +268,18 @@ class Parser:
operands = [] operands = []
while True: while True:
tok = self.peek() tok = self.peek()
if tok is None or tok[0] == 'NEWLINE': if tok is None or tok[0] == 'НОВСТР':
break break
if tok[0] == 'COMMA': if tok[0] == 'ЗАПЯТАЯ':
self.next_token() self.next_token()
continue continue
operands.append(self.parse_operand()) operands.append(self.parse_operand())
# Преобразуем операнды в строки для AST
str_operands = [] str_operands = []
for op in operands: for op in operands:
if isinstance(op, tuple) and len(op) >= 2 and op[0] in ('absolute', 'register_indirect', 'complex'): if isinstance(op, tuple) and len(op) >= 2 and op[0] in ('absolute', 'register_indirect', 'complex'):
# Это кортеж памяти
str_operands.append(memory_operand_to_ast_string(op)) str_operands.append(memory_operand_to_ast_string(op))
else: else:
# Это обычная строка (регистр, число, метка, старый формат памяти)
str_operands.append(op) str_operands.append(op)
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(str_operands)) self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(str_operands))