Lichen

Annotated lplc

539:22f70469d2f0
2017-02-04 Paul Boddie Explicitly list program modules in an included file, avoiding problems with any obsolete source files still residing amongst the generated sources.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
from errors import *
paul@442 4
from os import rename
paul@0 5
from os.path import abspath, exists, join, split
paul@445 6
from pyparser import error
paul@442 7
from subprocess import Popen, PIPE
paul@0 8
from time import time
paul@126 9
import importer, deducer, optimiser, generator, translator
paul@0 10
import sys
paul@0 11
paul@0 12
libdirs = [
paul@0 13
    join(split(__file__)[0], "lib"),
paul@0 14
    "/usr/share/lichen/lib"
paul@0 15
    ]
paul@0 16
paul@0 17
def load_module(filename, module_name):
paul@0 18
    for libdir in libdirs:
paul@0 19
        path = join(libdir, filename)
paul@0 20
        if exists(path):
paul@0 21
            return i.load_from_file(path, module_name)
paul@0 22
    return None
paul@0 23
paul@41 24
def show_missing(missing):
paul@41 25
    missing = list(missing)
paul@41 26
    missing.sort()
paul@41 27
    for module_name, name in missing:
paul@41 28
        print >>sys.stderr, "Module %s references an unknown object: %s" % (module_name, name)
paul@41 29
paul@445 30
def show_syntax_error(exc):
paul@445 31
    print >>sys.stderr, "Syntax error at column %d on line %d in file %s:" % (exc.offset, exc.lineno, exc.filename)
paul@445 32
    print >>sys.stderr
paul@445 33
    print >>sys.stderr, exc.text.rstrip()
paul@445 34
    print >>sys.stderr, " " * exc.offset + "^"
paul@445 35
paul@0 36
def stopwatch(activity, now):
paul@0 37
    print >>sys.stderr, "%s took %.2f seconds" % (activity, time() - now)
paul@0 38
    return time()
paul@0 39
paul@442 40
def call(tokens, verbose=False):
paul@442 41
    out = not verbose and PIPE or None
paul@442 42
    cmd = Popen(tokens, stdout=out, stderr=out)
paul@442 43
    stdout, stderr = cmd.communicate()
paul@442 44
    return cmd.wait()
paul@442 45
paul@0 46
# Main program.
paul@0 47
paul@0 48
if __name__ == "__main__":
paul@442 49
    args = sys.argv[1:]
paul@525 50
    path = libdirs
paul@0 51
paul@442 52
    # Determine the options and arguments.
paul@442 53
paul@442 54
    debug = False
paul@442 55
    make = True
paul@442 56
    make_verbose = True
paul@525 57
    reset = False
paul@474 58
    traceback = False
paul@525 59
    verbose = False
paul@442 60
paul@442 61
    filenames = []
paul@442 62
    outputs = []
paul@442 63
paul@442 64
    # Obtain program filenames by default.
paul@442 65
paul@442 66
    l = filenames
paul@442 67
paul@442 68
    for arg in args:
paul@525 69
        if arg == "-c": make = False
paul@442 70
        elif arg == "-g": debug = True
paul@442 71
        elif arg == "-q": make_verbose = False
paul@525 72
        elif arg == "-r": reset = True
paul@474 73
        elif arg == "-tb": traceback = True
paul@442 74
        elif arg == "-o": l = outputs
paul@525 75
        elif arg == "-v": verbose = True
paul@442 76
        else:
paul@442 77
            l.append(arg)
paul@442 78
paul@442 79
            # Revert to collecting program filenames after obtaining the output
paul@442 80
            # executable filename.
paul@442 81
paul@442 82
            if l is outputs: l = filenames
paul@442 83
paul@442 84
    # Obtain the program filename.
paul@442 85
paul@442 86
    if len(filenames) > 1:
paul@442 87
        print >>sys.stderr, "Only one main program file can be specified."
paul@442 88
        sys.exit(1)
paul@442 89
paul@442 90
    filename = abspath(filenames[0])
paul@0 91
    path.append(split(filename)[0])
paul@0 92
paul@442 93
    # Obtain the output filename.
paul@442 94
paul@449 95
    output = outputs and outputs[0] or "_main"
paul@442 96
paul@442 97
    # Define the output data directories.
paul@442 98
paul@442 99
    datadir = "_lplc"
paul@442 100
    cache_dir = join(datadir, "_cache")
paul@442 101
    deduced_dir = join(datadir, "_deduced")
paul@442 102
    output_dir = join(datadir, "_output")
paul@442 103
    generated_dir = join(datadir, "_generated")
paul@0 104
paul@0 105
    # Load the program.
paul@0 106
paul@0 107
    try:
paul@0 108
        start = now = time()
paul@0 109
paul@442 110
        i = importer.Importer(path, cache_dir, verbose)
paul@0 111
        m = i.initialise(filename, reset)
paul@41 112
        success = i.finalise()
paul@0 113
paul@0 114
        now = stopwatch("Inspection", now)
paul@0 115
paul@41 116
        # Check for success, indicating missing references otherwise.
paul@41 117
paul@41 118
        if not success:
paul@41 119
            show_missing(i.missing)
paul@275 120
            sys.exit(1)
paul@41 121
paul@442 122
        d = deducer.Deducer(i, deduced_dir)
paul@44 123
        d.to_output()
paul@44 124
paul@44 125
        now = stopwatch("Deduction", now)
paul@44 126
paul@442 127
        o = optimiser.Optimiser(i, d, output_dir)
paul@92 128
        o.to_output()
paul@92 129
paul@92 130
        now = stopwatch("Optimisation", now)
paul@92 131
paul@442 132
        g = generator.Generator(i, o, generated_dir)
paul@183 133
        g.to_output(debug)
paul@126 134
paul@127 135
        now = stopwatch("Generation", now)
paul@126 136
paul@442 137
        t = translator.Translator(i, d, o, generated_dir)
paul@113 138
        t.to_output()
paul@113 139
paul@442 140
        now = stopwatch("Translation", now)
paul@442 141
paul@442 142
        # Compile the program unless otherwise indicated.
paul@442 143
paul@442 144
        if make:
paul@442 145
            make_clean_cmd = ["make", "-C", generated_dir, "clean"]
paul@442 146
            make_cmd = make_clean_cmd[:-1]
paul@442 147
paul@442 148
            retval = call(make_clean_cmd, make_verbose)
paul@442 149
            if not retval:
paul@442 150
                retval = call(make_cmd, make_verbose)
paul@442 151
paul@442 152
            if not retval:
paul@442 153
                stopwatch("Compilation", now)
paul@442 154
            else:
paul@442 155
                sys.exit(retval)
paul@442 156
paul@442 157
            # Move the executable into the current directory.
paul@442 158
paul@442 159
            rename(join(generated_dir, "main"), output)
paul@113 160
paul@0 161
    # Report any errors.
paul@0 162
paul@445 163
    except error.SyntaxError, exc:
paul@445 164
        show_syntax_error(exc)
paul@474 165
        if traceback:
paul@445 166
            raise
paul@445 167
        sys.exit(1)
paul@445 168
paul@0 169
    except ProcessingError, exc:
paul@0 170
        print exc
paul@474 171
        if traceback:
paul@0 172
            raise
paul@275 173
        sys.exit(1)
paul@0 174
paul@0 175
    else:
paul@275 176
        sys.exit(0)
paul@0 177
paul@0 178
# vim: tabstop=4 expandtab shiftwidth=4