public void generateCode() 
    {
        final List<FunctionDefinitionNode> functions = new ArrayList<>();
        final List<VariableDefinition> globalVars = new ArrayList<>();

        FunctionDefinitionNode mainFunction=null;
        for ( ASTNode node : ast.getChildren() ) 
        {
            node = unwrapStatement(node);
            
            if ( node instanceof FunctionDefinitionNode ) 
            {
                FunctionDefinitionNode fun = (FunctionDefinitionNode) node;
                if ( fun.getName().getStringValue().equals("main" ) ) {
                    mainFunction = fun;
                } else {
                    functions.add( fun );
                }
            } else if ( node instanceof VariableDefinition ) {
                globalVars.add( (VariableDefinition) node);
            }
        }

        final IAssemblyEmitter globalWriter = createIAssemblyWriter( ast.getGlobalScope() );

        // output .dat directives for global variables
        for ( VariableDefinition def : globalVars ) {
            outputGlobalVarDefinition( def , globalWriter );
        }   

        if ( mainFunction != null ) 
        {
            final LabelDefinition mainLabel = labelManager.getOrDefineLabel( mainFunction ); 
            final LabelDefinition endLabel = labelManager.defineLabel( ast.getGlobalScope() , new Identifier( "end" ) );   

            globalWriter.load( endLabel , mainFunction);
            globalWriter.pushValue( "store return address" ); 
            globalWriter.jumpTo( mainLabel.ref() , "jump to main method" );

            globalWriter.defineCodeLabel( endLabel );
        }

        globalWriter.outputCommentLine("======== Program ends here =========");

        globalWriter.halt("Terminate program");
        
        globalWriter.close();
        
        if ( mainFunction != null ) 
        {
            outputFunction( ast.getGlobalScope() , mainFunction );
        }

        // output functions
        for ( FunctionDefinitionNode node  : functions ) 
        {
            outputFunction( ast.getGlobalScope() , node );
        }
    }