def _return(f): scanner._skip_white(f) if scanner._nchar == '(': scanner._match(f, '(') _expression(f) # Copy value over to return value register codegen._load_address(codegen.RETURN_VAL, codegen.PRIMARY, 0) scanner._match(f, ')') # Pop the return address into the BRANCH_TARGET register and return to it codegen._pop(codegen.BRANCH_TARGET) codegen._br_def()
def _function(f): global _local_offset identifier = scanner._get_name(f) scanner._skip_white(f) if identifier in _symtab[0]: # Eventually calls sys.exit() error._error("Duplicate symbol function: " + str(identifier)) _symtab[0][identifier] = {'type': 'function'} scanner._match(f, '(') # Offset from base pointer for the local variables, leave one space for # pointer to the parent's bp offset = (codegen.WORD // codegen.BYTE) # Set local offset to track location of local variables. _local_offset = _init_local_offset local_symbols = {} while scanner._is_valid_identifier_start(scanner._nchar): id = scanner._get_name(f) if id in local_symbols: error._error("Duplicate parameter: " + str(id)) local_symbols[id] = {'type': 'local_var', 'offset': offset, 'base': codegen.BASE} scanner._skip_white(f) if scanner._nchar == ',': scanner._match(f, ',') scanner._skip_white(f) else: # No more parameters to read break offset += codegen.WORD // codegen.BYTE _symtab[0][identifier]['num_param'] = len(local_symbols) # Add our local symbols so that descendant blocks can see them _symtab.append(local_symbols) scanner._match(f, ')') scanner._match(f, '{') if identifier == MAIN: label = MAIN_LABEL else: label = "{}_{}{}".format(FUNCTION_PREFIX, identifier, _next_label()) _symtab[0][identifier]['offset'] = label _symtab[0][identifier]['base'] = codegen.ZERO codegen._post_label(label) # Function assembly body starts here # Save return address codegen._push_ret() _block(f) scanner._match(f, '}') # Remove our local symbol table _symtab.pop()
def _local_var(f): global _local_offset _local_offset -= (codegen.WORD // codegen.BYTE) identifier = scanner._get_name(f) if identifier in _symtab[-1]: error._error("Repeat local identifier: " + str(identifier)) _symtab[-1][identifier] = {'type': 'local_var', 'offset': _local_offset, 'base': codegen.BASE} # Allocate space for var on stack codegen._alloc_stack(codegen.WORD // codegen.BYTE) scanner._skip_white(f) if scanner._nchar == '=': scanner._match(f, '=') _expression(f) entry = _symtab[-1][identifier] codegen._store_primary(entry['offset'], entry['base'])
def _block(f): _symtab.append({}) scanner._skip_white(f) local_allocations = 0 dealloc = True while scanner._nchar != '}': if scanner._is_valid_identifier_start(scanner._nchar): identifier = scanner._get_name(f) if identifier == 'if': _if(f) elif identifier == 'while': _while(f) elif identifier == 'var': local_allocations += 1 _local_var(f) elif identifier == 'break': pass elif identifier == 'return': dealloc = False # Dealloc local vars - special case because the code will exit # the block before the "end" codegen._dealloc_stack(local_allocations * codegen.WORD // codegen.BYTE) _return(f) else: # Either an assignment or a function call # Look in symbol table to tell which is which entry = _lookup(identifier) if entry is not None: if entry['type'] == 'local_var' or entry['type'] == \ 'global_var': _assignment(f, entry) elif entry['type'] == 'function': _function_call(f, entry) else: error._error("Undeclared identifier: " + str(identifier)) elif scanner._nchar == '@': scanner._match(f, '@') _p_assignment(f) else: error._expected("Identifier or '@', got {}".format(scanner._nchar)) scanner._skip_white(f) if dealloc: # Dealloc local vars to get back to return address codegen._dealloc_stack(local_allocations * codegen.WORD // codegen.BYTE) _symtab.pop()
def _global_var(f): global _symtab, _keywords identifier = scanner._get_name(f) scanner._skip_white(f) if identifier in _symtab[0]: # Eventually calls sys.exit() error._error("Duplicate symbol variable: " + str(identifier)) if identifier in _keywords: error._error("Variable shadows keyword: " + str(identifier)) label = GLOBAL_PREFIX + str(_next_label()) codegen._alloc_global(label) _symtab[0][identifier] = {'type': 'global_var', 'offset': label, 'base': codegen.ZERO} if scanner._nchar == '=': # This variable is initialized scanner._match(f, '=') scanner._skip_white(f) _expression(f) codegen._store_primary_abs(label)
def _if(f): scanner._match(f, '(') _expression(f) scanner._match(f, ')') scanner._match(f, '{') label = LABEL_PREFIX + str(_next_label()) codegen._load_branch_address_relative(label) codegen._brzr_def() _block(f) codegen._post_label(label) scanner._match(f, '}') scanner._skip_white(f)
def _function_call(f, entry): num_param = entry['num_param'] codegen._alloc_stack(num_param * codegen.WORD // codegen.BYTE) offset = codegen.WORD // codegen.BYTE scanner._match(f, '(') for i in range(num_param): _expression(f) codegen._store_primary(offset, codegen.STACK) offset += codegen.WORD // codegen.BYTE scanner._skip_white(f) if scanner._nchar == ',': scanner._match(f, ',') else: break scanner._match(f, ')') codegen._push(codegen.BASE) # Base pointer points to the thing that was just pushed (address of old # bp) and we need to offset b/c STACK points off end of stack codegen._load_address(codegen.BASE, codegen.STACK, codegen.WORD // codegen.BYTE) codegen._load_branch_address_relative(entry['offset']) codegen._brl_def() # Clean up the stack: restore rb and remove the args we pushed codegen._pop(codegen.BASE) codegen._dealloc_stack(num_param * codegen.WORD // codegen.BYTE)
def _while(f): label_loop, label_exit = LABEL_PREFIX + str(_next_label()), \ LABEL_PREFIX + str(_next_label()) codegen._post_label(label_loop) scanner._match(f, '(') _expression(f) scanner._match(f, ')') scanner._match(f, '{') codegen._load_branch_address_relative(label_exit) codegen._brzr_def() _block(f) codegen._load_branch_address_relative(label_loop) codegen._br_def() scanner._match(f, '}')
def _a_factor(f): op = scanner._get_operator(f) if op in scanner._add_ops: _a_factor(f) if op == '-': # Negate the primary reg, if + we don't have to do anything codegen._neg(codegen.PRIMARY, codegen.PRIMARY) elif op == '': if scanner._nchar == '(': scanner._match(f, '(') _expression(f) scanner._match(f, ')') elif scanner._is_valid_identifier_start(scanner._nchar): id = scanner._get_name(f) entry = _lookup(id) if entry is None: error._error("Undeclared identifier: " + str(id)) if entry['type'] == 'global_var' or entry['type'] == 'local_var': codegen._load_primary(entry['offset'], entry['base']) elif entry['type'] == 'function': _function_call(f, entry) # Move return value to primary codegen._load_primary_address(codegen.RETURN_VAL, 0) else: error._error("Unknown type of entry: {}".format(str(entry))) elif scanner._is_num(scanner._nchar): n = scanner._get_num(f) codegen._load_primary_address_relative(n) elif scanner._nchar == '@': scanner._match(f, '@') scanner._match(f, '(') _expression(f) scanner._match(f, ')') codegen._load_primary(0, codegen.PRIMARY) else: # Unget the op chars and reset scanner._nchar f.seek(-len(op), 1) scanner._nchar = f.read(1).decode("utf-8")
def _p_assignment(f): scanner._match(f, '(') _expression(f) scanner._match(f, ')') codegen._push_primary() scanner._match(f, '=') _expression(f) codegen._pop_secondary() codegen._store_primary(0, codegen.SECONDARY)
def _assignment(f, entry): scanner._match(f, '=') _expression(f) codegen._store_primary(entry['offset'], entry['base'])