убрать ооп

This commit is contained in:
mzhoot 2026-06-27 11:24:45 +03:00
parent a3194093e4
commit 7ba9590286
3 changed files with 268 additions and 250 deletions

View file

@ -11,8 +11,7 @@
Разбор строк — только посимвольно, через циклы и сравнения. Это делает код переносимым на другие языки без изменений логики. Разбор строк — только посимвольно, через циклы и сравнения. Это делает код переносимым на другие языки без изменений логики.
2. **Минимум ООП.** 2. **Минимум ООП.**
- Существующие классы (`Pass1`, `Pass2`, `Parser`) **не переписывать** без явной команды. Они работают — не трогай. - Классы **не создавать**. Код пишется процедурно: функции принимают данные, возвращают данные.
- Новые классы **не создавать**. Код пишется процедурно: функции принимают данные, возвращают данные.
3. **Изоляция логики важнее DRY.** 3. **Изоляция логики важнее DRY.**
Если модуль может быть автономным — он должен быть автономным. Допустимо дублирование кода ради независимости модуля от других. Пример: кодировщики фиксированного и переменного размера содержат копии функций, чтобы не импортировать их и оставаться полностью самостоятельными. Если модуль может быть автономным — он должен быть автономным. Допустимо дублирование кода ради независимости модуля от других. Пример: кодировщики фиксированного и переменного размера содержат копии функций, чтобы не импортировать их и оставаться полностью самостоятельными.

View file

