コード例 #1
0
ファイル: interpreter.py プロジェクト: eshnil2000/crianza
def isbool(*args):
    """Checks if value is boolean."""
    true_or_false = [
        instructions.lookup(instructions.true_),
        instructions.lookup(instructions.false_)
    ]
    return all(map(lambda c: isinstance(c, bool) or c in true_or_false, args))
コード例 #2
0
def to_bool(instr):
    if isinstance(instr, bool):
        return instr
    elif instr == instructions.lookup(instructions.true_):
        return True
    elif instr == instructions.lookup(instructions.false_):
        return False
    else:
        raise CompileError("Unknown instruction: %s" % instr)
コード例 #3
0
def check(code):
    """Checks code for obvious errors."""
    def safe_lookup(op):
        try:
            return instructions.lookup(op)
        except Exception:
            return op

    for i, a in enumerate(code):
        b = code[i + 1] if i + 1 < len(code) else None

        # Does instruction exist?
        if not isconstant(a):
            try:
                instructions.lookup(a)
            except KeyError as err:
                # Skip embedded push closures
                if not (len(err.args) == 1 and is_embedded_push(err.args[0])):
                    raise CompileError(
                        "Instruction at index %d is unknown: %s" % (i, a))

        # Invalid: <str> int
        if isstring(a) and safe_lookup(b) == instructions.cast_int:
            raise CompileError(
                "Cannot convert string to integer (index %d): %s %s" %
                (i, a, b))

        # Invalid: <int> <binary op>
        boolean_ops = [
            instructions.boolean_not, instructions.boolean_or,
            instructions.boolean_and
        ]

        if not isbool(a) and safe_lookup(b) in boolean_ops:
            raise CompileError(
                "Can only use binary operators on booleans (index %d): %s %s" %
                (i, a, b))
    return code
コード例 #4
0
ファイル: interpreter.py プロジェクト: eshnil2000/crianza
def code_to_string(code):
    from crianza import compiler
    s = []
    for op in code:
        if isconstant(op):
            if isstring(op):
                s.append(repr(op))
            else:
                s.append(str(op))
        elif compiler.is_embedded_push(op):
            v = compiler.get_embedded_push_value(op)
            s.append('"%s"' % repr(v)[1:-1] if isinstance(v, str) else repr(v))
        else:
            s.append(str(instructions.lookup(op)))
    return " ".join(s)
コード例 #5
0
def native_types(code):
    """Convert code elements from strings to native Python types."""
    out = []
    for c in code:
        if isconstant(c, quoted=True):
            if isstring(c, quoted=True):
                v = c[1:-1]
            elif isbool(c):
                v = to_bool(c)
            elif isnumber(c):
                v = c
            else:
                raise CompileError("Unknown type %s: %s" %
                                   (type(c).__name__, c))

            # Instead of pushing constants in the code, we always push callable
            # Python functions, for fast dispatching:
            out.append(make_embedded_push(v))
        else:
            try:
                out.append(instructions.lookup(c))
            except KeyError:
                raise CompileError("Unknown word '%s'" % c)
    return out
コード例 #6
0
ファイル: native.py プロジェクト: ajbetteridge/crianza
    """Compiles to native Python bytecode and runs program, returning the
    topmost value on the stack.

    Args:
        optimize: Whether to optimize the code after parsing it.

    Returns:
        None: If the stack is empty
        obj: If the stack contains a single value
        [obj, obj, ...]: If the stack contains many values
    """
    native = xcompile(source, optimize=optimize)
    return native()

