Ejemplo n.º 1
0
    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);
        }
    }
Ejemplo n.º 2
0
    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);
    }