mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-09 06:34:04 +09:00
basic:generator model for 'FOR'
This commit is contained in:
133
assets/basic.js
133
assets/basic.js
@@ -11,7 +11,7 @@ Operators
|
|||||||
Test Programs:
|
Test Programs:
|
||||||
|
|
||||||
1 REM Random Maze
|
1 REM Random Maze
|
||||||
10 PRINT(CHR$(47+ROUND(RND(1))*45);)
|
10 PRINT(CHR(47+ROUND(RND(1))*45);)
|
||||||
20 GOTO 10
|
20 GOTO 10
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@@ -203,7 +203,7 @@ let BasicAST = function() {
|
|||||||
return sb;
|
return sb;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let literalTypes = ["string", "num", "bool", "array"];
|
let literalTypes = ["string", "num", "bool", "array", "generator"];
|
||||||
/*
|
/*
|
||||||
@param variable SyntaxTreeReturnObj, of which the 'troType' is defined in BasicAST.
|
@param variable SyntaxTreeReturnObj, of which the 'troType' is defined in BasicAST.
|
||||||
@return a value, if the input type if string or number, its literal value will be returned. Otherwise will search the
|
@return a value, if the input type if string or number, its literal value will be returned. Otherwise will search the
|
||||||
@@ -302,6 +302,46 @@ let initBvars = function() {
|
|||||||
"EULER": new BasicVar(Math.E, "num")
|
"EULER": new BasicVar(Math.E, "num")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let ForGen = function(s,e,t) {
|
||||||
|
this.start = s;
|
||||||
|
this.end = e;
|
||||||
|
this.step = t || 1;
|
||||||
|
|
||||||
|
this.current = this.start;
|
||||||
|
this.stepsgn = (this.step > 0) ? 1 : -1;
|
||||||
|
|
||||||
|
this.hasNext = function() {
|
||||||
|
return this.current*this.stepsgn + this.step*this.stepsgn <= (this.end + this.step)*this.stepsgn;
|
||||||
|
// 1 to 10 step 1
|
||||||
|
// 1 + 1 <= 11 -> true
|
||||||
|
// 10 + 1 <= 11 -> true
|
||||||
|
// 11 + 1 <= 11 -> false
|
||||||
|
|
||||||
|
// 10 to 1 step -1
|
||||||
|
// -10 + 1 <= 0 -> true
|
||||||
|
// -1 + 1 <= 0 -> true
|
||||||
|
// 0 + 1 <= 0 -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns undefined if there is no next()
|
||||||
|
this.getNext = function() {
|
||||||
|
this.current += this.step;
|
||||||
|
return this.hasNext() ? this.current : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toArray = function() {
|
||||||
|
let a = [];
|
||||||
|
let cur = this.start;
|
||||||
|
while (cur*this.stepsgn + this.step*this.stepsgn <= (this.end + this.step)*this.stepsgn) {
|
||||||
|
a.push(cur);
|
||||||
|
cur += this.step;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
this.toString = function() {
|
||||||
|
return `Generator: ${this.start} to ${this.end}`+((this.step !== 1) ? ` step ${this.step}` : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
let bStatus = {};
|
let bStatus = {};
|
||||||
bStatus.gosubStack = [];
|
bStatus.gosubStack = [];
|
||||||
bStatus.forLnums = {}; // key: forVar, value: linenum
|
bStatus.forLnums = {}; // key: forVar, value: linenum
|
||||||
@@ -348,9 +388,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
var rh = resolve(args[1]);
|
var rh = resolve(args[1]);
|
||||||
if (rh === undefined) throw lang.refError(lnum, "RH:"+args[1].troValue);
|
if (rh === undefined) throw lang.refError(lnum, "RH:"+args[1].troValue);
|
||||||
|
|
||||||
if (troValue.arrObj !== undefined) {
|
if (troValue.arrObj !== undefined) { // assign to existing array
|
||||||
if (isNaN(rh) && !Array.isArray(rh)) throw lang.illegalType(lnum, rh);
|
if (isNaN(rh) && !Array.isArray(rh)) throw lang.illegalType(lnum, rh);
|
||||||
|
|
||||||
troValue.arrObj[troValue.arrIndex] = rh|0;
|
troValue.arrObj[troValue.arrIndex] = rh|0;
|
||||||
return {asgnVarName: troValue.arrName, asgnValue: rh|0};
|
return {asgnVarName: troValue.arrName, asgnValue: rh|0};
|
||||||
}
|
}
|
||||||
@@ -361,7 +400,23 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
bStatus.vars[varname] = new BasicVar(rh, type);
|
bStatus.vars[varname] = new BasicVar(rh, type);
|
||||||
return {asgnVarName: varname, asgnValue: rh};
|
return {asgnVarName: varname, asgnValue: rh};
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"IN" : function(lnum, args) { // almost same as =, but don't actually make new variable. Used by FOR statement
|
||||||
|
if (args.length != 2) throw lang.syntaxfehler(lnum, args.length+lang.aG);
|
||||||
|
var troValue = args[0].troValue;
|
||||||
|
|
||||||
|
var rh = resolve(args[1]);
|
||||||
|
if (rh === undefined) throw lang.refError(lnum, "RH:"+args[1].troValue);
|
||||||
|
|
||||||
|
if (troValue.arrObj !== undefined) {
|
||||||
|
throw lang.syntaxfehler(lnum);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var varname = troValue.toUpperCase();
|
||||||
|
var type = JStoBASICtype(rh);
|
||||||
|
if (bStatus.consts[varname]) throw lang.asgnOnConst(lnum, varname);
|
||||||
|
return {asgnVarName: varname, asgnValue: rh};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"==" : function(lnum, args) {
|
"==" : function(lnum, args) {
|
||||||
return twoArg(lnum, args, (lh,rh) => lh == rh);
|
return twoArg(lnum, args, (lh,rh) => lh == rh);
|
||||||
@@ -457,7 +512,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return twoArgNum(lnum, args, (lh,rh) => Math.pow(lh, rh));
|
return twoArgNum(lnum, args, (lh,rh) => Math.pow(lh, rh));
|
||||||
},
|
},
|
||||||
"TO" : function(lnum, args) {
|
"TO" : function(lnum, args) {
|
||||||
return twoArgNum(lnum, args, (from, to) => {
|
/*return twoArgNum(lnum, args, (from, to) => {
|
||||||
var a = [];
|
var a = [];
|
||||||
if (from <= to) {
|
if (from <= to) {
|
||||||
for (var k = from; k <= to; k++) {
|
for (var k = from; k <= to; k++) {
|
||||||
@@ -471,10 +526,11 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}
|
}
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
});
|
});*/
|
||||||
|
return twoArgNum(lnum, args, (from, to) => new ForGen(from, to, 1));
|
||||||
},
|
},
|
||||||
"STEP" : function(lnum, args) {
|
"STEP" : function(lnum, args) {
|
||||||
if (args.length != 2) throw lang.syntaxfehler(lnum, args.length+lang.aG);
|
/*if (args.length != 2) throw lang.syntaxfehler(lnum, args.length+lang.aG);
|
||||||
var rsvArg0 = resolve(args[0]);
|
var rsvArg0 = resolve(args[0]);
|
||||||
if (rsvArg0 === undefined) throw lang.refError(lnum, rsvArg0);
|
if (rsvArg0 === undefined) throw lang.refError(lnum, rsvArg0);
|
||||||
if (!Array.isArray(rsvArg0)) throw lang.illegalType(lnum, rsvArg0);
|
if (!Array.isArray(rsvArg0)) throw lang.illegalType(lnum, rsvArg0);
|
||||||
@@ -486,7 +542,11 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (stepcnt == 0) a.push(v);
|
if (stepcnt == 0) a.push(v);
|
||||||
stepcnt = (stepcnt + 1) % rsvArg1;
|
stepcnt = (stepcnt + 1) % rsvArg1;
|
||||||
});
|
});
|
||||||
return a;
|
return a;*/
|
||||||
|
return twoArg(lnum, args, (gen, step) => {
|
||||||
|
if (!(gen instanceof ForGen)) throw lang.illegalType(lnum, gen);
|
||||||
|
return new ForGen(gen.start, gen.end, step);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
"DIM" : function(lnum, args) {
|
"DIM" : function(lnum, args) {
|
||||||
return varArgNum(lnum, args, (dims) => {
|
return varArgNum(lnum, args, (dims) => {
|
||||||
@@ -505,14 +565,19 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
// parse separators.
|
// parse separators.
|
||||||
// ; - concat
|
// ; - concat
|
||||||
// , - tab
|
// , - tab
|
||||||
// numbers always surrounded by 1 whitespace
|
|
||||||
if (llll >= 1) {
|
if (llll >= 1) {
|
||||||
if (seps[llll - 1] == ",") print("\t");
|
if (seps[llll - 1] == ",") print("\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
var rsvArg = resolve(args[llll]);
|
var rsvArg = resolve(args[llll]);
|
||||||
if (rsvArg === undefined && args[llll] !== undefined && args[llll].troType != "null") throw lang.refError(lnum, args[llll].troValue);
|
if (rsvArg === undefined && args[llll] !== undefined && args[llll].troType != "null") throw lang.refError(lnum, args[llll].troValue);
|
||||||
print((rsvArg === undefined) ? "" : rsvArg);
|
|
||||||
|
if (rsvArg === undefined)
|
||||||
|
print("");
|
||||||
|
else if (rsvArg.toString !== undefined)
|
||||||
|
print(rsvArg.toString());
|
||||||
|
else
|
||||||
|
print(rsvArg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +683,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (args.length != 1) throw lang.syntaxfehler(lnum, args.length+lang.aG);
|
if (args.length != 1) throw lang.syntaxfehler(lnum, args.length+lang.aG);
|
||||||
return resolve(args[0]);
|
return resolve(args[0]);
|
||||||
},
|
},
|
||||||
"FOR" : function(lnum, args) {
|
"FOREACH" : function(lnum, args) { // list comprehension model
|
||||||
var asgnObj = resolve(args[0]);
|
var asgnObj = resolve(args[0]);
|
||||||
// type check
|
// type check
|
||||||
if (asgnObj === undefined) throw lang.syntaxfehler(lnum);
|
if (asgnObj === undefined) throw lang.syntaxfehler(lnum);
|
||||||
@@ -632,8 +697,27 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
// stores entire array (sans head) into temporary storage
|
// stores entire array (sans head) into temporary storage
|
||||||
bStatus.vars["for var "+varname] = new BasicVar(asgnObj.asgnValue, "array");
|
bStatus.vars["for var "+varname] = new BasicVar(asgnObj.asgnValue, "array");
|
||||||
// put the varname to forstack
|
// put the varname to forstack
|
||||||
bStatus.forLnums[asgnObj.asgnVarName] = lnum;
|
bStatus.forLnums[varname] = lnum;
|
||||||
bStatus.forStack.push(asgnObj.asgnVarName);
|
bStatus.forStack.push(varname);
|
||||||
|
},
|
||||||
|
"FOR" : function(lnum, args) { // generator model
|
||||||
|
var asgnObj = resolve(args[0]);
|
||||||
|
// type check
|
||||||
|
if (asgnObj === undefined) throw lang.syntaxfehler(lnum);
|
||||||
|
if (!(asgnObj.asgnValue instanceof ForGen)) throw lang.illegalType(lnum, typeof asgnObj);
|
||||||
|
|
||||||
|
var varname = asgnObj.asgnVarName;
|
||||||
|
var generator = asgnObj.asgnValue;
|
||||||
|
|
||||||
|
|
||||||
|
// assign new variable
|
||||||
|
// the var itself will have head of the array, and the head itself will be removed from the array
|
||||||
|
bStatus.vars[varname] = new BasicVar(generator.start, "num");
|
||||||
|
// stores entire array (sans head) into temporary storage
|
||||||
|
bStatus.vars["for var "+varname] = new BasicVar(generator, "generator");
|
||||||
|
// put the varname to forstack
|
||||||
|
bStatus.forLnums[varname] = lnum;
|
||||||
|
bStatus.forStack.push(varname);
|
||||||
},
|
},
|
||||||
"NEXT" : function(lnum, args) {
|
"NEXT" : function(lnum, args) {
|
||||||
// if no args were given
|
// if no args were given
|
||||||
@@ -644,7 +728,12 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (forVarname === undefined) {
|
if (forVarname === undefined) {
|
||||||
throw lang.nextWithoutFor(lnum);
|
throw lang.nextWithoutFor(lnum);
|
||||||
}
|
}
|
||||||
bStatus.vars[forVarname].bvLiteral = bStatus.vars["for var "+forVarname].bvLiteral.shift();
|
var forVar = bStatus.vars["for var "+forVarname].bvLiteral;
|
||||||
|
|
||||||
|
if (forVar instanceof ForGen)
|
||||||
|
bStatus.vars[forVarname].bvLiteral = forVar.getNext();
|
||||||
|
else
|
||||||
|
bStatus.vars[forVarname].bvLiteral = forVar.shift();
|
||||||
|
|
||||||
if ((bStatus.vars[forVarname].bvLiteral !== undefined)) {
|
if ((bStatus.vars[forVarname].bvLiteral !== undefined)) {
|
||||||
// feed popped value back, we're not done yet
|
// feed popped value back, we're not done yet
|
||||||
@@ -652,7 +741,11 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return bStatus.forLnums[forVarname] + 1;
|
return bStatus.forLnums[forVarname] + 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bStatus.vars[forVarname] === undefined; // unregister the variable
|
if (forVar instanceof ForGen)
|
||||||
|
bStatus.vars[forVarname].bvLiteral = forVar.current; // true BASIC compatibility for generator
|
||||||
|
else
|
||||||
|
bStatus.vars[forVarname] === undefined; // unregister the variable
|
||||||
|
|
||||||
return lnum + 1;
|
return lnum + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -803,8 +896,9 @@ bF._opPrc = {
|
|||||||
"!":15,"~":15, // array CONS and PUSH
|
"!":15,"~":15, // array CONS and PUSH
|
||||||
"#": 16, // array concat
|
"#": 16, // array concat
|
||||||
"=":999,
|
"=":999,
|
||||||
|
"IN":1000
|
||||||
};
|
};
|
||||||
bF._opRh = {"^":1,"=":1,"!":1};
|
bF._opRh = {"^":1,"=":1,"!":1,"IN":1};
|
||||||
bF._keywords = {
|
bF._keywords = {
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -1212,8 +1306,10 @@ bF._parserElaboration = function(lnum, tokens, states) {
|
|||||||
// "IF" EXPRESSION "GOTO" NUMBERS "ELSE" NUMBERS |
|
// "IF" EXPRESSION "GOTO" NUMBERS "ELSE" NUMBERS |
|
||||||
// "IF" EXPRESSION "THEN" EXPRESSION |
|
// "IF" EXPRESSION "THEN" EXPRESSION |
|
||||||
// "IF" EXPRESSION "GOTO" NUMBERS
|
// "IF" EXPRESSION "GOTO" NUMBERS
|
||||||
// FOR_LOOP -> "FOR" FUNCTION_OR_VARIABLE_NAME "=" EXPRESSION "TO" EXPRESSION "STEP" EXPRESSION |
|
// FOR_LOOP -> "FOR" FUNCTION_OR_VARIABLE_NAME "IN" EXPRESSION "TO" EXPRESSION "STEP" EXPRESSION |
|
||||||
// "FOR" FUNCTION_OR_VARIABLE_NAME "=" EXPRESSION "TO" EXPRESSION
|
// "FOR" FUNCTION_OR_VARIABLE_NAME "IN" EXPRESSION "TO" EXPRESSION |
|
||||||
|
// "FOREACH" FUNCTION_OR_VARIABLE_NAME "=" EXPRESSION "TO" EXPRESSION "STEP" EXPRESSION |
|
||||||
|
// "FOREACH" FUNCTION_OR_VARIABLE_NAME "=" EXPRESSION "TO" EXPRESSION |
|
||||||
// WHILE_LOOP -> "WHILE" EXPERSSION
|
// WHILE_LOOP -> "WHILE" EXPERSSION
|
||||||
// BINARY_OP -> EXPRSSION OPERATOR EXPRESSION
|
// BINARY_OP -> EXPRSSION OPERATOR EXPRESSION
|
||||||
// UNARY_OP -> OPERATOR EXPRESSION
|
// UNARY_OP -> OPERATOR EXPRESSION
|
||||||
@@ -1549,6 +1645,7 @@ bF._parseTokens = function(lnum, tokens, states, recDepth) {
|
|||||||
// @return is defined in BasicAST
|
// @return is defined in BasicAST
|
||||||
let JStoBASICtype = function(object) {
|
let JStoBASICtype = function(object) {
|
||||||
if (typeof object === "boolean") return "bool";
|
if (typeof object === "boolean") return "bool";
|
||||||
|
else if (object instanceof ForGen) return "generator";
|
||||||
else if (Array.isArray(object)) return "array";
|
else if (Array.isArray(object)) return "array";
|
||||||
else if (!isNaN(object)) return "num";
|
else if (!isNaN(object)) return "num";
|
||||||
else if (typeof object === "string" || object instanceof String) return "string";
|
else if (typeof object === "string" || object instanceof String) return "string";
|
||||||
|
|||||||
Reference in New Issue
Block a user