русификация лексера
This commit is contained in:
parent
d445f40ac4
commit
5bc6436c07
53
kvs_lexer.py
53
kvs_lexer.py
|
|
@ -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:
|
||||||
|
|
|
||||||
100
kvs_parser.py
100
kvs_parser.py
|
|
@ -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))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue