wordprocessor wip

This commit is contained in:
minjaesong
2021-06-08 11:39:26 +09:00
parent d1313f1f6c
commit e1905f2ca5
6 changed files with 419 additions and 400 deletions

58
assets/bios/wp.js Normal file
View 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()

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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]
}

View File

@@ -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
))

View File

@@ -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]
}