From 2eb6a4a99e1e0d50dc3d9940298d13cc222e7449 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 7 Jun 2020 09:17:01 +0900 Subject: [PATCH] kinda working AST building? --- assets/JS_INIT.js | 42 +++++++- assets/tbas/basic.js | 109 ++++++++++++++++++--- assets/tvdos/command.js | 2 +- guicursor.kra | 3 + guicursor.png | Bin 0 -> 602 bytes src/net/torvald/tsvm/VMGUI.kt | 4 +- src/net/torvald/tsvm/VMRunnerFactory.kt | 2 +- src/net/torvald/tsvm/peripheral/IOSpace.kt | 4 +- terranmon.txt | 2 +- 9 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 guicursor.kra create mode 100644 guicursor.png diff --git a/assets/JS_INIT.js b/assets/JS_INIT.js index 7fe5a4b..ebd5a7c 100644 --- a/assets/JS_INIT.js +++ b/assets/JS_INIT.js @@ -134,4 +134,44 @@ base64.btoa = function(inputString) { return outStr; }; -Object.freeze(base64); \ No newline at end of file +Object.freeze(base64); +// Polyfilling some functions from ECMAScript6+ +if (!String.prototype.repeat) { + String.prototype.repeat = function(count) { + 'use strict'; + if (this == null) + throw new TypeError('can\'t convert ' + this + ' to object'); + + var str = '' + this; + // To convert string to integer. + count = +count; + // Check NaN + if (count != count) + count = 0; + + if (count < 0) + throw new RangeError('repeat count must be non-negative'); + + if (count == Infinity) + throw new RangeError('repeat count must be less than infinity'); + + count = Math.floor(count); + if (str.length == 0 || count == 0) + return ''; + + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) + throw new RangeError('repeat count must not overflow maximum string size'); + + var maxCount = str.length * count; + count = Math.floor(Math.log(count) / Math.log(2)); + while (count) { + str += str; + count--; + } + str += str.substring(0, maxCount - str.length); + return str; + } +} diff --git a/assets/tbas/basic.js b/assets/tbas/basic.js index 764b50e..834535f 100644 --- a/assets/tbas/basic.js +++ b/assets/tbas/basic.js @@ -80,10 +80,24 @@ function BasicArr() { // Abstract Syntax Tree // creates empty tree node function BasicAST() { + this.lnum = 0; this.depth = 0; this.leaves = []; this.value = undefined; this.type = "null"; // literal, operator, variable, function + + this.toString = function() { + var sb = ""; + var marker = ("literal" == this.type) ? ">" : ("operator" == this.type) ? "=" : "#"; + sb += "| ".repeat(this.depth) + marker+" Line "+this.lnum+" ("+this.type+")\n"; + sb += "| ".repeat(this.depth+1) + "leaves: "+(this.leaves.length)+"\n"; + sb += "| ".repeat(this.depth+1) + "value: "+this.value+"\n"; + for (var k = 0; k < this.leaves.length; k++) { + sb += this.leaves[k].toString(); + "\n"; + } + sb += "| ".repeat(this.depth+1) + "----------------\n"; + return sb; + }; } basicInterpreterStatus.gosubStack = []; basicInterpreterStatus.variables = {}; @@ -116,7 +130,10 @@ basicFunctions._isSeparator = function(code) { basicFunctions._isOperator = function(code) { return (code == 0x21 || code == 0x23 || code == 0x25 || code == 0x2A || code == 0x2B || code == 0x2D || code == 0x2E || code == 0x2F || (code >= 0x3C && code <= 0x3E) || code == 0x5E || code == 0x7C); }; -basicFunctions._parseTokens = function(tokens, states) { +basicFunctions._parseTokens = function(lnum, tokens, states, recDepth) { + // DO NOT PERFORM SEMANTIC ANALYSIS HERE + // at this point you can't (and shouldn't) distinguish whether or not defuns/variables are previously declared + // a line has one of these forms: // VARIABLE = LITERAL // VARIABLE = FUNCTION ARGUMENTS @@ -129,24 +146,69 @@ basicFunctions._parseTokens = function(tokens, states) { // "IF" EXPRESSION "GOTO" ARGUMENT // "IF" EXPRESSION "GOTO" ARGUMENT "ELSE" EXPRESSION // "WHILE" EXPRESSION + // additionally, sub-line also has one of these: + // LITERAL (leaf node) + // VARIABLE (leaf node) + // {VARIABLE, LITERAL} COMPARISON_OP {VARIABLE, LITERAL} + + println("Parser Ln "+lnum+", Rec "+recDepth+", Tkn: "+tokens.join("/")); + + if (tokens.length != states.length) throw "InternalError: size of tokens and states does not match (line: "+lnum+", recursion depth: "+recDepth+")"; + if (tokens.length == 0) throw "InternalError: empty tokens (line: "+lnum+", recursion depth: "+recDepth+")"; var i, j, k; var headWord = tokens[0].toLowerCase(); - var treeHead = BasicAST(); + var treeHead = new BasicAST(); + treeHead.depth = recDepth; + treeHead.lnum = lnum; - // is this a builtin function? - if (typeof basicInterpreterStatus.builtin[headWord] != "undefined") { + // IF statement + if ("IF" == tokens[0].toUpperCase()) { + throw "TODO"; + } + // LEAF: is this a literal? + else if (recDepth > 0 && ("quote" == states[0] || "number" == states[0])) { + treeHead.value = tokens[0]; + treeHead.type = "literal"; + } + // is this a function? + else { treeHead.value = headWord; treeHead.type = "function"; // find and mark position of separators + // properly deal with the nested function calls var separators = []; - for (i = 1; i < tokens.length; i++) if (states[i] == "sep") separators.push(i); + var sepInit = (tokens.length > 1 && "paren" == states[1]) ? 2 : 1; + var parenCount = 1; + for (k = sepInit; k < tokens.length; k++) { + if ("(" == tokens[k]) parenCount += 1; + else if (")" == tokens[k]) parenCount -= 1; + + if (parenCount == 1 && states[k] == "sep") separators.push(k); + + if (parenCount < 0) throw lang.syntaxfehler(lnum); + } + separators.push(tokens.length - (sepInit == 1 ? 0 : 1)); + + println("sep ("+sepInit+")["+separators.join(",")+"]"); // parse and populate leaves - // TODO + var leaves = []; + if (sepInit < separators[0]) { + for (k = 0; k < separators.length; k++) { + var subtkn = tokens.slice((k == 0) ? sepInit : separators[k - 1] + 1, separators[k]); + var substa = states.slice((k == 0) ? sepInit : separators[k - 1] + 1, separators[k]); + + leaves.push(basicFunctions._parseTokens(lnum, subtkn, substa, recDepth + 1)); + } + } + treeHead.leaves = leaves; } + + return treeHead; + }; // @returns: line number for the next command, normally (lnum + 1); if GOTO or GOSUB was met, returns its line number basicFunctions._interpretLine = function(lnum, cmd) { @@ -166,7 +228,7 @@ basicFunctions._interpretLine = function(lnum, cmd) { if (_debugprintStateTransition) print("Char: "+char+"("+charCode+"), state: "+mode); - if (mode == "literal") { + if ("literal" == mode) { if (0x22 == charCode) { // " tokens.push(sb); sb = ""; modes.push(mode); mode = "quote"; @@ -225,13 +287,32 @@ basicFunctions._interpretLine = function(lnum, cmd) { } else if ("quote_end" == mode) { if (basicFunctions._isNumber(charCode)) { + sb = "" + char; mode = "number"; } + else if (" " == char) { + sb = ""; + mode = "limbo"; + } else if (basicFunctions._isOperator(charCode)) { + sb = "" + char; mode = "operator"; } + else if (0x22 == charCode) { + sb = "" + char; + mode = "quote"; + } + else if (basicFunctions._isParen(charCode)) { + sb = "" + char; + mode = "paren"; + } + else if (basicFunctions._isSeparator(charCode)) { + sb = "" + char; + mode = "sep"; + } else { - mode = "limbo"; + sb = "" + char; + mode = "literal"; } } else if ("number" == mode) { @@ -319,7 +400,7 @@ basicFunctions._interpretLine = function(lnum, cmd) { } else if ("paren" == mode) { if (char == " ") { - tokens.push(sb); sb = "" + char; modes.push(mode); + tokens.push(sb); sb = ""; modes.push(mode); mode = "limbo"; } else if (basicFunctions._isNumber(charCode)) { @@ -331,7 +412,7 @@ basicFunctions._interpretLine = function(lnum, cmd) { mode = "operator" } else if (0x22 == charCode) { - tokens.push(sb); sb = "" + char; modes.push(mode); + tokens.push(sb); sb = ""; modes.push(mode); mode = "quote" } else if (basicFunctions._isParen(charCode)) { @@ -349,7 +430,7 @@ basicFunctions._interpretLine = function(lnum, cmd) { } else if ("sep" == mode) { if (char == " ") { - tokens.push(sb); sb = "" + char; modes.push(mode); + tokens.push(sb); sb = ""; modes.push(mode); mode = "limbo"; } else if (basicFunctions._isNumber(charCode)) { @@ -361,7 +442,7 @@ basicFunctions._interpretLine = function(lnum, cmd) { mode = "operator" } else if (0x22 == charCode) { - tokens.push(sb); sb = "" + char; modes.push(mode); + tokens.push(sb); sb = ""; modes.push(mode); mode = "quote" } else if (basicFunctions._isParen(charCode)) { @@ -391,7 +472,9 @@ basicFunctions._interpretLine = function(lnum, cmd) { // END TOKENISE // PARSING (SYNTAX ANALYSIS) - var syntaxTree = basicFunctions._parseTokens(tokens, modes); + var syntaxTree = basicFunctions._parseTokens(lnum, tokens, modes, 0); + println("k bye"); + serial.println(syntaxTree.toString()); // END PARSING diff --git a/assets/tvdos/command.js b/assets/tvdos/command.js index 7918963..95a58d2 100644 --- a/assets/tvdos/command.js +++ b/assets/tvdos/command.js @@ -7,7 +7,7 @@ var shell_pwd = [""]; var welcome_text = "TSVM Disk Operating System, version " + DOS_VERSION; function get_prompt_text() { - return CURRENT_DRIVE + ":\\\\" + shell_pwd.join("\\\\") + PROMPT_TEXT; + return CURRENT_DRIVE + ":\\" + shell_pwd.join("\\") + PROMPT_TEXT; } function greet() { diff --git a/guicursor.kra b/guicursor.kra new file mode 100644 index 0000000..1d35fb9 --- /dev/null +++ b/guicursor.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0df1e8c678058e829058c3244e535363a3e552f43918d94df13ecb726b74050 +size 63938 diff --git a/guicursor.png b/guicursor.png new file mode 100644 index 0000000000000000000000000000000000000000..ae0e6648d736f01d1704cc16e7e0563679255b0d GIT binary patch literal 602 zcmV-g0;T@6NB z`YxPrLHC2LIogjDlo`lm;Pn*R7ohV7T9+zW{jPIz52+1#qHEaOhp6oDSpEk8i)acc z$;K1_000SaNLh0L01sgR01sgSs6VG^0001yNklhzt o^fm}FKeF4X0?1bpb88Pj0XrCv?#wLOrT_o{07*qoM6N<$f_D}W1ONa4 literal 0 HcmV?d00001 diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index ac0f48a..26884d3 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -60,8 +60,8 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() fr1.close() //val fr = FileReader("./assets/tvdos/command.js") - //val fr = FileReader("./assets/tbas/basic.js") - val fr = FileReader("./assets/jscon.js") + val fr = FileReader("./assets/tbas/basic.js") + //val fr = FileReader("./assets/jscon.js") val prg = fr.readText() fr.close() diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt index ff39cd0..4cf5012 100644 --- a/src/net/torvald/tsvm/VMRunnerFactory.kt +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -76,4 +76,4 @@ object VMRunnerFactory { } fun toSingleLine(code: String) = code.replace(Regex("//[^\\n]*"), "").replace('\n', ' ') -fun sanitiseJS(code: String) = "eval('${toSingleLine(code)}')" +fun sanitiseJS(code: String) = "eval('${toSingleLine(code).replace("\\", "\\\\")}')" diff --git a/src/net/torvald/tsvm/peripheral/IOSpace.kt b/src/net/torvald/tsvm/peripheral/IOSpace.kt index 85f48cc..d6c2eb3 100644 --- a/src/net/torvald/tsvm/peripheral/IOSpace.kt +++ b/src/net/torvald/tsvm/peripheral/IOSpace.kt @@ -223,7 +223,7 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { override fun keyTyped(p0: Char): Boolean { if (keyboardInputRequested && !ttySpecialKeyLatched && p0.toInt() > 0) { - println("[IO] Key typed: ${p0.toInt()}") + //println("[IO] Key typed: ${p0.toInt()}") keyboardBuffer.appendHead(p0.toByte()) return true } @@ -254,7 +254,7 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { private var specialKey = listOf(19,20,21,22).toSortedSet() override fun keyDown(p0: Int): Boolean { if (keyboardInputRequested && !ttySpecialKeyLatched && p0 in specialKey) { - println("[IO] Key down: $p0") + //println("[IO] Key down: $p0") keyboardBuffer.appendHead(p0.toByte()) } diff --git a/terranmon.txt b/terranmon.txt index f69cdcc..916c757 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -126,7 +126,7 @@ From the start of the memory space: 250880 bytes Framebuffer 3 bytes - Initial background colour RGB, of which only the lower 4 bits per each channel are used + Initial background (and the border) colour RGB, of which only the lower 4 bits per each channel are used 1 byte command (writing to this memory address changes the status) 1: reset palette to default