예제 #1
0
    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)
예제 #2
0
    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