opmap = {
    cr.lookup("%"):      mod,
    cr.lookup("&"):      bitwise_and,
    cr.lookup("*"):      mul,
    cr.lookup("+"):      add,
    cr.lookup("-"):      sub,
    cr.lookup("."):      dot,
    cr.lookup("/"):      div,
    cr.lookup("<"):      less,
    cr.lookup("<="):     greater_equal,
    cr.lookup("<>"):     not_equal,
    cr.lookup("="):      equal,
    cr.lookup(">"):      greater,
    cr.lookup(">="):     greater_equal,
    cr.lookup("@"):      at,
    cr.lookup("^"):      bitwise_xor,
    cr.lookup("abs"):    abs_,
コード例 #7
0
ファイル: native.py プロジェクト: eshnil2000/crianza
    topmost value on the stack.

    Args:
        optimize: Whether to optimize the code after parsing it.

    Returns:
        None: If the stack is empty
        obj: If the stack contains a single value
        [obj, obj, ...]: If the stack contains many values
    """
    native = xcompile(source, optimize=optimize)
    return native()


opmap = {
    cr.lookup("%"): mod,
    cr.lookup("&"): bitwise_and,
    cr.lookup("*"): mul,
    cr.lookup("+"): add,
    cr.lookup("-"): sub,
    cr.lookup("."): dot,
    cr.lookup("/"): div,
    cr.lookup("<"): less,
    cr.lookup("<="): greater_equal,
    cr.lookup("<>"): not_equal,
    cr.lookup("="): equal,
    cr.lookup(">"): greater,
    cr.lookup(">="): greater_equal,
    cr.lookup("@"): at,
    cr.lookup("^"): bitwise_xor,
    cr.lookup("abs"): abs_,
コード例 #8
0
def compile(code, silent=True, ignore_errors=False, optimize=True):
    """Compiles subroutine-forms into a complete working code.

    A program such as:

        : sub1 <sub1 code ...> ;
        : sub2 <sub2 code ...> ;
        sub1 foo sub2 bar

    is compiled into:

        <sub1 address> call
        foo
        <sub2 address> call
        exit
        <sub1 code ...> return
        <sub2 code ...> return

    Optimizations are first done on subroutine bodies, then on the main loop
    and finally, symbols are resolved (i.e., placeholders for subroutine
    addresses are replaced with actual addresses).

    Args:
        silent: If set to False, will print optimization messages.

        ignore_errors: Only applies to the optimization engine, if set to False
            it will not raise any exceptions. The actual compilatio will still
            raise errors.

        optimize: Flag to control whether to optimize code.

    Raises:
        CompilationError - Raised if invalid code is detected.

    Returns:
        An array of code that can be run by a Machine. Typically, you want to
        pass this to a Machine without doing optimizations.

    Usage:
        source = parse("<source code>")
        code = compile(source)
        machine = Machine(code, optimize=False)
        machine.run()
    """
    assert (isinstance(code, list))

    output = []
    subroutine = {}
    builtins = Machine([]).instructions

    # Gather up subroutines
    try:
        it = code.__iter__()
        while True:
            word = next(it)
            if word == ":":
                name = next(it)
                if name in builtins:
                    raise CompileError(
                        "Cannot shadow internal word definition '%s'." % name)
                if name in [":", ";"]:
                    raise CompileError("Invalid word name '%s'." % name)
                subroutine[name] = []
                while True:
                    op = next(it)
                    if op == ";":
                        subroutine[name].append(
                            instructions.lookup(instructions.return_))
                        break
                    else:
                        subroutine[name].append(op)
            else:
                output.append(word)
    except StopIteration:
        pass

    # Expand all subroutine words to ["<name>", "call"]
    for name, code in subroutine.items():
        # For subroutines
        xcode = []
        for op in code:
            xcode.append(op)
            if op in subroutine:
                xcode.append(instructions.lookup(instructions.call))
        subroutine[name] = xcode

    # Compile main code (code outside of subroutines)
    xcode = []
    for op in output:
        xcode.append(op)
        if op in subroutine:
            xcode.append(instructions.lookup(instructions.call))

    # Because main code comes before subroutines, we need to explicitly add an
    # exit instruction
    output = xcode
    if len(subroutine) > 0:
        output += [instructions.lookup(instructions.exit)]

    # Optimize main code
    if optimize:
        output = optimizer.optimized(output,
                                     silent=silent,
                                     ignore_errors=False)

    # Add subroutines to output, track their locations
    location = {}
    for name, code in subroutine.items():
        location[name] = len(output)
        if optimize:
            output += optimizer.optimized(code,
                                          silent=silent,
                                          ignore_errors=False)
        else:
            output += code

    # Resolve all subroutine references
    for i, op in enumerate(output):
        if op in location:
            output[i] = location[op]

    output = native_types(output)
    if not ignore_errors:
        check(output)
    return output
コード例 #9
0
 def safe_lookup(op):
     try:
         return instructions.lookup(op)
     except Exception:
         return op
コード例 #10
0
ファイル: interpreter.py プロジェクト: eshnil2000/crianza
 def lookup(self, instruction):
     """Looks up name-to-function or function-to-name."""
     return instructions.lookup(instruction, self.instructions)
コード例 #11
0
 def isfunction(op):
     try:
         instructions.lookup(op)
         return True
     except KeyError:
         return False
コード例 #12
0
def constant_fold(code, silent=True, ignore_errors=True):
    """Constant-folds simple expressions like 2 3 + to 5.

    Args:
        code: Code in non-native types.
        silent: Flag that controls whether to print optimizations made.
        ignore_errors: Whether to raise exceptions on found errors.
    """
    # Loop until we haven't done any optimizations.  E.g., "2 3 + 5 *" will be
    # optimized to "5 5 *" and in the next iteration to 25.  Yes, this is
    # extremely slow, big-O wise. We'll fix that some other time. (TODO)

    arithmetic = list(
        map(instructions.lookup, [
            instructions.add,
            instructions.bitwise_and,
            instructions.bitwise_or,
            instructions.bitwise_xor,
            instructions.div,
            instructions.equal,
            instructions.greater,
            instructions.less,
            instructions.mod,
            instructions.mul,
            instructions.sub,
        ]))

    divzero = map(instructions.lookup, [
        instructions.div,
        instructions.mod,
    ])

    lookup = instructions.lookup

    def isfunction(op):
        try:
            instructions.lookup(op)
            return True
        except KeyError:
            return False

    def isconstant(op):
        return op is None or interpreter.isconstant(
            op, quoted=True) or not isfunction(op)

    keep_running = True
    while keep_running:
        keep_running = False
        # Find two consecutive numbes and an arithmetic operator
        for i, a in enumerate(code):
            b = code[i + 1] if i + 1 < len(code) else None
            c = code[i + 2] if i + 2 < len(code) else None

            # Constant fold arithmetic operations (TODO: Move to check-func)
            if interpreter.isnumber(a, b) and c in arithmetic:
                # Although we can detect division by zero at compile time, we
                # don't report it here, because the surrounding system doesn't
                # handle that very well. So just leave it for now.  (NOTE: If
                # we had an "error" instruction, we could actually transform
                # the expression to an error, or exit instruction perhaps)
                if b == 0 and c in divzero:
                    if ignore_errors:
                        continue
                    else:
                        raise errors.CompileError(
                            ZeroDivisionError("Division by zero"))

                # Calculate result by running on a machine (lambda vm: ... is
                # embedded pushes, see compiler)
                result = interpreter.Machine([
                    lambda vm: vm.push(a), lambda vm: vm.push(b),
                    instructions.lookup(c)
                ]).run().top
                del code[i:i + 3]
                code.insert(i, result)

                if not silent:
                    print("Optimizer: Constant-folded %s %s %s to %s" %
                          (a, b, c, result))

                keep_running = True
                break

            # Translate <constant> dup to <constant> <constant>
            if isconstant(a) and b == lookup(instructions.dup):
                code[i + 1] = a
                if not silent:
                    print("Optimizer: Translated %s %s to %s %s" %
                          (a, b, a, a))
                keep_running = True
                break

            # Dead code removal: <constant> drop
            if isconstant(a) and b == lookup(instructions.drop):
                del code[i:i + 2]
                if not silent:
                    print("Optimizer: Removed dead code %s %s" % (a, b))
                keep_running = True
                break

            if a == lookup(instructions.nop):
                del code[i]
                if not silent:
                    print("Optimizer: Removed dead code %s" % a)
                keep_running = True
                break

            # Dead code removal: <integer> cast_int
            if isinstance(a, int) and b == lookup(instructions.cast_int):
                del code[i + 1]
                if not silent:
                    print("Optimizer: Translated %s %s to %s" % (a, b, a))
                keep_running = True
                break

            # Dead code removal: <float> cast_float
            if isinstance(a, float) and b == lookup(instructions.cast_float):
                del code[i + 1]
                if not silent:
                    print("Optimizer: Translated %s %s to %s" % (a, b, a))
                keep_running = True
                break

            # Dead code removal: <string> cast_str
            if isinstance(a, str) and b == lookup(instructions.cast_str):
                del code[i + 1]
                if not silent:
                    print("Optimizer: Translated %s %s to %s" % (a, b, a))
                keep_running = True
                break

            # Dead code removal: <boolean> cast_bool
            if isinstance(a, bool) and b == lookup(instructions.cast_bool):
                del code[i + 1]
                if not silent:
                    print("Optimizer: Translated %s %s to %s" % (a, b, a))
                keep_running = True
                break

            # <c1> <c2> swap -> <c2> <c1>
            if isconstant(a) and isconstant(b) and c == lookup(
                    instructions.swap):
                del code[i:i + 3]
                code = code[:i] + [b, a] + code[i:]
                if not silent:
                    print("Optimizer: Translated %s %s %s to %s %s" %
                          (a, b, c, b, a))
                keep_running = True
                break

            # a b over -> a b a
            if isconstant(a) and isconstant(b) and c == lookup(
                    instructions.over):
                code[i + 2] = a
                if not silent:
                    print("Optimizer: Translated %s %s %s to %s %s %s" %
                          (a, b, c, a, b, a))
                keep_running = True
                break

            # "123" cast_int -> 123
            if interpreter.isstring(a) and b == lookup(instructions.cast_int):
                try:
                    number = int(a)
                    del code[i:i + 2]
                    code.insert(i, number)
                    if not silent:
                        print("Optimizer: Translated %s %s to %s" %
                              (a, b, number))
                    keep_running = True
                    break
                except ValueError:
                    pass

            if isconstant(a) and b == lookup(instructions.cast_str):
                del code[i:i + 2]
                code.insert(i, str(a))  # TODO: Try-except here
                if not silent:
                    print("Optimizer: Translated %s %s to %s" % (a, b, str(a)))
                keep_running = True
                break

            if isconstant(a) and b == lookup(instructions.cast_bool):
                del code[i:i + 2]
                code.insert(i, bool(a))  # TODO: Try-except here
                if not silent:
                    print("Optimizer: Translated %s %s to %s" %
                          (a, b, bool(a)))
                keep_running = True
                break

            if isconstant(a) and b == lookup(instructions.cast_float):
                try:
                    v = float(a)
                    del code[i:i + 2]
                    code.insert(i, v)
                    if not silent:
                        print("Optimizer: Translated %s %s to %s" % (a, b, v))
                    keep_running = True
                    break
                except ValueError:
                    pass
    return code