@ -57,255 +57,295 @@ def read_tokens(input_file):
return tokens return tokens
class Parser: # ==================== СОСТОЯНИЕ ПАРСЕРА ====================
def __init__(self, tokens): # Хранится в словаре, передаётся во все функции явно
self.tokens = tokens
self.pos = 0 def parser_init(tokens):
self.current_section = ".text" """Создаёт начальное состояние парсера"""
self.parsed_lines = [] return {
'tokens': tokens,
def peek(self): 'pos': 0,
if self.pos < len(self.tokens): 'current_section': '.text',
return self.tokens[self.pos] '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:
return None return None
def next_token(self): if tok[0] == 'СК_ОТКР':
tok = self.peek() parser_next_token(state)
if tok: return parse_memory_operand(state)
self.pos += 1 elif tok[0] == 'ЧИСЛО':
return tok return parser_next_token(state)[1]
elif tok[0] == 'СТРОКА':
def expect(self, expected_type): return parser_next_token(state)[1]
tok = self.next_token() elif tok[0] == 'СЛОВО':
if tok is None or tok[0] != expected_type: return parser_next_token(state)[1]
raise ValueError(f"Ожидался {expected_type}, получено {tok}") else:
return tok raise ValueError(f"Неожиданный токен в операнде: {tok}")
def expect_one_of(self, expected_types):
"""Ожидает один из нескольких типов токенов""" def parse_memory_operand(state):
tok = self.peek() """Разбирает операнд памяти"""
if tok is None or tok[0] not in expected_types: parts = []
raise ValueError(f"Ожидался один из {expected_types}, получено {tok}")
return self.next_token() while True:
tok = parser_peek(state)
def parse_line(self): if tok is None:
tok = self.peek() raise ValueError("Незакрытая квадратная скобка")
if tok[0] == 'СК_ЗАКР':
parser_next_token(state)
break
if tok[0] in ('СЛОВО', 'ЧИСЛО', 'ПЛЮС', 'МИНУС', 'ЗВЕЗДА'):
parts.append(parser_next_token(state)[1])
else:
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)
else:
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"Ошибка: размер резервирования должен быть положительным целым числом"
)
state['parsed_lines'].append(
f"DIRECTIVE:{word}:{state['current_section']}:{count}"
)
else:
raise ValueError(f"Неизвестная директива: {word}")
# ==================== РАЗБОР ИНСТРУКЦИЙ ====================
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))
else:
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: if tok is None:
return False return False
# Пропускаем маркер конца строки # Проверяем, есть ли метка (СЛОВО за которым следует ДВОЕТОЧИЕ)
if tok[0] == 'НОВСТР': if tok[0] == 'СЛОВО':
self.next_token() if state['pos'] + 1 < len(state['tokens']) and state['tokens'][state['pos'] + 1][0] == 'ДВОЕТОЧИЕ':
tok = self.peek() label_name = parser_next_token(state)[1]
if tok is None: parser_next_token(state) # ДВОЕТОЧИЕ
return False state['parsed_lines'].append(f"LABEL:{label_name}:{state['current_section']}")
# Проверяем, есть ли метка (СЛОВО за которым следует ДВОЕТОЧИЕ) next_tok = parser_peek(state)
if tok[0] == 'СЛОВО': if next_tok and next_tok[0] != 'НОВСТР':
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'ДВОЕТОЧИЕ': return parse_instruction_or_directive(state)
label_name = self.next_token()[1]
self.next_token() # ДВОЕТОЧИЕ
self.parsed_lines.append(f"LABEL:{label_name}:{self.current_section}")
next_tok = self.peek()
if next_tok and next_tok[0] != 'НОВСТР':
return self.parse_instruction_or_directive()
return True
return self.parse_instruction_or_directive()
def parse_instruction_or_directive(self):
tok = self.peek()
if tok is None or tok[0] == 'НОВСТР':
return True return True
if tok[0] != 'СЛОВО':
raise ValueError(f"Ожидалось слово, получено: {tok}")
word = self.next_token()[1]
if word.startswith('.'):
self.parse_directive(word)
else:
self.parse_instruction(word)
return True
def parse_directive(self, word):
"""
Разбор директив ассемблера.
Вызывает ошибку при некорректном синтаксисе или контексте.
"""
# === Секции ===
if word == '.текст':
self.current_section = ".text"
self.parsed_lines.append(f"DIRECTIVE:{word}")
elif word == '.данные': return parse_instruction_or_directive(state)
self.current_section = ".data"
self.parsed_lines.append(f"DIRECTIVE:{word}")
elif word == '.бнд':
self.current_section = ".бнд"
self.parsed_lines.append(f"DIRECTIVE:{word}")
# === Глобальные метки === # ==================== ГЛАВНЫЙ ЦИКЛ ====================
elif word == '.глобал':
label = self.expect('СЛОВО')[1]
self.parsed_lines.append(f"DIRECTIVE:{word}:{label}")
# === Строковые директивы (запрещены в .бнд) === def parse_all(tokens):
elif word in ('.строкауль', '.строка'): """Разбирает все токены, возвращает список строк AST"""
if self.current_section == ".бнд": state = parser_init(tokens)
raise ValueError( while state['pos'] < len(state['tokens']):
"Ошибка: секция .бнд не поддерживает инициализированные данные " parse_line(state)
"(используйте .резб, .резс, .рездс, .резкс)" return state['parsed_lines']
)
string_tok = self.expect('СТРОКА')
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}")
# === Константы (запрещены в .бнд) ===
elif word == '.константа':
if self.current_section == ".бнд":
raise ValueError(
"Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)"
)
name = self.expect('СЛОВО')[1]
eq_tok = self.peek()
if eq_tok and eq_tok[0] == 'СЛОВО' and eq_tok[1] == '=':
self.next_token()
value_tok = self.expect_one_of(['СЛОВО', 'ЧИСЛО'])
self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}")
# === Байты (запрещены в .бнд) ===
elif word == '.байт':
if self.current_section == ".бнд":
raise ValueError(
"Ошибка: секция .бнд не поддерживает инициализированные данные "
"(используйте .резб, .резс, .рездс, .резкс)"
)
bytes_list = []
while True:
tok = self.peek()
if tok is None or tok[0] == 'НОВСТР' or tok[0] == 'ЗАПЯТАЯ':
if tok and tok[0] == 'ЗАПЯТАЯ':
self.next_token()
else:
break
else:
val_tok = self.expect_one_of(['СЛОВО', 'ЧИСЛО', 'СТРОКА'])
bytes_list.append(val_tok[1])
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list))
# === Директивы резервирования (только в .бнд) ===
elif word in ('.резб', '.резс', '.рездс', '.резкс'):
if self.current_section != ".бнд":
raise ValueError(
f"Ошибка: директива {word} допустима только внутри секции .бнд"
)
count_tok = self.expect('ЧИСЛО')
count = int(count_tok[1])
if count <= 0:
raise ValueError(
f"Ошибка: размер резервирования должен быть положительным целым числом"
)
self.parsed_lines.append(
f"DIRECTIVE:{word}:{self.current_section}:{count}"
)
else:
raise ValueError(f"Неизвестная директива: {word}")
def parse_operand(self):
"""Разбирает один операнд"""
tok = self.peek()
if tok is None:
return None
if tok[0] == 'СК_ОТКР':
self.next_token()
return self.parse_memory_operand()
elif tok[0] == 'ЧИСЛО':
return self.next_token()[1]
elif tok[0] == 'СТРОКА':
return self.next_token()[1]
elif tok[0] == 'СЛОВО':
return self.next_token()[1]
else:
raise ValueError(f"Неожиданный токен в операнде: {tok}")
def parse_memory_operand(self):
"""Разбирает операнд памяти"""
parts = []
while True:
tok = self.peek()
if tok is None:
raise ValueError("Незакрытая квадратная скобка")
if tok[0] == 'СК_ЗАКР':
self.next_token()
break
if tok[0] in ('СЛОВО', 'ЧИСЛО', 'ПЛЮС', 'МИНУС', 'ЗВЕЗДА'):
parts.append(self.next_token()[1])
else:
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_instruction(self, mnemonic):
if mnemonic not in INSTRUCTIONS:
raise ValueError(f"Неизвестная инструкция: {mnemonic}")
operands = []
while True:
tok = self.peek()
if tok is None or tok[0] == 'НОВСТР':
break
if tok[0] == 'ЗАПЯТАЯ':
self.next_token()
continue
operands.append(self.parse_operand())
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))
else:
str_operands.append(op)
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(str_operands))
def parse_all(self):
while self.pos < len(self.tokens):
self.parse_line()
return self.parsed_lines
# ==================== ЗАПИСЬ РЕЗУЛЬТАТА ====================
def write_ast(ast_lines, output_file): def write_ast(ast_lines, output_file):
"""Записывает строки AST в файл"""
with open(output_file, 'w', encoding='utf-8') as f: with open(output_file, 'w', encoding='utf-8') as f:
for line in ast_lines: for line in ast_lines:
f.write(line + '\n') f.write(line + '\n')
# ==================== ТОЧКА ВХОДА ====================
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) != 3: if len(sys.argv) != 3:
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>") print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
sys.exit(1) sys.exit(1)
input_file = sys.argv[1] input_file = sys.argv[1]
output_file = sys.argv[2] output_file = sys.argv[2]
tokens = read_tokens(input_file) tokens = read_tokens(input_file)
parser = Parser(tokens) ast_lines = parse_all(tokens)
ast_lines = parser.parse_all()
write_ast(ast_lines, output_file) write_ast(ast_lines, output_file)
print(f"Парсер: {len(ast_lines)} строк AST записано в {output_file}") print(f"Парсер: {len(ast_lines)} строк AST записано в {output_file}")

View file

@ -1,22 +1 @@
; тесты/вычитание/вычитание.квс вызвать и вернуться уже реализованы, но не тестировались
; Тест вычитания: 50 - 8 = 42
.текст
.глобал _start
_start:
; Загружаем числа
переместить_имм раикс, 50
переместить_имм рбикс, 12
; Вычитаем: раикс = раикс - рбикс
вычесть раикс, рбикс
; Результат (42) в RDI для exit
переместить рдиай, раикс
; Выход
переместить_имм раикс, 60
вызов_системы
.данные
_пусто: .байт 0