main.py 2.91 KB
import argparse, sys, os.path

import ply.yacc as yacc
from . import cparse, cvisitors, cx86

class CompileError (Exception) :
    "Exception raised when there's been a compilation error."
    pass

class Compiler (object) :
    """This object encapsulates the front-end for the compiler and
    serves as a facade interface to the 'meat' of the compiler
    underneath."""
    def __init__ (self) :
        self.total_errors = 0
        self.total_warnings = 0
    def _parse (self) :
        "Parses the source code."
        self.ast = yacc.parse(self.code)
    def _compile_phase (self, visitor) :
        "Applies a visitor to the abstract syntax tree."
        visitor.visit(self.ast)
        self.total_errors += visitor.errors
        self.total_warnings += visitor.warnings
        if visitor.has_errors():
            raise CompileError()
    def _do_compile (self, outfile, ast_file) :
        """Compiles the code to the given file object.  Enabling
        show_ast prints out the abstract syntax tree."""
        self._parse()
        self._compile_phase(cvisitors.SymtabVisitor())
        self._compile_phase(cvisitors.TypeCheckVisitor())
        self._compile_phase(cvisitors.FlowControlVisitor())
        self._compile_phase(cx86.CodeGenVisitor(outfile))
        if ast_file is not None:
            self._compile_phase(cvisitors.ASTPrinterVisitor(ast_file))
    def _print_stats (self) :
        "Prints the total number of errors/warnings from compilation."
        print("%d errors, %d warnings." % (self.total_errors, self.total_warnings))
    def compile (self, code, outfile, show_ast) :
        "Compiles the given code string to the given file object."
        self.code = code
        try:
            self._do_compile(outfile, show_ast)
        except cparse.ParseError:
            print("Errors encountered, bailing.")
            return 1
        except CompileError:
            self._print_stats()
            print("Errors encountered, bailing.")
            return 1
        self._print_stats()
        print("Compile successful.")
        return 0

def main (args=None) :
    parser = argparse.ArgumentParser(prog="cct")
    parser.add_argument("-o", action="store", metavar="PATH",
                        type=argparse.FileType('w'), default=sys.stdout,
                        help="write output to PATH")
    parser.add_argument("--ast", action="store_true", default=False,
                        help="dump AST for each C file")
    parser.add_argument("source", nargs="+", metavar="PATH",
                        help="C source files(s) to compile")
    args = parser.parse_args(args)
    for src in args.source :
        if args.ast :
            ast_file = open(os.path.splitext(src)[0] + ".ast", "w")
        else :
            ast_file = None
        retval = Compiler().compile(open(src).read(), args.o, ast_file)
        if ast_file is not None :
            ast_file.close()
        if retval != 0 :
            sys.exit(retval)