Ejemplo n.º 1
0
    def to_str(op):
        if is_embedded_push(op):
            op = get_embedded_push_value(op)

        if isstring(op):
            return '"%s"' % repr(op)[1:-1]
        elif callable(op):
            return vm.lookup(op)
        else:
            return str(op)
Ejemplo n.º 2
0
    def to_str(op):
        if is_embedded_push(op):
            op = get_embedded_push_value(op)

        if isstring(op):
            return '"%s"' % repr(op)[1:-1]
        elif callable(op):
            return vm.lookup(op)
        else:
            return str(op)
Ejemplo n.º 3
0
def if_stmt(vm):
    false_clause = vm.pop()
    true_clause = vm.pop()
    test = vm.pop()

    # False values: False, 0, "", everyting else is true
    if interpreter.isbool(test) and test == False:
        result = False
    elif interpreter.isstring(test) and len(test) == 0:
        result = False
    elif interpreter.isnumber(test) and test == 0:
        result = False
    else:
        result = True

    if result == True:
        vm.push(true_clause)
    else:
        vm.push(false_clause)
Ejemplo n.º 4
0
def if_stmt(vm):
    false_clause = vm.pop()
    true_clause = vm.pop()
    test = vm.pop()

    # False values: False, 0, "", everyting else is true
    if interpreter.isbool(test) and test == False:
        result = False
    elif interpreter.isstring(test) and len(test) == 0:
        result = False
    elif interpreter.isnumber(test) and test == 0:
        result = False
    else:
        result = True

    if result == True:
        vm.push(true_clause)
    else:
        vm.push(false_clause)
Ejemplo n.º 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
Ejemplo n.º 6
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
Ejemplo n.º 7
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 = 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
Ejemplo n.º 8
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 = 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