def from_ast(cls, filename: str, ast: AstNode, **extras): declarator = next(c for c in ast.children if c.type == AstType.direct_declarator) fn_name = next(c for c in declarator if c.type == AstType.IDENTIFIER).text assert fn_name is not None assert ast.type == AstType.function_definition ret_type = ast[0].text assert ret_type is not None env = Environment.empty() if ret_type != "void": env["ret"] = AtomicType(ret_type) params = declarator[2] if params.type == AstType.parameter_list: for p in params.children: if p.type == AstType.parameter_declaration: ty = p[0].text assert ty is not None and ty in ( "int", "float", "bool", ) ty = AtomicType(ty) name = None if p[1].type == AstType.IDENTIFIER: name = p[1].text elif (p[1].type == AstType.direct_declarator and p[1][0].type == AstType.IDENTIFIER and p[1][1].text == "["): name = p[1][0].text ty = ArrayType(ty) else: assert False assert name is not None env[name] = ty params = env.get_vars() requires = None for s in ast[-1][1].children: if (s.type == AstType.expression_statement and s[0].type == AstType.postfix_expression and s[0][1].type == AstType.paren_left and s[0][0].type == AstType.IDENTIFIER and s[0][0].text == "requires"): requires = Expr.from_ast(s[0][2], env) cfg = create_cfg(ast, requires, env) vars = env.get_vars() for p in params: del vars[p] vars = [Variable(v, t) for v, t in vars.items()] params = [Variable(v, t) for v, t in params.items()] return cls(filename, cfg=cfg, name=fn_name, vars=vars, params=params, **extras)
def create_cfg(self, ast: AstNode) -> CfgNode: if ast.type == AstType.labeled_statement: if ast[0].type == AstType.DEFAULT: statement = self.create_cfg(ast[2]) self.labels.append((None, statement)) return statement else: assert ast[0].type == AstType.CASE value = Expr.from_ast(ast[1], self.env) statement = self.create_cfg(ast[3]) self.labels.append((value, statement)) return statement elif ast.type == AstType.semicolon: return self.next_node elif ast.type == AstType.selection_statement: if ast[0].type == AstType.IF: return CondNode( code_location=ast[2].range, condition=Expr.from_ast(ast[2], self.env), true_br=self.create_cfg(ast[4]), false_br=self.create_cfg(ast[6]) if len(ast.children) == 7 else self.next_node, ) elif ast[0].type == AstType.SWITCH: switch_value = Expr.from_ast(ast[2], self.env) statement_env = self.enter_switch(self.next_node) statement = statement_env.create_cfg(ast[4]) conds: list[CondNode] = [] default = self.next_node for case_value, statement in statement_env.labels: if case_value is None: default = statement continue dummy = DummyNode(None) conds.append( CondNode( None, RelExpr("==", switch_value, case_value), statement, dummy, )) for cond, next_ in zip( conds, cast("list[CfgNode]", conds[1:]) + [default]): cond.false_br = next_ return conds[0] if conds else default else: assert False elif ast.type == AstType.compound_statement: self.open_scope() statements: list[CfgNode] = [] dummies: list[DummyNode] = [] for s in ast[1].children: dummy = DummyNode(None) statement = self.with_next(dummy).create_cfg(s) if statement is not dummy: dummies.append(dummy) statements.append(statement) statements.append(self.next_node) for s, s_next, d in zip(statements, statements[1:], dummies): s.replace(d, s_next, set()) self.close_scope() return statements[0] elif ast.type == AstType.jump_statement: # TODO? handle goto if ast[0].type == AstType.BREAK: assert self.loop_end is not None return self.loop_end elif ast[0].type == AstType.CONTINUE: assert self.loop_start is not None return self.loop_start elif ast[0].type == AstType.RETURN: if len(ast.children) == 3: return AssignmentNode( ast.range, expression=Expr.from_ast(ast[1], self.env), var=Variable("ret", self.env["ret"]), next_node=self.end_node, ) else: return self.end_node else: assert False elif ast.type == AstType.declaration: # TODO: what about "int x, y;" type_ = ast[0].text assert type_ is not None if (ast[1].type == AstType.direct_declarator and ast[1][1].type == AstType.bracket_left): var = ast[1][0].text assert var is not None self.env[var] = ArrayType(AtomicType(type_)) return self.next_node type_ = AtomicType(type_) if ast[1].type == AstType.IDENTIFIER: var = ast[1].text assert var is not None self.env[var] = type_ return self.next_node var = ast[1][0].text assert var is not None value = Expr.from_ast(ast[1][2], self.env) self.env[var] = type_ return AssignmentNode( ast.range, expression=value, var=Variable(self.env.rename(var), type_), next_node=self.next_node, ) elif ast.type == AstType.expression_statement: if (ast[0].type == AstType.postfix_expression and ast[0][1].type == AstType.paren_left and ast[0][0].type == AstType.IDENTIFIER): fn = ast[0][0].text if fn == "assert": assertion = Expr.from_ast(ast[0][2], self.env) remembers = tuple(chain.from_iterable(self.remembers)) assertion = (And(remembers + (assertion, )) if remembers else assertion) return AssertNode( ast.range, assertion=assertion, next_node=self.next_node, ) elif fn == "ensures": assert self.end_node.assertion is None self.end_node.assertion = Expr.from_ast( ast[0][2], self.env) self.end_node.code_location = ast.range return self.next_node elif fn == "requires": assert self.start_node.requires is None self.start_node.requires = Expr.from_ast( ast[0][2], self.env) self.start_node.code_location = ast.range return self.next_node elif fn == "freeze": args = ast[0][2] right = Expr.from_ast(args[2], self.env) assert args[0].type == AstType.IDENTIFIER assert args[0].text is not None self.env[args[0].text] = right.get_type() var = Expr.from_ast(args[0], self.env) assert isinstance(var, Variable) return AssignmentNode(ast.range, right, var, self.next_node) elif fn == "remember": self.remembers[-1].append( Expr.from_ast(ast[0][2], self.env)) return self.next_node elif fn == "assume": return AssumeNode(ast.range, Expr.from_ast(ast[0][2], self.env), self.next_node) else: assert False, f"unknown function {fn}" if ast[0].type == AstType.postfix_expression and ast[0][1].type in ( AstType.INC_OP, AstType.DEC_OP, ): left = Expr.from_ast(ast[0][0], self.env) operator = "+=" if ast[0][1].type == AstType.INC_OP else "-=" value = IntValue(1) elif ast[0].type == AstType.unary_expression and ast[0][0].type in ( AstType.INC_OP, AstType.DEC_OP, ): left = Expr.from_ast(ast[0][1], self.env) operator = "+=" if ast[0][0].type == AstType.INC_OP else "-=" value = IntValue(1) elif ast[0].type != AstType.assignment_expression: # FIXME return self.next_node else: value = Expr.from_ast(ast[0][2], self.env) operator = ast[0][1].text assert operator is not None left = Expr.from_ast(ast[0][0], self.env) # TODO? handle chained assignments? # handle other assignment operators: *= /= %= += -= >>= <<= &= |= ^= if operator != "=": operator = operator[:-1] value = BinaryExpr(operator=operator, lhs=left, rhs=value) if isinstance(left, Variable): return AssignmentNode(ast.range, expression=value, var=left, next_node=self.next_node) elif isinstance(left, ArraySelect): # TODO? what about 2d+ arrays? assert isinstance(left.array, Variable) return AssignmentNode( ast.range, var=left.array, expression=ArrayStore( array=left.array, index=left.index, value=value, ), next_node=self.next_node, ) else: assert False elif ast.type == AstType.iteration_statement: if ast[0].type == AstType.WHILE: self.open_scope() while_node = CondNode( ast[2].range, Expr.from_ast(ast[2], self.env), DummyNode(None), self.next_node, ) while_node.true_br = (self.enter_loop( start=while_node, end=self.next_node).with_next(while_node).create_cfg( ast[4])) self.close_scope() return while_node elif ast[0].type == AstType.DO: self.open_scope() cond = CondNode( ast[4].range, Expr.from_ast(ast[4], self.env), DummyNode(None), self.next_node, ) cond.true_br = (self.enter_loop( start=cond, end=self.next_node).with_next(cond).create_cfg(ast[1])) self.close_scope() return cond.true_br elif ast[0].type == AstType.FOR: self.open_scope() if ast[2].type == AstType.declaration: decl = self.with_next(DummyNode(None)).create_cfg(ast[2]) assert isinstance(decl, AssignmentNode) else: assert ast[2].type == AstType.semicolon decl = None if ast[3].type == AstType.expression_statement: cond = CondNode( ast[3].range, Expr.from_ast(ast[3][0], self.env), DummyNode(None), self.next_node, ) else: assert ast[3].type == AstType.semicolon cond = CondNode( ast[3].range, BoolValue(True), DummyNode(None), self.next_node, ) if decl is not None: decl.next_node = cond if ast[4].type == AstType.paren_right: inc = None else: inc = self.with_next(cond).create_cfg( AstNode(None, AstType.expression_statement, ast[4].range, [ast[4]])) cond.true_br = (self.enter_loop( start=cond, end=self.next_node).with_next( inc or cond).create_cfg(ast[5] if inc is None else ast[6])) self.close_scope() return decl or cond else: assert False else: assert False