tuna-master/joern/src/main/java/parsing/C/Functions/builder/FunctionContentBuilder.java
package parsing.C.Functions.builder;

import antlr.C.FunctionParser.*;
import ast.ASTNode;
import ast.ASTNodeBuilder;
import ast.declarations.ClassDefStatement;
import ast.declarations.IdentifierDecl;
import ast.expressions.*;
import ast.statements.*;
import org.antlr.v4.runtime.ParserRuleContext;
import parsing.C.Shared.InitDeclContextWrapper;
import parsing.C.Shared.builders.ClassDefBuilder;
import parsing.C.Shared.builders.IdentifierDeclBuilder;

import java.util.EmptyStackException;

/**
 * The FunctionContentBuilder is invoked while walking the parse tree to create
 * ASTs for the contents of functions, i.e., the first-level compound statements
 * of functions.
 * <p/>
 * Since the fuzzy parser avoids using nested grammar rules as these rules often
 * require reading all tokens of a file only to realize that the default rule
 * must be taken, the most difficult task this code fulfills is to produce a
 * correctly nested AST.
 */

public class FunctionContentBuilder extends ASTNodeBuilder {
    ContentBuilderStack stack = new ContentBuilderStack();
    NestingReconstructor nesting = new NestingReconstructor(stack);

    // exitStatements is called when the entire
    // function-content has been walked

    public void exitStatements(StatementsContext ctx) {
        if (stack.size() != 1)
            throw new RuntimeException("Broken stack while parsing");

    }

    // For all statements, begin by pushing a Statement Object
    // onto the stack.

    public void enterStatement(StatementContext ctx) {
        ASTNode statementItem = new Statement();
        statementItem.initializeFromContext(ctx);
        stack.push(statementItem);
    }

    // Mapping of grammar-rules to CodeItems.

    public void enterOpeningCurly(Opening_curlyContext ctx) {
        replaceTopOfStack(new CompoundStatement());
    }

    public void enterClosingCurly(Closing_curlyContext ctx) {
        replaceTopOfStack(new BlockCloser());
    }

    public void enterBlockStarter(Block_starterContext ctx) {
        replaceTopOfStack(new BlockStarter());
    }

    public void enterExprStatement(Expr_statementContext ctx) {
        replaceTopOfStack(new ExpressionStatement());
    }

    public void enterIf(If_statementContext ctx) {
        replaceTopOfStack(new IfStatement());
    }

    public void enterFor(For_statementContext ctx) {
        replaceTopOfStack(new ForStatement());
    }

    public void enterWhile(While_statementContext ctx) {
        replaceTopOfStack(new WhileStatement());
    }

    public void enterDo(Do_statementContext ctx) {
        replaceTopOfStack(new DoStatement());
    }

    public void enterElse(Else_statementContext ctx) {
        replaceTopOfStack(new ElseStatement());
    }

    public void exitStatement(StatementContext ctx) {
        if (stack.size() == 0)
            throw new RuntimeException();

        ASTNode itemToRemove = stack.peek();
        itemToRemove.initializeFromContext(ctx);

        if (itemToRemove instanceof BlockCloser) {
            closeCompoundStatement();
            return;
        }

        // We keep Block-starters and compound items
        // on the stack. They are removed by following
        // statements.
        if (itemToRemove instanceof BlockStarter
                || itemToRemove instanceof CompoundStatement)
            return;

        nesting.consolidate();
    }

    private void closeCompoundStatement() {
        stack.pop(); // remove 'CloseBlock'

        CompoundStatement compoundItem = (CompoundStatement) stack.pop();
        nesting.consolidateBlockStarters(compoundItem);
    }

    // Expression handling

    public void enterExpression(ExprContext ctx) {
        Expression expression = new Expression();
        stack.push(expression);
    }

