mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-09 22:54:03 +09:00
wordprocessor wip
This commit is contained in:
58
assets/bios/wp.js
Normal file
58
assets/bios/wp.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const COL_TEXT = 253
|
||||||
|
const COL_BACK = 255
|
||||||
|
const COL_SUPERTEXT = 239
|
||||||
|
const COL_DIMTEXT = 249
|
||||||
|
const COL_LNUMBACK = 18
|
||||||
|
const COL_LNUMFORE = 253
|
||||||
|
const COL_CARET_ROW = 81
|
||||||
|
const PAINT_START_X = 5
|
||||||
|
const PAINT_START_Y = 2
|
||||||
|
const BIG_STRIDE = 999
|
||||||
|
const TAB_SIZE = 4
|
||||||
|
|
||||||
|
const caretLeft = 10
|
||||||
|
const caretRight = 80
|
||||||
|
|
||||||
|
let scroll = 0
|
||||||
|
let scrollHor = 0
|
||||||
|
let textbuffer = [""]
|
||||||
|
let cursorRow = 0
|
||||||
|
let cursorCol = 0
|
||||||
|
let exit = false
|
||||||
|
let scene = -1 // -1: main, 0: filemenu, 1: editmenu , ...
|
||||||
|
let bulletinShown = false
|
||||||
|
let cursoringCol = 0
|
||||||
|
|
||||||
|
let windowWidth = 0
|
||||||
|
let windowHeight = 0
|
||||||
|
let paintWidth = 0
|
||||||
|
let paintHeight = 0
|
||||||
|
let scrollPeek = 0
|
||||||
|
function drawInit() {
|
||||||
|
windowWidth = con.getmaxyx()[1]
|
||||||
|
windowHeight = con.getmaxyx()[0]
|
||||||
|
paintWidth = windowWidth - PAINT_START_X + 1
|
||||||
|
paintHeight = windowHeight - PAINT_START_Y + 1
|
||||||
|
scrollPeek = Math.ceil((paintHeight / 7))
|
||||||
|
}
|
||||||
|
const scrollHorPeek = 1; // to accommodate the scroll indicator
|
||||||
|
|
||||||
|
|
||||||
|
function drawMain() {
|
||||||
|
con.curs_set(0)
|
||||||
|
drawInit()
|
||||||
|
con.clear()
|
||||||
|
con.color_pair(COL_TEXT, COL_BACK)
|
||||||
|
|
||||||
|
// column indicator
|
||||||
|
con.move(2,1)
|
||||||
|
for (let k = 0; k < 9; k++) print(`${k}....:....`)
|
||||||
|
con.color_pair(COL_BACK, COL_TEXT)
|
||||||
|
con.mvaddch(2,1+caretLeft,91)
|
||||||
|
con.mvaddch(2,1+caretRight,93)
|
||||||
|
|
||||||
|
con.color_pair(COL_BACK, COL_TEXT)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
drawMain()
|
||||||
@@ -312,6 +312,14 @@ let monadToString = function(monad, depth) {
|
|||||||
}*/
|
}*/
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
let arrayToString = function(a) {
|
||||||
|
let acc = "";
|
||||||
|
for (let k = 0; k < a.length; k++) {
|
||||||
|
if (k > 0) acc += ",";
|
||||||
|
acc += (Array.isArray(a[k])) ? arrayToString(a[k]) : a[k];
|
||||||
|
}
|
||||||
|
return "{"+acc+"}";
|
||||||
|
}
|
||||||
let theLambdaBoundVars = function() {
|
let theLambdaBoundVars = function() {
|
||||||
let sb = "";
|
let sb = "";
|
||||||
lambdaBoundVars.forEach((it,i) => {
|
lambdaBoundVars.forEach((it,i) => {
|
||||||
@@ -325,11 +333,7 @@ let theLambdaBoundVars = function() {
|
|||||||
})
|
})
|
||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
let makeBase32Hash = function() {
|
let makeBase32Hash = ()=>[1,2,3,4,5].map(i=>"YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random()*32|0]).join('');
|
||||||
let e = "YBNDRFG8EJKMCPQXOTLVWIS2A345H769";
|
|
||||||
let m = e.length;
|
|
||||||
return e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)]
|
|
||||||
}
|
|
||||||
let BasicAST = function() {
|
let BasicAST = function() {
|
||||||
this.astLnum = 0;
|
this.astLnum = 0;
|
||||||
this.astLeaves = [];
|
this.astLeaves = [];
|
||||||
@@ -431,8 +435,11 @@ let resolve = function(variable) {
|
|||||||
else if (literalTypes.includes(variable.troType) || variable.troType.startsWith("internal_"))
|
else if (literalTypes.includes(variable.troType) || variable.troType.startsWith("internal_"))
|
||||||
return variable.troValue;
|
return variable.troValue;
|
||||||
else if (variable.troType == "lit") {
|
else if (variable.troType == "lit") {
|
||||||
|
if (isNumable(variable.troValue)) { // rarely we get a number as a variable name, notably on (&)
|
||||||
|
return tonum(variable.troValue);
|
||||||
|
}
|
||||||
// when program tries to call builtin function (e.g. SIN), return usrdefun-wrapped version
|
// when program tries to call builtin function (e.g. SIN), return usrdefun-wrapped version
|
||||||
if (bS.builtin[variable.troValue] !== undefined) {
|
else if (bS.builtin[variable.troValue] !== undefined) {
|
||||||
return bS.wrapBuiltinToUsrdefun(variable.troValue);
|
return bS.wrapBuiltinToUsrdefun(variable.troValue);
|
||||||
}
|
}
|
||||||
// else, it's just a plain-old variable :p
|
// else, it's just a plain-old variable :p
|
||||||
@@ -636,17 +643,7 @@ let varArgNum = function(lnum, stmtnum, args, action) {
|
|||||||
return action(rsvArg);
|
return action(rsvArg);
|
||||||
}
|
}
|
||||||
let makeIdFun = () => {
|
let makeIdFun = () => {
|
||||||
let i = new BasicAST();
|
return JSON.parse(`{"astLnum":"**","astLeaves":[],"astSeps":[],"astValue":[0,0],"astType":"defun_args","astHash":"IDFUN"}`);
|
||||||
i.astValue = [0,0];
|
|
||||||
i.astType = "defun_args";
|
|
||||||
i.astLnum = "**";
|
|
||||||
|
|
||||||
let a = new BasicAST();
|
|
||||||
a.astValue = i;
|
|
||||||
a.astType = "usrdefun";
|
|
||||||
a.astLnum = "**";
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
let _basicConsts = {
|
let _basicConsts = {
|
||||||
"NIL": new BasicVar([], "array"),
|
"NIL": new BasicVar([], "array"),
|
||||||
@@ -1082,7 +1079,9 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
//serial.println(`${lnum} PRINT ${lang.ord(llll)} arg: ${Object.entries(args[llll])}, resolved: ${rsvArg}`);
|
//serial.println(`${lnum} PRINT ${lang.ord(llll)} arg: ${Object.entries(args[llll])}, resolved: ${rsvArg}`);
|
||||||
|
|
||||||
let printstr = "";
|
let printstr = "";
|
||||||
if (rsvArg === undefined || rsvArg === "")
|
if (Array.isArray(rsvArg))
|
||||||
|
printstr = arrayToString(rsvArg);
|
||||||
|
else if (rsvArg === undefined || rsvArg === "")
|
||||||
printstr = "";
|
printstr = "";
|
||||||
else if (rsvArg.toString !== undefined)
|
else if (rsvArg.toString !== undefined)
|
||||||
printstr = rsvArg.toString();
|
printstr = rsvArg.toString();
|
||||||
@@ -1221,7 +1220,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}},
|
}},
|
||||||
"TEST" : {argc:1, f:function(lnum, stmtnum, args) {
|
"TEST" : {argc:1, 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]); // string 'false' will be interpreted as truthy; this is completely intentional
|
||||||
}},
|
}},
|
||||||
"FOREACH" : {f: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]);
|
||||||
@@ -1626,6 +1625,9 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
return bF._executeSyntaxTree(lnum, stmtnum, newTree, 0);
|
return bF._executeSyntaxTree(lnum, stmtnum, newTree, 0);
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
"&" : {argc:2, f:function(lnum, stmtnum, args) {
|
||||||
|
return bS.builtin["$"].f(lnum, stmtnum, [args[1], args[0]].concat(args.slice(2)));
|
||||||
|
}},
|
||||||
"REDUCE" : {noprod:1, argc:1, f:function(lnum, stmtnum, args) {
|
"REDUCE" : {noprod:1, argc:1, f:function(lnum, stmtnum, args) {
|
||||||
return oneArg(lnum, stmtnum, args, bv => {
|
return oneArg(lnum, stmtnum, args, bv => {
|
||||||
if (isAST(bv)) {
|
if (isAST(bv)) {
|
||||||
@@ -1769,7 +1771,7 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
print(String.fromCharCode(27,91)+"48;5;"+(col|0)+"m");
|
print(String.fromCharCode(27,91)+"48;5;"+(col|0)+"m");
|
||||||
});
|
});
|
||||||
}},
|
}},
|
||||||
/** type: (list of function) <*> (list of functor)
|
/** type: (list of function) <*> (a functor)
|
||||||
* Sequnetial application
|
* Sequnetial application
|
||||||
*
|
*
|
||||||
* Can be implemented on pure TerranBASIC using:
|
* Can be implemented on pure TerranBASIC using:
|
||||||
@@ -1783,16 +1785,35 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
if (isGenerator(functor)) functor = genToArray(functor);
|
if (isGenerator(functor)) functor = genToArray(functor);
|
||||||
|
|
||||||
let ret = [];
|
let ret = [];
|
||||||
fns.forEach(fn => ret.push(functor.map(it => bS.getDefunThunk(fn)(lnum, stmtnum, [it]))));
|
fns.forEach(fn => ret = ret.concat(functor.map(it => bS.getDefunThunk(fn)(lnum, stmtnum, [it]))));
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
}},
|
}},
|
||||||
/** type: (a function) <*> (list of functor)
|
/** type: (a function) <*> (a functor)
|
||||||
* Infix MAP
|
* Infix MAP
|
||||||
*/
|
*/
|
||||||
"<$>" : {argc:2, f:function(lnum, stmtnum, args) {
|
"<$>" : {argc:2, f:function(lnum, stmtnum, args) {
|
||||||
return bS.builtin.MAP.f(lnum, stmtnum, args);
|
return bS.builtin.MAP.f(lnum, stmtnum, args);
|
||||||
}},
|
}},
|
||||||
|
/** type: (a function/list of functions) <~> (a functor)
|
||||||
|
* SEQUENTIAL CURRY-MAP
|
||||||
|
*
|
||||||
|
* returns a list of functions curried with each element of the functor
|
||||||
|
*/
|
||||||
|
"<~>" : {argc:2, f:function(lnum, stmtnum, args) {
|
||||||
|
return twoArg(lnum, stmtnum, args, (fns, functor) => {
|
||||||
|
if (!isRunnable(fns) && !(Array.isArray(fns) && isRunnable(fns[0]))) throw lang.badFunctionCallFormat(lnum, "first argument is not a function: got "+JStoBASICtype(fns));
|
||||||
|
if (!isGenerator(functor) && !Array.isArray(functor)) throw lang.syntaxfehler(lnum, "not a mappable type: "+functor+((typeof functor == "object") ? Object.entries(functor) : ""));
|
||||||
|
// generator?
|
||||||
|
if (isGenerator(functor)) functor = genToArray(functor);
|
||||||
|
// single function?
|
||||||
|
if (!Array.isArray(fns)) fns = [fns];
|
||||||
|
|
||||||
|
let ret = [];
|
||||||
|
fns.forEach(fn => ret = ret.concat(functor.map(it => bS.builtin["~<"].f(lnum, stmtnum, [fn, it]))));
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}},
|
||||||
"OPTIONDEBUG" : {f: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);
|
||||||
@@ -1842,7 +1863,10 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
|
|||||||
}},
|
}},
|
||||||
"UNRESOLVE0" : {debugonly:1, argc:1, f:function(lnum, stmtnum, args) {
|
"UNRESOLVE0" : {debugonly:1, argc:1, f:function(lnum, stmtnum, args) {
|
||||||
println(Object.entries(args[0]));
|
println(Object.entries(args[0]));
|
||||||
}}
|
}},
|
||||||
|
"TOJSON" : {debugonly:1, argc:1, f:function(lnum, stmtnum, args) {
|
||||||
|
println(JSON.stringify(resolve(args[0])));
|
||||||
|
}},
|
||||||
};
|
};
|
||||||
Object.freeze(bS.builtin);
|
Object.freeze(bS.builtin);
|
||||||
let bF = {}; // BASIC functions
|
let bF = {}; // BASIC functions
|
||||||
@@ -1905,22 +1929,24 @@ bF._opPrc = {
|
|||||||
"BOR":202,
|
"BOR":202,
|
||||||
"AND":300,
|
"AND":300,
|
||||||
"OR":301,
|
"OR":301,
|
||||||
"TO":400,
|
"TO":400,"STEP":401,
|
||||||
"STEP":401,
|
"!":500,
|
||||||
"!":500,"~":501, // array CONS and PUSH
|
"~":501, // array CONS and PUSH
|
||||||
"#":502, // array concat
|
"#":502, // array concat
|
||||||
".": 600, // compo operator
|
".": 600, // compo operator
|
||||||
"$": 600, // apply operator
|
"$": 600, // apply operator
|
||||||
|
"&": 600, // pipe operator
|
||||||
"~<": 601, // curry operator
|
"~<": 601, // curry operator
|
||||||
"<*>": 602, // sequential application operator
|
|
||||||
"<$>": 602, // infix map operator
|
"<$>": 602, // infix map operator
|
||||||
|
"<*>": 602, // sequential application operator
|
||||||
|
"<~>": 602, // infix curry-map operator
|
||||||
"@":700, // MRET
|
"@":700, // MRET
|
||||||
"~>": 1000, // closure operator
|
"~>": 1000, // closure operator
|
||||||
">>~": 1000, // monad sequnce operator
|
">>~": 1000, // monad sequnce operator
|
||||||
">>=": 1000, // monad bind operator
|
">>=": 1000, // monad bind operator
|
||||||
"=":9999,"IN":9999
|
"=":9999,"IN":9999
|
||||||
}; // when to ops have same index of prc but different in associativity, right associative op gets higher priority (at least for the current parser implementation)
|
}; // when to ops have same index of prc but different in associativity, right associative op gets higher priority (at least for the current parser implementation)
|
||||||
bF._opRh = {"^":1,"=":1,"!":1,"IN":1,"~>":1,"$":1,".":1,">>=":1,">>~":1,">!>":1,"@":1,"`":1,"<*>":1,"<$>":1}; // ~< and ~> cannot have same associativity
|
bF._opRh = {"^":1,"=":1,"!":1,"IN":1,"~>":1,"$":1,".":1,">>=":1,">>~":1,">!>":1,"@":1,"`":1,"<$>":1}; // ~< and ~> cannot have same associativity
|
||||||
// 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._tokenise = function(lnum, cmd) {
|
bF._tokenise = function(lnum, cmd) {
|
||||||
var _debugprintStateTransition = false;
|
var _debugprintStateTransition = false;
|
||||||
@@ -2402,26 +2428,23 @@ stmt =
|
|||||||
| expr ; (* if the statement is 'lit' and contains only one word, treat it as function_call
|
| expr ; (* if the statement is 'lit' and contains only one word, treat it as function_call
|
||||||
e.g. NEXT for FOR loop *)
|
e.g. NEXT for FOR loop *)
|
||||||
|
|
||||||
array_inner =
|
|
||||||
"{" , expr , "}" , ["," , "{" , expr , "}"] ;
|
|
||||||
|
|
||||||
expr = (* this basically blocks some funny attemps such as using DEFUN as anon function
|
expr = (* this basically blocks some funny attemps such as using DEFUN as anon function
|
||||||
because everything is global in BASIC *)
|
because everything is global in BASIC *)
|
||||||
? empty string ?
|
? empty string ?
|
||||||
| lit
|
| lit
|
||||||
| array_inner
|
| "{" , [expr , {"," , expr}] , "}"
|
||||||
| "(" , expr , ")"
|
| "(" , expr , ")"
|
||||||
| ident_tuple
|
| ident_tuple
|
||||||
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr]
|
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr]
|
||||||
| kywd , expr - "(" (* also deals with FOR statement *)
|
| ("FOR"|"FOREACH") , expr
|
||||||
(* at this point, if OP is found in paren-level 0, skip function_call *)
|
|
||||||
| function_call
|
|
||||||
| expr , op , expr
|
| expr , op , expr
|
||||||
| op_uni , expr ;
|
| op_uni , expr
|
||||||
|
| kywd , expr - "("
|
||||||
|
| function_call ;
|
||||||
|
|
||||||
expr_sans_asgn = ? identical to expr except errors out whenever "=" is found ? ;
|
expr_sans_asgn = ? identical to expr except errors out whenever "=" is found ? ;
|
||||||
|
|
||||||
ident_tuple = "[" , ident , ["," , ident] , "]" ;
|
ident_tuple = "[" , ident , {"," , ident} , "]" ;
|
||||||
|
|
||||||
function_call =
|
function_call =
|
||||||
ident , "(" , [expr , {argsep , expr} , [argsep]] , ")"
|
ident , "(" , [expr , {argsep , expr} , [argsep]] , ")"
|
||||||
@@ -2433,10 +2456,11 @@ kywd = ? words that exists on the list of predefined function that are not opera
|
|||||||
argsep = "," | ";" ;
|
argsep = "," | ";" ;
|
||||||
ident = alph , [digits] ; (* variable and function names *)
|
ident = alph , [digits] ; (* variable and function names *)
|
||||||
lit = alph , [digits] | num | string ; (* ident + numbers and string literals *)
|
lit = alph , [digits] | num | string ; (* ident + numbers and string literals *)
|
||||||
op = "^" | "*" | "/" | "MOD" | "+" | "-" | "<<" | ">>" | "<" | ">" | "<="
|
op = "^" | "*" | "/" | "\" | "MOD" | "+" | "-" | "<<" | ">>" | "<" | ">"
|
||||||
| "=<" | ">=" | "=>" | "==" | "<>" | "><" | "BAND" | "BXOR" | "BOR"
|
| "<=" | "=<" | ">=" | "=>" | "==" | "<>" | "><" | "MIN" | "MAX" | "BAND" | "BXOR" | "BOR"
|
||||||
| "AND" | "OR" | "TO" | "STEP" | "!" | "~" | "#" | "=" ;
|
| "AND" | "OR" | "TO" | "STEP" | "!" | "~" | "#" | "." | "$" | "&" | "~<" | "<$>" | "<*>"
|
||||||
op_uni = "-" | "+" ;
|
| "<~>" | "~>" | ">>~" | ">>=" | "=" ;
|
||||||
|
op_uni = "-" | "+" | "NOT" | "BNOT" | "`" | "@" ;
|
||||||
|
|
||||||
alph = letter | letter , alph ;
|
alph = letter | letter , alph ;
|
||||||
digits = digit | digit , digits ;
|
digits = digit | digit , digits ;
|
||||||
@@ -2446,7 +2470,7 @@ num = digits | digits , "." , [digits] | "." , digits
|
|||||||
| ("0x"|"0X") , hexdigits
|
| ("0x"|"0X") , hexdigits
|
||||||
| ("0b"|"0B") , bindigits ; (* sorry, no e-notation! *)
|
| ("0b"|"0B") , bindigits ; (* sorry, no e-notation! *)
|
||||||
visible = ? ASCII 0x20 to 0x7E ? ;
|
visible = ? ASCII 0x20 to 0x7E ? ;
|
||||||
string = '"' , (visible | visible , stringlit) , '"' ;
|
string = '"' , {visible} , '"' ;
|
||||||
|
|
||||||
letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N"
|
letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N"
|
||||||
| "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b"
|
| "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b"
|
||||||
@@ -2493,6 +2517,11 @@ LAMBDA (type: op, value: "~>")
|
|||||||
[2. arg1]
|
[2. arg1]
|
||||||
[3. argN...]
|
[3. argN...]
|
||||||
2. stmt
|
2. stmt
|
||||||
|
|
||||||
|
ARRAY CONSTRUCTOR (type: function, value: undefined)
|
||||||
|
1. 0th element of the array
|
||||||
|
2. 1st element of the array
|
||||||
|
[3. Nth element of the array...]
|
||||||
*/
|
*/
|
||||||
// @returns BasicAST
|
// @returns BasicAST
|
||||||
bF._EquationIllegalTokens = ["IF","THEN","ELSE","DEFUN","ON"];
|
bF._EquationIllegalTokens = ["IF","THEN","ELSE","DEFUN","ON"];
|
||||||
@@ -2787,15 +2816,15 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) {
|
|||||||
expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *)
|
expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *)
|
||||||
? empty string ?
|
? empty string ?
|
||||||
| lit
|
| lit
|
||||||
| array_inner
|
| "{" , [expr , {"," , expr}] , "}"
|
||||||
| "(" , expr , ")"
|
| "(" , expr , ")"
|
||||||
| ident_tuple
|
| ident_tuple
|
||||||
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr]
|
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr]
|
||||||
| kywd , expr - "(" (* also deals with FOR statement *)
|
| ("FOR"|"FOREACH") , expr
|
||||||
(* at this point, if OP is found in paren-level 0, skip function_call *)
|
|
||||||
| function_call
|
|
||||||
| expr , op , expr
|
| expr , op , expr
|
||||||
| op_uni , expr ;
|
| op_uni , expr
|
||||||
|
| kywd , expr - "("
|
||||||
|
| function_call ;
|
||||||
|
|
||||||
* @return: BasicAST
|
* @return: BasicAST
|
||||||
*/
|
*/
|
||||||
@@ -2905,7 +2934,7 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
|
|||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
// ## case for:
|
// ## case for:
|
||||||
// | array_inner
|
// | "{" , [expr , {"," , expr}] , "}"
|
||||||
if (curlyStart == 0 && curlyEnd == tokens.length - 1) {
|
if (curlyStart == 0 && curlyEnd == tokens.length - 1) {
|
||||||
bF.parserPrintdbgline('e', "Array", lnum, recDepth);
|
bF.parserPrintdbgline('e', "Array", lnum, recDepth);
|
||||||
return bF._parseArrayLiteral(lnum, tokens, states, recDepth + 1);
|
return bF._parseArrayLiteral(lnum, tokens, states, recDepth + 1);
|
||||||
@@ -2943,30 +2972,15 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
|
|||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
// ## case for:
|
// ## case for:
|
||||||
// | kywd , expr (* kywd = ? words that exists on the list of predefined function that are not operators ? ; *)
|
// | ("FOR"|"FOREACH") , expr
|
||||||
if (bS.builtin[headTkn] && headSta == "lit" && !bF._opPrc[headTkn] &&
|
try {
|
||||||
states[1] != "paren" && tokens[1] != "("
|
bF.parserPrintdbgline('e', "Trying FOR Expression...", lnum, recDepth);
|
||||||
) {
|
return bF._parseForLoop(lnum, tokens, states, recDepth + 1);
|
||||||
bF.parserPrintdbgline('e', 'Builtin Function Call w/o Paren', lnum, recDepth);
|
|
||||||
|
|
||||||
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1);
|
|
||||||
}
|
}
|
||||||
|
// if ParserError is raised, continue applying other rules
|
||||||
/*************************************************************************/
|
catch (e) {
|
||||||
|
if (!(e instanceof ParserError)) throw e;
|
||||||
// ## case for:
|
bF.parserPrintdbgline('e', 'It was NOT!', lnum, recDepth);
|
||||||
// (* at this point, if OP is found in paren-level 0, skip function_call *)
|
|
||||||
// | function_call ;
|
|
||||||
if (topmostOp === undefined) { // don't remove this IF statement!
|
|
||||||
try {
|
|
||||||
bF.parserPrintdbgline('e', "Trying Function Call...", lnum, recDepth);
|
|
||||||
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1);
|
|
||||||
}
|
|
||||||
// if ParserError is raised, continue applying other rules
|
|
||||||
catch (e) {
|
|
||||||
if (!(e instanceof ParserError)) throw e;
|
|
||||||
bF.parserPrintdbgline('e', 'It was NOT!', lnum, recDepth);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
@@ -3020,10 +3034,38 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
|
|||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
|
// ## case for:
|
||||||
|
// | kywd , expr - "("
|
||||||
|
if (bS.builtin[headTkn] && headSta == "lit" && !bF._opPrc[headTkn] &&
|
||||||
|
states[1] != "paren" && tokens[1] != "("
|
||||||
|
) {
|
||||||
|
bF.parserPrintdbgline('e', 'Builtin Function Call w/o Paren', lnum, recDepth);
|
||||||
|
|
||||||
|
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
// ## case for:
|
||||||
|
// | function_call ;
|
||||||
|
if (topmostOp === undefined) { // don't remove this IF statement!
|
||||||
|
try {
|
||||||
|
bF.parserPrintdbgline('e', "Trying Function Call...", lnum, recDepth);
|
||||||
|
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1);
|
||||||
|
}
|
||||||
|
// if ParserError is raised, continue applying other rules
|
||||||
|
catch (e) {
|
||||||
|
if (!(e instanceof ParserError)) throw e;
|
||||||
|
bF.parserPrintdbgline('e', 'It was NOT!', lnum, recDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
throw new ParserError(`Expression "${tokens.join(" ")}" cannot be parsed in ${lnum}`);
|
throw new ParserError(`Expression "${tokens.join(" ")}" cannot be parsed in ${lnum}`);
|
||||||
} // END of EXPR
|
} // END of EXPR
|
||||||
/** Parses following EBNF rule:
|
/** Parses following EBNF rule:
|
||||||
"{" , expr , "}" , ["," , "{" , expr , "}"] ;
|
"{" , [expr , {"," , expr}] , "}"
|
||||||
* @return: BasicAST
|
* @return: BasicAST
|
||||||
*/
|
*/
|
||||||
bF._parseArrayLiteral = function(lnum, tokens, states, recDepth) {
|
bF._parseArrayLiteral = function(lnum, tokens, states, recDepth) {
|
||||||
@@ -3191,6 +3233,40 @@ bF._parseIfMode = function(lnum, tokens, states, recDepth, exprMode) {
|
|||||||
|
|
||||||
throw new ParserError("not an IF "+(exprMode) ? "expression" : "statement");
|
throw new ParserError("not an IF "+(exprMode) ? "expression" : "statement");
|
||||||
} // END of IF
|
} // END of IF
|
||||||
|
/** Parses following EBNF rule:
|
||||||
|
("FOR"|"FOREACH") , expr
|
||||||
|
* @return: BasicAST
|
||||||
|
*/
|
||||||
|
bF._parseForLoop = function(lnum, tokens, states, recDepth) {
|
||||||
|
bF.parserPrintdbg2('\\', lnum, tokens, states, recDepth);
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
let headTkn = tokens[0].toUpperCase();
|
||||||
|
let headSta = states[0];
|
||||||
|
|
||||||
|
let treeHead = new BasicAST();
|
||||||
|
treeHead.astLnum = lnum;
|
||||||
|
|
||||||
|
// ## case for:
|
||||||
|
// ("FOR"|"FOREACH") , expr
|
||||||
|
if (("FOR" == headTkn || "FOREACH" == headTkn) && "lit" == headSta) {
|
||||||
|
|
||||||
|
treeHead.astValue = headTkn;
|
||||||
|
treeHead.astType = "function";
|
||||||
|
|
||||||
|
treeHead.astLeaves[0] = bF._parseExpr(lnum,
|
||||||
|
tokens.slice(1),
|
||||||
|
states.slice(1),
|
||||||
|
recDepth + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
return treeHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParserError("not an FOR/FOREACH expression");
|
||||||
|
|
||||||
|
} // END of FOR
|
||||||
/** Parses following EBNF rule:
|
/** Parses following EBNF rule:
|
||||||
ident_tuple = "[" , ident , ["," , ident] , "]" ;
|
ident_tuple = "[" , ident , ["," , ident] , "]" ;
|
||||||
* @return: BasicAST
|
* @return: BasicAST
|
||||||
@@ -3330,7 +3406,7 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) {
|
|||||||
|
|
||||||
// recursively parse function arguments
|
// recursively parse function arguments
|
||||||
treeHead.astLeaves = argPos.map((x,i) => {
|
treeHead.astLeaves = argPos.map((x,i) => {
|
||||||
bF.parserPrintdbgline("F", 'Function Arguments #'+(i+1), lnum, recDepth);
|
bF.parserPrintdbgline("F", `Function Arguments #${i+1} of ${argPos.length}`, lnum, recDepth);
|
||||||
|
|
||||||
// check for empty tokens
|
// check for empty tokens
|
||||||
if (x.end - x.start < 0) throw new ParserError("not a function call because it's malformed");
|
if (x.end - x.start < 0) throw new ParserError("not a function call because it's malformed");
|
||||||
|
|||||||
@@ -23,24 +23,26 @@ public class AppLoader {
|
|||||||
appConfig.resizable = false;
|
appConfig.resizable = false;
|
||||||
appConfig.title = appTitle;
|
appConfig.title = appTitle;
|
||||||
appConfig.forceExit = true;
|
appConfig.forceExit = true;
|
||||||
appConfig.width = 560;//720;
|
appConfig.width = 810;//720;
|
||||||
appConfig.height = 448;//480;
|
appConfig.height = 300;//480;
|
||||||
|
|
||||||
|
|
||||||
// val vm = VM(64.kB(), TheRealWorld(), arrayOf(GenericBios))
|
|
||||||
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE});
|
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE});
|
||||||
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
||||||
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE});
|
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE});
|
||||||
|
VM vm = new VM(256 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
|
||||||
|
|
||||||
// uncomment to target the TerranBASIC runner
|
// uncomment to target the TerranBASIC runner
|
||||||
VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TBASRelBios.INSTANCE});
|
//VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TBASRelBios.INSTANCE});
|
||||||
|
|
||||||
EmulInstance reference = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0");
|
EmulInstance reference = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0");
|
||||||
EmulInstance reference2 = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", "assets/disk0");
|
EmulInstance reference2 = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", "assets/disk0");
|
||||||
EmulInstance term = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.TexticsAdapter", "assets/disk0");
|
EmulInstance term = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.TexticsAdapter", "assets/disk0");
|
||||||
EmulInstance portable = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", "assets/disk0");
|
EmulInstance portable = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", "assets/disk0");
|
||||||
|
|
||||||
new LwjglApplication(new VMGUI(reference), appConfig);
|
EmulInstance wp = new EmulInstance(appConfig, vm, "net.torvald.tsvm.peripheral.WpTerm", "assets/wpdisk");
|
||||||
|
|
||||||
|
new LwjglApplication(new VMGUI(wp), appConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShaderProgram loadShaderFromFile(String vert, String frag) {
|
public static ShaderProgram loadShaderFromFile(String vert, String frag) {
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package net.torvald.tsvm.peripheral
|
|
||||||
|
|
||||||
import net.torvald.tsvm.CompressorDelegate
|
|
||||||
import net.torvald.tsvm.CompressorDelegate.GZIP_HEADER
|
|
||||||
import net.torvald.tsvm.VM
|
|
||||||
import net.torvald.tsvm.startsWith
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object OEMBios : VMProgramRom {
|
|
||||||
|
|
||||||
private val contents: ByteArray
|
|
||||||
|
|
||||||
init {
|
|
||||||
val bytes = File("./assets/bios/TBMBIOS.js").readBytes()
|
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ import net.torvald.tsvm.VM
|
|||||||
import net.torvald.tsvm.kB
|
import net.torvald.tsvm.kB
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class TexticsAdapter(vm: VM) : GraphicsAdapter(vm, AdapterConfig(
|
open class TexticsAdapter(vm: VM, config: AdapterConfig = AdapterConfig(
|
||||||
"crt_white",
|
"crt_white",
|
||||||
720,
|
720,
|
||||||
480,
|
480,
|
||||||
@@ -21,19 +21,7 @@ class TexticsAdapter(vm: VM) : GraphicsAdapter(vm, AdapterConfig(
|
|||||||
"./hp2640.png",
|
"./hp2640.png",
|
||||||
0.32f,
|
0.32f,
|
||||||
GraphicsAdapter.TEXT_TILING_SHADER_MONOCHROME
|
GraphicsAdapter.TEXT_TILING_SHADER_MONOCHROME
|
||||||
)) {
|
)) : GraphicsAdapter(vm, config) {
|
||||||
/*class TexticsAdapter(vm: VM) : GraphicsAdapter(vm, AdapterConfig(
|
|
||||||
"crt_color",
|
|
||||||
560,
|
|
||||||
448,
|
|
||||||
80,
|
|
||||||
32,
|
|
||||||
254,
|
|
||||||
255,
|
|
||||||
256.kB(),
|
|
||||||
"./cp437_fira_code.png",
|
|
||||||
0.64f
|
|
||||||
)) {*/
|
|
||||||
|
|
||||||
private val crtGradTex = Texture("./assets/crt_grad.png")
|
private val crtGradTex = Texture("./assets/crt_grad.png")
|
||||||
|
|
||||||
@@ -88,3 +76,17 @@ class TexticsAdapter(vm: VM) : GraphicsAdapter(vm, AdapterConfig(
|
|||||||
super.dispose()
|
super.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WpTerm(vm: VM) : TexticsAdapter(vm = vm, AdapterConfig(
|
||||||
|
"crt_amber",
|
||||||
|
810,
|
||||||
|
300,
|
||||||
|
90,
|
||||||
|
20,
|
||||||
|
254,
|
||||||
|
0,
|
||||||
|
256.kB(),
|
||||||
|
"./hp2640.png",
|
||||||
|
0.32f,
|
||||||
|
GraphicsAdapter.TEXT_TILING_SHADER_MONOCHROME
|
||||||
|
))
|
||||||
@@ -6,20 +6,15 @@ import net.torvald.tsvm.VM
|
|||||||
import net.torvald.tsvm.startsWith
|
import net.torvald.tsvm.startsWith
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
interface VMProgramRom {
|
open class VMProgramRom(path: String) {
|
||||||
fun readAll(): String
|
|
||||||
operator fun get(addr: Int): Byte
|
|
||||||
}
|
|
||||||
|
|
||||||
object GenericBios : VMProgramRom {
|
|
||||||
private val contents: ByteArray
|
private val contents: ByteArray
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val bytes = File("./assets/bios/bios1.bin").readBytes()
|
val bytes = File(path).readBytes()
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readAll(): String {
|
fun readAll(): String {
|
||||||
// check if bios is compressed in gzip
|
// check if bios is compressed in gzip
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
return if (contents.startsWith(GZIP_HEADER))
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
||||||
@@ -27,100 +22,14 @@ object GenericBios : VMProgramRom {
|
|||||||
contents.toString(VM.CHARSET)
|
contents.toString(VM.CHARSET)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
fun get(addr: Int): Byte = contents[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
object QuickBios : VMProgramRom {
|
object GenericBios : VMProgramRom("./assets/bios/bios1.bin")
|
||||||
private val contents: ByteArray
|
object OEMBios : VMProgramRom("./assets/bios/TBMBIOS.js")
|
||||||
|
object QuickBios : VMProgramRom("./assets/bios/quick.js")
|
||||||
init {
|
object BasicBios : VMProgramRom("./assets/bios/basicbios.js")
|
||||||
val bytes = File("./assets/bios/quick.js").readBytes()
|
object TandemBios : VMProgramRom("./assets/bios/tandemport.js")
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
object BasicRom : VMProgramRom("./assets/bios/basic.bin")
|
||||||
}
|
object TBASRelBios : VMProgramRom("./assets/bios/tbasdist.js")
|
||||||
|
object WPBios : VMProgramRom("./assets/bios/wp.js")
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
}
|
|
||||||
|
|
||||||
object BasicBios : VMProgramRom {
|
|
||||||
private val contents: ByteArray
|
|
||||||
|
|
||||||
init {
|
|
||||||
val bytes = File("./assets/bios/basicbios.js").readBytes()
|
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
}
|
|
||||||
|
|
||||||
object TandemBios : VMProgramRom {
|
|
||||||
private val contents: ByteArray
|
|
||||||
|
|
||||||
init {
|
|
||||||
val bytes = File("./assets/bios/tandemport.js").readBytes()
|
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
}
|
|
||||||
|
|
||||||
object BasicRom : VMProgramRom {
|
|
||||||
private val contents: ByteArray
|
|
||||||
|
|
||||||
init {
|
|
||||||
val bytes = File("./assets/bios/basic.bin").readBytes()
|
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
}
|
|
||||||
|
|
||||||
object TBASRelBios : VMProgramRom {
|
|
||||||
private val contents: ByteArray
|
|
||||||
|
|
||||||
init {
|
|
||||||
val bytes = File("./assets/bios/tbasdist.js").readBytes()
|
|
||||||
contents = bytes.sliceArray(0 until minOf(65536, bytes.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAll(): String {
|
|
||||||
// check if bios is compressed in gzip
|
|
||||||
return if (contents.startsWith(GZIP_HEADER))
|
|
||||||
CompressorDelegate.decomp(contents).toString(VM.CHARSET)
|
|
||||||
else
|
|
||||||
contents.toString(VM.CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(addr: Int): Byte = contents[addr]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user