public void renderBinary(List <token> tokens) { // backannotation for flow control constructs Stack <flowcontrol> fc = new Stack <flowcontrol>(); // backannotation for direct use of BRA, BZ, CALL HashSet <flowcontrol2> fcBranch = new HashSet <flowcontrol2>(); for (int ix = 0; ix < tokens.Count; ++ix) { token tt = tokens[ix]; string t = tt.body; if (builtinKeywords.Contains(t.ToUpper()) && !builtinKeywords.Contains(t)) { throw tt.buildException("lower-/mixed case variants of built-in keyword >>>" + t + "<<< is not permitted"); } if (t == "#EOF") { if (fc.Count > 0) { flowcontrol a = fc.Peek(); throw a.src.buildException("dangling '" + a.src.body + "' at end of file"); } continue; } if (t.StartsWith("#CODEADDR:")) { t = t.Substring(10); string[] t3 = t.Split('='); if (t3.Length != 1) { throw tt.buildException("#CODEADDR: invalid syntax, expecting CODEADDR:value"); } bool flag; uint val; try { flag = util.tryParseNum(t3[0], out val, enableFloat: false); } catch (Exception e) { throw tt.buildException(e); } if (!flag) { throw tt.buildException("#CODEADDR:value invalid syntax. Failed to parse value"); } this.codeMemPtr = val; continue; } if (t.StartsWith("#DATAADDR:")) { t = t.Substring(10); string[] t3 = t.Split('='); if (t3.Length != 1) { throw tt.buildException("#DATAADDR: invalid syntax, expecting DATAADDR:value"); } bool flag; uint val; try { flag = util.tryParseNum(t3[0], out val, enableFloat: false); } catch (Exception e) { throw tt.buildException(e); } if (!flag) { throw tt.buildException("#DATAADDR:value invalid syntax. Failed to parse value"); } this.dataMemPtr = val; continue; } if (t.StartsWith("BRA:")) { t = t.Substring(4); fcBranch.Add(new flowcontrol2() { t = flowcontrol2.t_e.BRA, addr = this.codeMemPtr, label = t, src = tt }); string m = "core.bra" + util.hex4(/*will be updated in the 2nd pass */ 0); this.writeCode(mnemonic: m, annotation: tt.body + " " + tt.getAnnotation() + " "); continue; } if (t.StartsWith("BZ:")) { t = t.Substring(3); fcBranch.Add(new flowcontrol2() { t = flowcontrol2.t_e.BZ, addr = this.codeMemPtr, label = t, src = tt }); string m = "core.bz" + util.hex4(/*will be updated in the 2nd pass */ 0); this.writeCode(mnemonic: m, annotation: tt.body + " " + tt.getAnnotation() + " "); continue; } // === BEGIN ... UNTIL === if (t == "BEGIN") { this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "__BEGIN__ loop label"); fc.Push(new flowcontrol() { t = flowcontrol.t_e.BEGIN, addr = this.codeMemPtr, src = tt }); continue; } if (t == "AGAIN") { flowcontrol fcBegin = (fc.Count > 0) && (fc.Peek().t == flowcontrol.t_e.BEGIN) ? fc.Pop() : null; if (fcBegin == null) { throw tt.buildException("'AGAIN' without matching 'BEGIN'"); } this.writeCode("core.bra" + util.hex4((UInt16)fcBegin.addr), "__AGAIN__" + tt.body + " " + tt.getAnnotation() + " "); continue; } if (t == "UNTIL") { flowcontrol fcBegin = (fc.Count > 0) && (fc.Peek().t == flowcontrol.t_e.BEGIN) ? fc.Pop() : null; if (fcBegin == null) { throw tt.buildException("'UNTIL' without matching 'BEGIN'"); } this.writeCode("core.bz" + util.hex4((UInt16)fcBegin.addr), "UNTIL loop if-not-exit" + tt.getAnnotation() + " "); continue; } if (t == "WHILE") { flowcontrol fcBegin = (fc.Count > 0) && (fc.Peek().t == flowcontrol.t_e.BEGIN) ? fc.Peek() : null; // note, does not pop if (fcBegin == null) { throw tt.buildException("'WHILE' without matching 'BEGIN'"); } fc.Push(new flowcontrol() { t = flowcontrol.t_e.WHILE, addr = this.codeMemPtr, src = tt }); this.writeCode(mnemonic: "core.bz" + util.hex4(0), annotation: "__WHILE__ loop test" + tt.body + " " + tt.getAnnotation() + " "); continue; } if (t == "REPEAT") { flowcontrol fcWhile = fc.Count > 0 ? fc.Pop() : null; flowcontrol fcBegin = fc.Count > 0 ? fc.Pop() : null; if ((fcWhile == null) || fcWhile.t != flowcontrol.t_e.WHILE) { throw tt.buildException("'REPEAT' without matching 'WHILE'"); } if ((fcBegin == null) || fcBegin.t != flowcontrol.t_e.BEGIN) { throw tt.buildException("'REPEAT' without matching 'BEGIN'"); } this.writeCode("core.bra" + util.hex4((UInt16)fcBegin.addr), "__REPEAT__" + tt.body + " " + tt.getAnnotation() + " "); this.codeToMem(fcWhile.addr, "core.bz" + util.hex4((UInt16)this.codeMemPtr), null); continue; } // === IF ... ELSE ... ENDIF === if (t == "IF") { fc.Push(new flowcontrol() { t = flowcontrol.t_e.IF, addr = this.codeMemPtr, src = tt }); string m = "core.bz" + util.hex4(/*will be updated when the destination address is reached*/ 0); this.writeCode(m, "'if-not' branch" + tt.getAnnotation() + " "); continue; } if (t == "ELSE") { flowcontrol fcIf = (fc.Count > 0) && (fc.Peek().t == flowcontrol.t_e.IF) ? fc.Peek() : null; // note, does not pop if (fcIf == null) { throw tt.buildException("'else' without matching 'if'"); } string m = "core.bra" + util.hex4(/*will be updated when the destination address is reached*/ 0); this.writeCode(m, "'skip-else' branch" + tt.getAnnotation() + " "); // note: the flowcontrol entry is one instruction behind the skip-else branch fc.Push(new flowcontrol() { t = flowcontrol.t_e.ELSE, addr = this.codeMemPtr, src = tt }); continue; } if (t == "ENDIF") { if (fc.Count < 1) { throw tt.buildException("dangling ENDIF (empty stack)"); } if (fc.Peek().t == flowcontrol.t_e.ELSE) { // === IF-THEN-ENDIF construct === flowcontrol fcElse = fc.Pop(); if (fc.Count < 1) { throw tt.buildException("dangling ENDIF (??? 1)"); } if (fc.Peek().t != flowcontrol.t_e.IF) { throw tt.buildException("dangling ENDIF (??? 2)"); } flowcontrol fcIf = fc.Pop(); // === modify the unconditional branch before "else" to jump to current address this.codeToMem(fcElse.addr - 1, "core.bra" + util.hex4((UInt16)this.codeMemPtr), /*already annotated*/ null); this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "else bypass target"); // === update the IF branch to point to the ELSE section === this.codeToMem(fcIf.addr, "core.bz" + util.hex4((UInt16)fcElse.addr), /*already annotated */ null); this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "if-not target"); } else if (fc.Peek().t == flowcontrol.t_e.IF) { // === IF-ENDIF construct === flowcontrol fcIf = fc.Pop(); this.codeToMem(fcIf.addr, "core.bz" + util.hex4((UInt16)this.codeMemPtr), /*already annotated */ null); this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "if-not target"); } else { throw tt.buildException("ENDIF expected opening IF or ELSE but got " + fc.Peek().t + " from " + fc.Peek().src.getAnnotation()); } continue; } // === DO ... LOOP === if (t == "DO") { this.writeCode("core.pushR", "'__DO__' push limit to R " + tt.getAnnotation() + " "); this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "DO loop comparison"); fc.Push(new flowcontrol() { t = flowcontrol.t_e.DO_STARTLABEL, addr = this.codeMemPtr, src = tt }); this.writeCode("core.dup", "'DO' duplicate counter " + tt.getAnnotation() + " "); this.writeCode("core.fetchR", "'DO' get copy of limit " + tt.getAnnotation() + " "); this.writeCode("core.lessThanSigned", "'DO' comparison " + tt.getAnnotation() + " "); fc.Push(new flowcontrol() { t = flowcontrol.t_e.DO_EXITBRANCH, addr = this.codeMemPtr, src = tt }); this.writeCode("core.bz" + util.hex4(/* to be updated later */ 0), "'DO' comparison " + tt.getAnnotation() + " "); continue; } if (t == "LOOP") { if (fc.Count < 2) { throw tt.buildException("dangling LOOP"); } flowcontrol exitBranch = fc.Pop(); if (exitBranch.t != flowcontrol.t_e.DO_EXITBRANCH) { throw tt.buildException("LOOP without DO (check for open flow control e.g. IF without ENDIF)"); } flowcontrol startLabel = fc.Pop(); if (startLabel.t != flowcontrol.t_e.DO_STARTLABEL) { throw tt.buildException("LOOP internal error"); } // === increase counter === this.writeCode("core.imm" + util.hex4((UInt16)1), "'DO' counter increment value" + tt.getAnnotation() + " "); this.writeCode("core.plus", "'DO' counter increment " + tt.getAnnotation() + " "); // === jump back to start of loop === this.writeCode("core.bra" + util.hex4((UInt16)startLabel.addr), "'DO' jump backwards to comparison " + tt.getAnnotation() + " "); // === update the exit branch target to point here === this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, "DO exit condition target"); string m = "core.bz" + util.hex4((UInt16)this.codeMemPtr); this.codeToMem(exitBranch.addr, m, /*already annotated */ null); this.writeCode("core.drop", "'DO' clean up loop variable"); // this.writeCode("core.popR", "'DO' clean up saved limit from rstack"); this.writeCode("core.drop", "'__DO__' last instruction: clean up saved limit from stack"); continue; } if (t.StartsWith("VAR:")) { string t2 = t.Substring(4); string[] t3 = t2.Split('='); if (t3.Length != 2) { throw tt.buildException("VAR: invalid syntax, expecting VAR:name:value"); } string n = t3[0]; bool flag; uint val; try { flag = util.tryParseNum(t3[1], out val, enableFloat: true); } catch (Exception e) { throw tt.buildException(e); } if (!flag) { throw tt.buildException("VAR: invalid syntax. Failed to parse value in VAR:name=value"); } string prevDef = this.nameExists(n); if (prevDef != null) { throw tt.buildException("VAR: name already exists as " + prevDef); } this.dataSymbols[n] = this.dataMemPtr; this.writeData(val, t); continue; } if (t.StartsWith(":")) { // === code label === this.lstFileWriter.annotateCodeLabel(this.codeMemPtr, t); t = t.Substring(1); // drop leading colon from name if (this.nameExists(t) != null) { throw tt.buildException("label has already been defined as " + this.nameExists(t)); } this.codeSymbols[t] = this.codeMemPtr; continue; } // === single-quote (address of) === if (t.StartsWith("'")) { t = t.Substring(1); // === load address of variable === if (this.dataSymbols.ContainsKey(t)) { string mnem = "core.imm" + util.hex4((UInt16)this.dataSymbols[t]); this.writeCode(mnem, "data addr: '" + t + "'" + tt.getAnnotation()); continue; } // === load address of code label === if (this.codeSymbols.ContainsKey(t)) { string mnem = "core.imm" + util.hex4((UInt16)this.codeSymbols[t]); this.writeCode(mnem, "code addr: '" + t + "'" + tt.getAnnotation()); continue; } throw tt.buildException(t + " address: label is neither code nor data"); } // === Mnemonic to numerical opcode === if (this.opcodes.ContainsKey(t)) { this.writeCode(t, tt.getAnnotation()); continue; } if (t.StartsWith("CALL:")) { t = t.Substring(5); } fcBranch.Add(new flowcontrol2() { t = flowcontrol2.t_e.CALL, addr = this.codeMemPtr, label = t, src = tt }); string m5 = "core.call" + util.hex4(/*will be updated in the 2nd pass */ 0); this.writeCode(mnemonic: m5, annotation: "call '" + t + "' " + tt.getAnnotation() + " "); } // === update (possibly) backwards branches) foreach (flowcontrol2 f in fcBranch) { string m = null; if (!this.codeSymbols.ContainsKey(f.label)) { throw f.src.buildException("label '" + f.label + "' not found (must be defined using : )"); } string aHex = util.hex4((UInt16)this.codeSymbols[f.label]); switch (f.t) { case flowcontrol2.t_e.BRA: m = "core.bra" + aHex; break; case flowcontrol2.t_e.BZ: m = "core.bz" + aHex; break; case flowcontrol2.t_e.CALL: m = "core.call" + aHex; break; } this.codeToMem(f.addr, m, /*already annotated */ null); } }
List <token> pass_extractMacros(List <token> tokensIn) { List <token> tokensOut = new List <token>(); string activeMacroName = null; token activeMacroToken = null; foreach (token t in tokensIn) { if (t.body.StartsWith("::")) { if (activeMacroName != null) { throw t.buildException("macro recursion is not allowed"); } activeMacroToken = t; activeMacroName = t.body.Substring(2); if (activeMacroName.Length == 0) { throw t.buildException("missing macro name"); } bool flag; try { UInt32 dummy; flag = util.tryParseNum(activeMacroName, out dummy, enableFloat: true); } catch (Exception e) { throw t.buildException(e); } if (flag) { throw t.buildException("invalid macro name (must not be a number)"); } string prevDef = this.nameExists(activeMacroName); if (prevDef != null) { throw t.buildException(":: name already exists as " + prevDef); } this.macros[activeMacroName] = new List <token>(); } else { if (activeMacroName != null) { // do not add "return" to macros (execution continues past the macro end) if (t.body != ";") { this.macros[activeMacroName].Add(t); } } else { tokensOut.Add(t); } if (t.body == ";") { if (activeMacroName != null) { activeMacroName = null; activeMacroToken = null; } } } } if (activeMacroName != null) { throw activeMacroToken.buildException("open macro at end of input: >>>" + activeMacroName + "<<<"); } return(tokensOut); }