def p_element_instruction_label(p): 'element : IDENTIFIER instruction' name, size, lineno = p[1], p[2][1], p.lineno(1) if name in p.parser.inst_table: error("Duplicate label definition: {0}", name, lineno=lineno, errors=p.parser.errors) p[0] = Inst(p[1], p[2], p[2][1], p.lineno(1)) p.parser.inst_table[name] = p[0] p.parser.inst_table[SIZE] += size
def analyse(ast, const_table, data_table, inst_table, errors): "Semantic analysis for the AST." if MAIN not in inst_table: error("Main entry point not defined", errors=errors) first_pass(ast, const_table, data_table, inst_table, errors) second_pass(ast, data_table, inst_table, errors) errors.report_errors()
def p_element_declaration_variable(p): 'element : IDENTIFIER RMB NUM' name, size, lineno = p[1], p[3], p.lineno(1) if name in p.parser.data_table: error("Duplicate name definition {0}", name, lineno=lineno, errors=p.parser.errors) if size <= 0: error("Error in variable declaration {0}: the number of bytes must be greater than zero", name, lineno=lineno, errors=p.parser.errors) p[0] = Var(p[1], p[3], lineno) p.parser.data_table[name] = p[0] p.parser.data_table[SIZE] += size
def second_pass(ast, data_table, inst_table, errors): """The second pass handles unsigned values (color registers, fps) and checks that immediate values are of the correct size (one or two bytes). """ for elem in ast: if isinstance(elem, Inst): if len(elem.inst) == 4: name, size, inst_type, value = elem.inst if name in INST_UNSIGNED: if value < 0 or value > 255: error("Value out of range {0} (instruction {1})", value, name, lineno=elem.lineno, errors=errors) else: elem.inst = (name, size, inst_type, value) elif inst_type == 'imm' and name != 'DRSYM': if size == 2: if value < -128 or value > 127: error("Value out of range {0} (instruction {1})", value, name, lineno=elem.lineno, errors=errors) elif size == 3: if value < -32768 or value > 32767: error("Value out of range {0} (instruction {1})", value, name, lineno=elem.lineno, errors=errors) elif len(elem.inst) == 5: name, _, inst_type, offset, _ = elem.inst if inst_type == 'ind': if offset < -128 or offset > 127: error("Value out of range {0} (instruction {1})", offset, name, lineno=elem.lineno, errors=errors)
def eval_expr(ast, p, lineno): "Returns the value obtained by evaluating an expression." expr_type = ast[0] if expr_type == 'num': return ast[1] elif expr_type == 'const': name = ast[1] if name not in p.parser.const_table: error("Undefined constant {0}", name, lineno=lineno, errors=p.parser.errors) raise SyntaxError p.parser.const_table[name].used = True return p.parser.const_table[name].value elif expr_type == '+': return eval_expr(ast[1], p, lineno) + eval_expr(ast[2], p, lineno) elif expr_type == '-': return eval_expr(ast[1], p, lineno) - eval_expr(ast[2], p, lineno) elif expr_type == '*': return eval_expr(ast[1], p, lineno) * eval_expr(ast[2], p, lineno) elif expr_type == '/': return eval_expr(ast[1], p, lineno) / eval_expr(ast[2], p, lineno)
def p_error(t): value = t.value if t.value != '\n' else 'NEWLINE' error("Syntax error near token {0}", value, lineno=t.lineno)
def p_element_instruction_error(p): 'element : error' error("Syntax error in instruction", lineno=p.lineno(1), errors=p.parser.errors)
def p_element_instruction_label_error(p): 'element : IDENTIFIER error' error("Syntax error in instruction at label {0}", p[1], lineno=p.lineno(1), errors=p.parser.errors)
def p_element_declaration_error(p): 'element : IDENTIFIER error expr' error("Syntax error in declaration {0}", p[1], lineno=p.lineno(1), errors=p.parser.errors)
def t_error(t): error("Illegal character '{0}'", t.value[0], errors=t.lexer.errors, lineno=t.lexer.lineno) t.lexer.skip(1)
def first_pass(ast, const_table, data_table, inst_table, errors): """The first pass assigns an address to variables (data segment) and labels, and checks for undefined references. It also warns about constants, variables and labels that are not used, and about mismatches between variable size and instruction size. """ data_offset = inst_table[SIZE] code_offset = 0 main_lineno = 0 for elem in ast: if isinstance(elem, Var): data_table[elem.id].addr = data_offset data_offset += elem.size if isinstance(elem, Inst): if elem.label != '': if elem.label == MAIN: main_lineno = elem.lineno inst_table[elem.label].addr = code_offset code_offset += elem.size if len(elem.inst) == 3: _, _, label = elem.inst if label not in inst_table: error("Undefined label {}", label, lineno=elem.lineno, errors=errors) else: inst_table[label].used = True elif len(elem.inst) == 4: name, size, inst_type, value = elem.inst if inst_type == 'var': if value not in data_table: error("Undefined variable {}", value, lineno=elem.lineno, errors=errors) else: data_table[value].used = True elem.inst = (name, size, 'ext', data_table[value].addr) var_size = data_table[value].size inst_size = 1 if name in INST_ONE_BYTE else 2 if inst_size != var_size: warn("Size mismatch: instruction {0} expects {1:d} byte{2}, variable {3} has {4:d} byte{5}", name, inst_size, 's' if inst_size > 1 else '', value, var_size, 's' if var_size > 1 else '', lineno=elem.lineno) elif len(elem.inst) == 5: _, _, inst_type, _, label = elem.inst if inst_type == 'imm-rel': if label not in inst_table: error("Undefined label {}", label, lineno=elem.lineno, errors=errors) else: inst_table[label].used = True # Warnings for unused constants, variables and labels for const in [k for k in const_table if const_table[k].used == False]: warn("Unused constant {}", const, lineno=const_table[const].lineno) for var in [k for k in data_table if k != SIZE and data_table[k].used == False]: warn("Unused variable {}", var, lineno=data_table[var].lineno) for label in [k for k in inst_table if k != SIZE and inst_table[k].used == False]: warn("Unused label {}", label, lineno=inst_table[label].lineno) if MAIN in inst_table and inst_table[MAIN].addr != MAIN_ADDR: error("Main label should be the first instruction",lineno=main_lineno, errors=errors)