mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-14 08:24:04 +09:00
variable assignment
This commit is contained in:
@@ -29,6 +29,13 @@ lang.syntaxfehler = function(line, reason) {
|
|||||||
else
|
else
|
||||||
return "Syntax error in " + line + ": " + reason;
|
return "Syntax error in " + line + ": " + reason;
|
||||||
};
|
};
|
||||||
|
lang.illegalType = function(line) {
|
||||||
|
if (line === undefined)
|
||||||
|
return "Type mismatch";
|
||||||
|
else
|
||||||
|
return "Type mismatch in " + line;
|
||||||
|
};
|
||||||
|
Object.freeze(lang);
|
||||||
|
|
||||||
function getUsedMemSize() {
|
function getUsedMemSize() {
|
||||||
return cmdbufMemFootPrint; // + array's dimsize * 8 + variables' sizeof literal + functions' expression length
|
return cmdbufMemFootPrint; // + array's dimsize * 8 + variables' sizeof literal + functions' expression length
|
||||||
@@ -52,12 +59,12 @@ println("Terran BASIC 1.0 "+vmemsize+" bytes free");
|
|||||||
println(prompt);
|
println(prompt);
|
||||||
|
|
||||||
// variable object constructor
|
// variable object constructor
|
||||||
function BasicVar(linenum, literal, type) {
|
function BasicVar(literal, type) {
|
||||||
this.literal = literal;
|
this.literal = literal;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
// DEFUN (GW-BASIC equiv. of DEF FN) constructor
|
// DEFUN (GW-BASIC equiv. of DEF FN) constructor
|
||||||
function BasicFun(linenum, params, expression) {
|
function BasicFun(params, expression) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
@@ -98,7 +105,7 @@ function BasicAST() {
|
|||||||
var marker = ("literal" == this.type) ? "i" : ("operator" == this.type) ? "+" : "f";
|
var marker = ("literal" == this.type) ? "i" : ("operator" == this.type) ? "+" : "f";
|
||||||
sb += "| ".repeat(this.depth) + marker+" Line "+this.lnum+" ("+this.type+")\n";
|
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) + "leaves: "+(this.leaves.length)+"\n";
|
||||||
sb += "| ".repeat(this.depth+1) + "value: "+this.value+"\n";
|
sb += "| ".repeat(this.depth+1) + "value: "+this.value+" (type: "+typeof this.value+")\n";
|
||||||
for (var k = 0; k < this.leaves.length; k++) {
|
for (var k = 0; k < this.leaves.length; k++) {
|
||||||
sb += this.leaves[k].toString(); + "\n";
|
sb += this.leaves[k].toString(); + "\n";
|
||||||
}
|
}
|
||||||
@@ -106,16 +113,44 @@ function BasicAST() {
|
|||||||
return sb;
|
return sb;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function parseSigil(s) {
|
||||||
|
var rettype;
|
||||||
|
if (s.endsWith("$"))
|
||||||
|
rettype = "string";
|
||||||
|
else if (s.endsWith("%"))
|
||||||
|
rettype = "integer";
|
||||||
|
else if (s.endsWith("!") || s.endsWith("#"))
|
||||||
|
rettype = "float";
|
||||||
|
|
||||||
|
return {name:(rettype === undefined) ? s : s.substring(0, s.length - 1), type:rettype};
|
||||||
|
}
|
||||||
var basicInterpreterStatus = {};
|
var basicInterpreterStatus = {};
|
||||||
basicInterpreterStatus.gosubStack = [];
|
basicInterpreterStatus.gosubStack = [];
|
||||||
basicInterpreterStatus.variables = {};
|
basicInterpreterStatus.variables = {};
|
||||||
basicInterpreterStatus.defuns = {};
|
basicInterpreterStatus.defuns = {};
|
||||||
basicInterpreterStatus.builtin = {};
|
basicInterpreterStatus.builtin = {};
|
||||||
basicInterpreterStatus.builtin.PRINT = function(args) {
|
basicInterpreterStatus.builtin["="] = function(lnum, args) {
|
||||||
|
var parsed = parseSigil(args[0].value);
|
||||||
|
basicInterpreterStatus.variables[parsed.name] = new BasicVar(args[1].value, (parsed.type === undefined) ? "float" : parsed.type);
|
||||||
|
};
|
||||||
|
basicInterpreterStatus.builtin["+"] = function(lnum, args) {
|
||||||
|
// TODO read from variables
|
||||||
|
return args[0].value + args[1].value;
|
||||||
|
};
|
||||||
|
basicInterpreterStatus.builtin["-"] = function(lnum, args) {
|
||||||
|
// TODO read from variables
|
||||||
|
if (args[0].type != "number" || args[1].type != "number") throw lang.illegalType(lnum);
|
||||||
|
return args[0].value - args[1].value;
|
||||||
|
};
|
||||||
|
basicInterpreterStatus.builtin.PRINT = function(lnum, args) {
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
println();
|
println();
|
||||||
else
|
else
|
||||||
println(args.join("\t"));
|
println(args.map(function(it) {
|
||||||
|
return (it.type == "literal")
|
||||||
|
? basicInterpreterStatus.variables[parseSigil(it.value).name].literal
|
||||||
|
: args.value;
|
||||||
|
} ).join("\t"));
|
||||||
};
|
};
|
||||||
Object.freeze(basicInterpreterStatus.builtin);
|
Object.freeze(basicInterpreterStatus.builtin);
|
||||||
var basicFunctions = {};
|
var basicFunctions = {};
|
||||||
@@ -577,12 +612,12 @@ basicFunctions._parseTokens = function(lnum, tokens, states, recDepth) {
|
|||||||
/*
|
/*
|
||||||
for DEF*s, you might be able to go away with BINARY_OP, as the parsing tree would be:
|
for DEF*s, you might be able to go away with BINARY_OP, as the parsing tree would be:
|
||||||
|
|
||||||
+ Line 10 (operator)
|
f Line 10 (function)
|
||||||
| leaves: 2
|
| leaves: 1
|
||||||
| value: =
|
| value: defun
|
||||||
| f Line 10 (function)
|
| + Line 10 (operator)
|
||||||
| | leaves: 1
|
| | leaves: 2
|
||||||
| | value: DEFUN
|
| | value: =
|
||||||
| | f Line 10 (function)
|
| | f Line 10 (function)
|
||||||
| | | leaves: 1
|
| | | leaves: 1
|
||||||
| | | value: sinc
|
| | | value: sinc
|
||||||
@@ -591,22 +626,22 @@ for DEF*s, you might be able to go away with BINARY_OP, as the parsing tree woul
|
|||||||
| | | | value: X
|
| | | | value: X
|
||||||
| | | `-----------------
|
| | | `-----------------
|
||||||
| | `-----------------
|
| | `-----------------
|
||||||
| `-----------------
|
| | + Line 10 (operator)
|
||||||
| + Line 10 (operator)
|
| | | leaves: 2
|
||||||
| | leaves: 2
|
| | | value: /
|
||||||
| | value: /
|
| | | f Line 10 (function)
|
||||||
| | f Line 10 (function)
|
| | | | leaves: 1
|
||||||
| | | leaves: 1
|
| | | | value: sin
|
||||||
| | | value: sin
|
| | | | i Line 10 (literal)
|
||||||
|
| | | | | leaves: 0
|
||||||
|
| | | | | value: X
|
||||||
|
| | | | `-----------------
|
||||||
|
| | | `-----------------
|
||||||
| | | i Line 10 (literal)
|
| | | i Line 10 (literal)
|
||||||
| | | | leaves: 0
|
| | | | leaves: 0
|
||||||
| | | | value: X
|
| | | | value: X
|
||||||
| | | `-----------------
|
| | | `-----------------
|
||||||
| | `-----------------
|
| | `-----------------
|
||||||
| | i Line 10 (literal)
|
|
||||||
| | | leaves: 0
|
|
||||||
| | | value: X
|
|
||||||
| | `-----------------
|
|
||||||
| `-----------------
|
| `-----------------
|
||||||
`-----------------
|
`-----------------
|
||||||
|
|
||||||
@@ -724,10 +759,10 @@ for input "DEFUN sinc(x) = sin(x) / x"
|
|||||||
for (k = 0; k < tokens.length; k++) {
|
for (k = 0; k < tokens.length; k++) {
|
||||||
if (tokens[k] == "(" && states[k] != "quote") {
|
if (tokens[k] == "(" && states[k] != "quote") {
|
||||||
parenDepth += 1;
|
parenDepth += 1;
|
||||||
if (parenDepth == 1) parenStart = k;
|
if (parenStart == -1 && parenDepth == 1) parenStart = k;
|
||||||
}
|
}
|
||||||
else if (tokens[k] == ")" && states[k] != "quote") {
|
else if (tokens[k] == ")" && states[k] != "quote") {
|
||||||
if (parenDepth == 1) parenEnd = k;
|
if (parenEnd == -1 && parenDepth == 1) parenEnd = k;
|
||||||
parenDepth -= 1;
|
parenDepth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,6 +776,7 @@ for input "DEFUN sinc(x) = sin(x) / x"
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets);
|
if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets);
|
||||||
|
if (_debugSyntaxAnalysis) println("Paren position: "+parenStart+", "+parenEnd);
|
||||||
|
|
||||||
// if there is no paren or paren does NOT start index 1
|
// if there is no paren or paren does NOT start index 1
|
||||||
// e.g. negative three should NOT require to be written as "-(3)"
|
// e.g. negative three should NOT require to be written as "-(3)"
|
||||||
@@ -756,13 +792,14 @@ for input "DEFUN sinc(x) = sin(x) / x"
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the position of parens and separators
|
// get the position of parens and separators
|
||||||
|
parenStart = -1; parenEnd = -1; parenDepth = 0;
|
||||||
for (k = 0; k < tokens.length; k++) {
|
for (k = 0; k < tokens.length; k++) {
|
||||||
if (tokens[k] == "(" && states[k] != "quote") {
|
if (tokens[k] == "(" && states[k] != "quote") {
|
||||||
parenDepth += 1;
|
parenDepth += 1;
|
||||||
if (parenDepth == 1) parenStart = k;
|
if (parenStart == -1 && parenDepth == 1) parenStart = k;
|
||||||
}
|
}
|
||||||
else if (tokens[k] == ")" && states[k] != "quote") {
|
else if (tokens[k] == ")" && states[k] != "quote") {
|
||||||
if (parenDepth == 1) parenEnd = k;
|
if (parenEnd == -1 && parenDepth == 1) parenEnd = k;
|
||||||
parenDepth -= 1;
|
parenDepth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,6 +816,7 @@ for input "DEFUN sinc(x) = sin(x) / x"
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets);
|
if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets);
|
||||||
|
if (_debugSyntaxAnalysis) println("NEW Paren position: "+parenStart+", "+parenEnd);
|
||||||
|
|
||||||
// BINARY_OP/UNARY_OP
|
// BINARY_OP/UNARY_OP
|
||||||
if (topmostOp !== undefined) {
|
if (topmostOp !== undefined) {
|
||||||
@@ -872,32 +910,33 @@ for input "DEFUN sinc(x) = sin(x) / x"
|
|||||||
return treeHead;
|
return treeHead;
|
||||||
|
|
||||||
};
|
};
|
||||||
// @returns: line number for the next command, normally (lnum + 1); if GOTO or GOSUB was met, returns its line number
|
|
||||||
basicFunctions._executeSyntaxTree = function(lnum, syntaxTree) {
|
basicFunctions._executeSyntaxTree = function(lnum, syntaxTree) {
|
||||||
var _debugExec = true;
|
var _debugExec = true;
|
||||||
|
|
||||||
if (_debugExec) serial.println("@@ EXECUTE @@");
|
if (_debugExec) serial.println("@@ EXECUTE @@");
|
||||||
if (_debugExec) serial.println(syntaxTree.toString());
|
if (_debugExec) serial.println(syntaxTree.toString());
|
||||||
|
|
||||||
if (syntaxTree.type == "function") {
|
if (syntaxTree === undefined)
|
||||||
|
throw "InternalError: tree is undefined";
|
||||||
|
else if (syntaxTree.type == "function" || syntaxTree.type == "operator") {
|
||||||
var func = basicInterpreterStatus.builtin[syntaxTree.value.toUpperCase()];
|
var func = basicInterpreterStatus.builtin[syntaxTree.value.toUpperCase()];
|
||||||
var args = syntaxTree.leaves.map(function(it) { return basicFunctions._executeSyntaxTree(lnum, it); });
|
var args = syntaxTree.leaves.map(function(it) { return basicFunctions._executeSyntaxTree(lnum, it); });
|
||||||
if (_debugExec) serial.println("fn call args: "+args.join(" "));
|
if (_debugExec)
|
||||||
func(args);
|
serial.println("fn call args: "+(args.map(function(it) { return it.type+" "+it.value; })).join(", "));
|
||||||
|
|
||||||
|
return func(lnum, args);
|
||||||
}
|
}
|
||||||
else if (syntaxTree.type == "number") {
|
else if (syntaxTree.type == "number") {
|
||||||
return +(syntaxTree.value);
|
return {type:syntaxTree.type, value:+(syntaxTree.value)};
|
||||||
}
|
}
|
||||||
else if (syntaxTree.type == "string") {
|
else if (syntaxTree.type == "string" || syntaxTree.type == "literal") {
|
||||||
return syntaxTree.value;
|
return {type:syntaxTree.type, value:syntaxTree.value};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
serial.println("Parse error at line "+lnum);
|
serial.println("Parse error in "+lnum);
|
||||||
serial.println(syntaxTree.toString);
|
serial.println(syntaxTree.toString());
|
||||||
throw "Parse error";
|
throw "Parse error";
|
||||||
}
|
}
|
||||||
|
|
||||||
return lnum + 1;
|
|
||||||
};
|
};
|
||||||
// @returns: line number for the next command, normally (lnum + 1); if GOTO or GOSUB was met, returns its line number
|
// @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) {
|
basicFunctions._interpretLine = function(lnum, cmd) {
|
||||||
@@ -923,13 +962,13 @@ basicFunctions._interpretLine = function(lnum, cmd) {
|
|||||||
// PARSING (SYNTAX ANALYSIS)
|
// PARSING (SYNTAX ANALYSIS)
|
||||||
var syntaxTree = basicFunctions._parseTokens(lnum, tokens, states, 0);
|
var syntaxTree = basicFunctions._parseTokens(lnum, tokens, states, 0);
|
||||||
|
|
||||||
var nextLine = basicFunctions._executeSyntaxTree(lnum, syntaxTree);
|
basicFunctions._executeSyntaxTree(lnum, syntaxTree);
|
||||||
|
|
||||||
serial.println(syntaxTree.toString());
|
serial.println(syntaxTree.toString());
|
||||||
|
|
||||||
|
|
||||||
// EXECUTE
|
// EXECUTE
|
||||||
return nextLine;
|
return lnum + 1;
|
||||||
|
|
||||||
|
|
||||||
}; // end INTERPRETLINE
|
}; // end INTERPRETLINE
|
||||||
|
|||||||
Reference in New Issue
Block a user