    public void exitExpression(ExprContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterAssignment(Assign_exprContext ctx) {
        AssignmentExpr expr = new AssignmentExpr();
        stack.push(expr);
    }

    public void exitAssignment(Assign_exprContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterConditionalExpr(Conditional_expressionContext ctx) {
        ConditionalExpression expr = new ConditionalExpression();
        stack.push(expr);
    }

    public void exitConditionalExpr(Conditional_expressionContext ctx) {
        introduceCndNodeForCndExpr();
        nesting.consolidateSubExpression(ctx);
    }

    public void enterOrExpression(Or_expressionContext ctx) {
        OrExpression expr = new OrExpression();
        stack.push(expr);
    }

    public void exitrOrExpression(Or_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterAndExpression(And_expressionContext ctx) {
        AndExpression expr = new AndExpression();
        stack.push(expr);
    }

    public void exitAndExpression(And_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterInclusiveOrExpression(Inclusive_or_expressionContext ctx) {
        InclusiveOrExpression expr = new InclusiveOrExpression();
        stack.push(expr);
    }

    public void exitInclusiveOrExpression(Inclusive_or_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterExclusiveOrExpression(Exclusive_or_expressionContext ctx) {
        ExclusiveOrExpression expr = new ExclusiveOrExpression();
        stack.push(expr);
    }

    public void exitExclusiveOrExpression(Exclusive_or_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterBitAndExpression(Bit_and_expressionContext ctx) {
        BitAndExpression expr = new BitAndExpression();
        stack.push(expr);
    }

    public void enterEqualityExpression(Equality_expressionContext ctx) {
        EqualityExpression expr = new EqualityExpression();
        stack.push(expr);
    }

    public void exitEqualityExpression(Equality_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void exitBitAndExpression(Bit_and_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterRelationalExpression(Relational_expressionContext ctx) {
        RelationalExpression expr = new RelationalExpression();
        stack.push(expr);
    }

    public void exitRelationalExpression(Relational_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterShiftExpression(Shift_expressionContext ctx) {
        ShiftExpression expr = new ShiftExpression();
        stack.push(expr);
    }

    public void exitShiftExpression(Shift_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterAdditiveExpression(Additive_expressionContext ctx) {
        AdditiveExpression expr = new AdditiveExpression();
        stack.push(expr);
    }

    public void exitAdditiveExpression(Additive_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterMultiplicativeExpression(
            Multiplicative_expressionContext ctx) {
        MultiplicativeExpression expr = new MultiplicativeExpression();
        stack.push(expr);
    }

    public void exitMultiplicativeExpression(
            Multiplicative_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterCastExpression(Cast_expressionContext ctx) {
        CastExpression expr = new CastExpression();
        stack.push(expr);
    }

    public void exitCastExpression(Cast_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterCast_target(Cast_targetContext ctx) {
        CastTarget expr = new CastTarget();
        stack.push(expr);
    }

    public void exitCast_target(Cast_targetContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterFuncCall(FuncCallContext ctx) {
        CallExpression expr = new CallExpression();
        stack.push(expr);
    }

    public void exitFuncCall(FuncCallContext ctx) {
        introduceCalleeNode();
        nesting.consolidateSubExpression(ctx);
    }

    public void enterSizeof(SizeofContext ctx) {
        Sizeof expr = new Sizeof();
        stack.push(expr);
    }

    public void exitSizeof(SizeofContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    private void introduceCalleeNode() {
        CallExpression expr;
        try {
            expr = (CallExpression) stack.peek();
        } catch (EmptyStackException ex) {
            return;
        }

        ASTNode child = expr.getChild(0);
        if (child == null)
            return;

        Callee callee = new Callee();
        callee.addChild(child);
        expr.replaceFirstChild(callee);
    }

    private void introduceCndNodeForCndExpr() {
        ConditionalExpression expr;
        try {
            expr = (ConditionalExpression) stack.peek();
        } catch (EmptyStackException ex) {
            return;
        }

        ASTNode child = expr.getChild(0);
        if (child == null)
            return;
        Condition cnd = new Condition();
        cnd.addChild(child);
        expr.replaceFirstChild(cnd);

    }

    public void enterArgumentList(Function_argument_listContext ctx) {
        ArgumentList expr = new ArgumentList();
        stack.push(expr);
    }

    public void exitArgumentList(Function_argument_listContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterCondition(ConditionContext ctx) {
        Condition expr = new Condition();
        stack.push(expr);
    }

    public void exitCondition(ConditionContext ctx) {
        Condition cond = (Condition) stack.pop();
        cond.initializeFromContext(ctx);
        nesting.addItemToParent(cond);
    }

    public void enterDeclByClass(DeclByClassContext ctx) {
        ClassDefBuilder classDefBuilder = new ClassDefBuilder();
        classDefBuilder.createNew(ctx);
        classDefBuilder.setName(ctx.class_def().class_name());
        replaceTopOfStack(classDefBuilder.getItem());
    }

    public void exitDeclByClass() {
        nesting.consolidate();
    }

    public void enterInitDeclSimple(InitDeclSimpleContext ctx) {
        ASTNode identifierDecl = buildDeclarator(ctx);
        stack.push(identifierDecl);
    }

    public void exitInitDeclSimple() {
        IdentifierDecl identifierDecl = (IdentifierDecl) stack.pop();
        ASTNode stmt = stack.peek();
        stmt.addChild(identifierDecl);
    }

    public void enterInitDeclWithAssign(InitDeclWithAssignContext ctx) {
        IdentifierDecl identifierDecl = buildDeclarator(ctx);
        stack.push(identifierDecl);
    }

    public void exitInitDeclWithAssign(InitDeclWithAssignContext ctx) {
        IdentifierDecl identifierDecl = (IdentifierDecl) stack.pop();

        Expression lastChild = (Expression) identifierDecl.popLastChild();
        AssignmentExpr assign = new AssignmentExpr();
        assign.initializeFromContext(ctx);

        // watchout here, we're not making a copy.
        // This is also a bit of a hack. As we go up,
        // we introduce an artificial assignment-node.

        assign.addChild(identifierDecl.getName().clone());
        assign.addChild(lastChild);

        identifierDecl.addChild(assign);

        ASTNode stmt = stack.peek();
        stmt.addChild(identifierDecl);
    }

    public void enterInitDeclWithCall(InitDeclWithCallContext ctx) {
        ASTNode identifierDecl = buildDeclarator(ctx);
        stack.push(identifierDecl);
    }

    public void exitInitDeclWithCall() {
        IdentifierDecl identifierDecl = (IdentifierDecl) stack.pop();
        ASTNode stmt = stack.peek();
        stmt.addChild(identifierDecl);
    }

    private IdentifierDecl buildDeclarator(ParserRuleContext ctx) {
        InitDeclContextWrapper wrappedContext = new InitDeclContextWrapper(ctx);
        ParserRuleContext typeName = getTypeFromParent();
        IdentifierDeclBuilder declBuilder = new IdentifierDeclBuilder();
        declBuilder.createNew(ctx);
        declBuilder.setType(wrappedContext, typeName);
        IdentifierDecl identifierDecl = (IdentifierDecl) declBuilder.getItem();
        return identifierDecl;
    }

    private ParserRuleContext getTypeFromParent() {
        ASTNode parentItem = stack.peek();
        ParserRuleContext typeName;
        if (parentItem instanceof IdentifierDeclStatement)
            typeName = ((IdentifierDeclStatement) parentItem)
                    .getTypeNameContext();
        else if (parentItem instanceof ClassDefStatement)
            typeName = ((ClassDefStatement) parentItem).getName()
                    .getParseTreeNodeContext();
        else
            throw new RuntimeException(
                    "No matching declaration statement/class definiton for init declarator");
        return typeName;
    }

    public void enterIncDec(Inc_decContext ctx) {
        IncDec expr = new IncDec();
        stack.push(expr);
    }

    public void exitIncDec(Inc_decContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterArrayIndexing(ArrayIndexingContext ctx) {
        ArrayIndexing expr = new ArrayIndexing();
        stack.push(expr);
    }

    public void exitArrayIndexing(ArrayIndexingContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterMemberAccess(MemberAccessContext ctx) {
        MemberAccess expr = new MemberAccess();
        stack.push(expr);
    }

    public void exitMemberAccess(MemberAccessContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterIncDecOp(IncDecOpContext ctx) {
        IncDecOp expr = new IncDecOp();
        stack.push(expr);
    }

    public void exitIncDecOp(IncDecOpContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterPrimary(Primary_expressionContext ctx) {
        PrimaryExpression expr = new PrimaryExpression();
        stack.push(expr);
    }

    public void exitPrimary(Primary_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterUnaryExpression(Unary_expressionContext ctx) {
        UnaryExpression expr = new UnaryExpression();
        stack.push(expr);
    }

    public void exitUnaryExpression(Unary_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterIdentifier(IdentifierContext ctx) {
        Identifier expr = new Identifier();
        stack.push(expr);
    }

    public void exitIdentifier(IdentifierContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterArgument(Function_argumentContext ctx) {
        Argument expr = new Argument();
        stack.push(expr);
    }

    public void exitArgument(Function_argumentContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterInitializerList(Initializer_listContext ctx) {
        InitializerList expr = new InitializerList();
        stack.push(expr);
    }

    public void exitInitializerList(Initializer_listContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterPtrMemberAccess(PtrMemberAccessContext ctx) {
        PtrMemberAccess expr = new PtrMemberAccess();
        stack.push(expr);
    }

    public void exitPtrMemberAccess(PtrMemberAccessContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterInitFor(For_init_statementContext ctx) {
        ForInit expr = new ForInit();
        stack.push(expr);
    }

    public void exitInitFor(For_init_statementContext ctx) {
        ASTNode node = stack.pop();
        node.initializeFromContext(ctx);
        ForStatement forStatement = (ForStatement) stack.peek();
        forStatement.addChild(node);
    }

    public void enterSwitchStatement(Switch_statementContext ctx) {
        replaceTopOfStack(new SwitchStatement());
    }

    public void enterLabel(LabelContext ctx) {
        replaceTopOfStack(new Label());
    }

    public void enterReturnStatement(ReturnStatementContext ctx) {
        replaceTopOfStack(new ReturnStatement());
    }

    public void enterBreakStatement(BreakStatementContext ctx) {
        replaceTopOfStack(new BreakStatement());
    }

    public void enterContinueStatement(ContinueStatementContext ctx) {
        replaceTopOfStack(new ContinueStatement());
    }

    public void enterGotoStatement(GotoStatementContext ctx) {
        replaceTopOfStack(new GotoStatement());
    }

    @Override
    public void createNew(ParserRuleContext ctx) {
        item = new CompoundStatement();
        CompoundStatement rootItem = (CompoundStatement) item;
        item.initializeFromContext(ctx);
        stack.push(rootItem);
    }

    public void addLocalDecl(IdentifierDecl decl) {
        IdentifierDeclStatement declStmt = (IdentifierDeclStatement) stack
                .peek();
        declStmt.addChild(decl);
    }

    public void enterDeclByType(ParserRuleContext ctx,
                                Type_nameContext type_nameContext) {
        IdentifierDeclStatement declStmt = new IdentifierDeclStatement();
        declStmt.initializeFromContext(ctx);
        declStmt.setTypeNameContext(type_nameContext);

        if (stack.peek() instanceof Statement)
            replaceTopOfStack(declStmt);
        else
            stack.push(declStmt);
    }

    public void exitDeclByType() {
        nesting.consolidate();
    }

    protected void replaceTopOfStack(ASTNode item) {
        stack.pop();
        stack.push(item);
    }

    public void enterSizeofExpr(Sizeof_expressionContext ctx) {
        SizeofExpr expr = new SizeofExpr();
        stack.push(expr);
    }

    public void exitSizeofExpr(Sizeof_expressionContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterSizeofOperand2(Sizeof_operand2Context ctx) {
        SizeofOperand expr = new SizeofOperand();
        stack.push(expr);
    }

    public void enterSizeofOperand(Sizeof_operandContext ctx) {
        SizeofOperand expr = new SizeofOperand();
        stack.push(expr);
    }

    public void exitSizeofOperand2(Sizeof_operand2Context ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void exitSizeofOperand(Sizeof_operandContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterUnaryOpAndCastExpr(Unary_op_and_cast_exprContext ctx) {
        UnaryOp expr = new UnaryOp();
        stack.push(expr);
    }

    public void exitUnaryOpAndCastExpr(Unary_op_and_cast_exprContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterUnaryOperator(Unary_operatorContext ctx) {
        UnaryOperator expr = new UnaryOperator();
        stack.push(expr);
    }

    public void exitUnaryOperator(Unary_operatorContext ctx) {
        nesting.consolidateSubExpression(ctx);
    }

    public void enterTryStatement(Try_statementContext ctx) {
        replaceTopOfStack(new TryStatement());
    }

    public void enterCatchStatement(Catch_statementContext ctx) {
        replaceTopOfStack(new CatchStatement());
    }

    public void enterThrowStatement(ThrowStatementContext ctx) {
        replaceTopOfStack(new ThrowStatement());
    }

}
