mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-11 07:14:04 +09:00
basic: currying is now operator & does not require special treatment on tree execution
This commit is contained in:
480
assets/basic.js
480
assets/basic.js
@@ -584,7 +584,7 @@ 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
|
||||||
*/
|
*/
|
||||||
"=" : function(lnum, stmtnum, args) {
|
"=" : {f:function(lnum, stmtnum, 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;
|
||||||
@@ -623,8 +623,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return {asgnVarName: varname, asgnValue: rh};
|
return {asgnVarName: varname, asgnValue: rh};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"IN" : function(lnum, stmtnum, args) { // almost same as =, but don't actually make new variable. Used by FOR statement
|
"IN" : {f:function(lnum, stmtnum, 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;
|
||||||
|
|
||||||
@@ -640,56 +640,56 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
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};
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"==" : function(lnum, stmtnum, args) {
|
"==" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => lh == rh);
|
return twoArg(lnum, stmtnum, args, (lh,rh) => lh == rh);
|
||||||
},
|
}},
|
||||||
"<>" : function(lnum, stmtnum, args) {
|
"<>" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => lh != rh);
|
return twoArg(lnum, stmtnum, args, (lh,rh) => lh != rh);
|
||||||
},
|
}},
|
||||||
"><" : function(lnum, stmtnum, args) {
|
"><" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => lh != rh);
|
return twoArg(lnum, stmtnum, args, (lh,rh) => lh != rh);
|
||||||
},
|
}},
|
||||||
"<=" : function(lnum, stmtnum, args) {
|
"<=" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh <= rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh <= rh);
|
||||||
},
|
}},
|
||||||
"=<" : function(lnum, stmtnum, args) {
|
"=<" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh <= rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh <= rh);
|
||||||
},
|
}},
|
||||||
">=" : function(lnum, stmtnum, args) {
|
">=" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >= rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >= rh);
|
||||||
},
|
}},
|
||||||
"=>" : function(lnum, stmtnum, args) {
|
"=>" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >= rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >= rh);
|
||||||
},
|
}},
|
||||||
"<" : function(lnum, stmtnum, args) {
|
"<" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh < rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh < rh);
|
||||||
},
|
}},
|
||||||
">" : function(lnum, stmtnum, args) {
|
">" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh > rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh > rh);
|
||||||
},
|
}},
|
||||||
"<<" : function(lnum, stmtnum, args) {
|
"<<" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh << rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh << rh);
|
||||||
},
|
}},
|
||||||
">>" : function(lnum, stmtnum, args) {
|
">>" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >>> rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh >>> rh);
|
||||||
},
|
}},
|
||||||
"UNARYMINUS" : function(lnum, stmtnum, args) {
|
"UNARYMINUS" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => -lh);
|
return oneArgNum(lnum, stmtnum, args, (lh) => -lh);
|
||||||
},
|
}},
|
||||||
"UNARYPLUS" : function(lnum, stmtnum, args) {
|
"UNARYPLUS" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => +lh);
|
return oneArgNum(lnum, stmtnum, args, (lh) => +lh);
|
||||||
},
|
}},
|
||||||
"BAND" : function(lnum, stmtnum, args) {
|
"BAND" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh & rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh & rh);
|
||||||
},
|
}},
|
||||||
"BOR" : function(lnum, stmtnum, args) {
|
"BOR" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh | rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh | rh);
|
||||||
},
|
}},
|
||||||
"BXOR" : function(lnum, stmtnum, args) {
|
"BXOR" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh ^ rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh ^ rh);
|
||||||
},
|
}},
|
||||||
"!" : function(lnum, stmtnum, args) { // Haskell-style CONS
|
"!" : {f:function(lnum, stmtnum, args) { // Haskell-style CONS
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => {
|
return twoArg(lnum, stmtnum, 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
|
||||||
@@ -697,8 +697,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
throw lang.illegalType(lnum, rh);
|
throw lang.illegalType(lnum, rh);
|
||||||
return [lh].concat(rh);
|
return [lh].concat(rh);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"~" : function(lnum, stmtnum, args) { // array PUSH
|
"~" : {f:function(lnum, stmtnum, args) { // array PUSH
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => {
|
return twoArg(lnum, stmtnum, 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
|
||||||
@@ -706,8 +706,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
throw lang.illegalType(lnum, lh);
|
throw lang.illegalType(lnum, lh);
|
||||||
return lh.concat([rh]);
|
return lh.concat([rh]);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"#" : function(lnum, stmtnum, args) { // array CONCAT
|
"#" : {f:function(lnum, stmtnum, args) { // array CONCAT
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => {
|
return twoArg(lnum, stmtnum, args, (lh,rh) => {
|
||||||
if (!Array.isArray(rh))
|
if (!Array.isArray(rh))
|
||||||
throw lang.illegalType(lnum, rh);
|
throw lang.illegalType(lnum, rh);
|
||||||
@@ -715,52 +715,52 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
throw lang.illegalType(lnum, lh);
|
throw lang.illegalType(lnum, lh);
|
||||||
return lh.concat(rh);
|
return lh.concat(rh);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"+" : function(lnum, stmtnum, args) { // addition, string concat
|
"+" : {f:function(lnum, stmtnum, args) { // addition, string concat
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => (!isNaN(lh) && !isNaN(rh)) ? (lh*1 + rh*1) : (lh + rh));
|
return twoArg(lnum, stmtnum, args, (lh,rh) => (!isNaN(lh) && !isNaN(rh)) ? (lh*1 + rh*1) : (lh + rh));
|
||||||
},
|
}},
|
||||||
"-" : function(lnum, stmtnum, args) {
|
"-" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh - rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh - rh);
|
||||||
},
|
}},
|
||||||
"*" : function(lnum, stmtnum, args) {
|
"*" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh * rh);
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => lh * rh);
|
||||||
},
|
}},
|
||||||
"/" : function(lnum, stmtnum, args) {
|
"/" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
||||||
if (rh == 0) throw lang.divByZero;
|
if (rh == 0) throw lang.divByZero;
|
||||||
return lh / rh;
|
return lh / rh;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"\\" : function(lnum, stmtnum, args) { // integer division, rounded towards zero
|
"\\" : {f:function(lnum, stmtnum, args) { // integer division, rounded towards zero
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
||||||
if (rh == 0) throw lang.divByZero;
|
if (rh == 0) throw lang.divByZero;
|
||||||
return (lh / rh)|0;
|
return (lh / rh)|0;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"MOD" : function(lnum, stmtnum, args) {
|
"MOD" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
||||||
if (rh == 0) throw lang.divByZero;
|
if (rh == 0) throw lang.divByZero;
|
||||||
return lh % rh;
|
return lh % rh;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"^" : function(lnum, stmtnum, args) {
|
"^" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
return twoArgNum(lnum, stmtnum, args, (lh,rh) => {
|
||||||
let r = Math.pow(lh, rh);
|
let r = Math.pow(lh, rh);
|
||||||
if (isNaN(r)) throw lang.badFunctionCallFormat();
|
if (isNaN(r)) throw lang.badFunctionCallFormat();
|
||||||
if (!isFinite(r)) throw lang.divByZero;
|
if (!isFinite(r)) throw lang.divByZero;
|
||||||
return r;
|
return r;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"TO" : function(lnum, stmtnum, args) {
|
"TO" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArgNum(lnum, stmtnum, args, (from, to) => new ForGen(from, to, 1));
|
return twoArgNum(lnum, stmtnum, args, (from, to) => new ForGen(from, to, 1));
|
||||||
},
|
}},
|
||||||
"STEP" : function(lnum, stmtnum, args) {
|
"STEP" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (gen, step) => {
|
return twoArg(lnum, stmtnum, 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" : function(lnum, stmtnum, args) {
|
"DIM" : {f:function(lnum, stmtnum, args) {
|
||||||
return varArgNum(lnum, stmtnum, args, (revdims) => {
|
return varArgNum(lnum, stmtnum, args, (revdims) => {
|
||||||
let dims = revdims.reverse();
|
let dims = revdims.reverse();
|
||||||
let arraydec = "Array(dims[0]).fill(0)";
|
let arraydec = "Array(dims[0]).fill(0)";
|
||||||
@@ -769,8 +769,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}
|
}
|
||||||
return eval(arraydec);
|
return eval(arraydec);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"PRINT" : function(lnum, stmtnum, args, seps) {
|
"PRINT" : {f:function(lnum, stmtnum, args, seps) {
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
println();
|
println();
|
||||||
else {
|
else {
|
||||||
@@ -801,8 +801,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}
|
}
|
||||||
|
|
||||||
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" : function(lnum, stmtnum, args, seps) {
|
"EMIT" : {f:function(lnum, stmtnum, args, seps) {
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
println();
|
println();
|
||||||
else {
|
else {
|
||||||
@@ -834,14 +834,14 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}
|
}
|
||||||
|
|
||||||
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" : function(lnum, stmtnum, args) {
|
"POKE" : {f:function(lnum, stmtnum, args) {
|
||||||
twoArgNum(lnum, stmtnum, args, (lh,rh) => sys.poke(lh, rh));
|
twoArgNum(lnum, stmtnum, args, (lh,rh) => sys.poke(lh, rh));
|
||||||
},
|
}},
|
||||||
"PEEK" : function(lnum, stmtnum, args) {
|
"PEEK" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => sys.peek(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => sys.peek(lh));
|
||||||
},
|
}},
|
||||||
"GOTO" : function(lnum, stmtnum, args) {
|
"GOTO" : {f:function(lnum, stmtnum, args) {
|
||||||
// search from gotoLabels first
|
// search from gotoLabels first
|
||||||
let line = gotoLabels[args[0].troValue];
|
let line = gotoLabels[args[0].troValue];
|
||||||
// if not found, use resolved variable
|
// if not found, use resolved variable
|
||||||
@@ -849,8 +849,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (line < 0) throw lang.syntaxfehler(lnum, line);
|
if (line < 0) throw lang.syntaxfehler(lnum, line);
|
||||||
|
|
||||||
return new JumpObj(line, 0, lnum, line);
|
return new JumpObj(line, 0, lnum, line);
|
||||||
},
|
}},
|
||||||
"GOSUB" : function(lnum, stmtnum, args) {
|
"GOSUB" : {f:function(lnum, stmtnum, args) {
|
||||||
// search from gotoLabels first
|
// search from gotoLabels first
|
||||||
let line = gotoLabels[args[0].troValue];
|
let line = gotoLabels[args[0].troValue];
|
||||||
// if not found, use resolved variable
|
// if not found, use resolved variable
|
||||||
@@ -860,20 +860,20 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
bStatus.gosubStack.push([lnum, stmtnum + 1]);
|
bStatus.gosubStack.push([lnum, stmtnum + 1]);
|
||||||
//println(lnum+" GOSUB into "+lh);
|
//println(lnum+" GOSUB into "+lh);
|
||||||
return new JumpObj(line, 0, lnum, line);
|
return new JumpObj(line, 0, lnum, line);
|
||||||
},
|
}},
|
||||||
"RETURN" : function(lnum, stmtnum, args) {
|
"RETURN" : {f:function(lnum, stmtnum, 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 new JumpObj(r[0], r[1], lnum, r);
|
return new JumpObj(r[0], r[1], lnum, r);
|
||||||
},
|
}},
|
||||||
"CLEAR" : function(lnum, stmtnum, args) {
|
"CLEAR" : {f:function(lnum, stmtnum, args) {
|
||||||
bStatus.vars = initBvars();
|
bStatus.vars = initBvars();
|
||||||
},
|
}},
|
||||||
"PLOT" : function(lnum, stmtnum, args) {
|
"PLOT" : {f:function(lnum, stmtnum, args) {
|
||||||
threeArgNum(lnum, stmtnum, args, (xpos, ypos, color) => graphics.plotPixel(xpos, ypos, color));
|
threeArgNum(lnum, stmtnum, args, (xpos, ypos, color) => graphics.plotPixel(xpos, ypos, color));
|
||||||
},
|
}},
|
||||||
"AND" : function(lnum, stmtnum, args) {
|
"AND" : {f:function(lnum, stmtnum, 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) => {
|
||||||
@@ -885,8 +885,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return it;
|
return it;
|
||||||
});
|
});
|
||||||
return argum[0] && argum[1];
|
return argum[0] && argum[1];
|
||||||
},
|
}},
|
||||||
"OR" : function(lnum, stmtnum, args) {
|
"OR" : {f:function(lnum, stmtnum, 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) => {
|
||||||
@@ -898,37 +898,37 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return it;
|
return it;
|
||||||
});
|
});
|
||||||
return argum[0] || argum[1];
|
return argum[0] || argum[1];
|
||||||
},
|
}},
|
||||||
"RND" : function(lnum, stmtnum, args) {
|
"RND" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => {
|
return oneArgNum(lnum, stmtnum, 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" : function(lnum, stmtnum, args) {
|
"ROUND" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => Math.round(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => Math.round(lh));
|
||||||
},
|
}},
|
||||||
"FLOOR" : function(lnum, stmtnum, args) {
|
"FLOOR" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => Math.floor(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => Math.floor(lh));
|
||||||
},
|
}},
|
||||||
"INT" : function(lnum, stmtnum, args) { // synonymous to FLOOR
|
"INT" : {f:function(lnum, stmtnum, args) { // synonymous to FLOOR
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => Math.floor(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => Math.floor(lh));
|
||||||
},
|
}},
|
||||||
"CEIL" : function(lnum, stmtnum, args) {
|
"CEIL" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => Math.ceil(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => Math.ceil(lh));
|
||||||
},
|
}},
|
||||||
"FIX" : function(lnum, stmtnum, args) {
|
"FIX" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => (lh|0));
|
return oneArgNum(lnum, stmtnum, args, (lh) => (lh|0));
|
||||||
},
|
}},
|
||||||
"CHR" : function(lnum, stmtnum, args) {
|
"CHR" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => String.fromCharCode(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => String.fromCharCode(lh));
|
||||||
},
|
}},
|
||||||
"TEST" : function(lnum, stmtnum, args) {
|
"TEST" : {f:function(lnum, stmtnum, 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" : function(lnum, stmtnum, args) { // list comprehension model
|
"FOREACH" : {f:function(lnum, stmtnum, 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);
|
||||||
@@ -944,8 +944,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
// put the varname to forstack
|
// put the varname to forstack
|
||||||
bStatus.forLnums[varname] = [lnum, stmtnum];
|
bStatus.forLnums[varname] = [lnum, stmtnum];
|
||||||
bStatus.forStack.push(varname);
|
bStatus.forStack.push(varname);
|
||||||
},
|
}},
|
||||||
"FOR" : function(lnum, stmtnum, args) { // generator model
|
"FOR" : {f:function(lnum, stmtnum, 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);
|
||||||
@@ -962,8 +962,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
// put the varname to forstack
|
// put the varname to forstack
|
||||||
bStatus.forLnums[varname] = [lnum, stmtnum];
|
bStatus.forLnums[varname] = [lnum, stmtnum];
|
||||||
bStatus.forStack.push(varname);
|
bStatus.forStack.push(varname);
|
||||||
},
|
}},
|
||||||
"NEXT" : function(lnum, stmtnum, args) {
|
"NEXT" : {f:function(lnum, stmtnum, 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
|
||||||
@@ -999,19 +999,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw lang.syntaxfehler(lnum, "extra arguments for NEXT");
|
throw lang.syntaxfehler(lnum, "extra arguments for NEXT");
|
||||||
},
|
}},
|
||||||
/*"BREAKTO" : function(lnum, stmtnum, args) {
|
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => {
|
|
||||||
var forVarname = bStatus.forStack.pop();
|
|
||||||
if (forVarname === undefined) {
|
|
||||||
throw lang.nextWithoutFor(lnum);
|
|
||||||
}
|
|
||||||
if (TRACEON) serial.println(`[BASIC.FOR] breaking from ${forVarname}, jump to ${lh}`);
|
|
||||||
|
|
||||||
if (lh < 0) throw lang.syntaxfehler(lnum, lh);
|
|
||||||
return new JumpObj(lh, 0, lnum, lh);
|
|
||||||
});
|
|
||||||
},*/
|
|
||||||
/*
|
/*
|
||||||
10 input;"what is your name";a$
|
10 input;"what is your name";a$
|
||||||
|
|
||||||
@@ -1049,7 +1037,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
| `-----------------
|
| `-----------------
|
||||||
`-----------------
|
`-----------------
|
||||||
*/
|
*/
|
||||||
"INPUT" : function(lnum, stmtnum, args) {
|
"INPUT" : {f:function(lnum, stmtnum, 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);
|
||||||
let troValue = args[0].troValue;
|
let troValue = args[0].troValue;
|
||||||
|
|
||||||
@@ -1075,85 +1063,85 @@ 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};
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"END" : function(lnum, stmtnum, args) {
|
"END" : {f:function(lnum, stmtnum, args) {
|
||||||
serial.println("Program terminated in "+lnum);
|
serial.println("Program terminated in "+lnum);
|
||||||
return new JumpObj(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER - 1, lnum, undefined); // GOTO far-far-away
|
return new JumpObj(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER - 1, lnum, undefined); // GOTO far-far-away
|
||||||
},
|
}},
|
||||||
"SPC" : function(lnum, stmtnum, args) {
|
"SPC" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => " ".repeat(lh));
|
return oneArgNum(lnum, stmtnum, args, (lh) => " ".repeat(lh));
|
||||||
},
|
}},
|
||||||
"LEFT" : function(lnum, stmtnum, args) {
|
"LEFT" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (str, len) => str.substring(0, len));
|
return twoArg(lnum, stmtnum, args, (str, len) => str.substring(0, len));
|
||||||
},
|
}},
|
||||||
"MID" : function(lnum, stmtnum, args) {
|
"MID" : {f:function(lnum, stmtnum, args) {
|
||||||
return threeArg(lnum, stmtnum, args, (str, start, len) => str.substring(start-INDEX_BASE, start-INDEX_BASE+len));
|
return threeArg(lnum, stmtnum, args, (str, start, len) => str.substring(start-INDEX_BASE, start-INDEX_BASE+len));
|
||||||
},
|
}},
|
||||||
"RIGHT" : function(lnum, stmtnum, args) {
|
"RIGHT" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (str, len) => str.substring(str.length - len, str.length));
|
return twoArg(lnum, stmtnum, args, (str, len) => str.substring(str.length - len, str.length));
|
||||||
},
|
}},
|
||||||
"SGN" : function(lnum, stmtnum, args) {
|
"SGN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => (it > 0) ? 1 : (it < 0) ? -1 : 0);
|
return oneArgNum(lnum, stmtnum, args, (it) => (it > 0) ? 1 : (it < 0) ? -1 : 0);
|
||||||
},
|
}},
|
||||||
"ABS" : function(lnum, stmtnum, args) {
|
"ABS" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.abs(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.abs(it));
|
||||||
},
|
}},
|
||||||
"SIN" : function(lnum, stmtnum, args) {
|
"SIN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.sin(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.sin(it));
|
||||||
},
|
}},
|
||||||
"COS" : function(lnum, stmtnum, args) {
|
"COS" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.cos(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.cos(it));
|
||||||
},
|
}},
|
||||||
"TAN" : function(lnum, stmtnum, args) {
|
"TAN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.tan(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.tan(it));
|
||||||
},
|
}},
|
||||||
"EXP" : function(lnum, stmtnum, args) {
|
"EXP" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.exp(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.exp(it));
|
||||||
},
|
}},
|
||||||
"ASN" : function(lnum, stmtnum, args) {
|
"ASN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.asin(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.asin(it));
|
||||||
},
|
}},
|
||||||
"ACO" : function(lnum, stmtnum, args) {
|
"ACO" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.acos(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.acos(it));
|
||||||
},
|
}},
|
||||||
"ATN" : function(lnum, stmtnum, args) {
|
"ATN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.atan(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.atan(it));
|
||||||
},
|
}},
|
||||||
"SQR" : function(lnum, stmtnum, args) {
|
"SQR" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.sqrt(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.sqrt(it));
|
||||||
},
|
}},
|
||||||
"CBR" : function(lnum, stmtnum, args) {
|
"CBR" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.cbrt(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.cbrt(it));
|
||||||
},
|
}},
|
||||||
"SINH" : function(lnum, stmtnum, args) {
|
"SINH" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.sinh(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.sinh(it));
|
||||||
},
|
}},
|
||||||
"COSH" : function(lnum, stmtnum, args) {
|
"COSH" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.cosh(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.cosh(it));
|
||||||
},
|
}},
|
||||||
"TANH" : function(lnum, stmtnum, args) {
|
"TANH" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.tanh(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.tanh(it));
|
||||||
},
|
}},
|
||||||
"LOG" : function(lnum, stmtnum, args) {
|
"LOG" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (it) => Math.log(it));
|
return oneArgNum(lnum, stmtnum, args, (it) => Math.log(it));
|
||||||
},
|
}},
|
||||||
"RESTORE" : function(lnum, stmtnum, args) {
|
"RESTORE" : {f:function(lnum, stmtnum, args) {
|
||||||
DATA_CURSOR = 0;
|
DATA_CURSOR = 0;
|
||||||
},
|
}},
|
||||||
"READ" : function(lnum, stmtnum, args) {
|
"READ" : {f:function(lnum, stmtnum, 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" : function(lnum, stmtnum, args) {
|
"OPTIONBASE" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArgNum(lnum, stmtnum, args, (lh) => {
|
return oneArgNum(lnum, stmtnum, 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" : function() { /*DATA must do nothing when encountered; they must be pre-processed*/ },
|
"DATA" : {f:function() { /*DATA must do nothing when encountered; they must be pre-processed*/ }},
|
||||||
/* Syopsis: MAP function, functor
|
/* Syopsis: MAP function, functor
|
||||||
*/
|
*/
|
||||||
"MAP" : function(lnum, stmtnum, args) {
|
"MAP" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (fn, functor) => {
|
return twoArg(lnum, stmtnum, 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");
|
||||||
@@ -1163,11 +1151,11 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
|
|
||||||
return functor.map(it => bStatus.getDefunThunk(lnum, stmtnum, fn)(lnum, stmtnum, [it]));
|
return functor.map(it => bStatus.getDefunThunk(lnum, stmtnum, fn)(lnum, stmtnum, [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" : function(lnum, stmtnum, args) {
|
"FOLD" : {f:function(lnum, stmtnum, args) {
|
||||||
return threeArg(lnum, stmtnum, args, (fn, init, functor) => {
|
return threeArg(lnum, stmtnum, 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");
|
||||||
@@ -1182,10 +1170,10 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
|
|
||||||
return akku;
|
return akku;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
/* Syopsis: FILTER function, functor
|
/* Syopsis: FILTER function, functor
|
||||||
*/
|
*/
|
||||||
"FILTER" : function(lnum, stmtnum, args) {
|
"FILTER" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (fn, functor) => {
|
return twoArg(lnum, stmtnum, 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");
|
||||||
@@ -1195,18 +1183,18 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
|
|
||||||
return functor.filter(it => bStatus.getDefunThunk(lnum, stmtnum, fn)(lnum, stmtnum, [it]));
|
return functor.filter(it => bStatus.getDefunThunk(lnum, stmtnum, fn)(lnum, stmtnum, [it]));
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
/* 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" : function(lnum, stmtnum, args) {
|
"DO" : {f:function(lnum, stmtnum, args) {
|
||||||
return args[args.length - 1];
|
return args[args.length - 1];
|
||||||
},
|
}},
|
||||||
"LABEL" : function(lnum, stmtnum, args) {
|
"LABEL" : {f:function(lnum, stmtnum, args) {
|
||||||
let labelname = args[0].troValue;
|
let labelname = args[0].troValue;
|
||||||
|
|
||||||
if (labelname === undefined) throw lang.syntaxfehler(lnum, "empty LABEL");
|
if (labelname === undefined) throw lang.syntaxfehler(lnum, "empty LABEL");
|
||||||
gotoLabels[labelname] = lnum;
|
gotoLabels[labelname] = lnum;
|
||||||
},
|
}},
|
||||||
"ON" : function(lnum, stmtnum, args) {
|
"ON" : {f:function(lnum, stmtnum, args) {
|
||||||
//args: functionName (string), testvalue (SyntaxTreeReturnObj), arg0 (SyntaxTreeReturnObj), arg1 (SyntaxTreeReturnObj), ...
|
//args: functionName (string), testvalue (SyntaxTreeReturnObj), arg0 (SyntaxTreeReturnObj), arg1 (SyntaxTreeReturnObj), ...
|
||||||
if (args[2] === undefined) throw lang.syntaxfehler(lnum);
|
if (args[2] === undefined) throw lang.syntaxfehler(lnum);
|
||||||
|
|
||||||
@@ -1223,14 +1211,14 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
return bStatus.builtin[jmpFun](lnum, stmtnum, [jmpTarget]);
|
return bStatus.builtin[jmpFun](lnum, stmtnum, [jmpTarget]);
|
||||||
},
|
}},
|
||||||
"MIN" : function(lnum, stmtnum, args) {
|
"MIN" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => (lh > rh) ? rh : lh);
|
return twoArg(lnum, stmtnum, args, (lh,rh) => (lh > rh) ? rh : lh);
|
||||||
},
|
}},
|
||||||
"MAX" : function(lnum, stmtnum, args) {
|
"MAX" : {f:function(lnum, stmtnum, args) {
|
||||||
return twoArg(lnum, stmtnum, args, (lh,rh) => (lh < rh) ? rh : lh);
|
return twoArg(lnum, stmtnum, args, (lh,rh) => (lh < rh) ? rh : lh);
|
||||||
},
|
}},
|
||||||
"GETKEYSDOWN" : function(lnum, stmtnum, args) {
|
"GETKEYSDOWN" : {f:function(lnum, stmtnum, 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);
|
||||||
let troValue = args[0].troValue;
|
let troValue = args[0].troValue;
|
||||||
let varname = troValue.toUpperCase();
|
let varname = troValue.toUpperCase();
|
||||||
@@ -1243,60 +1231,79 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (_basicConsts[varname]) throw lang.asgnOnConst(lnum, varname);
|
if (_basicConsts[varname]) throw lang.asgnOnConst(lnum, varname);
|
||||||
bStatus.vars[varname] = new BasicVar(keys, "array");
|
bStatus.vars[varname] = new BasicVar(keys, "array");
|
||||||
return {asgnVarName: varname, asgnValue: keys};
|
return {asgnVarName: varname, asgnValue: keys};
|
||||||
},
|
}},
|
||||||
"CURRY" : function(lnum, stmtnum, args) {
|
"<~" : {f:function(lnum, stmtnum, args) { // CURRY operator
|
||||||
throw new InternalError("Uh-oh you're not supposed to see this!");
|
return twoArg(lnum, stmtnum, args, (fn, value) => {
|
||||||
},
|
// TODO test only works with DEFUN'd functions
|
||||||
"TYPEOF" : function(lnum, stmtnum, args) {
|
if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Only works with DEFUN'd functions yet");
|
||||||
|
|
||||||
|
if (DBGON) {
|
||||||
|
serial.println("[BASIC.BUILTIN.CURRY] currying this function tree...");
|
||||||
|
serial.println(astToString(fn));
|
||||||
|
serial.println("[BASIC.BUILTIN.CURRY] with this value: "+value);
|
||||||
|
serial.println(Object.entries(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
let curriedTree = curryDefun(fn, value);
|
||||||
|
|
||||||
|
if (DBGON) {
|
||||||
|
serial.println("[BASIC.BUILTIN.CURRY] Here's your curried tree:");
|
||||||
|
serial.println(astToString(curriedTree));
|
||||||
|
}
|
||||||
|
|
||||||
|
return curriedTree;
|
||||||
|
});
|
||||||
|
}},
|
||||||
|
"TYPEOF" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, bv => {
|
return oneArg(lnum, stmtnum, args, bv => {
|
||||||
if (bv.Type === undefined || !(bv instanceof BasicVar))
|
if (bv.Type === undefined || !(bv instanceof BasicVar))
|
||||||
return JStoBASICtype(bv);
|
return JStoBASICtype(bv);
|
||||||
return bv.bvType;
|
return bv.bvType;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"LEN" : function(lnum, stmtnum, args) {
|
"LEN" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, lh => {
|
return oneArg(lnum, stmtnum, args, lh => {
|
||||||
if (lh.length === undefined) throw lang.illegalType();
|
if (lh.length === undefined) throw lang.illegalType();
|
||||||
return lh.length;
|
return lh.length;
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"HEAD" : function(lnum, stmtnum, args) {
|
"HEAD" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, lh => {
|
return oneArg(lnum, stmtnum, args, lh => {
|
||||||
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
||||||
return lh[0];
|
return lh[0];
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"TAIL" : function(lnum, stmtnum, args) {
|
"TAIL" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, lh => {
|
return oneArg(lnum, stmtnum, args, lh => {
|
||||||
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
||||||
return lh.slice(1, lh.length);
|
return lh.slice(1, lh.length);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"INIT" : function(lnum, stmtnum, args) {
|
"INIT" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, lh => {
|
return oneArg(lnum, stmtnum, args, lh => {
|
||||||
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
||||||
return lh.slice(0, lh.length - 1);
|
return lh.slice(0, lh.length - 1);
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"LAST" : function(lnum, stmtnum, args) {
|
"LAST" : {f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, lh => {
|
return oneArg(lnum, stmtnum, args, lh => {
|
||||||
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
if (lh.length === undefined || lh.length < 1) throw lang.illegalType();
|
||||||
return lh[lh.length - 1];
|
return lh[lh.length - 1];
|
||||||
});
|
});
|
||||||
},
|
}},
|
||||||
"OPTIONDEBUG" : function(lnum, stmtnum, args) {
|
"OPTIONDEBUG" : {f:function(lnum, stmtnum, args) {
|
||||||
oneArgNum(lnum, stmtnum, args, (lh) => {
|
oneArgNum(lnum, stmtnum, 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" : function(lnum, stmtnum, args) {
|
"OPTIONTRACE" : {f:function(lnum, stmtnum, args) {
|
||||||
oneArgNum(lnum, stmtnum, args, (lh) => {
|
oneArgNum(lnum, stmtnum, 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" : function(lnum, stmtnum, args) {
|
"RESOLVE" : {f:function(lnum, stmtnum, args) {
|
||||||
if (DBGON) {
|
if (DBGON) {
|
||||||
return oneArg(lnum, stmtnum, args, (it) => {
|
return oneArg(lnum, stmtnum, args, (it) => {
|
||||||
println(it);
|
println(it);
|
||||||
@@ -1305,8 +1312,8 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
else {
|
else {
|
||||||
throw lang.syntaxfehler(lnum);
|
throw lang.syntaxfehler(lnum);
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"RESOLVEVAR" : function(lnum, stmtnum, args) {
|
"RESOLVEVAR" : {f:function(lnum, stmtnum, args) {
|
||||||
if (DBGON) {
|
if (DBGON) {
|
||||||
return oneArg(lnum, stmtnum, args, (it) => {
|
return oneArg(lnum, stmtnum, args, (it) => {
|
||||||
let v = bStatus.vars[args[0].troValue];
|
let v = bStatus.vars[args[0].troValue];
|
||||||
@@ -1317,28 +1324,28 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
else {
|
else {
|
||||||
throw lang.syntaxfehler(lnum);
|
throw lang.syntaxfehler(lnum);
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"UNRESOLVE" : function(lnum, stmtnum, args) {
|
"UNRESOLVE" : {f:function(lnum, stmtnum, args) {
|
||||||
if (DBGON) {
|
if (DBGON) {
|
||||||
println(args[0]);
|
println(args[0]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw lang.syntaxfehler(lnum);
|
throw lang.syntaxfehler(lnum);
|
||||||
}
|
}
|
||||||
},
|
}},
|
||||||
"UNRESOLVE0" : function(lnum, stmtnum, args) {
|
"UNRESOLVE0" : {f:function(lnum, stmtnum, 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 = {};
|
||||||
bF._1os = {"!":1,"~":1,"#":1,"<":1,"=":1,">":1,"*":1,"+":1,"-":1,"/":1,"^":1,":":1};
|
bF._1os = {"!":1,"~":1,"#":1,"<":1,"=":1,">":1,"*":1,"+":1,"-":1,"/":1,"^":1,":":1,"$":1};
|
||||||
bF._2os = {"<":1,"=":1,">":1};
|
bF._2os = {"<":1,"=":1,">":1,"~":1,"$":1,"*":1};
|
||||||
bF._uos = {"+":1,"-":1,"NOT":1,"BNOT":1};
|
bF._uos = {"+":1,"-":1,"NOT":1,"BNOT":1};
|
||||||
bF._isNum = function(code) {
|
bF._isNum = function(code) {
|
||||||
return (code >= 0x30 && code <= 0x39) || code == 0x5F;
|
return (code >= 0x30 && code <= 0x39) || code == 0x5F;
|
||||||
@@ -1391,12 +1398,13 @@ bF._opPrc = {
|
|||||||
"STEP":41,
|
"STEP":41,
|
||||||
"!":50,"~":51, // array CONS and PUSH
|
"!":50,"~":51, // array CONS and PUSH
|
||||||
"#": 52, // array concat
|
"#": 52, // array concat
|
||||||
|
"<~": 100, // curry operator
|
||||||
|
"~>": 101, // closure operator
|
||||||
"=":999,
|
"=":999,
|
||||||
"IN":1000
|
"IN":1000
|
||||||
};
|
};
|
||||||
bF._opRh = {"^":1,"=":1,"!":1,"IN":1};
|
bF._opRh = {"^":1,"=":1,"!":1,"IN":1,"<~":1,"~>":1};
|
||||||
// these names appear on executeSyntaxTree as "exceptional terms" on parsing (regular function calls are not "exceptional terms")
|
// these names appear on executeSyntaxTree as "exceptional terms" on parsing (regular function calls are not "exceptional terms")
|
||||||
bF._keywords = {"IF":1,"THEN":1,"ELSE":1,"DEFUN":1};
|
|
||||||
bF._tokenise = function(lnum, cmd) {
|
bF._tokenise = function(lnum, cmd) {
|
||||||
var _debugprintStateTransition = false;
|
var _debugprintStateTransition = false;
|
||||||
var k;
|
var k;
|
||||||
@@ -2588,7 +2596,7 @@ let JStoBASICtype = function(object) {
|
|||||||
else if (object.arrName !== undefined) return "internal_arrindexing_lazy";
|
else if (object.arrName !== undefined) return "internal_arrindexing_lazy";
|
||||||
else if (object.asgnVarName !== undefined) return "internal_assignment_object";
|
else if (object.asgnVarName !== undefined) return "internal_assignment_object";
|
||||||
else if (object instanceof ForGen) return "generator";
|
else if (object instanceof ForGen) return "generator";
|
||||||
else if (object instanceof BasicAST) return "usrdefun";
|
else if (object instanceof BasicAST || object.astLeaves !== undefined) return "usrdefun";
|
||||||
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";
|
||||||
@@ -2638,7 +2646,7 @@ bF._executeSyntaxTree = function(lnum, stmtnum, 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];
|
var func = (bStatus.builtin[funcName] === undefined) ? undefined : bStatus.builtin[funcName].f;
|
||||||
|
|
||||||
|
|
||||||
if ("IF" == funcName) {
|
if ("IF" == funcName) {
|
||||||
@@ -2653,7 +2661,7 @@ bF._executeSyntaxTree = function(lnum, stmtnum, syntaxTree, recDepth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var iftest = bStatus.builtin["TEST"](lnum, stmtnum, [testedval]);
|
var iftest = bStatus.builtin["TEST"].f(lnum, stmtnum, [testedval]);
|
||||||
|
|
||||||
if (!iftest && syntaxTree.astLeaves[2] !== undefined)
|
if (!iftest && syntaxTree.astLeaves[2] !== undefined)
|
||||||
return bF._executeSyntaxTree(lnum, stmtnum, syntaxTree.astLeaves[2], recDepth + 1);
|
return bF._executeSyntaxTree(lnum, stmtnum, syntaxTree.astLeaves[2], recDepth + 1);
|
||||||
@@ -2724,7 +2732,7 @@ bF._executeSyntaxTree = function(lnum, stmtnum, syntaxTree, recDepth) {
|
|||||||
arrays.push(bF._executeSyntaxTree(lnum, stmtnum, syntaxTree.astLeaves[k], recDepth + 1));
|
arrays.push(bF._executeSyntaxTree(lnum, stmtnum, syntaxTree.astLeaves[k], recDepth + 1));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let r = bStatus.builtin["ON"](lnum, stmtnum, [functionName, testValue].concat(arrays))
|
let r = bStatus.builtin["ON"].f(lnum, stmtnum, [functionName, testValue].concat(arrays))
|
||||||
return new SyntaxTreeReturnObj(JStoBASICtype(r.jmpReturningValue), r.jmpReturningValue, r.jmpNext);
|
return new SyntaxTreeReturnObj(JStoBASICtype(r.jmpReturningValue), r.jmpReturningValue, r.jmpNext);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@@ -2732,38 +2740,6 @@ bF._executeSyntaxTree = function(lnum, stmtnum, syntaxTree, recDepth) {
|
|||||||
throw lang.errorinline(lnum, "ON error", e);
|
throw lang.errorinline(lnum, "ON error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ("CURRY" == funcName) {
|
|
||||||
if (syntaxTree.astLeaves.length !== 2) throw lang.badFunctionCallFormat();
|
|
||||||
let functionName = syntaxTree.astLeaves[0].astValue;
|
|
||||||
let functionTreeVar = bStatus.vars[functionName];
|
|
||||||
if (functionTreeVar.bvType !== "usrdefun") throw lang.badFunctionCallFormat(`'${functionName}' is not a user-defined function`);
|
|
||||||
|
|
||||||
let functionTree = functionTreeVar.bvLiteral;
|
|
||||||
let valueTree = syntaxTree.astLeaves[1];
|
|
||||||
|
|
||||||
if (DBGON) {
|
|
||||||
serial.println("[BASIC.CURRY] currying this function tree...");
|
|
||||||
serial.println(astToString(functionTree));
|
|
||||||
serial.println("[BASIC.CURRY] with this value tree:");
|
|
||||||
serial.println(astToString(valueTree));
|
|
||||||
serial.println("\n[BASIC.CURRY] now resolving the value tree...")
|
|
||||||
}
|
|
||||||
let curryingValue = resolve(bF._executeSyntaxTree(lnum, stmtnum, valueTree, recDepth + 1));
|
|
||||||
|
|
||||||
if (DBGON) {
|
|
||||||
serial.println("\n[BASIC.CURRY] I'm back from resolving the value tree!");
|
|
||||||
serial.println(`[BASIC.CURRY] It seems you want to curry '${functionName}' with '${curryingValue}' amirite? Let's do it!\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let curriedTree = curryDefun(functionTree, curryingValue);
|
|
||||||
|
|
||||||
if (DBGON) {
|
|
||||||
serial.println("[BASIC.CURRY] Here's your curried tree:");
|
|
||||||
serial.println(astToString(curriedTree));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SyntaxTreeReturnObj("internal_lambda_curry", curriedTree, [lnum, stmtnum + 1]);
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
var args = syntaxTree.astLeaves.map(it => bF._executeSyntaxTree(lnum, stmtnum, it, recDepth + 1));
|
var args = syntaxTree.astLeaves.map(it => bF._executeSyntaxTree(lnum, stmtnum, it, recDepth + 1));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
1 KS=4!1!2!5!3!NIL
|
1 KS=4!1!2!5!3!NIL
|
||||||
10 DEFUN LESSER(P,X)=X<P
|
10 DEFUN LESSER(P,X)=X<P
|
||||||
11 KLS=CURRY(LESSER,KS(0))
|
11 KLS=LESSER<~HEAD KS
|
||||||
20 PRINT KLS(6)
|
20 FOR K=1 TO 5
|
||||||
|
21 PRINT KLS(K)
|
||||||
|
22 NEXT
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
10 DEFUN F(K,T)=ABS(T)==K
|
10 DEFUN F(K,T)=ABS(T)==K
|
||||||
11 CF=CURRY(F,32)
|
20 CF=F<~32
|
||||||
20 PRINT TYPEOF(F):REM must be usrdefun
|
30 PRINT CF(24) : REM will print 'false'
|
||||||
21 PRINT TYPEOF(CF):REM also must be usrdefun
|
40 PRINT CF(-32) : REM will print 'true'
|
||||||
30 PRINT CF(24):PRINT CF(-32)
|
|
||||||
31 REM Expected printout: false true
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
1 XS=4!1!2!3!5!NIL
|
1 XS=4!1!2!3!5!NIL
|
||||||
10 DEFUN K(P,X)=X<P
|
10 DEFUN K(P,X)=X<P
|
||||||
20 NXS=FILTER(CURRY(K,XS(0)),XS)
|
20 NXS=FILTER(K<~HEAD XS,XS)
|
||||||
30 PRINT NXS
|
30 PRINT NXS
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
2 REM qsort xs = qsort [x | x<-tail xs, x<head xs] ++ [head xs] ++ qsort [x | x<-tail xs, x>=head xs]
|
2 REM qsort xs = qsort [x | x<-tail xs, x<head xs] ++ [head xs] ++ qsort [x | x<-tail xs, x>=head xs]
|
||||||
10 DEFUN LESS(P,X)=X<P
|
10 DEFUN LESS(P,X)=X<P
|
||||||
11 DEFUN GTEQ(P,X)=X>=P
|
11 DEFUN GTEQ(P,X)=X>=P
|
||||||
12 DEFUN QSORT(XS)=IF LEN(XS)<1 THEN NIL ELSE QSORT(FILTER(CURRY(LESS,HEAD XS),TAIL XS)) # HEAD(XS)!NIL # QSORT(FILTER(CURRY(GTEQ,HEAD XS),TAIL XS))
|
12 DEFUN QSORT(XS)=IF LEN(XS)<1 THEN NIL ELSE QSORT(FILTER(LESS<~HEAD(XS),TAIL(XS))) # HEAD(XS)!NIL # QSORT(FILTER(GTEQ<~HEAD(XS),TAIL(XS)))
|
||||||
100 L=7!9!4!5!2!3!1!8!6!NIL
|
100 L=7!9!4!5!2!3!1!8!6!NIL
|
||||||
110 SL=QSORT(L)
|
110 PRINT L
|
||||||
120 PRINT L:PRINT SL
|
120 PRINT QSORT(L)
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ There are six basic types: \emph{number}, \emph{boolean}, \emph{string}, \emph{
|
|||||||
|
|
||||||
\emph{Boolean} is the type of the values that is either \codebf{TRUE} or \codebf{FALSE}. Number \codebf{0} and \codebf{FALSE} makes condition \emph{false}. When used in numeric context, \codebf{FALSE} will be interpreted as 0 and \codebf{TRUE} as 1.
|
\emph{Boolean} is the type of the values that is either \codebf{TRUE} or \codebf{FALSE}. Number \codebf{0} and \codebf{FALSE} makes condition \emph{false}. When used in numeric context, \codebf{FALSE} will be interpreted as 0 and \codebf{TRUE} as 1.
|
||||||
|
|
||||||
\emph{String} represents immutable\footnote{cannot be altered directly} sequences of bytes. However, you can't weave them to make something like \emph{string array}\footnote{future feature\ldots maybe\ldots? Probably not\ldots}.
|
\emph{String} represents immutable\footnote{cannot be altered directly} sequences of bytes. However, you can't weave them to make something like \emph{string array}\footnote{future feature\ldots\ maybe\ldots? Probably not\ldots}.
|
||||||
|
|
||||||
\emph{Array} represents collection of numbers in 1- or more dimensions.
|
\emph{Array} represents collection of numbers in 1- or more dimensions.
|
||||||
|
|
||||||
\emph{Generator} represents a value that automatically counts up/down whenever they have been called in For-Next loop.
|
\emph{Generator} represents a value that automatically counts up/down whenever they have been called in For-Next loop.
|
||||||
|
|
||||||
\emph{Functions} are, well\ldots functions\footnote{This is not a closure; there is no way you can define a local- or anonymous variable in BASIC.}, especially user-defined ones. Functions are \emph{type} because some built-in functions will actually take \emph{functions} as arguments.
|
\emph{Functions} are, well\ldots\ functions\footnote{This is not a closure; there is no way you can define a local- or anonymous variable in BASIC.}, especially user-defined ones. Functions are \emph{type} because some built-in functions will actually take \emph{functions} as arguments.
|
||||||
|
|
||||||
\section{Control Flow}
|
\section{Control Flow}
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,13 @@ So what the fsck is currying? Consider the following code:
|
|||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
10 DEFUN F(K,T)=ABS(T)==K
|
10 DEFUN F(K,T)=ABS(T)==K
|
||||||
20 CF=CURRY(F,32)
|
20 CF=F<~32
|
||||||
30 PRINT CF(24) : REM will print 'false'
|
30 PRINT CF(24) : REM will print 'false'
|
||||||
40 PRINT CF(-32) : REM will print 'true'
|
40 PRINT CF(-32) : REM will print 'true'
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Here, \code{CF} is a curried function of \code{F}; built-in function \code{CURRY} applies \code{32} to the first parametre of the function \code{F}, which dynamically returns a \emph{function} of \code{CF(T) = ABS(T) == 32}. The fact that \code{CURRY} returns a \emph{function} opens many possibilities, for example, you can create loads of sibling functions without making loads of duplicate codes.
|
% NOTE: you can't use \basiccurry within \code{}
|
||||||
|
Here, \code{CF} is a curried function of \code{F}; built-in operator \code{$<\!\sim$} applies \code{32} to the first parameter of the function \code{F}, which dynamically returns a \emph{function} of \code{CF(T) = ABS(T) == 32}. The fact that Curry Operator returns a \emph{function} opens many possibilities, for example, you can create loads of sibling functions without making loads of duplicate codes.
|
||||||
|
|
||||||
\section[Wrapping-Up]{The Grand Unification}
|
\section[Wrapping-Up]{The Grand Unification}
|
||||||
|
|
||||||
@@ -54,20 +55,20 @@ Using all the knowledge we have learned, it should be trivial\footnote{/s} to wr
|
|||||||
10 DEFUN LESS(P,X)=X<P
|
10 DEFUN LESS(P,X)=X<P
|
||||||
11 DEFUN GTEQ(P,X)=X>=P
|
11 DEFUN GTEQ(P,X)=X>=P
|
||||||
12 DEFUN QSORT(XS)=IF LEN(XS)<1 THEN NIL ELSE
|
12 DEFUN QSORT(XS)=IF LEN(XS)<1 THEN NIL ELSE
|
||||||
QSORT(FILTER(CURRY(LESS,HEAD XS),TAIL XS)) # HEAD(XS)!NIL #
|
QSORT(FILTER(LESS<~HEAD(XS),TAIL(XS))) # HEAD(XS)!NIL #
|
||||||
QSORT(FILTER(CURRY(GTEQ,HEAD XS),TAIL XS))
|
QSORT(FILTER(GTEQ<~HEAD(XS),TAIL(XS)))
|
||||||
100 L=7!9!4!5!2!3!1!8!6!NIL
|
100 L=7!9!4!5!2!3!1!8!6!NIL
|
||||||
110 SL=QSORT(L)
|
110 PRINT L
|
||||||
120 PRINT L:PRINT SL
|
120 PRINT QSORT(L)
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Line 12 implements quicksort algorithm, using \code{LESS} and \code{GTEQ} as helper functions. \code{LESS} is a user-function version of less-than operator, and \code{GTEQ} is similar. \code{QSORT} selects a pivot using head-element of array \code{XS}\footnote{stands for \emph{X's}} using \code{HEAD XS}, then using curried version of \code{LESS} and \code{GTEQ} to move values lesser than pivot \emph{minus the head element (here \code{TAIL XS} returns such array)} to the left and greater to the right, and these two sorted \emph{chunks} are recursively sorted using the same \code{QSORT} function. Currying is used to give comparison functions a pivot-value to compare against, and also because \code{FILTER} wants a \emph{function} and not an \emph{expression}. \code{HEAD XS} simply fetches a head-elemement of \code{XS}. \code{HEAD(XS)!NIL} creates a single-element array contains \code{XS(0)}.
|
Line 12 implements quicksort algorithm, using \code{LESS} and \code{GTEQ} as helper functions. \code{LESS} is a user-function version of less-than operator, and \code{GTEQ} is similar. \code{QSORT} selects a pivot using head-element of array \code{XS}\footnote{stands for \emph{X's}} using \code{HEAD XS}, then using curried version of \code{LESS} and \code{GTEQ} to move values lesser than pivot \emph{minus the head element (here \code{TAIL XS} returns such array)} to the left and greater to the right, and these two sorted \emph{chunks} are recursively sorted using the same \code{QSORT} function. Currying is used to give comparison functions a pivot-value to compare against, and also because \code{FILTER} wants a \emph{function} and not an \emph{expression}. \code{HEAD XS} simply fetches a head-elemement of \code{XS}. \code{HEAD(XS)!NIL} creates a single-element array contains \code{XS(0)}.
|
||||||
|
|
||||||
%Uncomment this if you finally decided to support a closure%
|
%Uncomment this if you finally decided to support a closure%
|
||||||
Using \emph{closure}, the definition of \code{QSORT} can truly be a one-liner and be \emph{even more cryptic}:
|
%% Using \emph{closure}, the definition of \code{QSORT} can truly be a one-liner and be \emph{even more cryptic}:
|
||||||
|
%%
|
||||||
\begin{lstlisting}
|
%% \begin{lstlisting}
|
||||||
10 DEFUN QSORT(XS)=IF LEN(XS)<1 THEN NIL ELSE
|
%% 10 QSORT=[XS]~>IF LEN(XS)<1 THEN NIL ELSE
|
||||||
QSORT(FILTER([K]~>K<HEAD XS,TAIL XS)) # HEAD(XS)!NIL #
|
%% QSORT(FILTER([K]~>K<HEAD XS,TAIL XS)) # HEAD(XS)!NIL #
|
||||||
QSORT(FILTER([K]~>K>=HEAD XS,TAIL XS))
|
%% QSORT(FILTER([K]~>K>=HEAD XS,TAIL XS))
|
||||||
\end{lstlisting}
|
%% \end{lstlisting}
|
||||||
|
|||||||
@@ -72,13 +72,11 @@ Furthermore, \emph{sigils} are not used in the \tbas{} and attempting to use one
|
|||||||
|
|
||||||
Types of data recognised by \tbas{} are distinguished by some arcane magic of Javascript auto-casing mumbo-jumbo
|
Types of data recognised by \tbas{} are distinguished by some arcane magic of Javascript auto-casing mumbo-jumbo
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{RCL}
|
\begin{tabulary}{\textwidth}{rCL}
|
||||||
Type & Range & Precision \\
|
Type & Range & Precision \\
|
||||||
\hline \hline
|
\hline
|
||||||
String & As many as the machine can handle & \, \\
|
String & As many as the machine can handle & \, \\
|
||||||
\hline
|
|
||||||
Integer & $ \pm 2^{53}-1 $ & exact within the range \\
|
Integer & $ \pm 2^{53}-1 $ & exact within the range \\
|
||||||
\hline
|
|
||||||
Float & $ \pm 4.9406564584124654 \times 10^{-324} $ -- $ \pm 1.7976931348623157 \times 10^{308} $ & about 16 significant figures \\
|
Float & $ \pm 4.9406564584124654 \times 10^{-324} $ -- $ \pm 1.7976931348623157 \times 10^{308} $ & about 16 significant figures \\
|
||||||
\end{tabulary}
|
\end{tabulary}
|
||||||
|
|
||||||
@@ -87,21 +85,22 @@ Float & $ \pm 4.9406564584124654 \times 10^{-324} $ -- $ \pm 1.7976931348623157
|
|||||||
|
|
||||||
The order of precedence of the operators is as shown below, lower numbers means they have higher precedence (more tightly bound)
|
The order of precedence of the operators is as shown below, lower numbers means they have higher precedence (more tightly bound)
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CCC|CCC}
|
\begin{tabulary}{\textwidth}{cCc|cCc}
|
||||||
Precedence & Operators & Associativity & Precedence & Operators & Associativity \\
|
Order & Op & Associativity & Order & Op & Associativity \\
|
||||||
\hline
|
\hline
|
||||||
1 & \basicexp & Right & 10 & \condensedfont{BAND} & Left \\
|
1 & \basicexp & Right & 11 & \condensedfont{BXOR} & Left \\
|
||||||
2 & \ast \quad $/$ \quad $\backslash$ & Left & 11 & \condensedfont{BXOR} & Left \\
|
2 & \ast \quad $/$ \quad $\backslash$ & Left & 12 & \condensedfont{BOR} & Left \\
|
||||||
3 & \condensedfont{MOD} & Left & 12 & \condensedfont{BOR} & Left \\
|
3 & \condensedfont{MOD} & Left & 13 & \condensedfont{AND} & Left \\
|
||||||
4 & $+$ \quad $-$ & Left & 13 & \condensedfont{AND} & Left \\
|
4 & $+$ \quad $-$ & Left & 14 & \condensedfont{OR} & Left \\
|
||||||
5 & \condensedfont{NOT} \enskip \condensedfont{BNOT} & Left & 14 & \condensedfont{OR} & Left \\
|
5 & \condensedfont{NOT} \enskip \condensedfont{BNOT} & Left & 15 & \condensedfont{TO} \enskip \condensedfont{STEP} & Left \\
|
||||||
6 & <\!< \quad >\!> & Left & 15 & \condensedfont{TO} \enskip \condensedfont{STEP} & Left \\
|
6 & <\!< \quad >\!> & Left & 16 & ! & Right \\
|
||||||
7 & < \enskip > \enskip <\!= \enskip =\!< \enskip >\!= \enskip =\!> & Left & 16 & ! & Right \\
|
7 & < \enskip > \enskip <\!= \enskip =\!< \enskip >\!= \enskip =\!> & Left & 17 & \sim & Left\\
|
||||||
8 & == \quad <\!> \quad >\!< & Left & 17 & \sim & Left\\
|
8 & == \quad <\!> \quad >\!< & Left & 18 & \# & Left \\
|
||||||
9 & \condensedfont{MIN} \enskip \condensedfont{MAX} & Left & 18 & \# & Left \\
|
9 & \condensedfont{MIN} \enskip \condensedfont{MAX} & Left & 19 & \basiccurry & Right \\
|
||||||
& & & 19 & = & Right \\
|
10 & \condensedfont{BAND} & Left & 20 & = & Right \\
|
||||||
\end{tabulary}
|
\end{tabulary}
|
||||||
|
|
||||||
|
|
||||||
\subsubsection*{Examples}
|
\subsubsection*{Examples}
|
||||||
\begin{itemlist}
|
\begin{itemlist}
|
||||||
\item Exponentiation is more tightly bound than negation: \code{-1\basicexp 2 == -(1\basicexp 2) == -1} but \code{(-1)\basicexp 2 == 1}
|
\item Exponentiation is more tightly bound than negation: \code{-1\basicexp 2 == -(1\basicexp 2) == -1} but \code{(-1)\basicexp 2 == 1}
|
||||||
@@ -112,7 +111,7 @@ Precedence & Operators & Associativity & Precedence & Operators & Associativity
|
|||||||
|
|
||||||
Mathematical operators operate on expressions that returns numeric value only, except for the \code{+} operator which will take the action of string concatenation if either of the operand is non-numeric.
|
Mathematical operators operate on expressions that returns numeric value only, except for the \code{+} operator which will take the action of string concatenation if either of the operand is non-numeric.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Operation & Result \\
|
Code & Operation & Result \\
|
||||||
\hline
|
\hline
|
||||||
\emph{x} $=$ \emph{y} & Assignment & Assigns \emph{y} into \emph{x} \\
|
\emph{x} $=$ \emph{y} & Assignment & Assigns \emph{y} into \emph{x} \\
|
||||||
@@ -146,7 +145,7 @@ $-$ \emph{x} & Unary Minus & Negative value of \emph{x} \\
|
|||||||
|
|
||||||
Comparison operator can operate on numeric and string operands. String operands will be automatically converted to numeric value if they can be; if one operand is numeric and other is non-numeric string, the former will be converted to string value.
|
Comparison operator can operate on numeric and string operands. String operands will be automatically converted to numeric value if they can be; if one operand is numeric and other is non-numeric string, the former will be converted to string value.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Operation & Result \\
|
Code & Operation & Result \\
|
||||||
\hline
|
\hline
|
||||||
\emph{x} == \emph{y} & Equal & True if \emph{x} equals \emph{y} \\
|
\emph{x} == \emph{y} & Equal & True if \emph{x} equals \emph{y} \\
|
||||||
@@ -169,7 +168,7 @@ When comparing strings, the ordering is as follows:
|
|||||||
|
|
||||||
Bitwise operators operate on unsigned integers only. Floating points are truncated\footnote{truncated towards zero} to integers.
|
Bitwise operators operate on unsigned integers only. Floating points are truncated\footnote{truncated towards zero} to integers.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Operation & Result \\
|
Code & Operation & Result \\
|
||||||
\hline
|
\hline
|
||||||
\emph{x} <\!< \emph{y} & Bitwise Shift Left & Shifts entire bits of \emph{x} by \emph{y} \\
|
\emph{x} <\!< \emph{y} & Bitwise Shift Left & Shifts entire bits of \emph{x} by \emph{y} \\
|
||||||
@@ -184,7 +183,7 @@ Code & Operation & Result \\
|
|||||||
|
|
||||||
Boolean operators operate on boolean values. If one of the operand is not boolean, it will be cast to appropriate boolean value. See \ref{valuesandtypes} for casting rules.
|
Boolean operators operate on boolean values. If one of the operand is not boolean, it will be cast to appropriate boolean value. See \ref{valuesandtypes} for casting rules.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Operation & Result \\
|
Code & Operation & Result \\
|
||||||
\hline
|
\hline
|
||||||
\condensedfont{NOT} \emph{x} & Logical negation & True if \emph{x} is false and vice versa \\
|
\condensedfont{NOT} \emph{x} & Logical negation & True if \emph{x} is false and vice versa \\
|
||||||
@@ -196,7 +195,7 @@ Code & Operation & Result \\
|
|||||||
|
|
||||||
Generator operators operate on numeric values and generators to create and modify a generator.
|
Generator operators operate on numeric values and generators to create and modify a generator.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Result \\
|
Code & Result \\
|
||||||
\hline
|
\hline
|
||||||
\emph{x} \condensedfont{TO} \emph{y} & Creates an generator that counts from \emph{x} to \emph{y} \\
|
\emph{x} \condensedfont{TO} \emph{y} & Creates an generator that counts from \emph{x} to \emph{y} \\
|
||||||
@@ -207,7 +206,7 @@ Code & Result \\
|
|||||||
|
|
||||||
Array operators operate on arrays and numeric values.
|
Array operators operate on arrays and numeric values.
|
||||||
|
|
||||||
\begin{tabulary}{\textwidth}{CLL}
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
Code & Operation & Result \\
|
Code & Operation & Result \\
|
||||||
\hline
|
\hline
|
||||||
\emph{x} $!$ \emph{y} & Cons & Prepends a value of \emph{x} into an array of \emph{y} \\
|
\emph{x} $!$ \emph{y} & Cons & Prepends a value of \emph{x} into an array of \emph{y} \\
|
||||||
@@ -217,6 +216,17 @@ Code & Operation & Result \\
|
|||||||
|
|
||||||
Arbitrary arrays can be constructed using empty-array constant \codebf{NIL}.
|
Arbitrary arrays can be constructed using empty-array constant \codebf{NIL}.
|
||||||
|
|
||||||
|
\subsection{Function Operators}
|
||||||
|
|
||||||
|
Function operators operate on functions and some values.
|
||||||
|
|
||||||
|
\begin{tabulary}{\textwidth}{clL}
|
||||||
|
Code & Operation & Result \\
|
||||||
|
\hline
|
||||||
|
\emph{f} \basiccurry{} \emph{x} & Curry & Apply \emph{x} into the first parameter of the function \emph{f} \\
|
||||||
|
%{[}\emph{x},\,\emph{y}\ldots{]} \basicclosure{} \emph{e} & Closure & Creates a closure (anonymous function) from one or more parameters \emph{x},\,\emph{y}\ldots\ and an expression \emph{e} \\
|
||||||
|
\end{tabulary}
|
||||||
|
|
||||||
\section{Syntax In EBNF}
|
\section{Syntax In EBNF}
|
||||||
|
|
||||||
If you're \emph{that} into the language theory of computer science, texts above are just waste of bytes/inks/pixel-spaces/whatever; this little section should be more than enough!
|
If you're \emph{that} into the language theory of computer science, texts above are just waste of bytes/inks/pixel-spaces/whatever; this little section should be more than enough!
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ If \code{TRUTH\_VALUE} is truthy, executes \code{TRUE\_EXPRESSION}; if \code{TRU
|
|||||||
|
|
||||||
Jumps to \code{INDEX\_EXPRESSION}-th line number in the argements. If \code{INDEX\_EXPRESSION} is outside of range of the arguments, no jump will be performed.
|
Jumps to \code{INDEX\_EXPRESSION}-th line number in the argements. If \code{INDEX\_EXPRESSION} is outside of range of the arguments, no jump will be performed.
|
||||||
|
|
||||||
\subsubsection*{Parametres}
|
\subsubsection*{parameters}
|
||||||
|
|
||||||
\begin{itemlist}
|
\begin{itemlist}
|
||||||
\item \code{LINEn} can be a number, numeric expression (aka equations) or a line label.
|
\item \code{LINEn} can be a number, numeric expression (aka equations) or a line label.
|
||||||
@@ -39,7 +39,7 @@ With the aid of other statements\footnote{Actually, only the IF is useful, unles
|
|||||||
|
|
||||||
Oh, and you can define your own function, in traditional \code{DEF FN} sense.
|
Oh, and you can define your own function, in traditional \code{DEF FN} sense.
|
||||||
|
|
||||||
\subsubsection*{Parametres}
|
\subsubsection*{parameters}
|
||||||
|
|
||||||
\begin{itemlist}
|
\begin{itemlist}
|
||||||
\item \code{NAME} must be a valid variable name
|
\item \code{NAME} must be a valid variable name
|
||||||
|
|||||||
@@ -51,11 +51,15 @@
|
|||||||
\newenvironment{itemlist}{\vspace{0pt}\itemize}{\enditemize}
|
\newenvironment{itemlist}{\vspace{0pt}\itemize}{\enditemize}
|
||||||
|
|
||||||
%% Idioms %%
|
%% Idioms %%
|
||||||
\hyphenation{Java-scr-ipt}
|
\hyphenation{Java-script}
|
||||||
\hyphenation{ECMA-scr-ipt}
|
\hyphenation{ECMA-script}
|
||||||
|
|
||||||
\newcommand\forceindent{\hskip1.5em}
|
\newcommand\forceindent{\hskip1.5em}
|
||||||
|
|
||||||
|
%% BASIC operators %%
|
||||||
\newcommand\basicexp{\raisebox{0.25ex}{\scriptsize\wedge}}
|
\newcommand\basicexp{\raisebox{0.25ex}{\scriptsize\wedge}}
|
||||||
|
\newcommand\basiccurry{$<\!\sim$}
|
||||||
|
\newcommand\basicclosure{$\sim\!>$}
|
||||||
|
|
||||||
% Title styling
|
% Title styling
|
||||||
\pretitle{\begin{flushright}}
|
\pretitle{\begin{flushright}}
|
||||||
@@ -71,6 +75,9 @@
|
|||||||
\setbeforesubsecskip{\mytextsize}
|
\setbeforesubsecskip{\mytextsize}
|
||||||
\setbeforesubsubsecskip{\mytextsize}
|
\setbeforesubsubsecskip{\mytextsize}
|
||||||
|
|
||||||
|
% extra space for table
|
||||||
|
\setlength{\extrarowheight}{0.166ex}
|
||||||
|
|
||||||
% chapter title -- no now page after
|
% chapter title -- no now page after
|
||||||
\renewcommand\chapterheadstart{} % kill the drop
|
\renewcommand\chapterheadstart{} % kill the drop
|
||||||
\renewcommand\afterchapternum{\vskip 0.5em} % space between number and title
|
\renewcommand\afterchapternum{\vskip 0.5em} % space between number and title
|
||||||
|
|||||||
@@ -748,10 +748,12 @@ bF._opPrc = {
|
|||||||
"STEP":41,
|
"STEP":41,
|
||||||
"!":50,"~":51, // array CONS and PUSH
|
"!":50,"~":51, // array CONS and PUSH
|
||||||
"#": 52, // array concat
|
"#": 52, // array concat
|
||||||
|
"<~": 100, // curry operator
|
||||||
|
"~>": 101, // closure operator
|
||||||
"=":999,
|
"=":999,
|
||||||
"IN":1000
|
"IN":1000
|
||||||
};
|
};
|
||||||
bF._opRh = {"^":1,"=":1,"!":1,"IN":1};
|
bF._opRh = {"^":1,"=":1,"!":1,"IN":1,"<~":1,"~>":1};
|
||||||
let bStatus = {};
|
let bStatus = {};
|
||||||
bStatus.builtin = {};
|
bStatus.builtin = {};
|
||||||
["PRINT","NEXT","SPC","CHR","ROUND","SQR","RND","GOTO","GOSUB","DEFUN","FOR","MAP"].forEach(w=>{ bStatus.builtin[w] = 1 });
|
["PRINT","NEXT","SPC","CHR","ROUND","SQR","RND","GOTO","GOSUB","DEFUN","FOR","MAP"].forEach(w=>{ bStatus.builtin[w] = 1 });
|
||||||
@@ -826,10 +828,14 @@ let states15 = ["lit","lit","paren","lit","sep","num","op","num","paren"];
|
|||||||
let tokens16 = ["DEFUN","KA","(","X",")","=","IF","X",">","2","THEN","DO","(","PRINT","(","HAI",")",";","PRINT","(","X",")",")","ELSE","DO","(","PRINT","(","BYE",")",";","PRINT","(","X",")",")"];
|
let tokens16 = ["DEFUN","KA","(","X",")","=","IF","X",">","2","THEN","DO","(","PRINT","(","HAI",")",";","PRINT","(","X",")",")","ELSE","DO","(","PRINT","(","BYE",")",";","PRINT","(","X",")",")"];
|
||||||
let states16 = ["lit","lit","paren","lit","paren","op","lit","lit","op","num","lit","lit","paren","lit","paren","qot","paren","sep","lit","paren","lit","paren","paren","lit","lit","paren","lit","paren","qot","paren","sep","lit","paren","lit","paren","paren"];
|
let states16 = ["lit","lit","paren","lit","paren","op","lit","lit","op","num","lit","lit","paren","lit","paren","qot","paren","sep","lit","paren","lit","paren","paren","lit","lit","paren","lit","paren","qot","paren","sep","lit","paren","lit","paren","paren"];
|
||||||
|
|
||||||
|
// FILTER(FN<~HEAD XS, TAIL XS)
|
||||||
|
let tokens17 = ["FILTER","(","FN","<~","HEAD","XS",",","TAIL","XS",")"];
|
||||||
|
let states17 = ["lit","paren","lit","op","lit","lit","sep","lit","lit","paren"];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let trees = bF._parseTokens(lnum,
|
let trees = bF._parseTokens(lnum,
|
||||||
tokens16,
|
tokens17,
|
||||||
states16
|
states17
|
||||||
);
|
);
|
||||||
trees.forEach((t,i) => {
|
trees.forEach((t,i) => {
|
||||||
serial.println("\nParsed Statement #"+(i+1));
|
serial.println("\nParsed Statement #"+(i+1));
|
||||||
|
|||||||
Reference in New Issue
Block a user