// Used internally by doCreate - is not compiled into the dictionary public void doMyAddress(ref GlobalSimpleProps gsp) { CreoleWord cw = gsp.Cfb.Address[gsp.InnerPtr]; gsp.Scratch = cw.IndexField; gsp.Push(gsp.DataStack); }
// doLoop is treated as a special case of doPlusLoop // # ( -- ) Loops back to doDo until the start equals the end public void doLoop(ref GlobalSimpleProps gsp) { gsp.DataStack.Add(1); string runTimeAction = "dPlusLoop." + gsp.BFC.ImmediateVocab; CreoleWord cw = gsp.Cfb.Dict[runTimeAction]; Codefield codeField = cw.CodeField; codeField(ref gsp); }
// ( address -- ) Executes the word corresponding to the address on the stack public void doExecute(ref GlobalSimpleProps gsp) { int address = (int)gsp.Pop(gsp.DataStack); gsp.InnerPtr = address; CreoleWord cw = gsp.Cfb.Address[address]; cw.CodeField(ref gsp); }
// ( -- ) Flags a word as immediate (so it executes instead of compiling inside a colon definition) public void doImmediate(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string fqName = newCreoleWord.FQNameField; newCreoleWord.CompileActionField = "EXECUTE"; newCreoleWord.Vocabulary = "IMMEDIATE"; }
// ( beginLoc -- ) Compile-time code for UNTIL public void compileUntil(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; int beginLoc = (int)gsp.Pop(gsp.DataStack); string runTimeAction = "0BRANCH." + gsp.BFC.ImmediateVocab; int zeroBranchAddr = gsp.Cfb.Dict[runTimeAction].IndexField; newCreoleWord.ParamField.Add(zeroBranchAddr); newCreoleWord.ParamField.Add(beginLoc); }
public void buildPrimitive(string name, Codefield codefield, string codeFieldStr, string vocab, string compAction, string help) { List <int> paramList = new List <int>(); List <object> dataList = new List <object>(); string fqName = name + "." + vocab; CreoleWord cw = new CreoleWord(name, codefield, codeFieldStr, vocab, compAction, help, Address.Count() - 1, Address.Count(), Address.Count() - 1, Address.Count(), paramList, dataList); Dict[fqName] = cw; Address.Add(cw); }
public void buildHighLevel(ref GlobalSimpleProps gsp, string code, string help) { gsp.InputArea = code; gsp.PADarea.Clear(); Modules.Interpreter.doParseInput(ref gsp); Modules.Interpreter.doOuter(ref gsp); Address[Address.Count - 1].HelpField = help; CreoleWord newDef = Address[Address.Count - 1]; Dict[newDef.FQNameField] = newDef; }
// ( -- location ) Compile-time code for THEN public void compileThen(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; int branchPFLoc = (int)gsp.Pop(gsp.DataStack); string runTimeAction = "doThen." + gsp.BFC.ImmediateVocab; int thenAddr = gsp.Cfb.Dict[runTimeAction].IndexField; newCreoleWord.ParamField.Add(thenAddr); newCreoleWord.ParamField[branchPFLoc] = newCreoleWord.ParamField.Count - 1; }
// ( -- ) Jumps unconditionally to the parameter field location next to it and is compiled by ELSE public void doJump(ref GlobalSimpleProps gsp) { CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; List <int> paramField = currWord.ParamField; int jumpAddr = paramField[gsp.ParamFieldPtr + 1]; ReturnLoc rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); gsp.ParamFieldPtr = jumpAddr; rLoc.ParamFieldAddr = gsp.ParamFieldPtr; gsp.ReturnStack.Add(rLoc); }
// ( -- beginLoc ) Compile-time code for +LOOP public void compilePlusLoop(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string runTimeAction = "doPlusLoop." + gsp.BFC.ImmediateVocab; int loopAddr = gsp.Cfb.Dict[runTimeAction].IndexField; int doLoc = newCreoleWord.ParamField.Count - 1; newCreoleWord.ParamField.Add(loopAddr); newCreoleWord.ParamField.Add(doLoc); }
// ( inc -- ) Loops back to doDo until the start >= the end and increments with inc public void doPlusLoop(ref GlobalSimpleProps gsp) { int incVal = (int)gsp.Pop(gsp.DataStack); CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; List <int> paramField = currWord.ParamField; ReturnLoc rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); LoopInfo li = (LoopInfo)gsp.Pop(gsp.ReturnStack); int jumpAddr = paramField[rLoc.ParamFieldAddr]; int loopLimit = li.Limit; string loopLabel = li.Label; int currIndex = li.Index; if (incVal == 0) { loopLimit += incVal; } else { loopLimit -= incVal; } if ((incVal > 0) && (currIndex >= loopLimit) || (incVal < 0) && (currIndex <= loopLimit)) { gsp.ParamFieldPtr++; rLoc.ParamFieldAddr = gsp.ParamFieldPtr; gsp.LoopLabelPtr--; } else { gsp.ParamFieldPtr = jumpAddr; currIndex = currIndex + incVal; li.Index = currIndex; rLoc.ParamFieldAddr = gsp.ParamFieldPtr; gsp.ReturnStack.Add(li); } if (loopLabel == "I") { gsp.LoopCurrIndexes[0] = currIndex; } else if (loopLabel == "J") { gsp.LoopCurrIndexes[1] = currIndex;; } else if (loopLabel == "K") { gsp.LoopCurrIndexes[2] = currIndex;; } else { Debug.Print("Error: Invalid loop label"); } gsp.ReturnStack.Add(rLoc); }
// ( -- beginLoc ) Compile-time code for BEGIN public void compileBegin(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string runTimeAction = "doBegin." + gsp.BFC.ImmediateVocab; int beginAddr = gsp.Cfb.Dict[runTimeAction].IndexField; newCreoleWord.ParamField.Add(beginAddr); int beginLoc = newCreoleWord.ParamField.Count - 1; gsp.DataStack.Add(beginLoc); }
// ( n -- ) Compiles value off the TOS into the next parameter field cell public void doComma(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; gsp.Scratch = gsp.Pop(gsp.DataStack); int token = (int)gsp.Scratch; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; newCreoleWord.ParamField.Add(token); newCreoleWord.ParamFieldStart++; gsp.ParamFieldPtr = newCreoleWord.ParamField.Count + 1; }
// ( -- location ) Compile-time code for IF public void compileIf(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string runTimeAction = "0BRANCH." + gsp.BFC.ImmediateVocab; int zeroBranchAddr = gsp.Cfb.Dict[runTimeAction].IndexField; newCreoleWord.ParamField.Add(zeroBranchAddr); newCreoleWord.ParamField.Add(-1); gsp.ParamFieldPtr = newCreoleWord.ParamField.Count - 1; gsp.DataStack.Add(gsp.ParamFieldPtr); }
// ( -- lit ) Run-time code that pushes a literal onto the stack public void doLiteral(ref GlobalSimpleProps gsp) { CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; List <int> paramField = currWord.ParamField; List <object> dataField = currWord.DataField; ReturnLoc rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); int litDataFieldAddr = paramField[gsp.ParamFieldPtr]; gsp.DataStack.Add(dataField[litDataFieldAddr]); rLoc.ParamFieldAddr++; gsp.ParamFieldPtr = rLoc.ParamFieldAddr; gsp.ReturnStack.Add(rLoc); }
// ( -- ) Compiles doLit and a literal into the dictionary public void compileLiteral(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string runTimeAction = "doLiteral." + gsp.BFC.ImmediateVocab; int doLitAddr = gsp.Cfb.Dict[runTimeAction].IndexField; object litVal = gsp.Pop(gsp.DataStack); newCreoleWord.ParamField.Add(doLitAddr); newCreoleWord.DataField.Add(litVal); newCreoleWord.ParamField.Add(newCreoleWord.DataField.Count - 1); gsp.ParamFieldPtr = newCreoleWord.ParamField.Count - 1; }
// CREATE <name>. Adds a named entry into the dictionary public void doCreate(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: "; CreoleWord cw = new CreoleWord(name, gsp.Cfb.Modules.Compiler.doMyAddress, "Compiler.doMyAddress", gsp.CurrentVocab, "COMPINPF", help, hereLoc - 1, hereLoc, hereLoc - 1, hereLoc, paramList, data); string fqName = name + "." + gsp.CurrentVocab; gsp.Cfb.Dict[fqName] = cw; gsp.Cfb.Address.Add(gsp.Cfb.Dict[fqName]); gsp.OuterPtr += 2; }
// ( -- beginLoc ) Compile-time code for DO public void compileDo(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string doStartDoAddrLookup = "doStartDo." + gsp.BFC.ImmediateVocab; int doStartDoAddr = gsp.Cfb.Dict[doStartDoAddrLookup].IndexField; newCreoleWord.ParamField.Add(doStartDoAddr); string doAddrLookup = "doDo." + gsp.BFC.ImmediateVocab; int doAddr = gsp.Cfb.Dict[doAddrLookup].IndexField; newCreoleWord.ParamField.Add(doAddr); int doLoc = newCreoleWord.ParamField.Count - 1; gsp.DataStack.Add(doLoc); }
// Looks up the word based on its list index and executes whatever is in its code field public void doInner(ref GlobalSimpleProps gsp) { try { CreoleWord cw = gsp.Cfb.Address[gsp.InnerPtr]; Codefield cf = cw.CodeField; cf(ref gsp); } catch (System.IndexOutOfRangeException e) { Console.WriteLine("Error: Stack underflow " + e.ToString()); } catch (Exception e) { Console.WriteLine(e.ToString()); } }
// DOES> <list of runtime actions>. When defining word is created, copies code following it into the child definition public void compileDoes(ref GlobalSimpleProps gsp) { ReturnLoc rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); int parentRow = rLoc.DictAddr; int newRow = gsp.Cfb.Address.Count - 1; CreoleWord parentCreoleWord = gsp.Cfb.Address[parentRow]; CreoleWord childCreoleWord = gsp.Cfb.Address[newRow]; string fqNameField = childCreoleWord.FQNameField; int doesAddr = gsp.Cfb.Dict["DOES>.FORTH"].IndexField; int i = 0; childCreoleWord.CodeField = gsp.Cfb.Modules.Compiler.doDoes; childCreoleWord.CodeFieldStr = "doDoes"; // Find the location of the does address in the parent definition int startCopyPoint = -1; while (i < parentCreoleWord.ParamField.Count) { if (parentCreoleWord.ParamField[i] == doesAddr) { startCopyPoint = i + 1; break; } else { i++; } } // Need the definition's address do doDoes can get it easily either when it's being // called from the interpreter from from within a compiled definition childCreoleWord.ParamField.Add(newRow); childCreoleWord.ParamFieldStart = childCreoleWord.ParamField.Count; i = 0; while (startCopyPoint < parentCreoleWord.ParamField.Count) { childCreoleWord.ParamField.Add(parentCreoleWord.ParamField[startCopyPoint]); startCopyPoint++; i++; } rLoc.ParamFieldAddr += i; gsp.ReturnStack.Add(rLoc); gsp.Cfb.Address[newRow] = childCreoleWord; gsp.Cfb.Dict[fqNameField] = childCreoleWord; }
// Run time code for colon definitions public void doColon(ref GlobalSimpleProps gsp) { CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; List <int> paramField = currWord.ParamField; while (gsp.ParamFieldPtr < paramField.Count) { int addrInPF = paramField[gsp.ParamFieldPtr]; Codefield codeField = gsp.Cfb.Address[addrInPF].CodeField; gsp.ParamFieldPtr++; ReturnLoc rLoc = new ReturnLoc(gsp.InnerPtr, gsp.ParamFieldPtr); gsp.Scratch = rLoc; gsp.Push(gsp.ReturnStack); codeField(ref gsp); rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); gsp.InnerPtr = rLoc.DictAddr; gsp.ParamFieldPtr = rLoc.ParamFieldAddr; } }
// Searches vocabularies from top to bottom for work. If found, execute. If not, it gets pushed onto the stack. public void doOuter(ref GlobalSimpleProps gsp) { string rawWord = ""; string fqWord = ""; bool isFound = false; gsp.OuterPtr = 0; gsp.InnerPtr = 0; gsp.ParamFieldPtr = 0; while (gsp.OuterPtr < gsp.ParsedInput.Count) { if (gsp.Pause == false) { rawWord = gsp.ParsedInput[gsp.OuterPtr]; int searchVocabPtr = gsp.VocabStack.Count - 1; while (searchVocabPtr >= 0) { fqWord = rawWord.ToUpper() + "." + gsp.VocabStack[searchVocabPtr]; if (gsp.Cfb.Dict.ContainsKey(fqWord)) { CreoleWord cw = gsp.Cfb.Dict[fqWord]; gsp.InnerPtr = cw.IndexField; doInner(ref gsp); isFound = true; break; } else { searchVocabPtr -= 1; } } } if (isFound == false) { gsp.DataStack.Add(rawWord); } gsp.OuterPtr += 1; isFound = false; } gsp.PADarea.Clear(); }
// ( -- location ) Compile-time code for ELSE public void compileElse(ref GlobalSimpleProps gsp) { int newRow = gsp.Cfb.Address.Count - 1; CreoleWord newCreoleWord = gsp.Cfb.Address[newRow]; string jumpAddrLookup = "JUMP." + gsp.BFC.ImmediateVocab; int jumpAddr = gsp.Cfb.Dict[jumpAddrLookup].IndexField; string elseAddrLookup = "doElse." + gsp.BFC.ImmediateVocab; int elseAddr = gsp.Cfb.Dict[elseAddrLookup].IndexField; newCreoleWord.ParamField.Add(jumpAddr); newCreoleWord.ParamField.Add(-1); int jumpAddrPFLoc = newCreoleWord.ParamField.Count - 1; newCreoleWord.ParamField.Add(elseAddr); int zeroBrAddrPFLoc = (int)gsp.Pop(gsp.DataStack); newCreoleWord.ParamField[zeroBrAddrPFLoc] = newCreoleWord.ParamField.Count - 1; gsp.DataStack.Add(jumpAddrPFLoc); gsp.ParamFieldPtr = newCreoleWord.ParamField.Count - 1; }
// ( flag -- ) Run-time code for IF public void do0Branch(ref GlobalSimpleProps gsp) { CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; List <int> paramField = currWord.ParamField; ReturnLoc rLoc = (ReturnLoc)gsp.Pop(gsp.ReturnStack); int jumpAddr = paramField[rLoc.ParamFieldAddr]; int branchFlag = (int)gsp.Pop(gsp.DataStack); if (branchFlag == 0) { gsp.ParamFieldPtr = jumpAddr; } else { gsp.ParamFieldPtr++; } rLoc.ParamFieldAddr = gsp.ParamFieldPtr; gsp.ReturnStack.Add(rLoc); }
// Don't have to put it before compile-time definition, but you have to in Python so doing it here too. // ( address -- ) Run-time code for DOES>. public void doDoes(ref GlobalSimpleProps gsp) { CreoleWord currWord = gsp.Cfb.Address[gsp.InnerPtr]; string codeFieldStr = currWord.CodeFieldStr; // DOES> has to react differently depending on whether it's inside a colon definition or not if (codeFieldStr == "doDoes") { int execToken = currWord.IndexField; Debug.Print("Direct execution of doDoes"); gsp.ParamFieldPtr = currWord.ParamFieldStart; gsp.DataStack.Add(execToken); gsp.Cfb.Modules.Interpreter.doColon(ref gsp); } else { int execToken = currWord.ParamField[gsp.ParamFieldPtr - 1]; Debug.Print(gsp.ParamFieldPtr.ToString()); Debug.Print("Execution token is " + execToken.ToString()); gsp.DataStack.Add(execToken); gsp.Cfb.Modules.Compiler.doExecute(ref gsp); } }
// ( -- ) 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); } }