diff --git a/run-some-tests.py b/run-some-tests.py new file mode 100755 index 0000000..cf7e563 --- /dev/null +++ b/run-some-tests.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +import os +import sys +import getopt + +from utils import run_selected_tests, enable_tracing, enable_emulation + +import var + +sys.setrecursionlimit(10000) + +try: + opts, files = getopt.getopt(sys.argv[1:],"te") +except getopt.GetoptError: + print ('usage: run-tests.py [-t] [-e]') + sys.exit(1) + +for opt,arg in opts: + if opt == '-t': + enable_tracing() + if opt == '-e': + enable_emulation() + +run_selected_tests(files, compiler.Compiler()) + diff --git a/run-tests.py b/run-tests.py index 4c955af..4f2cb87 100644 --- a/run-tests.py +++ b/run-tests.py @@ -1,35 +1,11 @@ import os import compiler -import interp_Lvar -import type_check_Lvar from utils import run_tests, run_one_test -from interp_x86.eval_x86 import interp_x86 compiler = compiler.Compiler() -typecheck_Lvar = type_check_Lvar.TypeCheckLvar().type_check - -typecheck_dict = { - 'source': typecheck_Lvar, - 'remove_complex_operands': typecheck_Lvar, -} -interpLvar = interp_Lvar.InterpLvar().interp -interp_dict = { - 'remove_complex_operands': interpLvar, - 'select_instructions': interp_x86, - 'assign_homes': interp_x86, - 'patch_instructions': interp_x86, -} - if False: run_one_test(os.getcwd() + '/tests/var/zero.py', - 'var', - compiler, - 'var', - typecheck_dict, - interp_dict) + compiler) else: - run_tests('var', compiler, 'var', - typecheck_dict, - interp_dict) - + run_tests('var', compiler) diff --git a/utils.py b/utils.py index 9b00c52..4962f32 100644 --- a/utils.py +++ b/utils.py @@ -4,6 +4,7 @@ import ast from ast import * from dataclasses import dataclass +from contextlib import redirect_stdout,_RedirectStream # move these to the compilers, use a method with overrides -Jeremy builtin_functions = \ @@ -766,7 +767,7 @@ class Begin(expr): def __str__(self): indent() stmts = ''.join([str(s) for s in self.body]) - end = indent_stmt() + + 'produce ' + str(self.result) + end = indent_stmt() + 'produce ' + str(self.result) dedent() return '{\n' + stmts + end + '}' @@ -1120,17 +1121,29 @@ def label_name(n: str) -> str: tracing = False +emulate_x86 = False def enable_tracing(): global tracing tracing = True +def enable_emulation(): + global emulate_x86 + emulate_x86 = True + def trace(msg): if tracing: print(msg, file=sys.stderr) +def trace_ast_and_concrete(ast): + trace("concrete syntax:") + trace(ast) + trace("") + trace("AST:") + trace(repr(ast)) + def is_python_extension(filename): s = os.path.splitext(filename) if len(s) > 1: @@ -1138,6 +1151,8 @@ def is_python_extension(filename): else: return False +class redirect_stdin(_RedirectStream): + _stream = "stdin" # Given the `ast` output of a pass and a test program (root) name, # runs the interpreter on the program and compares the output to the @@ -1146,14 +1161,12 @@ def test_pass(passname, interp_dict, program_root, ast, compiler_name): if passname in interp_dict.keys(): input_file = program_root + '.in' + if not os.path.isfile(input_file): + input_file = "/dev/null" output_file = program_root + '.out' - stdin = sys.stdin - stdout = sys.stdout - sys.stdin = open(input_file, 'r') - sys.stdout = open(output_file, 'w') - interp_dict[passname](ast) - sys.stdin = stdin - sys.stdout = stdout + with open(input_file, 'r') as inf, open(output_file, 'w') as outf: + with redirect_stdout(outf), redirect_stdin(inf): + interp_dict[passname](ast) result = os.system('diff' + ' -b ' + output_file + ' ' + program_root + '.golden') if result == 0: trace('compiler ' + compiler_name + ' success on pass ' + passname \ @@ -1168,9 +1181,7 @@ def test_pass(passname, interp_dict, program_root, ast, return 0 # ?? -def compile_and_test(compiler, compiler_name, - type_check_dict, interp_dict, - program_filename): +def compile_and_test(compiler, program_filename): total_passes = 0 successful_passes = 0 from eval_x86 import interp_x86 @@ -1183,226 +1194,23 @@ def compile_and_test(compiler, compiler_name, trace(program) trace('') - if 'source' in type_check_dict.keys(): + if 'source' in compiler.typecheck_dict.keys(): trace('\n# type checking source program\n') - type_check_dict['source'](program) - - passname = 'shrink' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.shrink(program) - trace(program) - trace('') - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, compiler_name) - else: - trace("\n# no shrink pass!") - - passname = 'uniquify' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.uniquify(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, compiler_name) - - passname = 'reveal_functions' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.reveal_functions(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'resolve' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.resolve(program) - trace(program) - trace('') - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, compiler_name) + compiler.typecheck_dict['source'](program) - passname = 'erase_types' - if hasattr(compiler, passname): + passes = compiler.passes() + for passname, passfn in passes: trace('\n# ' + passname + '\n') - program = compiler.erase_types(program) + program = passfn(program) trace(program) trace('') - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, compiler_name) - - passname = 'cast_insert' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.cast_insert(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'lower_casts' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.lower_casts(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'differentiate_proxies' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.differentiate_proxies(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'reveal_casts' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.reveal_casts(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'convert_assignments' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.convert_assignments(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'convert_to_closures' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.convert_to_closures(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'limit_functions' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.limit_functions(program) - trace(program) - if passname in type_check_dict.keys(): - trace('type checking after ' + passname + '\n') - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'expose_allocation' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.expose_allocation(program) - trace(program) - if passname in type_check_dict.keys(): - trace('type checking after ' + passname + '\n') - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'remove_complex_operands' - trace('\n# ' + passname + '\n') - program = compiler.remove_complex_operands(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'explicate_control' - if hasattr(compiler, passname): - trace('\n# ' + passname + '\n') - program = compiler.explicate_control(program) - trace(program) - if passname in type_check_dict.keys(): - type_check_dict[passname](program) - trace('type checking passed') - else: - trace('skipped type checking') + if passname in compiler.typecheck_dict.keys(): + trace('compiler ' + compiler.compiler_name + ' type checking on pass ' + passname \ + + ' on test ' + program_root + '\n') + compiler.typecheck_dict[passname](program) total_passes += 1 successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'select_instructions' - trace('\n# ' + passname + '\n') - program = compiler.select_instructions(program) - trace(program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'assign_homes' - trace('\n# ' + passname + '\n') - program = compiler.assign_homes(program) - trace(program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - passname = 'patch_instructions' - trace('\n# ' + passname + '\n') - program = compiler.patch_instructions(program) - trace(program) - total_passes += 1 - successful_passes += \ - test_pass(passname, interp_dict, program_root, program, - compiler_name) - - trace('\n# prelude and conclusion\n') - program = compiler.prelude_and_conclusion(program) - trace(program) - trace("") + test_pass(passname, compiler.interp_dict, program_root, program, compiler.compiler_name) x86_filename = program_root + ".s" with open(x86_filename, "w") as dest: @@ -1411,23 +1219,25 @@ def compile_and_test(compiler, compiler_name, total_passes += 1 # Run the final x86 program - emulate_x86 = False + input_file = program_root + '.in' + if not os.path.isfile(input_file): + input_file = "/dev/null" + output_file = program_root + '.out' if emulate_x86: - stdin = sys.stdin - stdout = sys.stdout - sys.stdin = open(program_root + '.in', 'r') - sys.stdout = open(program_root + '.out', 'w') - interp_x86(program) - sys.stdin = stdin - sys.stdout = stdout + trace('emulating x86') + with open(input_file, 'r') as inf, open(output_file, 'w') as outf: + with redirect_stdout(outf), redirect_stdin(inf): + interp_x86(program) else: - if platform == 'darwin': - os.system('gcc -arch x86_64 runtime.o ' + x86_filename) - else: - os.system('gcc runtime.o ' + x86_filename) - input_file = program_root + '.in' - output_file = program_root + '.out' - os.system('./a.out < ' + input_file + ' > ' + output_file) + trace('executing x86') + arch = ' -arch x86_64' if platform == 'darwin' else '' + x86_executable = program_root + '.exe' + if os.path.isfile(x86_executable): + os.remove(x86_executable) + if os.path.isfile(output_file): + os.remove(output_file) + os.system('gcc' + arch + ' -o ' + x86_executable + ' runtime.o ' + x86_filename) + os.system(x86_executable + ' < ' + input_file + ' > ' + output_file) result = os.system('diff' + ' -b ' + program_root + '.out ' \ + program_root + '.golden') @@ -1435,124 +1245,46 @@ def compile_and_test(compiler, compiler_name, successful_passes += 1 return (successful_passes, total_passes, 1) else: - print('compiler ' + compiler_name + ', executable failed' \ + print('compiler ' + compiler.compiler_name + ', executable failed' \ + ' on test ' + program_root) return (successful_passes, total_passes, 0) -def trace_ast_and_concrete(ast): - trace("concrete syntax:") - trace(ast) - trace("") - trace("AST:") - trace(repr(ast)) - - # This function compiles the program without any testing -def compile(compiler, compiler_name, type_check_L, type_check_C, - program_filename): +def compile(compiler, program_filename): program_root = os.path.splitext(program_filename)[0] with open(program_filename) as source: program = parse(source.read()) - trace('\n# type check\n') - type_check_L(program) trace_ast_and_concrete(program) - if hasattr(compiler, 'shrink'): - trace('\n# shrink\n') - program = compiler.shrink(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'uniquify'): - trace('\n# uniquify\n') - program = compiler.uniquify(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'reveal_functions'): - trace('\n# reveal functions\n') - type_check_L(program) - program = compiler.reveal_functions(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'convert_assignments'): - trace('\n# assignment conversion\n') - type_check_L(program) - program = compiler.convert_assignments(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'limit_functions'): - trace('\n# limit functions\n') - type_check_L(program) - program = compiler.limit_functions(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'convert_to_closures'): - trace('\n# closure conversion\n') - type_check_L(program) - program = compiler.convert_to_closures(program) - trace_ast_and_concrete(program) - - if hasattr(compiler, 'expose_allocation'): - trace('\n# expose allocation\n') - type_check_L(program) - program = compiler.expose_allocation(program) - trace_ast_and_concrete(program) - - trace('\n# remove complex\n') - program = compiler.remove_complex_operands(program) - trace_ast_and_concrete(program) + trace('\n# type check\n') + compiler.typecheck_dict['source'](program) - if hasattr(compiler, 'explicate_control'): - trace('\n# explicate control\n') - program = compiler.explicate_control(program) + for passname, passfn in compiler.passes(): + trace('\n# ' + passname + '\n') + perogram = passfn(program) trace_ast_and_concrete(program) - if type_check_C: - type_check_C(program) - - trace('\n# select instructions\n') - pseudo_x86 = compiler.select_instructions(program) - trace_ast_and_concrete(pseudo_x86) - - trace('\n# assign homes\n') - almost_x86 = compiler.assign_homes(pseudo_x86) - trace_ast_and_concrete(almost_x86) - - trace('\n# patch instructions\n') - x86 = compiler.patch_instructions(almost_x86) - trace_ast_and_concrete(x86) - - trace('\n# prelude and conclusion\n') - x86 = compiler.prelude_and_conclusion(x86) - trace_ast_and_concrete(x86) - # Output x86 program to the .s file x86_filename = program_root + ".s" with open(x86_filename, "w") as dest: - dest.write(str(x86)) - - # Given a test file name, the name of a language, a compiler, a type - + dest.write(str(program)) -# checker and interpreter for the language, and an interpeter for the -# C intermediate language, run all the passes in the compiler, +# Given a test file name and a compiler, run all the passes in the compiler, # checking that the resulting programs produce output that matches the # golden file. -def run_one_test(test, lang, compiler, compiler_name, - type_check_dict, interp_dict): -# test_root = os.path.splitext(test)[0] -# test_name = os.path.basename(test_root) - return compile_and_test(compiler, compiler_name, type_check_dict, - interp_dict, test) - - -# Given the name of a language, a compiler, the compiler's name, a -# type checker and interpreter for the language, and an interpreter -# for the C intermediate language, test the compiler on all the tests -# in the directory of for the given language, i.e., all the -# python files in ./tests/. -def run_tests(lang, compiler, compiler_name, type_check_dict, interp_dict): +def run_one_test(test, compiler): + try: + return compile_and_test(compiler, test) + except Exception as exn: + from traceback import print_exc + print_exc(file=sys.stderr) + return (0,0,0) + +# Given the name of a language and a compiler, +# test the compiler on all the tests in ./tests/ +def run_tests(lang, compiler): # Collect all the test programs for this language. homedir = os.getcwd() directory = homedir + '/tests/' + lang + '/' @@ -1570,8 +1302,30 @@ def run_tests(lang, compiler, compiler_name, type_check_dict, interp_dict): total_tests = 0 for test in tests: (succ_passes, tot_passes, succ_test) = \ - run_one_test(test, lang, compiler, compiler_name, - type_check_dict, interp_dict) + run_one_test(test, compiler) + successful_passes += succ_passes + total_passes += tot_passes + successful_tests += succ_test + total_tests += 1 + + # Report the pass/fails + print('tests: ' + repr(successful_tests) + '/' + repr(total_tests) \ + + ' for compiler ' + compiler.compiler_name + ' on language ' + lang) + print('passes: ' + repr(successful_passes) + '/' + repr(total_passes) \ + + ' for compiler ' + compiler.compiler_name + ' on language ' + lang) + +# Given a list of file names and a compiler, +# test the compiler on all the tests in the list. +def run_selected_tests(tests, compiler): + # Compile and run each test program, comparing output to the golden file. + successful_passes = 0 + total_passes = 0 + successful_tests = 0 + total_tests = 0 + for test in tests: + print(test + ':', file=sys.stderr) + (succ_passes, tot_passes, succ_test) = \ + run_one_test(test, compiler) successful_passes += succ_passes total_passes += tot_passes successful_tests += succ_test @@ -1579,6 +1333,7 @@ def run_tests(lang, compiler, compiler_name, type_check_dict, interp_dict): # Report the pass/fails print('tests: ' + repr(successful_tests) + '/' + repr(total_tests) \ - + ' for compiler ' + compiler_name + ' on language ' + lang) + + ' for compiler ' + compiler.compiler_name) print('passes: ' + repr(successful_passes) + '/' + repr(total_passes) \ - + ' for compiler ' + compiler_name + ' on language ' + lang) + + ' for compiler ' + compiler.compiler_name) +