From 967be9e750221ab2ab783f95df79bb26d290a45e Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:07:58 +0200 Subject: add: added projects --- tiger-compiler/tcsh/python/ti.py | 356 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100755 tiger-compiler/tcsh/python/ti.py (limited to 'tiger-compiler/tcsh/python/ti.py') diff --git a/tiger-compiler/tcsh/python/ti.py b/tiger-compiler/tcsh/python/ti.py new file mode 100755 index 0000000..a7df479 --- /dev/null +++ b/tiger-compiler/tcsh/python/ti.py @@ -0,0 +1,356 @@ +#! /usr/bin/env python3 + +# Tiger interpreter. + +# Execute a Tiger program, using the Tiger Compiler through +# TCSH/Python and a supporting back-end environment (HAVM, Nolimips, +# or an IA-32 environment). +# +# This script needs a complete, dynamically-compiled Tiger compiler +# (with all its modules) to work. The Tree back-end needs a `havm' +# binary to be available in the PATH; the MIPS back-end, a `nolimips' +# binary; and the IA-32 back-end, an IA-32 execution environment. + +from __future__ import annotations +from collections.abc import Callable +from typing import List, Optional, Tuple, TypeVar, Union + +import optparse +import tempfile +import subprocess + +import os +import sys +import re + +import tc + + +# Subclass to redefine error method (for exit code to be 1). +class TiOptionParser(optparse.OptionParser): + def error(self, msg: str) -> None: + self.print_usage(sys.stderr) + self.exit(1, "%s: error: %s\n" % (self.get_prog_name(), msg)) + + +T = TypeVar("T") + + +def wrap_step( + modules: List[str] = [], + require: Optional[str] = None, + backend: Optional[tc.BackendType] = None, +) -> Callable[[Callable[[TiExecutor], T]], Callable[[TiExecutor], Optional[T]]]: + """ + Decorator for steps of TiExecutor, execute the require step if needed + """ + + def check_wrap_step( + f: Callable[[TiExecutor], T] + ) -> Callable[[TiExecutor], Optional[T]]: + def new_f(self: TiExecutor) -> Optional[T]: + if self.debug: + print(f"CALL {f.__name__} [{self.backend}]") + if backend and self.backend != backend: + self.backend = backend + if hasattr(self, "__step"): + delattr(self, "__step") + self._rm_asm() + self._rm_llvm() + self.data = TiExecutor.TiExecution() + if require and not hasattr(self, require): + raise RuntimeError(require + " step must exist") + if self.data.error: + return None + hasstep = hasattr(self, "__step") + if require and (not hasstep or hasstep and not self.__step == require): + getattr(self, require)() + if self.data.error: + return None + for mod in modules: + if not tc.has(mod): + msg = f"Module {mod} is not available to execute {f.__name__} step\nCheck your install of tc" + self.data.error = (-1, msg) + print(msg, file=sys.stderr) + if self.exit_on_error: + sys.exit(1) + return None + if self.debug: + print(f"ENTER {f.__name__} [{self.backend}]") + res = f(self) + if self.debug: + print(f"EXIT {f.__name__} [{self.backend}]") + self.__step = f.__name__ + return res + + new_f.__doc__ = f"Ast {f.__name__} step" + if require: + new_f.__doc__ += f"\nRequire {require} step, execute it otherwise" + if modules and len(modules): + new_f.__doc__ += "\nRequire {} tc modules".format(", ".join(modules)) + new_f.__name__ = f.__name__ + return new_f + + return check_wrap_step + + +class TiExecutor: + class TiExecution: + ast: Optional[tc.ast.ChunkList] = None + error: Optional[Tuple[int, str]] = None + fragments: Optional[tc.tree.Fragments] = None + lir_fragments: Optional[tc.assem.Fragments] = None + target: Optional[Union[tc.target.Ia32Target, tc.target.MipsTarget]] = None + tempmap: Optional[tc.temp.TempMap] = None + result: Optional[str] = None + llvm: Optional[str] = None + + def __str__(self) -> str: + res = "" + + def truncate(msg: str) -> str: + return msg if not msg or len(msg) < 100 else msg[:100] + "..." + + for e in dir(self): + if not e.startswith("_"): + res += e + ": " + truncate(repr(getattr(self, e))) + "\n" + return res + + def __init__( + self, + filename: str, + backend: tc.BackendType = tc.BackendType.mips, + exit_on_error: bool = True, + get_result: bool = False, + rename: bool = True, + desugar: bool = True, + object_enabled: bool = True, + debug: bool = False, + ): + self.filename = filename + self.backend = tc.BackendType(backend) + self.exit_on_error = exit_on_error + self.get = get_result + self.rename_enabled = rename + self.desugar_enabled = desugar + self.object_enabled = object_enabled + self.debug = debug + self.data = TiExecutor.TiExecution() + + def error_message(self) -> str: + if not self.data.error: + return "" + status, message = self.data.error + statusMessage = tc.misc.error.error_type_message()[status] + if re.match(r"(.*/)?tmp\w{8}\.tig$", self.filename): + if message.startswith(self.filename + ":"): + message = message.replace(self.filename + ":", "") + message = " " + message + else: + message = "\n" + message + return str(status) + " " + statusMessage + ":" + message + + def throw_error(self, e: Exception) -> None: + self.__step = "error" + self.data.error = e.args + status, message = e.args + if self.exit_on_error: + print(message, file=sys.stderr) + sys.exit(status) + else: + print(self.error_message(), end="", file=sys.stderr) + + def _rm_attribute_file(self, arg: str) -> None: + if hasattr(self, arg): + os.unlink(getattr(self, arg)) + delattr(self, arg) + + def _rm_attribute_dir(self, arg: str) -> None: + if hasattr(self, arg): + os.rmdir(getattr(self, arg)) + delattr(self, arg) + + def _rm_attribute_temp(self, arg: str) -> None: + if hasattr(self, arg): + os.unlink(getattr(self, arg).name) + delattr(self, arg) + + def _run_cmd(self, *cmd: str) -> Optional[str]: + if self.get: + proc = subprocess.run(cmd, capture_output=True) + self.data.result = proc.stdout.decode("utf-8") + else: + os.system(" ".join(cmd)) + self.data.result = None + return self.data.result + + @wrap_step(["misc", "parse"]) + def parse(self) -> Optional[tc.ast.ChunkList]: + lib = tc.misc.file_library() + try: + self.data.ast = tc.parse.parse("builtin", self.filename, lib) + except Exception as e: + return self.throw_error(e) + return self.data.ast + + @wrap_step(["bind"], "parse") + def bind(self) -> Optional[tc.ast.ChunkList]: + try: + if self.object_enabled and tc.has("object"): + tc.object.bind(self.data.ast).exit_on_error() + else: + tc.bind.bind(self.data.ast).exit_on_error() + except Exception as e: + return self.throw_error(e) + return self.data.ast + + @wrap_step([], "bind") + def rename(self) -> Optional[tc.ast.ChunkList]: + if ( + self.rename_enabled + and tc.has("bind") + and not self.object_enabled + and not tc.has("object") + ): + tc.bind.rename(self.data.ast) + return self.data.ast + + @wrap_step(["type"], "rename") + def type(self) -> Optional[tc.ast.ChunkList]: + try: + if self.object_enabled and tc.has("object"): + tc.object.types_check(self.data.ast).exit_on_error() + else: + tc.type.types_check(self.data.ast).exit_on_error() + except Exception as e: + return self.throw_error(e) + return self.data.ast + + @wrap_step([], "type") + def object_desugar(self) -> Optional[tc.ast.ChunkList]: + if self.object_enabled and tc.has("object"): + class_names = tc.object.rename(self.data.ast) + self.data.ast = tc.object.desugar(self.data.ast, class_names) + return self.data.ast + + @wrap_step([], "object_desugar") + def desugar(self) -> Optional[tc.ast.ChunkList]: + if self.desugar_enabled and tc.has("desugar"): + self.data.ast = tc.desugar.desugar(self.data.ast, True, True) + return self.data.ast + + @wrap_step(["llvmtranslate"], "desugar") + def llvm_file(self) -> str: + self.data.llvm = tc.llvmtranslate.translate(self.data.ast) + + self._rm_llvm() + self.llvm_temp_dir = tempfile.mkdtemp() + # Dump assembly code output into a temporary file. + self.llvm_output = os.path.join(self.llvm_temp_dir, "llvm.ll") + with open(self.llvm_output, "w") as f: + f.write(str(self.data.llvm)) + return self.llvm_output + + def _rm_llvm(self) -> None: + self._rm_attribute_file("llvm_output") + self._rm_attribute_file("llvm_binary") + self._rm_attribute_dir("llvm_temp_dir") + + @wrap_step([], "llvm_file") + def llvm_bin(self) -> str: + self._rm_attribute_file("llvm_binary") + self.llvm_binary = os.path.join(self.llvm_temp_dir, "bin") + os.system(f"clang -m32 {self.llvm_output} -o {self.llvm_binary}") + return self.llvm_binary + + @wrap_step([], "llvm_bin", tc.BackendType.llvm) + def llvm(self) -> Optional[str]: + self._run_cmd(self.llvm_binary) + self._rm_llvm() + return self.data.result + + def frontend_run(self) -> None: + """Run parse, bind and type depending of TC step""" + self.parse() + + self.bind() + self.rename() + self.type() + self.object_desugar() + self.desugar() + return None + + def backend_exec(self) -> Optional[str]: + """execute backends: llvm, hir, lir, mips and ia32""" + self.frontend_run() + if self.backend == tc.BackendType.llvm: + return self.llvm() + return None + + def backend_run(self) -> None: + self.get = False + self.backend_exec() + + def backend_get(self) -> Optional[str]: + self.get = True + return self.backend_exec() + + +def process_file( + filename: str, backend: tc.BackendType = tc.BackendType.mips, **kwargs +) -> None: + executor = TiExecutor(filename, backend=backend, **kwargs) + executor.backend_run() + + +if __name__ == "__main__": + # Parser creation. + parser = TiOptionParser( + """%prog [options] file.tig + Execute a Tiger program, using a given back-end.""" + ) + parser.add_option( + "-b", + "--back-end", + metavar="BACKEND", + dest="backend", + default=tc.BackendType.mips, + help="use BACKEND as back-end. Can be either " + f"`{tc.BackendType.llvm.value}' (LLVM), " + f"`{tc.BackendType.mips.value}' (MIPS assembly language) " + "[default: %default]", + ) + parser.add_option( + "-d", + "--debug", + action="store_true", + dest="debug", + default=False, + help="print debug call trace", + ) + + # Options parsing. + (options, args) = parser.parse_args() + + # Invalid argument. + me = os.path.basename(sys.argv[0]) + error = False + if len(args) != 1: + print(f"{me}: not enough arguments") + error = True + + if options.backend not in [e.value for e in tc.BackendType]: + print(f"{me}: select a valid backend") + error = True + + if error: + parser.print_help() + sys.exit(1) + + # Get filename from arguments. + filename = args[-1] + process_file(filename, **vars(options)) + +# Local Variables: +# mode: python +# End: -- cgit v1.2.3