// ( -- ) Starts compilation of a colon definition
        public void compileColon(ref GlobalSimpleProps gsp)
        {
            int           hereLoc        = gsp.Cfb.Address.Count;
            string        name           = gsp.ParsedInput[gsp.OuterPtr + 1];
            List <int>    paramList      = new List <int>();
            List <object> data           = new List <object>();
            string        help           = "TODO: ";
            string        rawWord        = "";
            int           searchVocabPtr = 0;
            bool          isFound        = false;
            string        compAction     = "";
            CompileInfo   compInfo;
            bool          isSemiPresent = false;
            int           colonIndex    = -1;
            Codefield     codefield;
            CreoleWord    cw;

            // I have to do a deep copy here
            GlobalSimpleProps gspComp = gspDeepCopy(gsp);

            // Elementary syntax check - if a colon isn't followed by a matching semicolon, you get an error message and the stacks and input are cleared.
            for (int i = 0; i < gsp.ParsedInput.Count; i++)
            {
                if (gsp.ParsedInput[i] == ":")
                {
                    colonIndex = i;
                }
                if (gsp.ParsedInput[i] == ";" && i > colonIndex)
                {
                    isSemiPresent = true;
                }
            }

            if (isSemiPresent == false)
            {
                Debug.Print("Error: colon def must have matching semicolon");
                gsp.cleanFields();
                return;
            }

            // Compilation is started when the IMMEDIATE vocabulary is pushed onto the vocabulary stack. No need for the usual Forth STATE flag.
            gsp.VocabStack.Add(gsp.BFC.ImmediateVocab);
            cw = new CreoleWord(name, gsp.Cfb.Modules.Interpreter.doColon, "Interpreter.doColon", gsp.CurrentVocab, "COMPINPF", help, hereLoc - 1, hereLoc, hereLoc - 1, hereLoc, paramList, data);
            // The smudge flag avoids accidental recursion. But it's easy enough to get around if you want to.
            string fqNameSmudged = name + "." + gsp.CurrentVocab + "." + gsp.BFC.SmudgeFlag;
            string fqName        = name + "." + gsp.CurrentVocab;
            string fqWord        = "";

            gsp.Cfb.Dict[fqNameSmudged] = cw;
            gsp.Cfb.Address.Add(gsp.Cfb.Dict[fqNameSmudged]);
            gsp.OuterPtr += 2;

            // Parameter field contents are set up in the PAD area. Each word is looked up one at a time in the dictionary, and its name, address, and
            // compilation action are placed in the CompileInfo triplet.
            while (gsp.OuterPtr < gsp.ParsedInput.Count && gsp.VocabStack[gsp.VocabStack.Count - 1] == gsp.BFC.ImmediateVocab && gsp.ParsedInput[gsp.OuterPtr] != ";")
            {
                rawWord        = gsp.ParsedInput[gsp.OuterPtr];
                searchVocabPtr = gsp.VocabStack.Count - 1;
                isFound        = false;
                while (searchVocabPtr >= 0)
                {
                    fqWord = rawWord.ToUpper() + "." + gsp.VocabStack[searchVocabPtr];
                    if (gsp.Cfb.Dict.ContainsKey(fqWord))
                    {
                        cw         = gsp.Cfb.Dict[fqWord];
                        compAction = cw.CompileActionField;
                        if (compAction != gsp.BFC.ExecZeroAction)
                        {
                            compInfo = new CompileInfo(fqWord, cw.IndexField, compAction);
                            gsp.PADarea.Add(compInfo);
                        }
                        else
                        {
                            // This is stuff where the outer ptr is manipulated such as comments
                            codefield = cw.CodeField;
                            codefield(ref gsp);
                        }
                        isFound = true;
                        break;
                    }
                    else
                    {
                        searchVocabPtr--;
                    }
                }

                // If no dictionary entry is found, it's tagged as a literal.
                if (isFound == false)
                {
                    compInfo = new CompileInfo(rawWord, rawWord, gsp.BFC.CompLitAction);
                    gsp.PADarea.Add(compInfo);
                }
                gsp.OuterPtr++;

                // 1. Builds the definition in the parameter field from the PAD area. Very simple; the address of each word appears before its associated
                //    compilation action. Most of the time, it will be COMPINPF, which will simply compile the word into the parameter field (it's actually
                //    , (comma) with a different name for readability purposes).
                //    Compiling words such as CompileIf will execute since that's the compilation action they're tagged with.
                // 2. Attaches it to the smudged definition.
                // 3. "Unsmudges" the new definition by copying it to its proper fully-qualified property and places it in the Address array.
                // 4. Deletes the smudged definition.
                // 5. Pops the IMMEDIATE vocabulary off the vocabulary stack and halts compilation

                int i = 0;
                gspComp.VocabStack = gsp.VocabStack;
                gspComp.Cfb.Address.Add(gsp.Cfb.Dict[fqNameSmudged]);

                // Putting the args and compilation actions together then executing them seems to cause a problem with compiling words.
                // Getting around this by putting one arg on the stack, one in the input area, then executing.

                while (i < gsp.PADarea.Count)
                {
                    compInfo = gsp.PADarea[i];
                    gspComp.DataStack.Add(compInfo.Address);
                    gspComp.InputArea = compInfo.CompileAction;
                    gspComp.Cfb.Modules.Interpreter.doParseInput(ref gspComp);
                    gspComp.Cfb.Modules.Interpreter.doOuter(ref gspComp);
                    i++;
                }

                gspComp.InputArea = ";";
                gspComp.Cfb.Modules.Interpreter.doParseInput(ref gspComp);
                gspComp.Cfb.Modules.Interpreter.doOuter(ref gspComp);

                cw = gspComp.Cfb.Address[hereLoc];
                gsp.Cfb.Dict[fqName] = cw;
                gsp.Cfb.Dict.Remove(fqNameSmudged);
            }
        }