def make_il(self, il_code, symbol_table, c): """Make code for this node.""" symbol_table.new_scope() if self.first: self.first.make_il(il_code, symbol_table, c) start = il_code.get_label() cont = il_code.get_label() end = il_code.get_label() c = c.set_continue(cont).set_break(end) il_code.add(control_cmds.Label(start)) with report_err(): if self.second: cond = self.second.make_il(il_code, symbol_table, c) il_code.add(control_cmds.JumpZero(cond, end)) with report_err(): self.stat.make_il(il_code, symbol_table, c) il_code.add(control_cmds.Label(cont)) with report_err(): if self.third: self.third.make_il(il_code, symbol_table, c) il_code.add(control_cmds.Jump(start)) il_code.add(control_cmds.Label(end)) symbol_table.end_scope()
def _nonarith(self, left, right, il_code): """Check equality of non-arithmetic expressions.""" # If either operand is a null pointer constant, cast it to the # other's pointer type. if left.ctype.is_pointer() and right.null_ptr_const: right = set_type(right, left.ctype, il_code) elif right.ctype.is_pointer() and left.null_ptr_const: left = set_type(left, right.ctype, il_code) # If both operands are not pointer types, quit now if not left.ctype.is_pointer() or not right.ctype.is_pointer(): with report_err(): err = "comparison between incomparable types" raise CompilerError(err, self.op.r) # If one side is pointer to void, cast the other to same. elif left.ctype.arg.is_void(): check_cast(right, left.ctype, self.op.r) right = set_type(right, left.ctype, il_code) elif right.ctype.arg.is_void(): check_cast(left, right.ctype, self.op.r) left = set_type(left, right.ctype, il_code) # If both types are still incompatible, warn! elif not left.ctype.compatible(right.ctype): with report_err(): err = "comparison between distinct pointer types" raise CompilerError(err, self.op.r) # Now, we can do comparison out = ILValue(ctypes.integer) il_code.add(self.eq_il_cmd(out, left, right)) return out
def make_il(self, il_code, symbol_table, c): """Make code for this declaration.""" decl_infos = self.get_decl_infos(self.node, symbol_table) for info in decl_infos: with report_err(): self.process(info, il_code, symbol_table, c)
def make_il(self, il_code, symbol_table, c): """Make code for this node.""" start = il_code.get_label() end = il_code.get_label() il_code.add(control_cmds.Label(start)) c = c.set_continue(start).set_break(end) with report_err(): cond = self.cond.make_il(il_code, symbol_table, c) il_code.add(control_cmds.JumpZero(cond, end)) with report_err(): self.stat.make_il(il_code, symbol_table, c) il_code.add(control_cmds.Jump(start)) il_code.add(control_cmds.Label(end))
def make_il(self, il_code, symbol_table, c): """Make code for this declaration.""" self.set_self_vars(il_code, symbol_table, c) decl_infos = self.get_decl_infos(self.node) for info in decl_infos: with report_err(): info.process(il_code, symbol_table, c)
def make_il(self, il_code, symbol_table, c): """Make IL code for every block item, in order.""" symbol_table.new_scope() c = c.set_global(False) for item in self.items: with report_err(): item.make_il(il_code, symbol_table, c) symbol_table.end_scope()
def make_il(self, il_code, symbol_table, c): """Make IL code for returning this value.""" label = self.get_label(c) if label: il_code.add(control_cmds.Jump(label)) else: with report_err(): err = f"{self.descrip} statement not in loop" raise CompilerError(err, self.r)
def make_il(self, il_code, symbol_table, c): """Make code for this if statement.""" endif_label = il_code.get_label() with report_err(): cond = self.cond.make_il(il_code, symbol_table, c) il_code.add(control_cmds.JumpZero(cond, endif_label)) with report_err(): self.stat.make_il(il_code, symbol_table, c) if self.else_stat: end_label = il_code.get_label() il_code.add(control_cmds.Jump(end_label)) il_code.add(control_cmds.Label(endif_label)) with report_err(): self.else_stat.make_il(il_code, symbol_table, c) il_code.add(control_cmds.Label(end_label)) else: il_code.add(control_cmds.Label(endif_label))
def _nonarith(self, left, right, il_code): """Check equality of non-arithmetic expressions.""" # If either operand is a null pointer constant, cast it to the # other's pointer type. if (left.ctype.is_pointer() and getattr(right.literal, "val", None) == 0): right = set_type(right, left.ctype, il_code) elif (right.ctype.is_pointer() and getattr(left.literal, "val", None) == 0): left = set_type(left, right.ctype, il_code) # If both operands are not pointer types, quit now if not left.ctype.is_pointer() or not right.ctype.is_pointer(): with report_err(): err = "comparison between incomparable types" raise CompilerError(err, self.op.r) # If one side is pointer to void, cast the other to same. elif left.ctype.arg.is_void(): check_cast(right, left.ctype, self.op.r) right = set_type(right, left.ctype, il_code) elif right.ctype.arg.is_void(): check_cast(left, right.ctype, self.op.r) left = set_type(left, right.ctype, il_code) # If both types are still incompatible, warn! elif not left.ctype.compatible(right.ctype): with report_err(): err = "comparison between distinct pointer types" raise CompilerError(err, self.op.r) # Now, we can do comparison out = ILValue(ctypes.integer) il_code.add(self.eq_il_cmd(out, left, right)) return out
def get_decl_infos(self, node, symbol_table): """Given a node, returns a list of decl_info objects for that node.""" any_dec = bool(node.decls) base_type, storage = self.make_specs_ctype( node.specs, any_dec, symbol_table) proc = zip(node.decls, node.ranges, node.inits) out = [] for decl, range, init in proc: with report_err(): ctype, identifier = self.make_ctype( decl, base_type, symbol_table) out.append(DeclInfo(identifier, ctype, range, storage, init)) return out
def make_il(self, il_code, symbol_table, c, no_scope=False): """Make IL code for every block item, in order. If no_scope is True, then do not create a new symbol table scope. Used by function definition so that parameters can live in the scope of the function body. """ if not no_scope: symbol_table.new_scope() c = c.set_global(False) for item in self.items: with report_err(): item.make_il(il_code, symbol_table, c) if not no_scope: symbol_table.end_scope()
def get_decl_infos(self, node): """Given a node, returns a list of decl_info objects for that node.""" any_dec = bool(node.decls) base_type, storage = self.make_specs_ctype(node.specs, any_dec) out = [] for decl, init in zip(node.decls, node.inits): with report_err(): ctype, identifier = self.make_ctype(decl, base_type) if ctype.is_function(): param_identifiers = self.extract_params(decl) else: param_identifiers = [] out.append( DeclInfo(identifier, ctype, decl.r, storage, init, self.body, param_identifiers)) return out
def get_decl_infos(self, node): """Given a node, returns a list of decl_info objects for that node.""" any_dec = bool(node.decls) base_type, storage = self.make_specs_ctype(node.specs, any_dec) out = [] for decl, init in zip(node.decls, node.inits): with report_err(): ctype, identifier = self.make_ctype(decl, base_type) if ctype.is_function(): param_identifiers = self.extract_params(decl) else: param_identifiers = [] out.append(DeclInfo( identifier, ctype, decl.r, storage, init, self.body, param_identifiers)) return out
def parse_struct_union_spec(self, node, redec): """Parse struct or union ctype from the given decl_nodes.Struct node. node (decl_nodes.Struct/Union) - the Struct or Union node to parse redec (bool) - Whether this declaration is alone like so: struct S; union U; or declares variables/has storage specifiers: struct S *p; extern struct S; union U *u; extern union U; If it's the first, then this is always a forward declaration for a new `struct S` but if it's the second and a `struct S` already exists in higher scope, it's just using the higher scope struct. """ has_members = node.members is not None if node.kind == token_kinds.struct_kw: ctype_req = StructCType else: ctype_req = UnionCType if node.tag: tag = str(node.tag) ctype = self.symbol_table.lookup_struct_union(tag) if ctype and not isinstance(ctype, ctype_req): err = f"defined as wrong kind of tag '{node.kind} {tag}'" raise CompilerError(err, node.r) if not ctype or has_members or redec: ctype = self.symbol_table.add_struct_union(tag, ctype_req(tag)) if has_members and ctype.is_complete(): err = f"redefinition of '{node.kind} {tag}'" raise CompilerError(err, node.r) else: # anonymous struct/union ctype = ctype_req(None) if not has_members: return ctype # Struct or union does have members members = [] members_set = set() for member in node.members: decl_infos = [] # needed in case get_decl_infos below fails with report_err(): decl_infos = self.get_decl_infos(member) for decl_info in decl_infos: with report_err(): self._check_struct_member_decl_info( decl_info, node.kind, members_set) name = decl_info.identifier.content members_set.add(name) members.append((name, decl_info.ctype)) ctype.set_members(members) return ctype
def make_il(self, il_code, symbol_table, c): """Make code for the root.""" for node in self.nodes: with report_err(): c = c.set_global(True) node.make_il(il_code, symbol_table, c)
def parse_struct_spec(self, node, redec, symbol_table): """Parse a struct ctype from the given decl_nodes.Struct node. node (decl_nodes.Struct) - the Struct node to parse redec (bool) - Whether this declaration is alone like so: struct S; or declares variables/has storage specifiers: struct S *p; extern struct S; If it's the first, then this is always a forward declaration for a new `struct S` but if it's the second and a `struct S` already exists in higher scope, it's just using the higher scope struct. """ has_members = node.members is not None if node.tag: tag = str(node.tag) ctype = symbol_table.lookup_struct(tag) if not ctype or has_members or redec: ctype = symbol_table.add_struct(tag, StructCType(tag)) if has_members and ctype.is_complete(): err = f"redefinition of 'struct {tag}'" raise CompilerError(err, node.r) else: ctype = StructCType(None) if not has_members: return ctype # Struct does have members members = [] member_set = set() for member in node.members: decl_infos = [] # needed in case get_decl_infos below fails with report_err(): decl_infos = self.get_decl_infos(member, symbol_table) for decl_info in decl_infos: with report_err(): if decl_info.identifier is None: # someone snuck an abstract declarator into here! err = "missing name of struct member" raise CompilerError(err, decl_info.range) if decl_info.storage is not None: err = "cannot have storage specifier on struct member" raise CompilerError(err, decl_info.range) if decl_info.ctype.is_function(): err = "cannot have function type as struct member" raise CompilerError(err, decl_info.range) # TODO: 6.7.2.1.18 (allow flexible array members) if not decl_info.ctype.is_complete(): err = "cannot have incomplete type as struct member" raise CompilerError(err, decl_info.range) # TODO: 6.7.2.1.13 (anonymous structs) attr = decl_info.identifier.content if attr in member_set: err = f"duplicate member '{attr}'" raise CompilerError(err, decl_info.identifier.r) members.append((attr, decl_info.ctype)) member_set.add(attr) ctype.set_members(members) return ctype