mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
kinda working AST building?
This commit is contained in:
@@ -134,4 +134,44 @@ base64.btoa = function(inputString) {
|
||||
|
||||
return outStr;
|
||||
};
|
||||
Object.freeze(base64);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
BIN
guicursor.kra
LFS
Normal file
BIN
guicursor.kra
LFS
Normal file
Binary file not shown.
BIN
guicursor.png
Normal file
BIN
guicursor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 602 B |
@@ -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()
|
||||
|
||||
|
||||
@@ -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("\\", "\\\\")}')"
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user