public virtual DataType ProcessIdentExpression(IdentExpressionAst identExpression)
        {
            var name = identExpression.IdentifierToken.Value;

            if (!SymbolScope.FindSymbolDeep(name, out Symbol symbol))
            {
                throw new SemanticException($"Symbol {name} not exist");
            }

            if (!(symbol is VariableSymbol variableSymbol))
            {
                throw new SemanticException($"Symbol {name} is not const/var");
            }

            // All check ok

            if (CodeGenerationEnabled)
            {
                // load-variable-addr
                if (variableSymbol.IsGlobal)
                {
                    var id = variableSymbol.GlobalVariableBuilder.Id;
                    ExpressionBucket.Add(new object[] { "globa", id });
                }
                else
                {
                    if (variableSymbol.LocalLocation.IsArgument)
                    {
                        var id = variableSymbol.LocalLocation.Id;
                        if (CurrentFunction.ReturnType != DataType.Void)
                        {
                            id++;// because arg0 is ret val
                        }
                        var instruction = Instruction.Pack("arga", id);
                        instruction.Comment = variableSymbol.Name + " (ident-expr)";
                        ExpressionBucket.Add(instruction);
                    }
                    else
                    {
                        // 局部变量
                        var id = variableSymbol.LocalLocation.Id;
                        ExpressionBucket.Add(new object[] { "loca", id });
                    }
                }

                // de-reference
                ExpressionBucket.Add(Instruction.Pack("load.64"));
            }

            return(variableSymbol.Type);
        }
        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 DataType ProcessAssignExpression(AssignExpressionAst assignExpression)
        {
            // IDENT '=' expr
            // IDENT 是可以赋值的变量,expr是int或者double

            var name = assignExpression.Identifier.Value;

            if (!SymbolScope.FindSymbolDeep(assignExpression.Identifier.Value, out Symbol symbol))
            {
                throw new SemanticException($"Symbol name '{name}' not found");
            }

            if (!(symbol is VariableSymbol variableSymbol))
            {
                throw new SemanticException($"Symbol '{name}' is function symbol, cannot assign value");
            }

            if (variableSymbol.IsConstant)
            {
                throw new SemanticException($"Symbol '{name}' is constant, cannot assign value");
            }

            var valueType = ProcessExpression(assignExpression.Expression);

            if (valueType != variableSymbol.Type)
            {
                throw new SemanticException(
                          $"Variable '{name}' is type '{variableSymbol.Type}', but expression is '{valueType}'");
            }

            var valueExpressionCode = ExpressionBucket.Pop();

            // All check ok

            if (CodeGenerationEnabled)
            {
                // load-variable-addr
                if (variableSymbol.IsGlobal)
                {
                    var id = variableSymbol.GlobalVariableBuilder.Id;
                    ExpressionBucket.Add(new object[] { "globa", id });
                }
                else
                {
                    if (variableSymbol.LocalLocation.IsArgument)
                    {
                        // 函数参数 (+1)
                        var id = variableSymbol.LocalLocation.Id;
                        if (CurrentFunction.ReturnType != DataType.Void)
                        {
                            id++;
                        }

                        var instruction = Instruction.Pack("arga", id);
                        instruction.Comment = variableSymbol.Name + " call-arg";
                        ExpressionBucket.Add(instruction);
                    }
                    else
                    {
                        // 局部变量
                        var id = variableSymbol.LocalLocation.Id;
                        ExpressionBucket.Add(new object[] { "loca", id });
                    }
                }

                // value-expr
                ExpressionBucket.AddRange(valueExpressionCode);

                // store.64
                ExpressionBucket.Add(new Instruction("store.64"));
            }

            return(DataType.Void);
        }
        public virtual DataType ProcessCallExpression(CallExpressionAst callExpression)
        {
            var name = callExpression.Identifier.Value;

            if (!SymbolScope.FindSymbolDeep(name, out Symbol symbol))
            {
                throw new SemanticException($"Symbol name '{name}' not found");
            }

            if (!(symbol is FunctionSymbol functionSymbol))
            {
                throw new SemanticException($"Symbol '{name}' is not function, cannot call.");
            }

            // ret-space: 如果是stdlibs,不需要预留空间。如果是返回void的,也不用。
            if (CodeGenerationEnabled && !IsStdLibCall(functionSymbol.Name) && functionSymbol.ReturnType != DataType.Void)
            {
                ExpressionBucket.Add(new object[] { "push", (long)0 });
            }

            var needCount     = functionSymbol.ParamTypes.Count;
            var providedCount = callExpression.HasParams ? callExpression.ParamList.Parameters.Count : 0;

            if (needCount != providedCount)
            {
                throw new SemanticException($"Function '{name}' needs {needCount} param(s)," +
                                            $"but provided {providedCount} param(s)");
            }

            // Now, 0:0 or n:n. Check the types when n:n
            if (needCount != 0)
            {
                var providedTypes = new List <DataType>();

                foreach (var parameter in callExpression.ParamList.Parameters)
                {
                    var expressionType = ProcessExpression(parameter);
                    providedTypes.Add(expressionType);
                }

                Debug.Assert(functionSymbol.ParamTypes.Count == providedTypes.Count);

                if (!functionSymbol.IsParamTypeMatch(providedTypes))
                {
                    throw new SemanticException($"Call to function '{name}' cannot match param types");
                }
            }


            // call func id
            if (CodeGenerationEnabled)
            {
                if (!IsStdLibCall(functionSymbol.Name))
                {
                    var funcId = functionSymbol.Builder.Id;
                    ExpressionBucket.Add(Instruction.Pack("call", funcId));
                }
                else
                {
                    // stdlib calls

                    var opcode = StdLibReference[functionSymbol.Name];
                    Debug.Assert(opcode != null);

                    ExpressionBucket.Add(new Instruction(opcode));
                }
            }

            return(functionSymbol.ReturnType);
        }