From ddb7437755af03efd47fe3d17eabda6f7c9740d1 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 14 Dec 2020 15:28:54 +0900 Subject: [PATCH] basic: fixing print with (;) would not work as intended --- assets/basic.js | 40 ++++++++++++++++++++++-------------- assets/fib.bas | 1 + assets/tbas/parser_wip.js | 43 +++++++++++++++++++++++++++++---------- 3 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 assets/fib.bas diff --git a/assets/basic.js b/assets/basic.js index 55ad8ae..f5a4404 100644 --- a/assets/basic.js +++ b/assets/basic.js @@ -223,9 +223,8 @@ let astToString = function(ast, depth) { sb += l__.repeat(recDepth+1) + "leaves: "+(ast.astLeaves.length)+"\n"; sb += l__.repeat(recDepth+1) + "value: "+ast.astValue+" (type: "+typeof ast.astValue+")\n"; for (var k = 0; k < ast.astLeaves.length; k++) { - if (k > 0) - sb += l__.repeat(recDepth+1) + " " + ast.astSeps[k - 1] + "\n"; sb += astToString(ast.astLeaves[k], recDepth + 1); + sb += l__.repeat(recDepth+1) + " " + ast.astSeps[k] + "\n"; } sb += l__.repeat(recDepth)+String.fromCharCode(0x2570)+String.fromCharCode(0x2500).repeat(13)+'\n'; return sb; @@ -1726,7 +1725,6 @@ bF.parserPrintdbgline = function(icon, msg, lnum, recDepth) { } /** - * The starting point to parse those tokens * @return ARRAY of BasicAST */ bF._parseTokens = function(lnum, tokens, states) { @@ -1740,7 +1738,7 @@ bF._parseTokens = function(lnum, tokens, states) { let parenStart = -1; let parenEnd = -1; let seps = []; - + // scan for parens and (:)s for (let k = 0; k < tokens.length; k++) { // increase paren depth and mark paren start position @@ -1757,17 +1755,17 @@ bF._parseTokens = function(lnum, tokens, states) { if (parenDepth == 0 && tokens[k] == ":" && states[k] == "seq") seps.push(k); } - + let startPos = [0].concat(seps.map(k => k+1)); let stmtPos = startPos.map((s,i) => {return{start:s, end:(seps[i] || tokens.length)}}); // use end of token position as separator position - + return stmtPos.map((x,i) => { if (stmtPos.length > 1) bF.parserPrintdbgline('Line ', 'Statement #'+(i+1), lnum, 0); - + // check for empty tokens if (x.end - x.start <= 0) throw new ParserError("Malformed Line"); - + let tree = bF._parseStmt(lnum, tokens.slice(x.start, x.end), states.slice(x.start, x.end), @@ -1876,7 +1874,7 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { // parse function name if (tokens[1] == "(") { // anonymous function - treeHead.astLeaves[0] = BasicAST(); + treeHead.astLeaves[0] = new BasicAST(); treeHead.astLeaves[0].astLnum = lnum; treeHead.astLeaves[0].astType = "lit"; } @@ -1997,6 +1995,20 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); + /*************************************************************************/ + + // ## special case for virtual dummy element (e.g. phantom element on "PRINT SPC(20);") + if (tokens[0] === undefined && states[0] === undefined) { + let treeHead = new BasicAST(); + treeHead.astLnum = lnum; + treeHead.astValue = undefined; + treeHead.astType = "null"; + + return treeHead; + } + + /*************************************************************************/ + let headTkn = tokens[0].toUpperCase(); let headSta = states[0]; @@ -2305,7 +2317,7 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { treeHead.astValue = bF._parseIdent(lnum, [tokens[0]], [states[0]], recDepth + 1).astValue; // always UPPERCASE // 5 8 11 [end] - let argSeps = parenUsed ? [].concat(_argsepsOnLevelOne) : [].concat(_argsepsOnLevelZero); // choose which "sep tray" to use + let argSeps = parenUsed ? _argsepsOnLevelOne : _argsepsOnLevelZero; // choose which "sep tray" to use bF.parserPrintdbgline(String.fromCharCode(0x192), "argSeps = "+argSeps, lnum, recDepth); // 1 6 9 12 let argStartPos = [1 + (parenUsed)].concat(argSeps.map(k => k+1)); @@ -2315,16 +2327,14 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { bF.parserPrintdbgline(String.fromCharCode(0x192), "argPos = "+argPos.map(it=>`${it.start}/${it.end}`), lnum, recDepth); // check for trailing separator - let hasTrailingSep = (states[((parenUsed) ? parenEnd : states.length) - 1] == "sep"); - // exclude last separator from recursion if input tokens has trailing separator - if (hasTrailingSep) argPos.pop(); + // recursively parse function arguments treeHead.astLeaves = argPos.map((x,i) => { bF.parserPrintdbgline(String.fromCharCode(0x192), 'Function Arguments #'+(i+1), lnum, recDepth); // 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"); return bF._parseExpr(lnum, tokens.slice(x.start, x.end), @@ -2334,12 +2344,12 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { ); treeHead.astType = "function"; treeHead.astSeps = argSeps.map(i => tokens[i]); + bF.parserPrintdbgline(String.fromCharCode(0x192), "astSeps = "+treeHead.astSeps, lnum, recDepth); return treeHead; } - bF._parseIdent = function(lnum, tokens, states, recDepth) { bF.parserPrintdbg2('i', lnum, tokens, states, recDepth); diff --git a/assets/fib.bas b/assets/fib.bas new file mode 100644 index 0000000..13c792a --- /dev/null +++ b/assets/fib.bas @@ -0,0 +1 @@ +10 DEFUN FIB(N)=IF N==0 THEN 0 ELSE IF N==1 THEN 1 ELSE FIB(N-1)+FIB(N-2) diff --git a/assets/tbas/parser_wip.js b/assets/tbas/parser_wip.js index b5c7af6..a3e1579 100644 --- a/assets/tbas/parser_wip.js +++ b/assets/tbas/parser_wip.js @@ -170,7 +170,7 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { // parse function name if (tokens[1] == "(") { // anonymous function - treeHead.astLeaves[0] = BasicAST(); + treeHead.astLeaves[0] = new BasicAST(); treeHead.astLeaves[0].astLnum = lnum; treeHead.astLeaves[0].astType = "lit"; } @@ -291,6 +291,20 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); + /*************************************************************************/ + + // ## special case for virtual dummy element (e.g. phantom element on "PRINT SPC(20);") + if (tokens[0] === undefined && states[0] === undefined) { + let treeHead = new BasicAST(); + treeHead.astLnum = lnum; + treeHead.astValue = undefined; + treeHead.astType = "null"; + + return treeHead; + } + + /*************************************************************************/ + let headTkn = tokens[0].toUpperCase(); let headSta = states[0]; @@ -599,7 +613,7 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { treeHead.astValue = bF._parseIdent(lnum, [tokens[0]], [states[0]], recDepth + 1).astValue; // always UPPERCASE // 5 8 11 [end] - let argSeps = parenUsed ? [].concat(_argsepsOnLevelOne) : [].concat(_argsepsOnLevelZero); // choose which "sep tray" to use + let argSeps = parenUsed ? _argsepsOnLevelOne : _argsepsOnLevelZero; // choose which "sep tray" to use bF.parserPrintdbgline(String.fromCharCode(0x192), "argSeps = "+argSeps, lnum, recDepth); // 1 6 9 12 let argStartPos = [1 + (parenUsed)].concat(argSeps.map(k => k+1)); @@ -609,16 +623,14 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { bF.parserPrintdbgline(String.fromCharCode(0x192), "argPos = "+argPos.map(it=>`${it.start}/${it.end}`), lnum, recDepth); // check for trailing separator - let hasTrailingSep = (states[((parenUsed) ? parenEnd : states.length) - 1] == "sep"); - // exclude last separator from recursion if input tokens has trailing separator - if (hasTrailingSep) argPos.pop(); + // recursively parse function arguments treeHead.astLeaves = argPos.map((x,i) => { bF.parserPrintdbgline(String.fromCharCode(0x192), 'Function Arguments #'+(i+1), lnum, recDepth); // 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"); return bF._parseExpr(lnum, tokens.slice(x.start, x.end), @@ -628,6 +640,7 @@ bF._parseFunctionCall = function(lnum, tokens, states, recDepth) { ); treeHead.astType = "function"; treeHead.astSeps = argSeps.map(i => tokens[i]); + bF.parserPrintdbgline(String.fromCharCode(0x192), "astSeps = "+treeHead.astSeps, lnum, recDepth); return treeHead; } @@ -688,9 +701,8 @@ let astToString = function(ast, depth) { sb += l__.repeat(recDepth+1) + "leaves: "+(ast.astLeaves.length)+"\n"; sb += l__.repeat(recDepth+1) + "value: "+ast.astValue+" (type: "+typeof ast.astValue+")\n"; for (var k = 0; k < ast.astLeaves.length; k++) { - if (k > 0) - sb += l__.repeat(recDepth+1) + " " + ast.astSeps[k - 1] + "\n"; sb += astToString(ast.astLeaves[k], recDepth + 1); + sb += l__.repeat(recDepth+1) + " " + ast.astSeps[k] + "\n"; } sb += l__.repeat(recDepth)+String.fromCharCode(0x2570)+String.fromCharCode(0x2500).repeat(13)+'\n'; return sb; @@ -729,7 +741,6 @@ let bStatus = {}; bStatus.builtin = {}; ["PRINT","NEXT","SPC","CHR","ROUND","SQR","RND","GOTO","GOSUB","DEFUN","FOR","MAP"].forEach(w=>{ bStatus.builtin[w] = 1 }); let lnum = 10; -// FIXME print's last (;) gets parsed but ignored // if s<2 then (nop1) else (if s < 9999 then nop2 else nop3) let tokens1 = ["if","s","<","2","then","(","nop1",")","else","(","if","s","<","9999","then","nop2","else","nop3",")"]; @@ -783,10 +794,20 @@ let states12 = ["lit","lit","paren","lit","paren","op","lit","lit","op","num","l let tokens13 = ["K","=","MAP","FAC",",","1","TO","10"]; let states13 = ["lit","op","lit","lit","sep","num","op","num"]; +// DEFUN FIB(N)=IF N==0 THEN 0 ELSE IF N==1 THEN 1 ELSE FIB(N-1)+FIB(N-2) +let tokens14 = ["DEFUN","FIB","(","N",")","=","IF","N","==","0","THEN","0", + "ELSE","IF","N","==","1","THEN","1", + "ELSE","FIB","(","N","-","1",")","+","FIB","(","N","-","2",")"]; +let states14 = ["lit","lit","paren","lit","paren","op","lit","lit","op","num","lit","num", + "lit","lit","lit","op","num","lit","num", + "lit","lit","paren","lit","op","num","paren","op","lit","paren","lit","op","num","paren"]; + +// PRINT(MAP FIB, 1 TO 10) is broken + try { let trees = bF._parseTokens(lnum, - tokens13, - states13 + tokens14, + states14 ); trees.forEach((t,i) => { serial.println("\nParsed Statement #"+(i+1));