basic: still broken but we're getting there

This commit is contained in:
minjaesong
2020-12-13 00:12:20 +09:00
parent 24cd1a030f
commit 5a09c4915d
3 changed files with 201 additions and 198 deletions

View File

@@ -518,10 +518,8 @@ bStatus.builtin = {
if no args were given (e.g. "10 NEXT()"), args[0] will be: {troType: null, troValue: , troNextLine: 11} if no args were given (e.g. "10 NEXT()"), args[0] will be: {troType: null, troValue: , troNextLine: 11}
if no arg text were given (e.g. "10 NEXT"), args will have zero length if no arg text were given (e.g. "10 NEXT"), args will have zero length
DEFUN'd functions must be treated as if their args is "vararg"
*/ */
"=" : {args:2, f:function(lnum, args) { "=" : function(lnum, args) {
// THIS FUNCTION MUST BE COPIED TO 'INPUT' // THIS FUNCTION MUST BE COPIED TO 'INPUT'
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 troValue = args[0].troValue; var troValue = args[0].troValue;
@@ -560,8 +558,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
return {asgnVarName: varname, asgnValue: rh}; return {asgnVarName: varname, asgnValue: rh};
} }
} }
}}, },
"IN" : {args:2, f:function(lnum, args) { // almost same as =, but don't actually make new variable. Used by FOR statement "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); if (args.length != 2) throw lang.syntaxfehler(lnum, args.length+lang.aG);
var troValue = args[0].troValue; var troValue = args[0].troValue;
@@ -577,56 +575,56 @@ DEFUN'd functions must be treated as if their args is "vararg"
if (_basicConsts[varname]) throw lang.asgnOnConst(lnum, varname); if (_basicConsts[varname]) throw lang.asgnOnConst(lnum, varname);
return {asgnVarName: varname, asgnValue: rh}; return {asgnVarName: varname, asgnValue: rh};
} }
}}, },
"==" : {args:2, f:function(lnum, args) { "==" : function(lnum, args) {
return twoArg(lnum, args, (lh,rh) => lh == rh); return twoArg(lnum, args, (lh,rh) => lh == rh);
}}, },
"<>" : {args:2, f:function(lnum, args) { "<>" : function(lnum, args) {
return twoArg(lnum, args, (lh,rh) => lh != rh); return twoArg(lnum, args, (lh,rh) => lh != rh);
}}, },
"><" : {args:2, f:function(lnum, args) { "><" : function(lnum, args) {
return twoArg(lnum, args, (lh,rh) => lh != rh); return twoArg(lnum, args, (lh,rh) => lh != rh);
}}, },
"<=" : {args:2, f:function(lnum, args) { "<=" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh <= rh); return twoArgNum(lnum, args, (lh,rh) => lh <= rh);
}}, },
"=<" : {args:2, f:function(lnum, args) { "=<" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh <= rh); return twoArgNum(lnum, args, (lh,rh) => lh <= rh);
}}, },
">=" : {args:2, f:function(lnum, args) { ">=" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh >= rh); return twoArgNum(lnum, args, (lh,rh) => lh >= rh);
}}, },
"=>" : {args:2, f:function(lnum, args) { "=>" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh >= rh); return twoArgNum(lnum, args, (lh,rh) => lh >= rh);
}}, },
"<" : {args:2, f:function(lnum, args) { "<" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh < rh); return twoArgNum(lnum, args, (lh,rh) => lh < rh);
}}, },
">" : {args:2, f:function(lnum, args) { ">" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh > rh); return twoArgNum(lnum, args, (lh,rh) => lh > rh);
}}, },
"<<" : {args:2, f:function(lnum, args) { "<<" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh << rh); return twoArgNum(lnum, args, (lh,rh) => lh << rh);
}}, },
">>" : {args:2, f:function(lnum, args) { ">>" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh >> rh); return twoArgNum(lnum, args, (lh,rh) => lh >> rh);
}}, },
"UNARYMINUS" : {args:1, f:function(lnum, args) { "UNARYMINUS" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => -lh); return oneArgNum(lnum, args, (lh) => -lh);
}}, },
"UNARYPLUS" : {args:1, f:function(lnum, args) { "UNARYPLUS" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => +lh); return oneArgNum(lnum, args, (lh) => +lh);
}}, },
"BAND" : {args:2, f:function(lnum, args) { "BAND" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh & rh); return twoArgNum(lnum, args, (lh,rh) => lh & rh);
}}, },
"BOR" : {args:2, f:function(lnum, args) { "BOR" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh | rh); return twoArgNum(lnum, args, (lh,rh) => lh | rh);
}}, },
"BXOR" : {args:2, f:function(lnum, args) { "BXOR" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh ^ rh); return twoArgNum(lnum, args, (lh,rh) => lh ^ rh);
}}, },
"!" : {args:2, f:function(lnum, args) { // Haskell-style CONS "!" : function(lnum, args) { // Haskell-style CONS
return twoArg(lnum, args, (lh,rh) => { return twoArg(lnum, args, (lh,rh) => {
if (isNaN(lh)) if (isNaN(lh))
throw lang.illegalType(lnum, lh); // BASIC array is numbers only throw lang.illegalType(lnum, lh); // BASIC array is numbers only
@@ -634,8 +632,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
throw lang.illegalType(lnum, rh); throw lang.illegalType(lnum, rh);
return [lh].concat(rh); return [lh].concat(rh);
}); });
}}, },
"~" : {args:2, f:function(lnum, args) { // array PUSH "~" : function(lnum, args) { // array PUSH
return twoArg(lnum, args, (lh,rh) => { return twoArg(lnum, args, (lh,rh) => {
if (isNaN(rh)) if (isNaN(rh))
throw lang.illegalType(lnum, rh); // BASIC array is numbers only throw lang.illegalType(lnum, rh); // BASIC array is numbers only
@@ -643,8 +641,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
throw lang.illegalType(lnum, lh); throw lang.illegalType(lnum, lh);
return lh.concat([rh]); return lh.concat([rh]);
}); });
}}, },
"#" : {args:2, f:function(lnum, args) { // array CONCAT "#" : function(lnum, args) { // array CONCAT
return twoArg(lnum, args, (lh,rh) => { return twoArg(lnum, args, (lh,rh) => {
if (!Array.isArray(rh)) if (!Array.isArray(rh))
throw lang.illegalType(lnum, rh); throw lang.illegalType(lnum, rh);
@@ -652,38 +650,38 @@ DEFUN'd functions must be treated as if their args is "vararg"
throw lang.illegalType(lnum, lh); throw lang.illegalType(lnum, lh);
return lh.concat(rh); return lh.concat(rh);
}); });
}}, },
"+" : {args:2, f:function(lnum, args) { // addition, string concat "+" : function(lnum, args) { // addition, string concat
return twoArg(lnum, args, (lh,rh) => (!isNaN(lh) && !isNaN(rh)) ? (lh*1 + rh*1) : (lh + rh)); return twoArg(lnum, args, (lh,rh) => (!isNaN(lh) && !isNaN(rh)) ? (lh*1 + rh*1) : (lh + rh));
}}, },
"-" : {args:2, f:function(lnum, args) { "-" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh - rh); return twoArgNum(lnum, args, (lh,rh) => lh - rh);
}}, },
"*" : {args:2, f:function(lnum, args) { "*" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh * rh); return twoArgNum(lnum, args, (lh,rh) => lh * rh);
}}, },
"/" : {args:2, f:function(lnum, args) { "/" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => { return twoArgNum(lnum, args, (lh,rh) => {
if (rh == 0) throw lang.divByZero; if (rh == 0) throw lang.divByZero;
return lh / rh return lh / rh
}); });
}}, },
"MOD" : {args:2, f:function(lnum, args) { "MOD" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => lh % rh); return twoArgNum(lnum, args, (lh,rh) => lh % rh);
}}, },
"^" : {args:2, f:function(lnum, args) { "^" : function(lnum, args) {
return twoArgNum(lnum, args, (lh,rh) => Math.pow(lh, rh)); return twoArgNum(lnum, args, (lh,rh) => Math.pow(lh, rh));
}}, },
"TO" : {args:2, f:function(lnum, args) { "TO" : function(lnum, args) {
return twoArgNum(lnum, args, (from, to) => new ForGen(from, to, 1)); return twoArgNum(lnum, args, (from, to) => new ForGen(from, to, 1));
}}, },
"STEP" : {args:2, f:function(lnum, args) { "STEP" : function(lnum, args) {
return twoArg(lnum, args, (gen, step) => { return twoArg(lnum, args, (gen, step) => {
if (!(gen instanceof ForGen)) throw lang.illegalType(lnum, gen); if (!(gen instanceof ForGen)) throw lang.illegalType(lnum, gen);
return new ForGen(gen.start, gen.end, step); return new ForGen(gen.start, gen.end, step);
}); });
}}, },
"DIM" : {args:2, f:function(lnum, args) { "DIM" : function(lnum, args) {
return varArgNum(lnum, args, (revdims) => { return varArgNum(lnum, args, (revdims) => {
let dims = revdims.reverse(); let dims = revdims.reverse();
let arraydec = "Array(dims[0]).fill(0)"; let arraydec = "Array(dims[0]).fill(0)";
@@ -692,8 +690,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
} }
return eval(arraydec); return eval(arraydec);
}); });
}}, },
"PRINT" : {args:"vararg", f:function(lnum, args, seps) { "PRINT" : function(lnum, args, seps) {
if (args.length == 0) if (args.length == 0)
println(); println();
else { else {
@@ -724,8 +722,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
} }
if (args[args.length - 1] !== undefined && args[args.length - 1].troType != "null") println(); if (args[args.length - 1] !== undefined && args[args.length - 1].troType != "null") println();
}}, },
"EMIT" : {args:"vararg", f:function(lnum, args, seps) { "EMIT" : function(lnum, args, seps) {
if (args.length == 0) if (args.length == 0)
println(); println();
else { else {
@@ -757,40 +755,40 @@ DEFUN'd functions must be treated as if their args is "vararg"
} }
if (args[args.length - 1] !== undefined && args[args.length - 1].troType != "null") println(); if (args[args.length - 1] !== undefined && args[args.length - 1].troType != "null") println();
}}, },
"POKE" : {args:2, f:function(lnum, args) { "POKE" : function(lnum, args) {
twoArgNum(lnum, args, (lh,rh) => sys.poke(lh, rh)); twoArgNum(lnum, args, (lh,rh) => sys.poke(lh, rh));
}}, },
"PEEK" : {args:1, f:function(lnum, args) { "PEEK" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => sys.peek(lh)); return oneArgNum(lnum, args, (lh) => sys.peek(lh));
}}, },
"GOTO" : {args:1, f:function(lnum, args) { "GOTO" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (lh < 0) throw lang.syntaxfehler(lnum, lh); if (lh < 0) throw lang.syntaxfehler(lnum, lh);
return lh; return lh;
}); });
}}, },
"GOSUB" : {args:1, f:function(lnum, args) { "GOSUB" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (lh < 0) throw lang.syntaxfehler(lnum, lh); if (lh < 0) throw lang.syntaxfehler(lnum, lh);
bStatus.gosubStack.push(lnum + 1); bStatus.gosubStack.push(lnum + 1);
//println(lnum+" GOSUB into "+lh); //println(lnum+" GOSUB into "+lh);
return lh; return lh;
}); });
}}, },
"RETURN" : {args:0, f:function(lnum, args) { "RETURN" : function(lnum, args) {
var r = bStatus.gosubStack.pop(); var r = bStatus.gosubStack.pop();
if (r === undefined) throw lang.nowhereToReturn(lnum); if (r === undefined) throw lang.nowhereToReturn(lnum);
//println(lnum+" RETURN to "+r); //println(lnum+" RETURN to "+r);
return r; return r;
}}, },
"CLEAR" : {args:0, f:function(lnum, args) { "CLEAR" : function(lnum, args) {
bStatus.vars = initBvars(); bStatus.vars = initBvars();
}}, },
"PLOT" : {args:3, f:function(lnum, args) { "PLOT" : function(lnum, args) {
threeArgNum(lnum, args, (xpos, ypos, color) => graphics.plotPixel(xpos, ypos, color)); threeArgNum(lnum, args, (xpos, ypos, color) => graphics.plotPixel(xpos, ypos, color));
}}, },
"AND" : {args:2, f:function(lnum, args) { "AND" : 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 rsvArg = args.map((it) => resolve(it)); var rsvArg = args.map((it) => resolve(it));
rsvArg.forEach((v) => { rsvArg.forEach((v) => {
@@ -802,8 +800,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
return it; return it;
}); });
return argum[0] && argum[1]; return argum[0] && argum[1];
}}, },
"OR" : {args:2, f:function(lnum, args) { "OR" : 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 rsvArg = args.map((it) => resolve(it)); var rsvArg = args.map((it) => resolve(it));
rsvArg.forEach((v) => { rsvArg.forEach((v) => {
@@ -815,37 +813,37 @@ DEFUN'd functions must be treated as if their args is "vararg"
return it; return it;
}); });
return argum[0] || argum[1]; return argum[0] || argum[1];
}}, },
"RND" : {args:1, f:function(lnum, args) { "RND" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (!(args.length > 0 && args[0].troValue === 0)) if (!(args.length > 0 && args[0].troValue === 0))
bStatus.rnd = Math.random();//(bStatus.rnd * 214013 + 2531011) % 16777216; // GW-BASIC does this bStatus.rnd = Math.random();//(bStatus.rnd * 214013 + 2531011) % 16777216; // GW-BASIC does this
return bStatus.rnd; return bStatus.rnd;
}); });
}}, },
"ROUND" : {args:1, f:function(lnum, args) { "ROUND" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => Math.round(lh)); return oneArgNum(lnum, args, (lh) => Math.round(lh));
}}, },
"FLOOR" : {args:1, f:function(lnum, args) { "FLOOR" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => Math.floor(lh)); return oneArgNum(lnum, args, (lh) => Math.floor(lh));
}}, },
"INT" : {args:1, f:function(lnum, args) { // synonymous to FLOOR "INT" : function(lnum, args) { // synonymous to FLOOR
return oneArgNum(lnum, args, (lh) => Math.floor(lh)); return oneArgNum(lnum, args, (lh) => Math.floor(lh));
}}, },
"CEIL" : {args:1, f:function(lnum, args) { "CEIL" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => Math.ceil(lh)); return oneArgNum(lnum, args, (lh) => Math.ceil(lh));
}}, },
"FIX" : {args:1, f:function(lnum, args) { "FIX" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => (lh|0)); return oneArgNum(lnum, args, (lh) => (lh|0));
}}, },
"CHR" : {args:1, f:function(lnum, args) { "CHR" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => String.fromCharCode(lh)); return oneArgNum(lnum, args, (lh) => String.fromCharCode(lh));
}}, },
"TEST" : {args:1, f:function(lnum, args) { "TEST" : function(lnum, args) {
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]);
}}, },
"FOREACH" : {args:1, f:function(lnum, args) { // list comprehension model "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);
@@ -861,8 +859,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
// put the varname to forstack // put the varname to forstack
bStatus.forLnums[varname] = lnum; bStatus.forLnums[varname] = lnum;
bStatus.forStack.push(varname); bStatus.forStack.push(varname);
}}, },
"FOR" : {args:1, f:function(lnum, args) { // generator model "FOR" : function(lnum, args) { // generator 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);
@@ -880,8 +878,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
// put the varname to forstack // put the varname to forstack
bStatus.forLnums[varname] = lnum; bStatus.forLnums[varname] = lnum;
bStatus.forStack.push(varname); bStatus.forStack.push(varname);
}}, },
"NEXT" : {args:"vararg", f:function(lnum, args) { "NEXT" : function(lnum, args) {
// if no args were given // if no args were given
if (args.length == 0 || (args.length == 1 && args.troType == "null")) { if (args.length == 0 || (args.length == 1 && args.troType == "null")) {
// go to most recent FOR // go to most recent FOR
@@ -916,8 +914,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
} }
throw lang.syntaxfehler(lnum, "extra arguments for NEXT"); throw lang.syntaxfehler(lnum, "extra arguments for NEXT");
}}, },
"BREAKTO" : {args:1, f:function(lnum, args) { "BREAKTO" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
var forVarname = bStatus.forStack.pop(); var forVarname = bStatus.forStack.pop();
if (forVarname === undefined) { if (forVarname === undefined) {
@@ -928,7 +926,7 @@ DEFUN'd functions must be treated as if their args is "vararg"
if (lh < 0) throw lang.syntaxfehler(lnum, lh); if (lh < 0) throw lang.syntaxfehler(lnum, lh);
return lh; return lh;
}); });
}}, },
/* /*
10 input;"what is your name";a$ 10 input;"what is your name";a$
@@ -966,7 +964,7 @@ DEFUN'd functions must be treated as if their args is "vararg"
| `----------------- | `-----------------
`----------------- `-----------------
*/ */
"INPUT" : {args:"vararg", f:function(lnum, args) { "INPUT" : function(lnum, args) {
if (args.length != 1) throw lang.syntaxfehler(lnum, args.length+lang.aG); if (args.length != 1) throw lang.syntaxfehler(lnum, args.length+lang.aG);
var troValue = args[0].troValue; var troValue = args[0].troValue;
@@ -992,85 +990,85 @@ DEFUN'd functions must be treated as if their args is "vararg"
bStatus.vars[varname] = new BasicVar(rh, type); bStatus.vars[varname] = new BasicVar(rh, type);
return {asgnVarName: varname, asgnValue: rh}; return {asgnVarName: varname, asgnValue: rh};
} }
}}, },
"END" : {args:0, f:function(lnum, args) { "END" : function(lnum, args) {
serial.println("Program terminated in "+lnum); serial.println("Program terminated in "+lnum);
return Number.MAX_SAFE_INTEGER; // GOTO far-far-away return Number.MAX_SAFE_INTEGER; // GOTO far-far-away
}}, },
"SPC" : {args:1, f:function(lnum, args) { "SPC" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => " ".repeat(lh)); return oneArgNum(lnum, args, (lh) => " ".repeat(lh));
}}, },
"LEFT" : {args:2, f:function(lnum, args) { "LEFT" : function(lnum, args) {
return twoArg(lnum, args, (str, len) => str.substring(0, len)); return twoArg(lnum, args, (str, len) => str.substring(0, len));
}}, },
"MID" : {args:3, f:function(lnum, args) { "MID" : function(lnum, args) {
return threeArg(lnum, args, (str, start, len) => str.substring(start-INDEX_BASE, start-INDEX_BASE+len)); return threeArg(lnum, args, (str, start, len) => str.substring(start-INDEX_BASE, start-INDEX_BASE+len));
}}, },
"RIGHT" : {args:2, f:function(lnum, args) { "RIGHT" : function(lnum, args) {
return twoArg(lnum, args, (str, len) => str.substring(str.length - len, str.length)); return twoArg(lnum, args, (str, len) => str.substring(str.length - len, str.length));
}}, },
"SGN" : {args:1, f:function(lnum, args) { "SGN" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => (it > 0) ? 1 : (it < 0) ? -1 : 0); return oneArgNum(lnum, args, (it) => (it > 0) ? 1 : (it < 0) ? -1 : 0);
}}, },
"ABS" : {args:1, f:function(lnum, args) { "ABS" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.abs(it)); return oneArgNum(lnum, args, (it) => Math.abs(it));
}}, },
"SIN" : {args:1, f:function(lnum, args) { "SIN" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.sin(it)); return oneArgNum(lnum, args, (it) => Math.sin(it));
}}, },
"COS" : {args:1, f:function(lnum, args) { "COS" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.cos(it)); return oneArgNum(lnum, args, (it) => Math.cos(it));
}}, },
"TAN" : {args:1, f:function(lnum, args) { "TAN" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.tan(it)); return oneArgNum(lnum, args, (it) => Math.tan(it));
}}, },
"EXP" : {args:1, f:function(lnum, args) { "EXP" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.exp(it)); return oneArgNum(lnum, args, (it) => Math.exp(it));
}}, },
"ASN" : {args:1, f:function(lnum, args) { "ASN" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.asin(it)); return oneArgNum(lnum, args, (it) => Math.asin(it));
}}, },
"ACO" : {args:1, f:function(lnum, args) { "ACO" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.acos(it)); return oneArgNum(lnum, args, (it) => Math.acos(it));
}}, },
"ATN" : {args:1, f:function(lnum, args) { "ATN" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.atan(it)); return oneArgNum(lnum, args, (it) => Math.atan(it));
}}, },
"SQR" : {args:1, f:function(lnum, args) { "SQR" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.sqrt(it)); return oneArgNum(lnum, args, (it) => Math.sqrt(it));
}}, },
"CBR" : {args:1, f:function(lnum, args) { "CBR" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.cbrt(it)); return oneArgNum(lnum, args, (it) => Math.cbrt(it));
}}, },
"SINH" : {args:1, f:function(lnum, args) { "SINH" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.sinh(it)); return oneArgNum(lnum, args, (it) => Math.sinh(it));
}}, },
"COSH" : {args:1, f:function(lnum, args) { "COSH" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.cosh(it)); return oneArgNum(lnum, args, (it) => Math.cosh(it));
}}, },
"TANH" : {args:1, f:function(lnum, args) { "TANH" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.tanh(it)); return oneArgNum(lnum, args, (it) => Math.tanh(it));
}}, },
"LOG" : {args:1, f:function(lnum, args) { "LOG" : function(lnum, args) {
return oneArgNum(lnum, args, (it) => Math.log(it)); return oneArgNum(lnum, args, (it) => Math.log(it));
}}, },
"RESTORE" : {args:0, f:function(lnum, args) { "RESTORE" : function(lnum, args) {
DATA_CURSOR = 0; DATA_CURSOR = 0;
}}, },
"READ" : {args:0, f:function(lnum, args) { "READ" : function(lnum, args) {
let r = DATA_CONSTS.shift(); let r = DATA_CONSTS.shift();
if (r === undefined) throw lang.outOfData(lnum); if (r === undefined) throw lang.outOfData(lnum);
}}, },
"OPTIONBASE" : {args:1, f:function(lnum, args) { "OPTIONBASE" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (lh != 0 && lh != 1) throw lang.syntaxfehler(line); if (lh != 0 && lh != 1) throw lang.syntaxfehler(line);
INDEX_BASE = lh|0; INDEX_BASE = lh|0;
}); });
}}, },
"DATA" : {args:"vararg", f:function() { /*DATA must do nothing when encountered; they must be pre-processed*/ }}, "DATA" : function() { /*DATA must do nothing when encountered; they must be pre-processed*/ },
/* Syopsis: MAP function, functor /* Syopsis: MAP function, functor
*/ */
"MAP" : {args:2, f:function(lnum, args) { "MAP" : function(lnum, args) {
return twoArg(lnum, args, (fn, functor) => { return twoArg(lnum, args, (fn, functor) => {
// TODO test only works with DEFUN'd functions // TODO test only works with DEFUN'd functions
if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Only works with DEFUN'd functions yet"); if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Only works with DEFUN'd functions yet");
@@ -1080,11 +1078,11 @@ DEFUN'd functions must be treated as if their args is "vararg"
return functor.map(it => bStatus.getDefunThunk(lnum, fn)(lnum, [it])); return functor.map(it => bStatus.getDefunThunk(lnum, fn)(lnum, [it]));
}); });
}}, },
/* Synopsis: FOLD function, init_value, functor /* Synopsis: FOLD function, init_value, functor
* a function must accept two arguments, of which first argument will be an accumulator * a function must accept two arguments, of which first argument will be an accumulator
*/ */
"FOLD" : {args:3, f:function(lnum, args) { "FOLD" : function(lnum, args) {
return threeArg(lnum, args, (fn, init, functor) => { return threeArg(lnum, args, (fn, init, functor) => {
// TODO test only works with DEFUN'd functions // TODO test only works with DEFUN'd functions
if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Only works with DEFUN'd functions yet"); if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Only works with DEFUN'd functions yet");
@@ -1099,25 +1097,25 @@ DEFUN'd functions must be treated as if their args is "vararg"
return akku; return akku;
}); });
}}, },
/* GOTO and GOSUB won't work but that's probably the best...? */ /* GOTO and GOSUB won't work but that's probably the best...? */
"DO" : {args:"vararg", f:function(lnum, args) { "DO" : function(lnum, args) {
//return resolve(args[args.length - 1]); //return resolve(args[args.length - 1]);
return undefined; return undefined;
}}, },
"OPTIONDEBUG" : {args:1, f:function(lnum, args) { "OPTIONDEBUG" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (lh != 0 && lh != 1) throw lang.syntaxfehler(line); if (lh != 0 && lh != 1) throw lang.syntaxfehler(line);
DBGON = (1 == lh|0); DBGON = (1 == lh|0);
}); });
}}, },
"OPTIONTRACE" : {args:1, f:function(lnum, args) { "OPTIONTRACE" : function(lnum, args) {
return oneArgNum(lnum, args, (lh) => { return oneArgNum(lnum, args, (lh) => {
if (lh != 0 && lh != 1) throw lang.syntaxfehler(line); if (lh != 0 && lh != 1) throw lang.syntaxfehler(line);
TRACEON = (1 == lh|0); TRACEON = (1 == lh|0);
}); });
}}, },
"RESOLVE" : {args:1, f:function(lnum, args) { "RESOLVE" : function(lnum, args) {
if (DBGON) { if (DBGON) {
return oneArg(lnum, args, (it) => { return oneArg(lnum, args, (it) => {
println(it); println(it);
@@ -1126,8 +1124,8 @@ DEFUN'd functions must be treated as if their args is "vararg"
else { else {
throw lang.syntaxfehler(lnum); throw lang.syntaxfehler(lnum);
} }
}}, },
"RESOLVE0" : {args:1, f:function(lnum, args) { "RESOLVE0" : function(lnum, args) {
if (DBGON) { if (DBGON) {
return oneArg(lnum, args, (it) => { return oneArg(lnum, args, (it) => {
println(Object.entries(it)); println(Object.entries(it));
@@ -1136,23 +1134,23 @@ DEFUN'd functions must be treated as if their args is "vararg"
else { else {
throw lang.syntaxfehler(lnum); throw lang.syntaxfehler(lnum);
} }
}}, },
"UNRESOLVE" : {args:1, f:function(lnum, args) { "UNRESOLVE" : function(lnum, args) {
if (DBGON) { if (DBGON) {
println(args[0]); println(args[0]);
} }
else { else {
throw lang.syntaxfehler(lnum); throw lang.syntaxfehler(lnum);
} }
}}, },
"UNRESOLVE0" : {args:1, f:function(lnum, args) { "UNRESOLVE0" : function(lnum, args) {
if (DBGON) { if (DBGON) {
println(Object.entries(args[0])); println(Object.entries(args[0]));
} }
else { else {
throw lang.syntaxfehler(lnum); throw lang.syntaxfehler(lnum);
} }
}} }
}; };
Object.freeze(bStatus.builtin); Object.freeze(bStatus.builtin);
let bF = {}; let bF = {};
@@ -1707,7 +1705,7 @@ bF.isSemanticLiteral = function(token, state) {
return "]" == token || ")" == token || return "]" == token || ")" == token ||
"qot" == state || "num" == state || "bool" == state || "lit" == state; "qot" == state || "num" == state || "bool" == state || "lit" == state;
} }
bF.parserDoDebugPrint = false; bF.parserDoDebugPrint = true;
bF.parserPrintdbg = any => { if (bF.parserDoDebugPrint) serial.println(any) }; bF.parserPrintdbg = any => { if (bF.parserDoDebugPrint) serial.println(any) };
bF.parserPrintdbg2 = function(icon, lnum, tokens, states, recDepth) { bF.parserPrintdbg2 = function(icon, lnum, tokens, states, recDepth) {
if (bF.parserDoDebugPrint) { if (bF.parserDoDebugPrint) {
@@ -1764,11 +1762,15 @@ bF._parseTokens = function(lnum, tokens, states) {
// check for empty tokens // check for empty tokens
if (x.end - x.start <= 0) throw new ParserError("Malformed Line"); if (x.end - x.start <= 0) throw new ParserError("Malformed Line");
return bF._parseStmt(lnum, let tree = bF._parseStmt(lnum,
tokens.slice(x.start, x.end), tokens.slice(x.start, x.end),
states.slice(x.start, x.end), states.slice(x.start, x.end),
1 1
); );
bF.parserPrintdbgline('Tree in ', '\n'+astToString(tree), lnum, 0);
return tree;
}); });
} }
@@ -1955,12 +1957,12 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) {
} }
catch (e) { catch (e) {
bF.parserPrintdbgline('$', 'Error!', lnum, recDepth); bF.parserPrintdbgline('$', 'Error!', lnum, recDepth);
throw new ParserError("Statement cannot be parsed: "+e.stack); throw new ParserError("Statement cannot be parsed in "+lnum+": "+e.stack);
} }
/*************************************************************************/ /*************************************************************************/
throw new ParserError("Statement cannot be parsed"); throw new ParserError("Statement cannot be parsed in "+lnum);
} // END of STMT } // END of STMT
@@ -2120,7 +2122,7 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
/*************************************************************************/ /*************************************************************************/
throw new ParserError("Expression cannot be parsed"); throw new ParserError("Expression cannot be parsed in "+lnum);
} // END of EXPR } // END of EXPR
@@ -2377,7 +2379,8 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) {
if (_debugExec) serial.println(recWedge+"function|operator"); if (_debugExec) serial.println(recWedge+"function|operator");
if (_debugExec) serial.println(recWedge+astToString(syntaxTree)); if (_debugExec) serial.println(recWedge+astToString(syntaxTree));
var funcName = syntaxTree.astValue.toUpperCase(); var funcName = syntaxTree.astValue.toUpperCase();
var func = bStatus.builtin[funcName].f; var func = bStatus.builtin[funcName];
if ("IF" == funcName) { if ("IF" == funcName) {
if (syntaxTree.astLeaves.length != 2 && syntaxTree.astLeaves.length != 3) throw lang.syntaxfehler(lnum); if (syntaxTree.astLeaves.length != 2 && syntaxTree.astLeaves.length != 3) throw lang.syntaxfehler(lnum);
@@ -2391,7 +2394,7 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) {
} }
try { try {
var iftest = bStatus.builtin["TEST"].f(lnum, [testedval]); var iftest = bStatus.builtin["TEST"](lnum, [testedval]);
if (!iftest && syntaxTree.astLeaves[2] !== undefined) if (!iftest && syntaxTree.astLeaves[2] !== undefined)
return bF._executeSyntaxTree(lnum, syntaxTree.astLeaves[2], recDepth + 1); return bF._executeSyntaxTree(lnum, syntaxTree.astLeaves[2], recDepth + 1);
@@ -2406,11 +2409,9 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) {
} }
else if ("DEFUN" == funcName) { else if ("DEFUN" == funcName) {
//if (recDepth > 0) throw lang.badFunctionCallFormat(); // nested DEFUN is TODO and it involves currying and de bruijn indexing //if (recDepth > 0) throw lang.badFunctionCallFormat(); // nested DEFUN is TODO and it involves currying and de bruijn indexing
if (syntaxTree.astLeaves.length !== 1) throw lang.syntaxfehler(lnum, "DEFUN 1"); if (syntaxTree.astLeaves.length !== 2) throw lang.syntaxfehler(lnum, "DEFUN 1");
if (syntaxTree.astLeaves[0].astValue !== "=") throw lang.syntaxfehler(lnum, "DEFUN 2 -- "+syntaxTree.astLeaves[0].astValue); let nameTree = syntaxTree.astLeaves[0];
if (syntaxTree.astLeaves[0].astLeaves.length !== 2) throw lang.syntaxfehler(lnum, "DEFUN 3"); let exprTree = syntaxTree.astLeaves[1];
let nameTree = syntaxTree.astLeaves[0].astLeaves[0];
let exprTree = syntaxTree.astLeaves[0].astLeaves[1];
// create parametres map // create parametres map
// NOTE: firstmost param ('x' as in foo(x,y,z)) gets index 0 // NOTE: firstmost param ('x' as in foo(x,y,z)) gets index 0
@@ -2431,17 +2432,15 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) {
it.astValue = defunRenamingMap[it.astValue]; it.astValue = defunRenamingMap[it.astValue];
} }
} }
// decrease the recursion counter while we're looping
it.astDepth -= 2;
}); });
// test print new tree // test print new tree
//serial.println("[BASIC.DEFUN] defun debug info for function "+defunName); serial.println("[BASIC.DEFUN] defun debug info for function "+defunName);
//serial.println("[BASIC.DEFUN] defun name tree: "); serial.println("[BASIC.DEFUN] defun name tree: ");
//serial.println(astToString(nameTree)); serial.println(astToString(nameTree));
//serial.println("[BASIC.DEFUN] defun renaming map: "+Object.entries(defunRenamingMap)); serial.println("[BASIC.DEFUN] defun renaming map: "+Object.entries(defunRenamingMap));
//serial.println("[BASIC.DEFUN] defun expression tree:"); serial.println("[BASIC.DEFUN] defun expression tree:");
//serial.println(astToString(exprTree)); serial.println(astToString(exprTree));
// check if the variable name already exists // check if the variable name already exists
// search from constants // search from constants

