private static SymbolRec ArgListTypesToSymbol(
            string functionName,
            DataTypes[] argsTypes,
            DataTypes returnType)
        {
            SymbolListRec head = null;

            for (int i = argsTypes.Length - 1; i >= 0; i--)
            {
                SymbolRec symbolArg = new SymbolRec(null);
                symbolArg.SymbolBecomeVariable(argsTypes[i]);
                SymbolListRec node = new SymbolListRec(symbolArg, head);
                head = node;
            }

            SymbolRec symbol = new SymbolRec(functionName);

            symbol.SymbolBecomeFunction(head, returnType);
            return(symbol);
        }
        /* compile a special function.  a special function has no function header, but is */
        /* simply some code to be executed.  the parameters the code is expecting are provided */
        /* in the FuncArray[] and NumParams.  the first parameter is deepest beneath the */
        /* top of stack.  the TextData is NOT altered.  if an error occurrs, *FunctionOut */
        /* will NOT contain a valid object */
        public static CompileErrors CompileSpecialFunction(
            CodeCenterRec CodeCenter,
            FunctionParamRec[] FuncArray,
            out int ErrorLineNumber,
            out DataTypes ReturnTypeOut,
            string TextData,
            bool suppressCILEmission,
            out PcodeRec FunctionOut,
            out ASTExpression ASTOut)
        {
            CompileErrors Error;

            ErrorLineNumber = -1;
            ASTOut          = null;
            FunctionOut     = null;
            ReturnTypeOut   = DataTypes.eInvalidDataType;

            ScannerRec <KeywordsType> TheScanner     = new ScannerRec <KeywordsType>(TextData, KeywordTable);
            SymbolTableRec            TheSymbolTable = new SymbolTableRec();

            // reconstitute function prototypes
            for (int i = 0; i < CodeCenter.RetainedFunctionSignatures.Length; i++)
            {
                SymbolRec functionSignature = ArgListTypesToSymbol(
                    CodeCenter.RetainedFunctionSignatures[i].Key,
                    CodeCenter.RetainedFunctionSignatures[i].Value.ArgsTypes,
                    CodeCenter.RetainedFunctionSignatures[i].Value.ReturnType);
                bool f = TheSymbolTable.Add(functionSignature);
                Debug.Assert(f); // should never fail (due to duplicate) since CodeCenter.RetainedFunctionSignatures is unique-keyed
            }

            /* build parameters into symbol table */
            int StackDepth         = 0;
            int MaxStackDepth      = 0;
            int ReturnAddressIndex = StackDepth;

            for (int i = 0; i < FuncArray.Length; i += 1)
            {
                SymbolRec TheParameter = new SymbolRec(FuncArray[i].ParameterName);
                TheParameter.SymbolBecomeVariable(FuncArray[i].ParameterType);
                /* allocate stack slot */
                StackDepth++;
                MaxStackDepth = Math.Max(MaxStackDepth, StackDepth);
                TheParameter.SymbolVariableStackLocation = StackDepth;
                if (!TheSymbolTable.Add(TheParameter)) // our own code should never pass in a formal arg list with duplicates
                {
                    Debug.Assert(false);
                    throw new InvalidOperationException();
                }
            }
            /* fence them off */
            TheSymbolTable.IncrementSymbolTableLevel();

            /* reserve spot for fake return address (so we have uniform calling convention everywhere) */
            StackDepth++;
            MaxStackDepth = Math.Max(MaxStackDepth, StackDepth);
            if (StackDepth != FuncArray.Length + 1)
            {
                // stack depth error before evaluating function
                Debug.Assert(false);
                throw new InvalidOperationException();
            }

            ASTExpressionList ListOfExpressions;

            Error = ParseExprList(
                out ListOfExpressions,
                new ParserContext(
                    TheScanner,
                    TheSymbolTable),
                out ErrorLineNumber);
            /* compile the thing */
            if (Error != CompileErrors.eCompileNoError)
            {
                return(Error);
            }
            ASTExpression TheExpressionThang = new ASTExpression(
                ListOfExpressions,
                TheScanner.GetCurrentLineNumber());

            /* make sure there is nothing after it */
            TokenRec <KeywordsType> Token = TheScanner.GetNextToken();

            if (Token.GetTokenType() != TokenTypes.eTokenEndOfInput)
            {
                ErrorLineNumber = TheScanner.GetCurrentLineNumber();
                return(CompileErrors.eCompileInputBeyondEndOfFunction);
            }

            DataTypes ResultingType;

            Error = TheExpressionThang.TypeCheck(out ResultingType, out ErrorLineNumber);
            if (Error != CompileErrors.eCompileNoError)
            {
                return(Error);
            }

            OptimizeAST(ref TheExpressionThang);

            PcodeRec TheFunctionCode = new PcodeRec();

            TheExpressionThang.PcodeGen(
                TheFunctionCode,
                ref StackDepth,
                ref MaxStackDepth);
            Debug.Assert(StackDepth <= MaxStackDepth);

            ReturnTypeOut = TheExpressionThang.ResultType;


            /* 2 extra words for retaddr, resultofexpr */
            if (StackDepth != FuncArray.Length + 1 /*retaddr*/ + 1 /*result*/)
            {
                // stack depth error after evaluating function
                Debug.Assert(false);
                throw new InvalidOperationException();
            }
            /* now put the return instruction */
            int unused;

            TheFunctionCode.AddPcodeInstruction(Pcodes.epReturnFromSubroutine, out unused, TheScanner.GetCurrentLineNumber());
            // special function returns without popping args -- so that args can be have in/out behavior
            TheFunctionCode.AddPcodeOperandInteger(0);
            StackDepth -= 1; /* pop retaddr */
            Debug.Assert(StackDepth <= MaxStackDepth);
            if (StackDepth != 1 + FuncArray.Length)
            {
                // stack depth is wrong at end of function
                Debug.Assert(false);
                throw new InvalidOperationException();
            }

            TheFunctionCode.MaxStackDepth = MaxStackDepth;

            /* optimize stupid things away */
            TheFunctionCode.OptimizePcode();


            if (CILObject.EnableCIL && !suppressCILEmission)
            {
                DataTypes[] argsTypes = new DataTypes[FuncArray.Length];
                string[]    argsNames = new string[FuncArray.Length];
                for (int i = 0; i < argsTypes.Length; i++)
                {
                    argsTypes[i] = FuncArray[i].ParameterType;
                    argsNames[i] = FuncArray[i].ParameterName;
                }
                CILAssembly cilAssembly = new CILAssembly();
                CILObject   cilObject   = new CILObject(
                    CodeCenter.ManagedFunctionLinker,
                    argsTypes,
                    argsNames,
                    TheExpressionThang.ResultType,
                    TheExpressionThang,
                    cilAssembly,
                    true /*argsByRef*/); // args by ref true for special functions to permit multiple return values
                TheFunctionCode.cilObject = cilObject;
                cilAssembly.Finish();
            }


            /* it worked, so return the dang thing */
            FunctionOut = TheFunctionCode;
            ASTOut      = TheExpressionThang;
            return(CompileErrors.eCompileNoError);
        }