<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def compile(code, silent=True, ignore_errors=False, optimize=True):
 """Compiles subroutine-forms into a complete working code. A program such as: sub1 foo sub2 bar is compiled into: <sub1 address> call foo <sub2 address> call exit 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