public void ProcessFunction(FunctionAst functionAst)
        {
            Guard.Against.Null(functionAst, nameof(functionAst));

            if (SymbolScope.FindSymbolDeep(functionAst.Name.Value, out Symbol symbol))
            {
                throw new SemanticException($"Symbol already exist: {symbol.Name}, {symbol.GetType()}");
            }

            if (!new[] { "int", "double", "void" }.Contains(functionAst.ReturnType.Value))
            {
                throw new SemanticException($"Bad returning type: {functionAst.ReturnType.Value}");
            }

            var returnType = DataTypeHelper.ParseIntDoubleVoid(functionAst.ReturnType.Value);

            // Add function to scope
            var paramTypeList = new List <DataType>();

            if (functionAst.HasParams)
            {
                foreach (var parameter in functionAst.FunctionParamList.FunctionParams)
                {
                    var paramType = parameter.Type.Value switch
                    {
                        "int" => DataType.Long,
                        "double" => DataType.Double,
                        _ => throw new SemanticException($"Bad param type: {parameter.Type.Value}")
                    };
                    paramTypeList.Add(paramType);
                }
            }
            var functionSymbol = new FunctionSymbol(functionAst.Name.Value, returnType, paramTypeList);

            SymbolScope.AddSymbol(functionSymbol);

            // register function
            if (CodeGenerationEnabled)
            {
                GlobalBuilder.RegisterFunction(functionSymbol);
            }

            // Create scope
            SymbolScope = SymbolScope.CreateChildScope();

            // for testing
            functionSymbol.BodyBlockScope = SymbolScope;

            EnterFunctionDefination(functionSymbol);
            {
                // register params

                if (functionAst.HasParams)
                {
                    foreach (var parameter in functionAst.FunctionParamList.FunctionParams)
                    {
                        var paramType = parameter.Type.Value switch
                        {
                            "int" => DataType.Long,
                            "double" => DataType.Double,
                            _ => throw new SemanticException($"Bad param type: {parameter.Type.Value}")
                        };

                        var name = parameter.Name.Value;
                        if (SymbolScope.FindSymbolShallow(name, out Symbol _))
                        {
                            throw new SemanticException($"Exist symbol: {name}");
                        }

                        // Add to scope
                        var argSymbol = new VariableSymbol(name, false, parameter.IsConstant, paramType);
                        SymbolScope.AddSymbol(argSymbol);

                        if (CodeGenerationEnabled)
                        {
                            CurrentFunction.Builder.RegisterArgument(argSymbol);
                        }
                    }
                }

                var canReturn = ProcessBlockStatement(functionAst.BodyBlock, suppressNewScopeCreation: true);

                if (!canReturn && ReturnCheckEnabled && returnType != DataType.Void)
                {
                    throw new SemanticException($"Cannot leave function {functionAst.Name.Value}");
                }

                // implecit void returning
                if (CodeGenerationEnabled && returnType == DataType.Void)
                {
                    CurrentFunction.Builder.Bucket.Add(new Instructions.Instruction("ret"));
                }
            }
            LeaveFunctionDefination();
            SymbolScope = SymbolScope.ParentScope;
        }
        public virtual void ProcessConstDeclarationStatement(ConstDeclarationStatementAst constDeclaration)
        {
            var name = constDeclaration.Name.Value;

            if (SymbolScope.FindSymbolShallow(name, out Symbol existingSymbol))
            {
                throw new SemanticException(
                          $"Cannot const declare because of duplicated name. " +
                          $"Existing symbol type: {existingSymbol.GetType()}");
            }

            if (!new[] { "int", "double" }.Contains(constDeclaration.Type.Value))
            {
                throw new SemanticException($"Type {constDeclaration.Type.Value} should be int or double");
            }

            var declaringType = constDeclaration.Type.Value switch
            {
                "int" => DataType.Long,
                "double" => DataType.Double,
                _ => throw new Exception("Not Reached")
            };


            var initialExpressionType = ProcessExpression(constDeclaration.ValueExpression);

            if (declaringType != initialExpressionType)
            {
                throw new SemanticException(
                          $"DeclaringType: {declaringType}, InitialExpressionType: {initialExpressionType}");
            }

            // All check ok
            var symbol = new VariableSymbol(name, !IsInFunction, true, declaringType);

            SymbolScope.AddSymbol(symbol);

            if (CodeGenerationEnabled)
            {
                if (symbol.IsGlobal)
                {
                    GlobalBuilder.RegisterGlobalVariable(symbol);

                    symbol.GlobalVariableBuilder.LoadValueInstructions = ExpressionBucket.Pop();
                    Debug.Assert(symbol.GlobalVariableBuilder.LoadValueInstructions.Count > 0);
                }
                else
                {
                    CurrentFunction.Builder.RegisterLocalVariable(symbol);

                    // load address
                    CurrentFunction.Builder.Bucket.Add(new object[] { "loca", symbol.LocalLocation.Id });

                    // load init expr
                    CurrentFunction.Builder.Bucket.AddRange(ExpressionBucket.Pop());

                    // set memory value
                    CurrentFunction.Builder.Bucket.AddSingle("store.64");
                }
            }
        }