kvs/квс_парсер.py

351 lines
13 KiB
Python
Raw Permalink Normal View History

2026-05-02 23:04:46 +03:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
2026-06-20 04:16:50 +03:00
Парсер КВС (адаптирован под русские токены)
Принимает: файл токенов от лексера (русские названия типов)
2026-05-02 23:04:46 +03:00
Выдаёт: текстовый файл с AST-структурой
Поддерживает сложную адресацию:
- [регистр + регистр*масштаб + смещение]
2026-05-02 23:04:46 +03:00
"""
import sys
sys.path.insert(0, '.')
from kvs_data import REGISTERS, INSTRUCTIONS
2026-05-17 15:18:35 +03:00
def make_memory_operand(addr_type, base_reg=None, displacement=None, label=None):
"""Создаёт кортеж операнда памяти.
Формат: (addr_type, base_reg, displacement, label)
"""
return (addr_type, base_reg, displacement, label)
def memory_operand_to_ast_string(mem_op):
"""Преобразует кортеж памяти в строку AST.
Формат: 'MEM:тип:данные'
"""
addr_type = mem_op[0]
if addr_type == 'absolute':
if mem_op[3] is not None: # label
return f"MEM:absolute:{mem_op[3]}"
else: # displacement
return f"MEM:absolute:{mem_op[2]}"
elif addr_type == 'register_indirect':
return f"MEM:reg_indirect:{mem_op[1]}"
elif addr_type == 'complex':
return f"MEM:complex:{mem_op[1]}+{mem_op[2]}*{mem_op[3]}+{mem_op[4]}"
else:
return f"MEM:unknown"
2026-05-02 23:04:46 +03:00
def read_tokens(input_file):
2026-06-20 04:16:50 +03:00
"""Читает токены из файла (русские названия типов)"""
2026-05-02 23:04:46 +03:00
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
2026-05-17 15:18:35 +03:00
2026-06-27 11:24:45 +03:00
# ==================== СОСТОЯНИЕ ПАРСЕРА ====================
# Хранится в словаре, передаётся во все функции явно
def parser_init(tokens):
"""Создаёт начальное состояние парсера"""
return {
'tokens': tokens,
'pos': 0,
'current_section': '.text',
'parsed_lines': []
}
def parser_peek(state):
"""Подсмотреть текущий токен"""
if state['pos'] < len(state['tokens']):
return state['tokens'][state['pos']]
return None
def parser_next_token(state):
"""Взять текущий токен и продвинуться"""
tok = parser_peek(state)
if tok:
state['pos'] += 1
return tok
def parser_expect(state, expected_type):
"""Ожидать токен определённого типа"""
tok = parser_next_token(state)
if tok is None or tok[0] != expected_type:
raise ValueError(f"Ожидался {expected_type}, получено {tok}")
return tok
def parser_expect_one_of(state, expected_types):
"""Ожидает один из нескольких типов токенов"""
tok = parser_peek(state)
if tok is None or tok[0] not in expected_types:
raise ValueError(f"Ожидался один из {expected_types}, получено {tok}")
return parser_next_token(state)
# ==================== РАЗБОР ОПЕРАНДОВ ====================
def parse_operand(state):
"""Разбирает один операнд"""
tok = parser_peek(state)
if tok is None:
2026-05-02 23:04:46 +03:00
return None
2026-06-27 11:24:45 +03:00
if tok[0] == 'СК_ОТКР':
parser_next_token(state)
return parse_memory_operand(state)
elif tok[0] == 'ЧИСЛО':
return parser_next_token(state)[1]
elif tok[0] == 'СТРОКА':
return parser_next_token(state)[1]
elif tok[0] == 'СЛОВО':
return parser_next_token(state)[1]
else:
raise ValueError(f"Неожиданный токен в операнде: {tok}")
def parse_memory_operand(state):
"""Разбирает операнд памяти"""
parts = []
while True:
tok = parser_peek(state)
2026-05-02 23:04:46 +03:00
if tok is None:
2026-06-27 11:24:45 +03:00
raise ValueError("Незакрытая квадратная скобка")
if tok[0] == 'СК_ЗАКР':
parser_next_token(state)
break
if tok[0] in ('СЛОВО', 'ЧИСЛО', 'ПЛЮС', 'МИНУС', 'ЗВЕЗДА'):
parts.append(parser_next_token(state)[1])
2026-05-02 23:04:46 +03:00
else:
2026-06-27 11:24:45 +03:00
raise ValueError(f"Неожиданный токен в адресации: {tok}")
if len(parts) == 1:
part = parts[0]
if part in REGISTERS:
return make_memory_operand('register_indirect', base_reg=part)
expr = ' '.join(parts)
return f"[{expr}]"
# ==================== РАЗБОР ДИРЕКТИВ ====================
def parse_directive(state, word):
"""
Разбор директив ассемблера.
Вызывает ошибку при некорректном синтаксисе или контексте.
"""
# === Секции ===
if word == '.текст':
state['current_section'] = ".text"
state['parsed_lines'].append(f"DIRECTIVE:{word}")
elif word == '.данные':
state['current_section'] = ".data"
state['parsed_lines'].append(f"DIRECTIVE:{word}")
elif word == '.бнд':
state['current_section'] = ".бнд"
state['parsed_lines'].append(f"DIRECTIVE:{word}")
# === Глобальные метки ===
elif word == '.глобал':
label = parser_expect(state, 'СЛОВО')[1]
state['parsed_lines'].append(f"DIRECTIVE:{word}:{label}")
# === Строковые директивы (запрещены в .бнд) ===
elif word in ('.строкауль', '.строка'):
if state['current_section'] == ".бнд":
raise ValueError(
"Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)"
)
string_tok = parser_expect(state, 'СТРОКА')
state['parsed_lines'].append(f"DIRECTIVE:{word}:{state['current_section']}:{string_tok[1]}")
# === Константы (запрещены в .бнд) ===
elif word == '.константа':
if state['current_section'] == ".бнд":
raise ValueError(
"Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)"
)
name = parser_expect(state, 'СЛОВО')[1]
eq_tok = parser_peek(state)
if eq_tok and eq_tok[0] == 'СЛОВО' and eq_tok[1] == '=':
parser_next_token(state)
value_tok = parser_expect_one_of(state, ['СЛОВО', 'ЧИСЛО'])
state['parsed_lines'].append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}")
# === Байты (запрещены в .бнд) ===
elif word == '.байт':
if state['current_section'] == ".бнд":
raise ValueError(
"Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)"
)
bytes_list = []
while True:
tok = parser_peek(state)
if tok is None or tok[0] == 'НОВСТР' or tok[0] == 'ЗАПЯТАЯ':
if tok and tok[0] == 'ЗАПЯТАЯ':
parser_next_token(state)
2026-05-02 23:04:46 +03:00
else:
2026-06-27 11:24:45 +03:00
break
else:
val_tok = parser_expect_one_of(state, ['СЛОВО', 'ЧИСЛО', 'СТРОКА'])
bytes_list.append(val_tok[1])
state['parsed_lines'].append(f"DIRECTIVE:{word}:{state['current_section']}:" + ",".join(bytes_list))
# === Директивы резервирования (только в .бнд) ===
elif word in ('.резб', '.резс', '.рездс', '.резкс'):
if state['current_section'] != ".бнд":
raise ValueError(
f"Ошибка: директива {word} допустима только внутри секции .бнд"
)
count_tok = parser_expect(state, 'ЧИСЛО')
count = int(count_tok[1])
if count <= 0:
raise ValueError(
f"Ошибка: размер резервирования должен быть положительным целым числом"
2026-05-17 11:22:39 +03:00
)
2026-06-27 11:24:45 +03:00
state['parsed_lines'].append(
f"DIRECTIVE:{word}:{state['current_section']}:{count}"
)
else:
raise ValueError(f"Неизвестная директива: {word}")
# ==================== РАЗБОР ИНСТРУКЦИЙ ====================
2026-05-17 11:22:39 +03:00
2026-06-27 11:24:45 +03:00
def parse_instruction(state, mnemonic):
"""Разбор инструкции с операндами"""
if mnemonic not in INSTRUCTIONS:
raise ValueError(f"Неизвестная инструкция: {mnemonic}")
operands = []
while True:
tok = parser_peek(state)
if tok is None or tok[0] == 'НОВСТР':
break
if tok[0] == 'ЗАПЯТАЯ':
parser_next_token(state)
continue
operands.append(parse_operand(state))
str_operands = []
for op in operands:
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))
2026-05-02 23:04:46 +03:00
else:
2026-06-27 11:24:45 +03:00
str_operands.append(op)
state['parsed_lines'].append(f"INSTR:{mnemonic}:{state['current_section']}:" + ",".join(str_operands))
# ==================== РАЗБОР ИНСТРУКЦИИ ИЛИ ДИРЕКТИВЫ ====================
def parse_instruction_or_directive(state):
"""Определяет, инструкция или директива, и вызывает нужный разбор"""
tok = parser_peek(state)
if tok is None or tok[0] == 'НОВСТР':
return True
if tok[0] != 'СЛОВО':
raise ValueError(f"Ожидалось слово, получено: {tok}")
word = parser_next_token(state)[1]
if word.startswith('.'):
parse_directive(state, word)
else:
parse_instruction(state, word)
return True
# ==================== РАЗБОР СТРОКИ ====================
def parse_line(state):
"""Разбор одной строки (может содержать метку)"""
tok = parser_peek(state)
if tok is None:
return False
# Пропускаем маркер конца строки
if tok[0] == 'НОВСТР':
parser_next_token(state)
tok = parser_peek(state)
if tok is None:
2026-06-27 11:24:45 +03:00
return False
# Проверяем, есть ли метка (СЛОВО за которым следует ДВОЕТОЧИЕ)
if tok[0] == 'СЛОВО':
if state['pos'] + 1 < len(state['tokens']) and state['tokens'][state['pos'] + 1][0] == 'ДВОЕТОЧИЕ':
label_name = parser_next_token(state)[1]
parser_next_token(state) # ДВОЕТОЧИЕ
state['parsed_lines'].append(f"LABEL:{label_name}:{state['current_section']}")
2026-05-02 23:04:46 +03:00
2026-06-27 11:24:45 +03:00
next_tok = parser_peek(state)
if next_tok and next_tok[0] != 'НОВСТР':
return parse_instruction_or_directive(state)
return True
return parse_instruction_or_directive(state)
# ==================== ГЛАВНЫЙ ЦИКЛ ====================
def parse_all(tokens):
"""Разбирает все токены, возвращает список строк AST"""
state = parser_init(tokens)
while state['pos'] < len(state['tokens']):
parse_line(state)
return state['parsed_lines']
# ==================== ЗАПИСЬ РЕЗУЛЬТАТА ====================
2026-05-17 15:18:35 +03:00
2026-05-02 23:04:46 +03:00
def write_ast(ast_lines, output_file):
2026-06-27 11:24:45 +03:00
"""Записывает строки AST в файл"""
2026-05-02 23:04:46 +03:00
with open(output_file, 'w', encoding='utf-8') as f:
for line in ast_lines:
f.write(line + '\n')
2026-05-17 15:18:35 +03:00
2026-06-27 11:24:45 +03:00
# ==================== ТОЧКА ВХОДА ====================
2026-05-02 23:04:46 +03:00
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
sys.exit(1)
2026-06-27 11:24:45 +03:00
2026-05-02 23:04:46 +03:00
input_file = sys.argv[1]
output_file = sys.argv[2]
2026-06-27 11:24:45 +03:00
2026-05-02 23:04:46 +03:00
tokens = read_tokens(input_file)
2026-06-27 11:24:45 +03:00
ast_lines = parse_all(tokens)
2026-05-02 23:04:46 +03:00
write_ast(ast_lines, output_file)
print(f"Парсер: {len(ast_lines)} строк AST записано в {output_file}")