#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Тестовый скрипт для проверки инструкций с фиксированной длиной Запуск: python3 test_fixed_instructions.py [--keep-files] """ import sys import os import subprocess import csv # ANSI color codes GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' RESET = '\033[0m' # === ТЕСТОВЫЙ ИСХОДНИК === TEST_SOURCE = '''.глобал _start .текст _start: нет_операции вызов_системы вернуться втолкнуть раикс втолкнуть р8 вытолкнуть раикс вытолкнуть р8 переместить раикс, рбикс переместить р8, р9 сравнить раикс, рбикс сравнить р8, р9 проверить раикс, рбикс проверить р8, р9 прибавить раикс, рбикс прибавить р8, р9 вычесть раикс, рбикс вычесть р8, р9 и раикс, рбикс и р8, р9 или раикс, рбикс или р8, р9 исключающее_или раикс, рбикс исключающее_или р8, р9 увеличить раикс увеличить р8 увеличить еаикс уменьшить раикс уменьшить р8 уменьшить еаикс инвертировать раикс инвертировать еаикс отрицать раикс отрицать еаикс переместить_имм раикс, 10 переместить_имм рбикс, 5 умножить рбикс переместить_имм раикс, 100 переместить_имм рбикс, 7 разделить рбикс умножить_знаковое рбикс разделить_знаковое рбикс сдвиг_влево раикс, 2 сдвиг_вправо раикс, 2 сдвиг_арифметический_вправо раикс, 2 вращать_влево раикс, 2 вращать_вправо раикс, 2 переход метка_длинная короткий_переход метка_короткая цикл метка_короткая вызвать метка_длинная метка_длинная: нет_операции нет_операции нет_операции метка_короткая: нет_операции переместить_имм раикс, 60 переместить_имм рдикс, 0 вызов_системы ''' # === ОЖИДАЕМЫЕ РЕЗУЛЬТАТЫ === # Для инструкций с фиксированным кодом — полная проверка EXPECTED_INSTRUCTIONS_FULL = { "нет_операции": [0x90], "вызов_системы": [0x0F, 0x05], "вернуться": [0xC3], "втолкнуть раикс": [0x50], "втолкнуть р8": [0x41, 0x50], "вытолкнуть раикс": [0x58], "вытолкнуть р8": [0x41, 0x58], "переместить раикс, рбикс": [0x48, 0x89, 0xD8], "переместить р8, р9": [0x4D, 0x89, 0xC8], "сравнить раикс, рбикс": [0x48, 0x39, 0xD8], "сравнить р8, р9": [0x4D, 0x39, 0xC8], "проверить раикс, рбикс": [0x48, 0x85, 0xD8], "проверить р8, р9": [0x4D, 0x85, 0xC8], "прибавить раикс, рбикс": [0x48, 0x01, 0xD8], "прибавить р8, р9": [0x4D, 0x01, 0xC8], "вычесть раикс, рбикс": [0x48, 0x29, 0xD8], "вычесть р8, р9": [0x4D, 0x29, 0xC8], "и раикс, рбикс": [0x48, 0x21, 0xD8], "и р8, р9": [0x4D, 0x21, 0xC8], "или раикс, рбикс": [0x48, 0x09, 0xD8], "или р8, р9": [0x4D, 0x09, 0xC8], "исключающее_или раикс, рбикс": [0x48, 0x31, 0xD8], "исключающее_или р8, р9": [0x4D, 0x31, 0xC8], "увеличить раикс": [0x48, 0xFF, 0xC0], "увеличить р8": [0x49, 0xFF, 0xC0], "увеличить еаикс": [0xFF, 0xC0], "уменьшить раикс": [0x48, 0xFF, 0xC8], "уменьшить р8": [0x49, 0xFF, 0xC8], "уменьшить еаикс": [0xFF, 0xC8], "инвертировать раикс": [0x48, 0xF7, 0xD0], "инвертировать еаикс": [0xF7, 0xD0], "отрицать раикс": [0x48, 0xF7, 0xD8], "отрицать еаикс": [0xF7, 0xD8], "умножить рбикс": [0x48, 0xF7, 0xE0], "разделить рбикс": [0x48, 0xF7, 0xF0], "умножить_знаковое рбикс": [0x48, 0xF7, 0xE8], "разделить_знаковое рбикс": [0x48, 0xF7, 0xF8], "сдвиг_влево раикс, 2": [0x48, 0xC1, 0xE0, 0x02], "сдвиг_вправо раикс, 2": [0x48, 0xC1, 0xE8, 0x02], "сдвиг_арифметический_вправо раикс, 2": [0x48, 0xC1, 0xF8, 0x02], "вращать_влево раикс, 2": [0x48, 0xC1, 0xC0, 0x02], "вращать_вправо раикс, 2": [0x48, 0xC1, 0xC8, 0x02], "переместить_имм раикс, 10": [0x48, 0xB8, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], "переместить_имм рбикс, 5": [0x48, 0xBB, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], "переместить_имм раикс, 100": [0x48, 0xB8, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], "переместить_имм рбикс, 7": [0x48, 0xBB, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], "переместить_имм раикс, 60": [0x48, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], "переместить_имм рдикс, 0": [0x48, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], } # Инструкции, у которых проверяем только opcode (первые байты) EXPECTED_INSTRUCTIONS_PREFIX = { "переход метка_длинная": [0xE9], # JMP rel32 "короткий_переход метка_короткая": [0xEB], # JMP SHORT "цикл метка_короткая": [0xE2], # LOOP "вызвать метка_длинная": [0xE8], # CALL rel32 } def read_csv_instructions(csv_file): """Читает CSV и возвращает словарь {мнемоника: [байты]}""" instructions = {} current_mnemonic = None current_bytes = [] with open(csv_file, 'r', encoding='utf-8') as f: reader = csv.reader(f, delimiter=';') header = next(reader) for row in reader: if len(row) < 4: continue cmd = row[3].strip() byte_str = row[1].strip() if not byte_str: continue byte_val = int(byte_str, 16) if cmd: if current_mnemonic and current_bytes: instructions[current_mnemonic] = current_bytes current_mnemonic = cmd current_bytes = [byte_val] else: if current_mnemonic: current_bytes.append(byte_val) if current_mnemonic and current_bytes: instructions[current_mnemonic] = current_bytes return instructions def run_tests(generated_instructions): """Сравнивает сгенерированные инструкции с ожидаемыми""" errors = [] missing = [] # Проверяем инструкции с полным совпадением for mnemonic, expected_bytes in EXPECTED_INSTRUCTIONS_FULL.items(): if mnemonic not in generated_instructions: missing.append(mnemonic) continue actual_bytes = generated_instructions[mnemonic] if actual_bytes != expected_bytes: expected_hex = ' '.join(f'{b:02X}' for b in expected_bytes) actual_hex = ' '.join(f'{b:02X}' for b in actual_bytes) errors.append(f" {mnemonic}: ожидалось [{expected_hex}], получено [{actual_hex}]") # Проверяем инструкции только по префиксу for mnemonic, expected_prefix in EXPECTED_INSTRUCTIONS_PREFIX.items(): if mnemonic not in generated_instructions: missing.append(mnemonic) continue actual_bytes = generated_instructions[mnemonic] prefix_len = len(expected_prefix) if len(actual_bytes) < prefix_len: errors.append(f" {mnemonic}: слишком короткая ({len(actual_bytes)} байт, ожидалось минимум {prefix_len})") elif actual_bytes[:prefix_len] != expected_prefix: expected_hex = ' '.join(f'{b:02X}' for b in expected_prefix) actual_hex = ' '.join(f'{b:02X}' for b in actual_bytes[:prefix_len]) errors.append(f" {mnemonic}: префикс [{expected_hex}...], получено [{actual_hex}...]") return errors, missing def cleanup(files_to_delete, keep_files=False): """Удаляет временные файлы""" if keep_files: print("\nФлаг --keep-files указан, промежуточные файлы сохранены:") for f in files_to_delete: if os.path.exists(f): print(f" {f}") return deleted = [] for f in files_to_delete: if os.path.exists(f): os.remove(f) deleted.append(f) if deleted: print(f"\nУдалено {len(deleted)} временных файлов") def main(): keep_files = '--keep-files' in sys.argv or '-k' in sys.argv base_name = "т1" source_file = f"{base_name}.квс" tokens_file = f"{base_name}.токены" ast_file = f"{base_name}.аст" p1_file = f"{base_name}.проход1" csv_file = f"{base_name}.csv" elf_file = f"{base_name}.elf" temp_files = [tokens_file, ast_file, p1_file, csv_file, elf_file] print("КВС Тестировщик - Инструкции с фиксированной длиной\n") print("=" * 60) # Шаг 1: Создание исходного файла print("Шаг 1: Создание тестового исходника...") with open(source_file, 'w', encoding='utf-8') as f: f.write(TEST_SOURCE) print(f" Создан {source_file}") # Шаг 2: Компиляция print("\nШаг 2: Компиляция...") result = subprocess.run( [sys.executable, "kvs_build.py", source_file], capture_output=True, text=True ) if result.returncode != 0: print(" ОШИБКА компиляции:") print(result.stderr) print("\nОставшиеся файлы для отладки:") print(f" {source_file}") if os.path.exists(csv_file): print(f" {csv_file}") sys.exit(1) print(" Компиляция успешна") # Шаг 3: Проверка результатов print("\nШаг 3: Проверка сгенерированного кода...") if not os.path.exists(csv_file): print(f" Файл {csv_file} не найден!") sys.exit(1) generated_instructions = read_csv_instructions(csv_file) errors, missing = run_tests(generated_instructions) # Вывод результатов print("\n" + "=" * 60) print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ:") print("=" * 60) if missing: print(f"\n{YELLOW}ОТСУТСТВУЮТ ИНСТРУКЦИИ ({len(missing)}):{RESET}") for m in missing[:10]: print(f" - {m}") if len(missing) > 10: print(f" ... и ещё {len(missing)-10}") if errors: print(f"\n{RED}НАЙДЕНО {len(errors)} ОШИБОК В КОДЕ:{RESET}") for err in errors: print(err) success = False else: print(f"\n{GREEN}НЕТ ОШИБОК! ВСЕ ИНСТРУКЦИИ ЗАКОДИРОВАНЫ ВЕРНО!{RESET}") success = True # Статистика print(f"\nСтатистика:") print(f" Ожидалось инструкций (полная проверка): {len(EXPECTED_INSTRUCTIONS_FULL)}") print(f" Ожидалось инструкций (проверка префикса): {len(EXPECTED_INSTRUCTIONS_PREFIX)}") print(f" Сгенерировано инструкций: {len(generated_instructions)}") # Шаг 4: Очистка cleanup([source_file] + temp_files, keep_files) # Финальный вердикт print("\n" + "=" * 60) if success: print(f"{GREEN}ТЕСТЫ ПРОЙДЕНЫ УСПЕШНО!{RESET}") sys.exit(0) else: print(f"{RED}ТЕСТЫ НЕ ПРОЙДЕНЫ!{RESET}") sys.exit(1) if __name__ == "__main__": main()