def fix_forward(lineno, value): # Move the SP to accomodate local variables local_variable_length = ice9_st_proc.st_proc[self.value].local_variable_line + \ + ice9_st_proc.st_proc[self.value].parameter_count + 2 ice9_parser_global.emit_code('LDA', 4, local_variable_length, 4, 'Move SP to accomodate local variables') # Set the new FP offset = (-1 * ice9_st_proc.st_proc[self.value].local_variable_line) ice9_parser_global.emit_code('LDA', 6, offset, 4, 'Set the FP to the current top of stack')
def generate_address(self): # Load last value as it is register = expr_node.generate_code(self.expr_list[-1]) # Get new register to calculate product for x in range(len(self.expr_list) - 2, -1, -1): product_reg = expr_node.generate_code(self.expr_list[x]) constant_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('LDC', constant_reg, get_follow_product(self.value, x + 1), 0, \ 'Array index : Load constant') ice9_parser_global.emit_code('MUL', product_reg, product_reg, constant_reg, \ 'Array index : Multiply') ice9_parser_global.emit_code('ADD', register, register, product_reg, 'Array index : Add') ice9_parser_global.reclaim_register() # Check array bounds for -ve index ice9_parser_global.emit_code('JGE', register, 1, 7, 'If non-negative array index, skip halt') ice9_parser_global.emit_code('LDC', 7, 5, 0, 'Array index out of bounds, write error and halt') # Check array bounds for index overflow max_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('LDC', max_reg, get_follow_product(self.value, 0), 0, 'Array index get Max value') ice9_parser_global.emit_code('SUB', max_reg, max_reg, register, 'Subtract index from maximum') ice9_parser_global.emit_code('JGT', max_reg, 1, 7, 'If non-negative array index, skip halt') ice9_parser_global.emit_code('LDC', 7, 5, 0, 'Array index out of bounds, write error and halt') if ice9_parser_global.scope[-1] != 'GLOBAL': temp_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('LDC', temp_reg, -1, 0, 'Array variable: Load -1') ice9_parser_global.emit_code('MUL', register, register, temp_reg, 'Array variable: Multilply index with -1') ice9_parser_global.emit_code('LDC', temp_reg, ice9_st_var.st_var[self.value][-1].address, \ 0, "Array variable in procedure: Load offset from symbol tab") ice9_parser_global.emit_code('ADD', register, register, temp_reg, "Array variable in procedure") ice9_parser_global.emit_code('ADD', register, register, 6, "Array variable in procedure: Add to FP") return register
def generate_code(self): # Issue halt if self.rule == 'EXIT': ice9_parser_global.emit_code('HALT', 0, 0, 0, "Stop execution") elif self.rule == 'READ': if self.pre_child.operation == 'ID': register = self.pre_child.generate_code() ice9_parser_global.emit_code('IN', register, 0, 0, "Read value from stdin") ice9_parser_global.store_variable_to_memory(register) elif self.pre_child.operation == 'ARRAY_REF': operand = expr_node.generate_address(self.pre_child) register = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('IN', register, 0, 0, "Read value from stdin") ice9_parser_global.emit_code('ST', register, 0, operand, "Store array") elif self.rule == 'BREAK': ice9_parser_global.break_stack[-1] = ice9_parser_global.lno ice9_parser_global.lno += 1 # Generate code for procedure definition elif self.rule == 'PROC': ice9_parser_global.emit_comment('---- Start of procedure definition for ' + str(self.pre_child) + ' ----') # Store the starting address of the procedure definition ice9_st_proc.st_proc[self.pre_child].starting_address = ice9_parser_global.lno for stmt in self.post_child: if stmt: stmt.generate_code() return_type = ice9_st_proc.st_proc[self.pre_child].return_type # Store the return address in a register address_register = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('LD', address_register, 1, 6, 'Store the return instruction in a register') # Restore the FP ice9_parser_global.emit_code('LD', 6, 0, 6, 'Restore the FP to the previous value') # Get the length of local variables local_variable_length = -1 * ice9_st_proc.st_proc[self.pre_child].local_variable_line local_variable_length += 1 # Restore the SP ice9_parser_global.emit_code('LDA', 4, local_variable_length, 4, 'Restore the SP to the previous value') # Restore the PC ice9_parser_global.emit_code('LDA', 7, 0, address_register, 'Restore the PC to the caller') ice9_parser_global.emit_comment('---- End of procedure definition for ' + str(self.pre_child) + ' ----') # Do a OUT on the result of the expression elif self.rule in ['WRITE', 'WRITES']: # Determine opcode based on the return value of expression if self.pre_child.return_data_type == 'int': # Generate code for the expression register = self.pre_child.generate_code() ice9_parser_global.emit_code('OUT', register, 0, 0, "Write to stdout") elif self.pre_child.return_data_type == 'string': # Case for a string literal if self.pre_child.operation == 'SLIT': ice9_parser_global.emit_code_for_string('.DATA', self.pre_child.length) ice9_parser_global.emit_code_for_string('.SDATA', self.pre_child.value) length_address = ice9_parser_global.string_location_dict[self.pre_child.value] register = ice9_parser_global.get_next_register() # Set GP to first character ice9_parser_global.emit_code('LDC', 5, length_address, 0, 'Load address of string length') ice9_parser_global.emit_code('LDA', 5, 1, 5,'GP points to first character') # Load length ice9_parser_global.emit_code('LDC', register, length_address, 0, 'Load address of string length') ice9_parser_global.emit_code('LD', register, 0, register, 'Load string length') # Case for a variable elif self.pre_child.operation == 'ID': register = self.pre_child.generate_code() # Set GP to first character ice9_parser_global.emit_code('LDA', 5, 1, register, 'GP points to first character') # Load length ice9_parser_global.emit_code('LD', register, 0, register, 'Load string length') ice9_parser_global.set_register(register) content_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('JEQ', register, 5, 7, 'Check length, if zero jump') ice9_parser_global.emit_code('LD', content_reg, 0, 5, 'Load character from memory') ice9_parser_global.emit_code('OUTC', content_reg, 0, 0, 'Write character') ice9_parser_global.emit_code('LDA', 5, 1, 5, 'Increment GP') ice9_parser_global.emit_code('LDA', register, -1, register, 'Decrement length') ice9_parser_global.emit_code('LDA', 7, -6, 7, 'Jump back to start of loop') # If WRITES emit code for new line if self.rule == 'WRITES': ice9_parser_global.emit_code('OUTNL', 0, 0, 0, "New line") # Reuse register ice9_parser_global.reclaim_register() # Simply generate code for the pre_child elif self.rule in ['UN_MINUS', 'QUEST', 'INT', 'FALSE', 'TRUE', 'EXPR']: self.pre_child.generate_code() elif self.rule == 'FA': # Push -1 on the break stack ice9_parser_global.break_stack.append(-1) initial_value_register = self.pre_child.pre_child.generate_code() # VAR symbol table setup # Obtain the location to store the fa loop variable if ice9_parser_global.scope[-1] == 'GLOBAL': address = ice9_parser_global.get_next_global_address() else: address = ice9_st_proc.st_proc[ice9_parser_global.scope[-1]].local_variable_line ice9_st_proc.st_proc[ice9_parser_global.scope[-1]].local_variable_line -= 1 st_var_entry = ice9_st_var.st_var_entry('int', 0, address, None) st_var_entry.basic_data_type = 'int' # Bypass the checks in st_var.add_entry_to_st_var # If variable not previously used, make an entry for it if self.pre_child.value not in ice9_st_var.st_var: self.pre_child.value_stack = list() ice9_st_var.st_var[self.pre_child.value] = self.pre_child.value_stack ice9_st_var.st_var[self.pre_child.value].append(st_var_entry) # Store initial value of loop variable into memory loop_register = ice9_parser_global.get_next_register() ice9_parser_global.load_variable_from_memory(loop_register, self.pre_child.value) ice9_parser_global.emit_code('LDA', loop_register, 0, initial_value_register, 'Loop variable : Copy initial value') ice9_parser_global.store_variable_to_memory(loop_register) ice9_parser_global.set_register(initial_value_register - 1) # Jump back here at the end of the loop jump_back_line = ice9_parser_global.lno loop_register = ice9_parser_global.get_next_register() ice9_parser_global.set_register(loop_register) ice9_parser_global.load_variable_from_memory(loop_register, self.pre_child.value) final_value_register = self.pre_child.post_child.generate_code() ice9_parser_global.emit_code('LDA', final_value_register, 1, final_value_register, 'Increment final value, makes it easier for comparison') ice9_parser_global.emit_code('SUB', final_value_register, final_value_register, loop_register, \ 'Loop variable : Subtract current from final') ice9_parser_global.emit_code('JNE', final_value_register, 1, 7, 'Loop variable : If not equal, go to loop body') ice9_parser_global.reclaim_register() # Loop exit instruction line loop_exit_line = ice9_parser_global.lno ice9_parser_global.lno += 1 for stmt in self.post_child: stmt.generate_code() decrement_register = ice9_parser_global.get_next_register() ice9_parser_global.load_variable_from_memory(decrement_register, self.pre_child.value) ice9_parser_global.emit_code('LDA', decrement_register, 1, decrement_register, 'Loop variabe : Increment') ice9_parser_global.store_variable_to_memory(decrement_register) ice9_parser_global.reclaim_register() # Jump back to start of the loop ice9_parser_global.emit_code('LDC', 7, jump_back_line, 0, 'Loop variable : Uncondition jump back to start') exit_line = ice9_parser_global.lno ice9_parser_global.emit_code_at_line(loop_exit_line, 'LDC', 7, exit_line, 0, 'Loop variable : Exit loop') # VAR symbol table clean up ice9_st_var.st_var[self.pre_child.value].pop() if len(ice9_st_var.st_var[self.pre_child.value]) == 0: del ice9_st_var.st_var[self.pre_child.value] break_line = ice9_parser_global.break_stack.pop() if break_line != -1: ice9_parser_global.emit_code_at_line(break_line, 'LDC', 7, exit_line, 0, 'Break : Exit loop') elif self.rule == 'IF': # Generate code for expression condition_register = self.condition.generate_code() # Store the line number to jump if condition is false false_number = ice9_parser_global.lno ice9_parser_global.lno += 1 # Generate code for the 'then' part for stmt in self.pre_child: stmt.generate_code() # Store the line number to jump if condition is true and the block has been executed true_number = ice9_parser_global.lno ice9_parser_global.lno += 1 # Emit the jump statement when condition is false jump_offset = ice9_parser_global.lno - false_number - 1 ice9_parser_global.emit_code_at_line(false_number, 'JEQ', condition_register, jump_offset, 7, "If false, jump to the next if-else condition") #Reuse condition register ice9_parser_global.reclaim_register() #ice9_parser_global.set_register(false_number) # Generate code for any subsequent Else-If or Else if self.post_child: self.post_child.generate_code() self.end_line = self.post_child.end_line else: self.end_line = ice9_parser_global.lno # Emit missing unconditional jump to end of loop jump_offset = self.end_line - true_number - 1 ice9_parser_global.emit_code_at_line(true_number, 'LDA', 7, jump_offset, 7, "Jump to the end of if-else block") elif self.rule == 'DO': # Push -1 on the break stack ice9_parser_global.break_stack.append(-1) condition_start_line_no = ice9_parser_global.lno # Generate code for expression condition_register = self.condition.generate_code() # Store the line number to jump if condition is false false_number = ice9_parser_global.lno ice9_parser_global.lno += 1 # Generate code for the 'then' part for stmt in self.pre_child: stmt.generate_code() difference = condition_start_line_no - ice9_parser_global.lno - 1 ice9_parser_global.emit_code('LDA', 7, difference, 7, "Jump back to the start of the loop") # Emit the jump statement when condition is false jump_offset = ice9_parser_global.lno - false_number - 1 ice9_parser_global.emit_code_at_line(false_number, 'JEQ', condition_register, jump_offset, 7, "If false, jump to the end of do block") #Resure condition register ice9_parser_global.reclaim_register() end_line = ice9_parser_global.lno break_line = ice9_parser_global.break_stack.pop() if break_line != -1: ice9_parser_global.emit_code_at_line(break_line, 'LDC', 7, end_line, 0, 'Break : Exit loop')
def handle_proc_call(self): # Set scope ice9_parser_global.register_variable_stack.append(ice9_parser_global.register_variable) ice9_parser_global.register_variable = [-1] * ice9_parser_global.no_of_user_reg # Save current state on the memory ice9_parser_global.save_state() jump_line_no = ice9_parser_global.lno ice9_parser_global.lno += 3 # Store the current FP ice9_parser_global.push(6) # Get a free register for storing the return value register = ice9_parser_global.get_next_register() # Leave space for the return address ice9_parser_global.emit_code('LDA', 4, -1, 4, 'Decrement stack pointer') # Reset the next register ice9_parser_global.set_register(-1) # Push parameters on to stack if self.expr_list: for expr in self.expr_list: param_register = expr.generate_code() ice9_parser_global.push(param_register) if ice9_st_proc.st_proc[self.value].defined: # Move the SP to accomodate local variables local_variable_length = ice9_st_proc.st_proc[self.value].local_variable_line + \ + ice9_st_proc.st_proc[self.value].parameter_count + 2 ice9_parser_global.emit_code('LDA', 4, local_variable_length, 4, 'Move SP to accomodate local variables') # Set the new FP offset = (-1 * ice9_st_proc.st_proc[self.value].local_variable_line) ice9_parser_global.emit_code('LDA', 6, offset, 4, 'Set the FP to the current top of stack') else: # Skip line to insert jump ice9_parser_global.forward_proc_offset_dict[ice9_parser_global.lno] = self.value ice9_parser_global.lno += 1 # Store the return instruction number ice9_parser_global.emit_code_at_line(jump_line_no, 'LDC', register, ice9_parser_global.lno + 1, 0, \ 'Calculate the return line number') ice9_parser_global.emit_code_at_line(jump_line_no + 1, 'ST', register, 0, 4, \ 'Push the return line number on the stack') ice9_parser_global.emit_code_at_line(jump_line_no + 2, 'LDA', 4, -1, 4, 'Decrement stack pointer') if ice9_st_proc.st_proc[self.value].defined: # Load starting address of procedure into PC ice9_parser_global.emit_code('LDC', 7, ice9_st_proc.st_proc[self.value].starting_address, 0, \ 'Jump to starting address of the procedure') else: ice9_parser_global.forward_proc_call_dict[ice9_parser_global.lno] = self.value ice9_parser_global.lno += 1 # Restore state from memory ice9_parser_global.restore_state() # Obtain return value from memory, ice9_parser_global.emit_code('LD', register, -6, 4, 'Get the return value from procedure call') # Clear scope ice9_parser_global.register_variable = ice9_parser_global.register_variable_stack.pop() return register
def generate_code(self): #Default values operand2 = -1 operand3 = -1 # Special case for ASSIGN, array variables need address calculation to be dynamic # Operators that do not require a new register allocated if self.operation == 'ASSIGN': if self.pre_child.operation == 'ARRAY_REF': operand2 = expr_node.generate_address(self.pre_child) operand3 = expr_node.generate_code(self.post_child) ice9_parser_global.emit_code('ST', operand3, 0, operand2, "Store array") elif self.pre_child.return_data_type == 'string' \ and self.pre_child.operation == 'SLIT' : operand2 = expr_node.generate_code(self.pre_child) operand3 = expr_node.generate_code(self.post_child) ice9_parser_global.emit_code('LDC', operand2, \ ice9_parser_global.string_location_dict[self.post_child.value], 0, 'String literal assignment') ice9_parser_global.store_variable_to_memory(operand2) else: operand2 = expr_node.generate_code(self.pre_child) operand3 = expr_node.generate_code(self.post_child) ice9_parser_global.emit_code('LDA', operand2, 0, operand3, "Assignment") ice9_parser_global.store_variable_to_memory(operand2) # Re-use register register = operand2 register -= 1 # Normal case else: if self.pre_child: operand2 = expr_node.generate_code(self.pre_child) if self.operation in ['PLUS_BOOL', 'STAR_BOOL']: bool_jump_line = ice9_parser_global.lno ice9_parser_global.lno += 2 if self.post_child: operand3 = expr_node.generate_code(self.post_child) post_child_end_line = ice9_parser_global.lno # For boolean addition, jump on true if self.operation == 'PLUS_BOOL': ice9_parser_global.emit_code_at_line(bool_jump_line, 'JEQ', operand2, 1, 7, \ 'Short circuit : On false, evaluation continues') ice9_parser_global.emit_code_at_line(bool_jump_line + 1, 'LDC', 7, post_child_end_line, 0, \ 'Short circuit : Evaluation of pre_child is true, skip post_child') # For boolean multiplication, jump on false elif self.operation == 'STAR_BOOL': ice9_parser_global.emit_code_at_line(bool_jump_line, 'JNE', operand2, 1, 7, \ 'Short circuit : On true, evaluation continues') ice9_parser_global.emit_code_at_line(bool_jump_line + 1, 'LDC', 7, post_child_end_line, 0, \ 'Short circuit : Evaluation of pre_child is true, skip post_child') register = operand2 if self.operation == 'PLUS': # If operation is PLUS, store the sum of left and right child in register ice9_parser_global.emit_code('ADD', register, operand2, operand3, "Addition") elif self.operation == 'PLUS_BOOL': # If operation is PLUS, store the sum of left and right child in register ice9_parser_global.emit_code('ADD', register, operand2, operand3, "Boolean addition") elif self.operation == 'MINUS': # If operation is PLUS, store the difference of left and right child in register ice9_parser_global.emit_code('SUB', register, operand2, operand3, "Subtraction") elif self.operation == 'STAR': # If operation is PLUS, store the product of left and right child in register ice9_parser_global.emit_code('MUL', register, operand2, operand3, "Multiplication") elif self.operation == 'STAR_BOOL': # If operation is PLUS, store the product of left and right child in register ice9_parser_global.emit_code('MUL', register, operand2, operand3, "Boolean multiplication") elif self.operation == 'SLASH': # If operation is PLUS, store the division of left and right child in register ice9_parser_global.emit_code('DIV', register, operand2, operand3, "Division") elif self.operation == 'QUEST': # If operation is Quest, ,compare result of left child with zero ice9_parser_global.emit_code('JNE', register, 2, 7, "Jump on less than") # When FALSE, jump over the TRUE line ice9_parser_global.emit_code('LDC', register, 0, 0, "Load constant - false") ice9_parser_global.emit_code('LDA', 7, 1, 7, "Unconditional jump") # When TRUE ice9_parser_global.emit_code('LDC', register, 1, 0, "Load constant - true") elif self.operation in ['LT', 'LE', 'GT', 'GE', 'EQ', 'NEQ']: # If operation is a logical operator, we first compute the difference on left and right child ice9_parser_global.emit_code('SUB', register, operand2, operand3, "Subtraction before comparison") # Depending upon the operation, do a comparison of register and 0 if self.operation == 'LT': ice9_parser_global.emit_code('JLT', register, 2, 7, "Jump on less than") elif self.operation == 'LE': ice9_parser_global.emit_code('JLE', register, 2, 7, "Jump on less than or equal to") elif self.operation == 'GT': ice9_parser_global.emit_code('JGT', register, 2, 7, "Jump on greater than") elif self.operation == 'GE': ice9_parser_global.emit_code('JGE', register, 2, 7, "Jump on greater than or equal to") elif self.operation == 'EQ': ice9_parser_global.emit_code('JEQ', register, 2, 7, "Jump on equal to") elif self.operation == 'NEQ': ice9_parser_global.emit_code('JNE', register, 2, 7, "Jump on not equal to") # When FALSE, jump over the TRUE line ice9_parser_global.emit_code('LDC', register, 0, 0, "Load constant - false") ice9_parser_global.emit_code('LDA', 7, 1, 7, "Unconditional jump") # When TRUE ice9_parser_global.emit_code('LDC', register, 1, 0, "Load constant - true") elif self.operation == 'MOD': # Since no direct instruction present for %, use / * - temp_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('DIV', temp_reg, operand2, operand3, "Modulo: division step") ice9_parser_global.emit_code('MUL', operand3, temp_reg, operand3, "Modulo: multiplication step") ice9_parser_global.emit_code('SUB', operand2, operand2, operand3, "Modulo: subtraction step") ice9_parser_global.reclaim_register() elif self.operation == 'UN_MINUS': # If operation is Unary Minus, ,multiply result of left child with -1 temp_reg = ice9_parser_global.get_next_register() ice9_parser_global.emit_code('LDC', temp_reg, -1, 0, "Unary minus: Load -1") ice9_parser_global.emit_code('MUL', operand2, operand2, temp_reg, "Unary minus: Multiply with -1") ice9_parser_global.reclaim_register() elif self.operation == 'NOT': ice9_parser_global.emit_code('LDA', operand2, -1, operand2, "Boolean NOT: Subtract 1") ice9_parser_global.emit_code('JEQ', operand2, 2, 7, "Boolean NOT: Jump if operand was True") ice9_parser_global.emit_code('LDC', operand2, 1, 0, "Boolean NOT: Load True") ice9_parser_global.emit_code('JNE', operand2, 1, 7, "Boolean NOT: Unconditional jump, operand was False") ice9_parser_global.emit_code('LDC', operand2, 0, 0, "Boolean NOT: Load False") # Operators that do require a new register allocated elif self.operation == 'ARRAY_REF': register = expr_node.generate_address(self) ice9_parser_global.load_array_variable_from_memory(register) elif self.operation == 'PROC_CALL': register = handle_proc_call(self) else: # Get a free register for use register = ice9_parser_global.get_next_register() if self.operation == 'INT': # If operation is a integer constant, load register with it ice9_parser_global.emit_code('LDC', register, self.value, 0, "Load constant") elif self.operation == 'SLIT': ice9_parser_global.emit_code_for_string('.DATA', self.length) ice9_parser_global.emit_code_for_string('.SDATA', self.value) ice9_parser_global.emit_code('LDC', register, \ ice9_parser_global.string_location_dict[self.value], 0, "Load address of string length") elif self.operation == 'ID': # If operation is a variable, load a register with it from memory ice9_parser_global.load_variable_from_memory(register, self.value) elif self.operation == 'TRUE': # If operation is a true, load register with 1 ice9_parser_global.emit_code('LDC', register, 1, 0, "Load constant True") elif self.operation == 'FALSE': # If operation is a false, load register with 0 ice9_parser_global.emit_code('LDC', register, 0, 0, "Load constant False") ice9_parser_global.set_register(register) return register