View File

@@ -247,12 +247,12 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) {
} }
catch (e) { catch (e) {
bF.parserPrintdbgline('$', 'Error!', lnum, recDepth); bF.parserPrintdbgline('$', 'Error!', lnum, recDepth);
throw new ParserError("Statement cannot be parsed: "+e.stack); throw new ParserError("Statement cannot be parsed in "+lnum+": "+e.stack);
} }
/*************************************************************************/ /*************************************************************************/
throw new ParserError("Statement cannot be parsed"); throw new ParserError("Statement cannot be parsed in "+lnum);
} // END of STMT } // END of STMT
@@ -412,7 +412,7 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
/*************************************************************************/ /*************************************************************************/
throw new ParserError("Expression cannot be parsed"); throw new ParserError("Expression cannot be parsed in "+lnum);
} // END of EXPR } // END of EXPR

View File

@@ -7,6 +7,7 @@ linenumber = digits ;
stmt = stmt =
"IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt]
| "FOR" , expr // TODO
| "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr
| "ON" , expr_sans_asgn , ("GOTO" | "GOSUB") , expr_sans_asgn , {"," , expr_sans_asgn} | "ON" , expr_sans_asgn , ("GOTO" | "GOSUB") , expr_sans_asgn , {"," , expr_sans_asgn}
| "(" , stmt , ")" | "(" , stmt , ")"
@@ -69,6 +70,9 @@ IF (type: function, value: IF)
2. true 2. true
[3. false] [3. false]
FOR (type: function, value: FOR)
1. expr (normally (=) but not necessarily)
DEFUN (type: function, value: DEFUN) DEFUN (type: function, value: DEFUN)
1. funcname 1. funcname
1. arg0 1. arg0