Example #1
0
def warning_not_used(lineno, id):
    if O_LEVEL() > 0:
        warning(lineno, "Variable '%s' is never used" % id)
Example #2
0
def traverse(tree):
    ''' Recursive function that will emmit the AST
    stored function to the MEMORY array
    '''
    global HAS_ATTR, PREV_TOKEN, CURR_TOKEN

    if tree is None:
        return

    if isinstance(tree, list):
        for l in tree:
            traverse(l)
        return

    PREV_TOKEN = CURR_TOKEN
    CURR_TOKEN = tree.token

    debmsg('AST -> ' + tree.token)

    if tree.token == 'BLOCK': # Code block?
        for i in tree.next:
            traverse(i)

    elif tree.token == 'NOP': # Do nothing
        pass

    elif tree.token == 'END': # end of code
        traverse(tree.next[0])
        emmit('end', tree.next[0].t)

    elif tree.token == 'ERROR': # Raises an error
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', '__ERROR', 0)
        REQUIRES.add('error.asm')

    elif tree.token == 'STOP': # Returns to BASIC with an error code
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', '__STOP', 0)
        emmit('end', 0)
        REQUIRES.add('error.asm')

    elif tree.token == 'CONST': # a CONSTant expression
        tree.t = '#' + traverse_const(tree.symbol.expr)

    elif tree.token == 'BORDER':
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', 'BORDER', 0) # Procedure call. Discard return
        REQUIRES.add('border.asm')

    # INK, PAPER, ... etc
    elif tree.token in ATTR:
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', tree.token, 0)
        REQUIRES.add('%s.asm' % tree.token.lower())
        HAS_ATTR = True

    elif tree.token == 'OUT':
        traverse(tree.next[0])
        traverse(tree.next[1])
        emmit('out', tree.next[0].t, tree.next[1].t)

    elif tree.token == 'PLOT':
        TMP_HAS_ATTR = check_attr(tree.next, 2)
        if TMP_HAS_ATTR:
            traverse(tree.next[2]) # Temporary attributes

        traverse(tree.next[0])
        emmit('paramu8', tree.next[0].t)
        traverse(tree.next[1])
        emmit('fparamu8', tree.next[1].t)
        emmit('call', 'PLOT', 0) # Procedure call. Discard return
        REQUIRES.add('plot.asm')
        HAS_ATTR = TMP_HAS_ATTR

    elif tree.token == 'DRAW':
        TMP_HAS_ATTR = check_attr(tree.next, 2)
        if TMP_HAS_ATTR:
            traverse(tree.next[2]) # Temporary attributes

        traverse(tree.next[0])
        emmit('parami16', tree.next[0].t)
        traverse(tree.next[1])
        emmit('fparami16', tree.next[1].t)
        emmit('call', 'DRAW', 0) # Procedure call. Discard return
        REQUIRES.add('draw.asm')
        HAS_ATTR = TMP_HAS_ATTR

    elif tree.token == 'DRAW3':
        TMP_HAS_ATTR = check_attr(tree.next, 3)
        if TMP_HAS_ATTR:
            traverse(tree.next[3]) # Temporary attributes

        traverse(tree.next[0])
        emmit('parami16', tree.next[0].t)
        traverse(tree.next[1])
        emmit('parami16', tree.next[1].t)
        traverse(tree.next[2])
        emmit('fparamf', tree.next[2].t)
        emmit('call', 'DRAW3', 0) # Procedure call. Discard return
        REQUIRES.add('draw3.asm')
        HAS_ATTR = TMP_HAS_ATTR

    elif tree.token == 'CIRCLE':
        TMP_HAS_ATTR = check_attr(tree.next, 3)
        if TMP_HAS_ATTR:
            traverse(tree.next[3]) # Temporary attributes

        traverse(tree.next[0])
        emmit('paramu8', tree.next[0].t)
        traverse(tree.next[1])
        emmit('paramu8', tree.next[1].t)
        traverse(tree.next[2])
        emmit('fparamu8', tree.next[2].t)
        emmit('call', 'CIRCLE', 0) # Procedure call. Discard return
        REQUIRES.add('circle.asm')
        HAS_ATTR = TMP_HAS_ATTR

    elif tree.token == 'BEEP':
        if tree.next[0].token == tree.next[1].token == 'NUMBER': # BEEP <const>, <const>
            DE, HL = arch.zx48k.beep.getDEHL(float(tree.next[0].t), float(tree.next[1].t))
            emmit('paramu16', HL)
            emmit('fparamu16', DE)
            emmit('call', '__BEEPER', 0) # Procedure call. Discard return
            REQUIRES.add('beeper.asm')
        else:
            traverse(tree.next[1])
            emmit('paramf', tree.next[1].t)
            traverse(tree.next[0])
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'BEEP', 0) # Procedure call. Discard return
            REQUIRES.add('beep.asm')

    elif tree.token == 'CLS':
        emmit('call', 'CLS', 0)
        REQUIRES.add('cls.asm')

    elif tree.token == 'POKE':
        traverse(tree.next[0])
        traverse(tree.next[1])

        if tree.next[0].token == 'ID' and tree.next[0]._class != 'const' and tree.next[0].symbol.scope == 'global': 
            emmit('store' + TSUFFIX[tree.next[1]._type], '*' + str(tree.next[0].t), tree.next[1].t)
        else:
            emmit('store' + TSUFFIX[tree.next[1]._type], tree.next[0].t, tree.next[1].t)

    elif tree.token == 'PAUSE':
        traverse(tree.next[0])
        emmit('fparamu16', tree.next[0].t)
        emmit('call', '__PAUSE', 0)
        REQUIRES.add('pause.asm')

    elif tree.token == 'CAST':
        traverse(tree.next[0])
        emmit('cast', tree.t, tree.next[0]._type, tree._type, tree.next[0].t)

    elif tree.token == 'UNARY':
        oper = tree.text

        if oper == 'RND': # A special "ZEROARY" function with no parameters
            emmit('call', 'RND', TYPE_SIZES['float'])
            REQUIRES.add('random.asm')
            return

        if oper == 'INKEY': # A special "ZEROARY" function with no parameters
            emmit('call', 'INKEY', TYPE_SIZES['string'])
            REQUIRES.add('inkey.asm')
            return

        if oper == 'IN': # In port
            emmit('in', tree.next[0].t)
            return

        if oper == 'ADDRESS' and tree.next[0].token != 'ARRAYACCESS':
            scope = tree.next[0].symbol.scope

            # It's a scalar variable
            if scope == 'global':
                emmit('load' + TSUFFIX[tree._type], tree.t, '#' + tree.next[0].symbol._mangled)
            elif scope == 'parameter':
                emmit('paddr', tree.next[0].symbol.offset + 1 if (tree.next[0]._type in 'u8', 'i8', 'float') else 0, tree.t)
            elif scope == 'local':
                emmit('paddr', -tree.next[0].symbol.offset, tree.t)
            return

        if oper in ('LBOUND', 'UBOUND'): # LBOUND is codified as a Unary with a t-uple arg
            entry = tree.array_id
            scope = entry.scope

            if oper == 'LBOUND':
                emmit('paramu16', '#__LBOUND__.' + entry._mangled)
            else:
                emmit('paramu16', '#__UBOUND__.' + entry._mangled)

            traverse(tree.next[0])
            t = optemps.new_t()
            emmit('fparamu16', t) 
            emmit('call', '__BOUND', TYPE_SIZES['u16'])
            REQUIRES.add('bound.asm')
            return

        traverse(tree.next[0])
        si = TYPE_SIZES[tree._type]
        s = TSUFFIX[tree._type]

        if oper == 'ADDRESS': # Address of the next expression
            scope = tree.next[0].symbol.scope

            # Address of an array element.
            if scope == 'global':
                emmit('aaddr', tree.t, tree.next[0].symbol._mangled)
            elif scope == 'parameter':
                emmit('paaddr', tree.t, tree.next[0].symbol.entry.offset)
            elif scope == 'local':
                emmit('paaddr', tree.t, -tree.next[0].symbol.entry.offset)
            return


        if oper == 'MINUS':
            ins = 'neg'
            emmit(ins + s, tree.t, tree.next[0].t)
            return

        elif oper == 'NOT':
            ins = 'not'
            emmit(ins + TSUFFIX[tree.next[0]._type], tree.t, tree.next[0].t)
            return

        elif oper == 'BNOT':
            ins = 'bnot'
            emmit(ins + TSUFFIX[tree.next[0]._type], tree.t, tree.next[0].t)
            return

        elif oper == 'SIN': # TRIGONOMETRICS
            emmit('fparam' + s, tree.next[0].t)
            emmit('call', 'SIN', si)
            REQUIRES.add('sin.asm')
            return

        elif oper == 'COS': # TRIGONOMETRICS
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'COS', si)
            REQUIRES.add('cos.asm')
            return

        elif oper == 'TAN': # TRIGONOMETRICS
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'TAN', si)
            REQUIRES.add('tan.asm')
            return

        elif oper == 'ASN': # TRIGONOMETRICS
            emmit('fparam' + s, tree.next[0].t)
            emmit('call', 'ASIN', si)
            REQUIRES.add('asin.asm')
            return

        elif oper == 'ACS': # TRIGONOMETRICS
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'ACOS', si)
            REQUIRES.add('acos.asm')
            return

        elif oper == 'ATN': # TRIGONOMETRICS
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'ATAN', si)
            REQUIRES.add('atan.asm')
            return

        elif oper == 'EXP': # e^x
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'EXP', si)
            REQUIRES.add('exp.asm')
            return

        elif oper == 'LN': # LogE
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'LN', si)
            REQUIRES.add('logn.asm')
            return

        elif oper == 'SQR': # Square Root
            emmit('fparamf', tree.next[0].t)
            emmit('call', 'SQRT', si)
            REQUIRES.add('sqrt.asm')
            return

        elif oper == 'USR': # USR call
            emmit('fparamu16', tree.next[0].t)
            emmit('call', 'USR', si)
            REQUIRES.add('usr.asm')
            return

        elif oper == 'USR_STR': # USR ADDR
            emmit('fparamstr', tree.next[0].t)
            emmit('call', 'USR_STR', si)
            REQUIRES.add('usr_str.asm')
            return

        elif oper == 'PEEK': # Peek a value from memory
            emmit('load' + s, tree.t, '*' + str(tree.next[0].t))
            return

        elif oper == 'LEN': # STRLEN
            emmit('lenstr', tree.t, tree.next[0].t)
            return

        elif oper == 'SGN':
            _type = tree.next[0]._type
            emmit('fparam' + TSUFFIX[_type], tree.next[0].t)
            if _type == 'fixed':
                _type = 'f16'
            elif _type == 'float':
                _type = 'f'

            emmit('call', '__SGN' + _type.upper(), 1)
            REQUIRES.add('sgn%s.asm' % _type)
            return

        elif oper == 'ABS':
            _type = tree.next[0]._type
            emmit('abs' + TSUFFIX[_type], tree.t, tree.next[0].t)
            return

        elif oper == 'VAL': # VAL
            emmit('fparamu16', tree.next[0].t)

            if tree.next[0].token != 'STRING' and tree.next[0].token != 'ID' and tree.next[0].t != '_':
                emmit('fparamu8', 1) # If the argument is not a variable, it must be freed
            else:
                emmit('fparamu8', 0)

            emmit('call', 'VAL', si)
            REQUIRES.add('val.asm')
            return

        elif oper == 'CODE': # CODE
            emmit('fparamu16', tree.next[0].t)

            if tree.next[0].token != 'STRING' and tree.next[0].token != 'ID' and tree.next[0].t != '_':
                emmit('fparamu8', 1) # If the argument is not a variable, it must be freed
            else:
                emmit('fparamu8', 0)

            emmit('call', '__ASC', si)
            REQUIRES.add('asc.asm')
            return

        elif oper == 'STR':
            emmit('fparamf', tree.next[0].t)
            emmit('call', '__STR_FAST', si)
            REQUIRES.add('str.asm')
            return

        elif oper == 'CHR':
            emmit('fparamu16', tree.next[0].symbol.count) # Number of args
            emmit('call', 'CHR', si)
            REQUIRES.add('chr.asm')
            return

        else: # Invalid Oper
            raise InvalidOperatorError(oper)


    elif tree.token == 'BINARY':
        traverse(tree.next[0])
        traverse(tree.next[1])

        oper = tree.text
        s = TSUFFIX[tree.next[0]._type]

        # Switch
        if oper == 'PLUS':        # Arithmetic
            ins = 'add'
        elif oper == 'MINUS':
            ins = 'sub'
        elif oper == 'MUL':
            ins = 'mul'
        elif oper == 'DIV':
            ins = 'div'
        elif oper == 'MOD':
            ins = 'mod'
        elif oper == 'POW':
            ins = 'pow'
        elif oper == 'SHL':
            ins = 'shl'
        elif oper == 'SHR':
            ins = 'shr'

        elif oper == 'LT':        # Comparisons
            ins = 'lt'
        elif oper == 'LE':
            ins = 'le'
        elif oper == 'GT':
            ins = 'gt'
        elif oper == 'GE':
            ins = 'ge'
        elif oper == 'EQ':
            ins = 'eq'
        elif oper == 'NE':
            ins = 'ne'

        elif oper == 'AND':
            ins = 'and'
        elif oper == 'OR':
            ins = 'or'
        elif oper == 'XOR':
            ins = 'xor'

        elif oper == 'BAND':
            ins = 'band'
        elif oper == 'BOR':
            ins = 'bor'
        elif oper == 'BXOR':
            ins = 'bxor'
        else:
            raise InvalidOperatorError(oper)

        emmit(ins + s, tree.t, str(tree.next[0].t), str(tree.next[1].t))

    elif tree.token == 'ID':
        scope = tree.symbol.scope

        if tree.t == tree.symbol._mangled and scope == 'global':
            return

        if tree._class in ('label', 'const'):
            return

        suffix = TSUFFIX[tree._type]
        p = '*' if tree.symbol.byref else '' # Indirection prefix
        alias = tree.symbol.alias

        if scope == 'global':
            emmit('load' + suffix, tree.t, tree.symbol._mangled)
        elif scope == 'parameter':
            emmit('pload' + suffix, tree.t, p + str(tree.symbol.offset))
        elif scope == 'local':
            offset = tree.symbol.offset
            if alias is not None and alias._class == 'array':
                offset -= 1 + 2 * alias.count

            emmit('pload' + suffix, tree.t, p + str(-offset))

    elif tree.token == 'STRING': # String constant
        if tree.text not in STRING_LABELS.keys():
            STRING_LABELS[tree.text] = backend.tmp_label()

        tree.t = '#' + STRING_LABELS[tree.text]

    elif tree.token == 'STRSLICE': # String slicing
        traverse(tree.next[0])

        if tree.next[0].token == 'STRING' or \
           tree.next[0].token == 'ID' and tree.next[0].symbol.scope == 'global':
            emmit('paramu16', tree.next[0].t)

        # Now emmit the slicing indexes

        traverse(tree.next[1])
        emmit('param' + TSUFFIX[tree.next[1]._type], tree.next[1].t)
        traverse(tree.next[2])
        emmit('param' + TSUFFIX[tree.next[2]._type], tree.next[2].t)

        if tree.next[0].token == 'ID' and tree.next[0].symbol._mangled[0] == '_' or \
            tree.next[0].token == 'STRING':
            emmit('fparamu8', 0)
        else:
            emmit('fparamu8', 1) # If the argument is not a variable, it must be freed

        emmit('call', '__STRSLICE', 2)
        REQUIRES.add('strslice.asm')

    elif tree.token == 'VARDECL': # Global Variable declaration
        if not tree.symbol.entry.accessed:
            warning_not_used(tree.symbol.entry.lineno, tree.symbol.entry.id)
            if O_LEVEL() > 1:
                return

        if tree.symbol.entry.addr is not None:
            emmit('deflabel', tree.symbol.entry._mangled, tree.symbol.entry.addr)
            for entry in tree.symbol.entry.aliased_by:
                emmit('deflabel', entry._mangled, entry.addr)
        elif tree.symbol.entry.alias is None:
            for alias in tree.symbol.entry.aliased_by:
                emmit('label', alias._mangled)
            if tree.symbol.entry.default_value is None:
                emmit('var', tree.text, tree.symbol.size)
            else:
                if isinstance(tree.symbol.entry.default_value, SymbolCONST) and \
                 tree.symbol.entry.default_value.token == 'CONST':
                    emmit('varx', tree.text, tree._type, [traverse_const(tree.symbol.entry.default_value)])
                else:
                    emmit('vard', tree.text, default_value(tree.symbol._type, tree.symbol.entry.default_value))

    elif tree.token == 'ARRAYDECL': # Global Array Declaration
        if not tree.symbol.entry.accessed:
            warning_not_used(tree.symbol.entry.lineno, tree.symbol.entry.id)
            if O_LEVEL() > 1:
                return

        l = ['%04X' % (len(tree.symbol.bounds.next) - 1)] # Number of dimensions - 1

        for bound in tree.symbol.bounds.next[1:]:
            l.append('%04X' % (bound.symbol.upper - bound.symbol.lower + 1))

        l.append('%02X' % TYPE_SIZES[tree.symbol._type])

        if tree.symbol.entry.default_value is not None:
            l.extend(array_default_value(tree.symbol._type, tree.symbol.entry.default_value))
        else:
            l.extend(['00'] * tree.symbol.size)

        for alias in tree.symbol.entry.aliased_by:
            offset = 1 + 2 * tree.symbol.entry.count + alias.offset
            emmit('deflabel', alias._mangled, '%s + %i' % (tree.symbol.entry._mangled, offset))

        emmit('vard', tree.text, l)

        if tree.symbol.entry.lbound_used:
            l = ['%04X' % len(tree.symbol.bounds.next)] + \
                ['%04X' % bound.symbol.lower for bound in tree.symbol.bounds.next]
            emmit('vard', '__LBOUND__.' + tree.symbol.entry._mangled, l)

        if tree.symbol.entry.ubound_used:
            l = ['%04X' % len(tree.symbol.bounds.next)] + \
                ['%04X' % bound.symbol.upper for bound in tree.symbol.bounds.next]
            emmit('vard', '__UBOUND__.' + tree.symbol.entry._mangled, l)


    elif tree.token == 'ARRAYACCESS': # Access to an array Element
        traverse(tree.next[0])
        if OPTIONS.arrayCheck.value:
            upper = tree.symbol.entry.bounds.next[0].symbol.upper
            lower = tree.symbol.entry.bounds.next[0].symbol.lower
            emmit('paramu16', upper - lower)

    elif tree.token == 'ARRAYLOAD': # Access to an array Element
        scope = tree.symbol.scope
        offset = None if len(tree.next) < 2 else tree.next[1]

        if offset is None:
            traverse(tree.next[0])

            if OPTIONS.arrayCheck.value:
                upper = tree.symbol.entry.bounds.next[0].symbol.upper
                lower = tree.symbol.entry.bounds.next[0].symbol.lower
                emmit('paramu16', upper - lower)

            if scope == 'global':
                emmit('aload' + TSUFFIX[tree._type], tree.symbol.t, tree.symbol._mangled)
            elif scope == 'parameter':
                emmit('paload' + TSUFFIX[tree._type], tree.t, tree.symbol.entry.offset)
            elif scope == 'local':
                emmit('paload' + TSUFFIX[tree._type], tree.t, -tree.symbol.entry.offset)
        else:
            offset = 1 + 2 * tree.symbol.entry.count + offset.value
            if scope == 'global':
                emmit('load' + TSUFFIX[tree._type], tree.symbol.t, '%s + %i' % (tree.symbol._mangled, offset))
            elif scope == 'parameter':
                emmit('pload' + TSUFFIX[tree._type], tree.t, tree.symbol.entry.offset - offset)
            elif scope == 'local':
                emmit('pload' + TSUFFIX[tree._type], tree.t, -(tree.symbol.entry.offset - offset))

    elif tree.token == 'ARRAYCOPY':
        tr = tree.next[0]
        scope = tr.symbol.scope
        offset = 1 + 2 * tr.symbol.count
        if scope == 'global':
            #emmit('loadu16', tr.symbol.t, '#%s + %i' % (tr.symbol._mangled, offset))
            t1 = "#%s + %i" % (tr.symbol._mangled, offset)
        elif scope == 'parameter':
            emmit('paddr', '%i' % (tr.symbol.offset - offset), tr.t)
            t1 = tr.t         
        elif scope == 'local':
            emmit('paddr', '%i' % -(tr.symbol.offset - offset), tr.t)
            t1 = tr.t         

        tr = tree.next[1]
        scope = tr.symbol.scope
        offset = 1 + 2 * tr.symbol.count
        if scope == 'global':
            #emmit('loadu16', tr.symbol.t, '#%s + %i' % (tr.symbol._mangled, offset))
            t2 = "#%s + %i" % (tr.symbol._mangled, offset)
        elif scope == 'parameter':
            emmit('paddr', '%i' % (tr.symbol.offset - offset), tr.t)
            t2 = tr.t         
        elif scope == 'local':
            emmit('paddr', '%i' % -(tr.symbol.offset - offset), tr.t)
            t2 = tr.t         

        t = optemps.new_t()
        if tr._type != 'string':
            emmit('loadu16', t, '%i' % tr.symbol.total_size)
            emmit('memcopy', t1, t2, t)
        else:
            emmit('loadu16', t, '%i' % (tr.symbol.total_size / TYPE_SIZES[tr._type]))
            emmit('call', 'STR_ARRAYCOPY', 0)
            REQUIRES.add('strarraycpy.asm')

    elif tree.token == 'LET':
        if O_LEVEL() < 2 or tree.next[0].symbol.accessed or tree.next[1].token == 'CONST':
            traverse(tree.next[1])

        emmit_let_left_part(tree)

    elif tree.token == 'LETARRAY':
        if O_LEVEL() > 1 and not tree.next[0].symbol.entry.accessed:
            return

        traverse(tree.next[1])
        scope = tree.next[0].symbol.scope

        if len(tree.next) <=  2 or tree.next[2] is None:
            traverse(tree.next[0])

            if scope == 'global':
                emmit('astore' + TSUFFIX[tree.next[0]._type], tree.next[0].symbol._mangled, tree.next[1].t)
            elif scope == 'parameter':
                emmit('pastore' + TSUFFIX[tree.next[0]._type], tree.next[0].symbol.entry.offset, tree.next[1].t)
            elif scope == 'local':
                emmit('pastore' + TSUFFIX[tree.next[0]._type], -tree.next[0].symbol.entry.offset, tree.next[1].t)
        else:
            offset = 1 + 2 * tree.next[0].symbol.entry.count + tree.next[2].value
            name = tree.next[0].symbol.entry._mangled
            if scope == 'global':
                emmit('store' + TSUFFIX[tree.next[0]._type], '%s + %i' % (name, offset), tree.next[1].t)
            elif scope == 'parameter':
                emmit('pstore' + TSUFFIX[tree.next[0]._type], tree.next[0].symbol.entry.offset - offset, tree.next[1].t)
            elif scope == 'local':
                emmit('pstore' + TSUFFIX[tree.next[0]._type], -(tree.next[0].symbol.entry.offset - offset), tree.next[1].t)

    elif tree.token == 'LETSUBSTR':
        traverse(tree.next[3])
        emmit('paramstr', tree.next[3].t)

        if tree.next[3].token != 'STRING' and (tree.next[3].token != 'ID' or tree.next[3].symbol._mangled[0] != '_'):
            emmit('paramu8', 1) # If the argument is not a variable, it must be freed
        else:
            emmit('paramu8', 0)

        traverse(tree.next[1])
        emmit('paramu16', tree.next[1].t)

        traverse(tree.next[2])
        emmit('paramu16', tree.next[2].t)

        emmit('fparamu16', tree.next[0].t)
        emmit('call', '__LETSUBSTR', 0)
        REQUIRES.add('letsubstr.asm')

    elif tree.token == 'WHILE':
        loop_label = backend.tmp_label()
        end_loop = backend.tmp_label()
        LOOPS.append(('WHILE', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE

        emmit('label', loop_label)
        traverse(tree.next[0])
        emmit('jzero' + TSUFFIX[tree.next[0]._type], tree.next[0].t, end_loop)

        if len(tree.next) > 1:
            traverse(tree.next[1])

        emmit('jump', loop_label)
        emmit('label', end_loop)
        LOOPS.pop()
        del loop_label, end_loop

    elif tree.token == 'DO_LOOP':
        loop_label = backend.tmp_label()
        end_loop = backend.tmp_label()
        LOOPS.append(('DO', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE

        emmit('label', loop_label)
        if len(tree.next):
            traverse(tree.next[0])
        emmit('jump', loop_label)
        emmit('label', end_loop)
        LOOPS.pop()
        del loop_label, end_loop

    elif tree.token in ('DO_UNTIL', 'UNTIL_DO'):
        loop_label = backend.tmp_label()
        end_loop = backend.tmp_label()
        continue_loop = backend.tmp_label()

        if tree.token == 'UNTIL_DO':
            emmit('jump', continue_loop)

        emmit('label', loop_label)
        LOOPS.append(('DO', end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE

        if len(tree.next) > 1:
            traverse(tree.next[1])

        emmit('label', continue_loop)
        traverse(tree.next[0])
        emmit('jzero' + TSUFFIX[tree.next[0]._type], tree.next[0].t, loop_label)
        emmit('label', end_loop)
        LOOPS.pop()
        del loop_label, end_loop, continue_loop

    elif tree.token in ('DO_WHILE', 'WHILE_DO'):
        loop_label = backend.tmp_label()
        end_loop = backend.tmp_label()
        continue_loop = backend.tmp_label()

        if tree.token == 'WHILE_DO':
            emmit('jump', continue_loop)

        emmit('label', loop_label)
        LOOPS.append(('DO', end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE

        if len(tree.next) > 1:
            traverse(tree.next[1])

        emmit('label', continue_loop)
        traverse(tree.next[0])
        emmit('jnzero' + TSUFFIX[tree.next[0]._type], tree.next[0].t, loop_label)
        emmit('label', end_loop)
        LOOPS.pop()
        del loop_label, end_loop, continue_loop

    elif tree.token == 'EXIT_DO':
        emmit('jump', loop_exit_label('DO'))

    elif tree.token == 'EXIT_WHILE':
        emmit('jump', loop_exit_label('WHILE'))

    elif tree.token == 'EXIT_FOR':
        emmit('jump', loop_exit_label('FOR'))

    elif tree.token == 'CONTINUE_DO':
        emmit('jump', loop_cont_label('DO'))

    elif tree.token == 'CONTINUE_WHILE':
        emmit('jump', loop_cont_label('WHILE'))

    elif tree.token == 'CONTINUE_FOR':
        emmit('jump', loop_cont_label('FOR'))

    elif tree.token == 'IF':
        if_label_else = backend.tmp_label()
        if_label_endif = backend.tmp_label()
        traverse(tree.next[0])

        if len(tree.next) == 3: # Has else?
            emmit('jzero' + TSUFFIX[tree.next[0]._type], tree.next[0].t, if_label_else)
        else:
            emmit('jzero' + TSUFFIX[tree.next[0]._type], tree.next[0].t, if_label_endif)

        traverse(tree.next[1]) # THEN...

        if len(tree.next) == 3: # Has else?
            emmit('jump', if_label_endif)
            emmit('label', if_label_else)
            traverse(tree.next[2])

        emmit('label', if_label_endif)

    elif tree.token == 'FUNCDECL':
        if O_LEVEL() > 0 and not tree.symbol.entry.accessed:
            warning(tree.symbol.entry.lineno, "Function '%s' is never called and has been ignored" % tree.symbol.entry.id)
        else:
            tree.symbol.token = 'FUNCTION' # Delay emission of functions 'til the end
            FUNCTIONS.append(tree)

    elif tree.token == 'FUNCTION':
        emmit('label', tree.symbol._mangled)

        if tree.symbol.entry.convention == '__fastcall__':
            emmit('enter', '__fastcall__')
        else:
            emmit('enter', tree.symbol.locals_size)

        for local_var in tree.symbol.local_symbol_table.values():
            if not local_var.accessed:
                warning_not_used(local_var.lineno, local_var.id)

            if local_var._class == 'array':
                l = [x.size for x in local_var.bounds.next[1:]]
                l = [len(l)] + l # Prepends len(l) (number of dimentions - 1)
                q = []
                for x in l:
                    q.append('%02X' % (x & 0xFF))
                    q.append('%02X' % (x >> 8))

                q.append('%02X' % local_var.size)
                if local_var.default_value is not None:
                    q.extend(array_default_value(local_var._type, local_var.default_value))
                emmit('lvard', local_var.offset, q) # Initalizes array bounds
            elif local_var._class == 'const':
                continue
            else:
                if local_var.default_value is not None and local_var.default_value != 0: # Local vars always defaults to 0, so if 0 we do nothing
                    if isinstance(local_var.default_value, SymbolCONST) and \
                        local_var.default_value.token == 'CONST':
                        emmit('lvarx', local_var.offset, local_var._type, [traverse_const(local_var.default_value)])
                    else:
                        q = default_value(local_var._type, local_var.default_value)
                        emmit('lvard', local_var.offset, q)

        for i in tree.next:
            traverse(i)

        emmit('label', '%s__leave' % tree.symbol._mangled)

        # Now free any local string from memory.
        preserve_hl = False
        for local_var in tree.symbol.local_symbol_table.values():
            if local_var._type == 'string': # Only if it's string we free it
                scope = local_var.scope
                if local_var._class != 'array': # Ok just free it
                    if scope == 'local' or (scope == 'parameter' and not local_var.byref):
                        if not preserve_hl:
                            preserve_hl = True
                            emmit('exchg')

                        offset = -local_var.offset if scope == 'local' else local_var.offset
                        emmit('fploadstr', local_var.t, offset)
                        emmit('call', '__MEM_FREE', 0)
                        REQUIRES.add('free.asm')
                elif local_var._class == 'const':
                    continue
                else: # This is an array of strings, we must free it unless it's a by_ref array
                    if scope == 'local' or (scope == 'parameter' and not local_var.byref):
                        if not preserve_hl:
                            preserve_hl = True
                            emmit('exchg')

                        offset = -local_var.offset if scope == 'local' else local_var.offset
                        elems = reduce(lambda x, y: x * y, [x.size for x in local_var.bounds.next])
                        emmit('paramu16', elems)
                        emmit('paddr', offset, local_var.t)
                        emmit('fparamu16', local_var.t)
                        emmit('call', '__ARRAY_FREE', 0)
                        REQUIRES.add('arrayfree.asm')

        if preserve_hl:
            emmit('exchg')

        if tree.symbol.entry.convention == '__fastcall__':
            emmit('leave', '__fastcall__')
        else:
            emmit('leave', tree.symbol.params_size)

    elif tree.token == 'ARGLIST':
        for i in range(tree.symbol.count - 1, -1, -1):
            traverse(tree.next[i])

    elif tree.token == 'ARGUMENT':
        if not tree.symbol.byref:
            if tree.next[0].token == 'ID' and \
                tree.symbol._type == 'string' and tree.next[0].t[0] == '$':
                    tree.next[0].t = optemps.new_t()

            traverse(tree.next[0])
            emmit('param' + TSUFFIX[tree.symbol._type], tree.next[0].t)
        else:
            scope = tree.symbol.arg.scope
            if tree.t[0] == '_':
                t = optemps.new_t()
            else:
                t = tree.t

            if scope == 'global':
                emmit('loadu16', t, '#' + tree.symbol._mangled)
            elif scope == 'parameter': # A function has used a parameter as an argument to another function call
                if not tree.symbol.arg.byref: # It's like a local variable
                    emmit('paddr', tree.symbol.arg.offset, t)
                else:
                    emmit('ploadu16', t, str(tree.symbol.arg.offset))
            elif scope == 'local':
                emmit('paddr', -tree.symbol.arg.offset, t)

            emmit('paramu16', t)

    elif tree.token == 'FUNCCALL': # Calls a Function, and the result is returned in registers
        traverse(tree.next[0]) # Arg list

        if tree.symbol.entry.convention == '__fastcall__':
            if tree.next[0].symbol.count > 0: # At least
                t = optemps.new_t()
                emmit('fparam' + TSUFFIX[tree.next[0].next[0]._type], t)

        emmit('call', tree.symbol.entry._mangled, tree.symbol.entry.size)

    elif tree.token == 'CALL': # Calls a SUB or a Function discarding its return value
        traverse(tree.next[0]) # Arg list
        if tree.symbol.entry.convention == '__fastcall__':
            if tree.next[0].symbol.count > 0: # At least
                t = optemps.new_t()
                emmit('fparam' + TSUFFIX[tree.next[0].next[0]._type], t)

        emmit('call', tree.symbol.entry._mangled, 0) # Procedure call. Discard return

        if tree.symbol.entry.kind == 'function' and tree.symbol.entry._type == 'string':
            emmit('call', '__MEM_FREE', 0) # Discard string return value if the called function has any
            REQUIRES.add('free.asm')

    elif tree.token == 'RETURN':
        if len(tree.next) == 2: # Something to return?
            traverse(tree.next[1])
            emmit('ret' + TSUFFIX[tree.next[1]._type], tree.next[1].t, '%s__leave' % tree.next[0].symbol._mangled)
        elif len(tree.next) == 1:
            emmit('ret', '%s__leave' % tree.next[0].symbol._mangled)
        else:
            emmit('leave', '__fastcall__')

    elif tree.token == 'FOR':
        loop_label_start = backend.tmp_label()
        loop_label_lt = backend.tmp_label()
        loop_label_gt = backend.tmp_label()
        end_loop = backend.tmp_label()
        loop_body = backend.tmp_label()
        loop_continue = backend.tmp_label()
        suffix = TSUFFIX[tree.next[0]._type]

        LOOPS.append(('FOR', end_loop, loop_continue)) # Saves which label to jump upon EXIT FOR and CONTINUE FOR

        traverse(tree.next[1]) # Get starting value (lower limit)
        emmit_let_left_part(tree) # Store it in the iterator variable
        emmit('jump', loop_label_start)

        # FOR body statements
        emmit('label', loop_body)
        traverse(tree.next[4]) 

        # Jump here to continue next iteration
        emmit('label', loop_continue)

        # VAR = VAR + STEP
        traverse(tree.next[0]) # Iterator Var
        traverse(tree.next[3]) # Step
        t = optemps.new_t()
        emmit('add' + suffix, t, tree.next[0].t, tree.next[3].t)
        emmit_let_left_part(tree, t)

        # Loop starts here
        emmit('label', loop_label_start)

        # Emmit condition
        if is_number(tree.next[3]) or is_unsigned(tree.next[3]._type):
            direct = True
        else:
            direct = False
            traverse(tree.next[3]) # Step
            emmit('jgezero' + suffix, tree.next[3].t, loop_label_gt)

        if not direct or tree.next[3].value < 0: # Here for negative steps
                                # Compares if var < limit2
            traverse(tree.next[0])  # Value of var
            traverse(tree.next[2])  # Value of limit2
            emmit('lt' + suffix, tree.t, tree.next[0].t, tree.next[2].t)
            emmit('jzerou8', tree.t, loop_body)

        if not direct:
            emmit('jump', end_loop)
            emmit('label', loop_label_gt)

        if not direct or tree.next[3].value >= 0: # Here for positive steps
                                   # Compares if var > limit2
            traverse(tree.next[0])  # Value of var
            traverse(tree.next[2])  # Value of limit2
            emmit('gt' + suffix, tree.t, tree.next[0].t, tree.next[2].t)
            emmit('jzerou8', tree.t, loop_body)
    
        emmit('label', end_loop)
        LOOPS.pop()

        del loop_label_start, end_loop, loop_label_gt, loop_label_lt, loop_body, loop_continue

    elif tree.token == 'SAVE': # The Save command. Only SAVE <string> CODE x, y supported
        traverse(tree.next[0])
        emmit('paramstr', tree.next[0].t)
        traverse(tree.next[1])
        emmit('paramu16', tree.next[1].t)
        traverse(tree.next[2])
        emmit('paramu16', tree.next[2].t)
        emmit('call', 'SAVE_CODE', 0)
        REQUIRES.add('save.asm')

    elif tree.token in ('LOAD', 'VERIFY'):
        traverse(tree.next[0])
        emmit('paramstr', tree.next[0].t)
        traverse(tree.next[1])
        emmit('paramu16', tree.next[1].t)
        traverse(tree.next[2])
        emmit('paramu16', tree.next[2].t)
        emmit('paramu8', int (tree.token == 'LOAD'))
        emmit('call', 'LOAD_CODE', 0)
        REQUIRES.add('load.asm')

    elif tree.token == 'RANDOMIZE':
        traverse(tree.next[0])
        emmit('fparamu32', tree.next[0].t)
        emmit('call', 'RANDOMIZE', 0)
        REQUIRES.add('random.asm')

    elif tree.token == 'PRINT': # The print sentence
        for i in tree.next:
            traverse(i)

            # Print subcommands (AT, OVER, INK, etc... must be skipped here)
            if i.token in ('PRINT_TAB', 'PRINT_AT', 'PRINT_COMMA',) + ATTR_TMP: continue
            emmit('fparam' + TSUFFIX[i._type], i.t)
            emmit('call', '__PRINT' + TSUFFIX[i._type].upper(), 0)
            REQUIRES.add('print' + TSUFFIX[i._type].lower() + '.asm')

        for i in tree.next:
            if i.token in ATTR_TMP or has_control_chars(i):
                HAS_ATTR = True
                break

        if tree.symbol.eol:
            if HAS_ATTR:
                emmit('call', 'PRINT_EOL_ATTR', 0)
                REQUIRES.add('print_eol_attr.asm')
                HAS_ATTR = False
            else:
                emmit('call', 'PRINT_EOL', 0)
                REQUIRES.add('print.asm')

    elif tree.token == 'PRINT_AT': # AT implemented as a sentence
        traverse(tree.next[0])
        emmit('paramu8', tree.next[0].t)
        traverse(tree.next[1])
        emmit('fparamu8', tree.next[1].t)
        emmit('call', 'PRINT_AT', 0) # Procedure call. Discard return
        REQUIRES.add('print.asm')

    elif tree.token == 'PRINT_TAB':
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', 'PRINT_TAB', 0)
        REQUIRES.add('print.asm')

    elif tree.token == 'PRINT_COMMA':
        emmit('call', 'PRINT_COMMA', 0)
        REQUIRES.add('print.asm')

    elif tree.token in ATTR_TMP:
        traverse(tree.next[0])
        emmit('fparamu8', tree.next[0].t)
        emmit('call', tree.token, 0) # Procedure call. Discard return
        ifile = tree.token.lower()
        ifile = ifile[:ifile.index('_')]
        REQUIRES.add(ifile + '.asm')

    elif tree.token == 'ASM':
        emmit('inline', tree.symbol.text, tree.symbol.lineno)

    elif tree.token == 'LABEL':
        emmit('label', tree.next[0].symbol._mangled)
        for tmp in tree.next[0].symbol.aliased_by:
            emmit('label', tmp._mangled)

    elif tree.token == 'GOTO':
        emmit('jump', tree.next[0].symbol._mangled)

    elif tree.token == 'GOSUB':
        emmit('call', tree.next[0].symbol._mangled, 0)

    elif tree.token == 'CHKBREAK' and PREV_TOKEN != tree.token:
        emmit('fparamu16', tree.next[0].t)
        emmit('call', 'CHECK_BREAK', 0)
        REQUIRES.add('break.asm')

    norm_attr()