private static void initCompiler(ref Compiler_t compiler, FunctionType type, Token funcName)
        {
            compiler.enclosing  = current;
            compiler.function   = null;
            compiler.type       = type;
            compiler.localCount = 0;
            compiler.scopeDepth = 0;
            compiler.function   = Object.newFunction();
            current             = compiler;

            if (type != FunctionType.TYPE_SCRIPT)
            {
                current.function.name = Object.copyString(funcName._char_ptr, funcName.start, funcName.length);
            }

            Local local = current.locals[current.localCount++];

            local.depth      = 0;
            local.isCaptured = false;

            if (type != FunctionType.TYPE_FUNCTION)
            {
                local.name._char_ptr = "this\0".ToCharArray();
                local.name.start     = 0;
                local.name.length    = 4;
            }
            else
            {
                local.name._char_ptr = new char[] { '\0' };
                local.name.start     = 0;
                local.name.length    = 0;
            }

            current.locals[current.localCount - 1] = local; // C sharp fix.
        }
        void function(Stmt.Function funStmt, FunctionType type)
        {
            /* clox: funDeclaration */
            byte global = 0;

            if (type == FunctionType.TYPE_FUNCTION)
            {
                global = parseVariable(funStmt.name, "Expect function name.");
                markInitialized();
            }


            /* clox: function */
            Compiler_t compiler = new Compiler_t();

            initCompiler(ref compiler, type, funStmt.name);

            captureToken(funStmt.name);

            beginScope();

            // Compile parameter list.
            if (funStmt.params_.Count > 255)
            {
                errorAtCurrent("Cannot have more than 255 parameters.");
            }
            foreach (Token param in funStmt.params_)
            {
                byte paramConstant = parseVariable(param, "Expect parameter name.");
                defineVariable(paramConstant);
            }
            current.function.arity = funStmt.params_.Count;


            // Compile the function body.
            foreach (Stmt stmt in funStmt.body)
            {
                compile(stmt);
            }

            // Create the function object.
            ObjFunction function = endCompiler();

            emitBytes((byte)OpCode.OP_CLOSURE, makeConstant(Value.OBJ_VAL(function)));

            for (int i = 0; i < function.upvalueCount; i++)
            {
                emitByte((byte)(compiler.upvalues[i].isLocal ? 1 : 0));
                emitByte(compiler.upvalues[i].index);
            }

            /* clox: funDeclaration */
            if (type == FunctionType.TYPE_FUNCTION)
            {
                defineVariable(global);
            }
        }
        public static void markCompilerRoots()
        {
            Compiler_t compiler = current;

            while (compiler != null)
            {
                Memory.markObject((Obj)compiler.function);
                compiler = compiler.enclosing;
            }
        }
 private static int resolveLocal(ref Compiler_t compiler, Token name)
 {
     for (int i = compiler.localCount - 1; i >= 0; i--)
     {
         Local local = compiler.locals[i];
         if (identifiersEqual(name, local.name))
         {
             if (local.depth == -1)
             {
                 error("Cannot read local variable in its own initializer.");
             }
             return(i);
         }
     }
     return(-1);
 }
        private static ObjFunction endCompiler()
        {
            emitReturn();
            ObjFunction function = current.function;

#if DEBUG_PRINT_CODE
            if (!hadError)
            {
                Chunk_t _chunk = currentChunk();
                Debug.disassembleChunk(ref _chunk, function.name != null ? function.name.chars : "<script>\0".ToCharArray());
            }
#endif

            current = current.enclosing;
            return(function);
        }
        /***  VISITOR COMPILER  ***/

        public ObjFunction compile()
        {
            Compiler_t compiler = new Compiler_t();
            Token      t        = new Token {
                line = 1
            };

            initCompiler(ref compiler, FunctionType.TYPE_SCRIPT, t);

            captureToken(t);

            foreach (Stmt stmt in statements)
            {
                compile(stmt);
            }

            ObjFunction function = endCompiler();

            return(hadError ? null : function);
        }
        private static int addUpvalue(ref Compiler_t compiler, byte index, bool isLocal)
        {
            int upvalueCount = compiler.function.upvalueCount;

            for (int i = 0; i < upvalueCount; i++)
            {
                Upvalue upvalue = compiler.upvalues[i];
                if (upvalue.index == index && upvalue.isLocal == isLocal)
                {
                    return(i);
                }
            }

            if (upvalueCount == UINT8_COUNT)
            {
                error("Too many closure variables in function.");
                return(0);
            }

            compiler.upvalues[upvalueCount].isLocal = isLocal;
            compiler.upvalues[upvalueCount].index   = index;
            return(compiler.function.upvalueCount++);
        }
        private static int resolveUpvalue(ref Compiler_t compiler, Token name)
        {
            if (compiler.enclosing == null)
            {
                return(-1);
            }

            int local = resolveLocal(ref compiler.enclosing, name);

            if (local != -1)
            {
                compiler.enclosing.locals[local].isCaptured = true;
                return(addUpvalue(ref compiler, (byte)local, true));
            }

            int upvalue = resolveUpvalue(ref compiler.enclosing, name);

            if (upvalue != -1)
            {
                return(addUpvalue(ref compiler, (byte)upvalue, false));
            }

            return(-1);
        }