From f1365ed34cda3ba87db85b9d394eee06544dd050 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 14 Dec 2020 11:01:16 +0900 Subject: [PATCH] basic: making sure 'PRINT -3' to work --- assets/basic.js | 66 ++++++++++++++++++-------------- assets/tbas/parser_wip.js | 79 +++++++++++++++++++++++---------------- assets/tbas/syntax.txt | 4 +- 3 files changed, 85 insertions(+), 64 deletions(-) diff --git a/assets/basic.js b/assets/basic.js index 8ebda94..c3f7c94 100644 --- a/assets/basic.js +++ b/assets/basic.js @@ -1193,7 +1193,7 @@ bF._opPrc = { "*":2,"/":2, "MOD":3, "+":4,"-":4, - //";":5, + "NOT":5,"BNOT":5, "<<":6,">>":6, "<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7, "==":8,"<>":8,"><":8, @@ -1784,7 +1784,6 @@ bF._parseTokens = function(lnum, tokens, states) { /** Parses following EBNF rule: stmt = "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] - | "FOR" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn} | "(" , stmt , ")" @@ -1864,25 +1863,6 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { /*************************************************************************/ - // ## case for: - // | "FOR" , expr - if ("FOR" == headTkn && "lit" == headSta) { - bF.parserPrintdbgline('$', 'FOR Stmt', lnum, recDepth); - - treeHead.astValue = "FOR"; - treeHead.astType = "function"; - - treeHead.astLeaves[0] = bF._parseExpr(lnum, - tokens.slice(1, tokens.length), - states.slice(1, states.length), - recDepth + 1 - ); - - return treeHead; - } - - /*************************************************************************/ - // ## case for: // | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr if ("DEFUN" == headTkn && "lit" == headSta && @@ -2007,6 +1987,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f lit | "(" , expr , ")" | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | kywd , expr (* also deals with FOR statement; kywd = ? words that exists on the list of predefined function that are not operators ? ; *) | function_call | expr , op , expr | op_uni , expr ; @@ -2016,11 +1997,13 @@ 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); + let headTkn = tokens[0].toUpperCase(); + let headSta = states[0]; + /*************************************************************************/ // ## case for: // lit - let headTkn = tokens[0].toUpperCase(); if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) { bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth); return bF._parseLit(lnum, tokens, states, recDepth + 1); @@ -2099,10 +2082,30 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { /*************************************************************************/ + // ## case for: + // | kywd , expr (* kywd = ? words that exists on the list of predefined function that are not operators ? ; *) + if (bStatus.builtin[headTkn] && headSta == "lit" && !bF._opPrc[headTkn] && + states[1] != "paren" + ) { + bF.parserPrintdbgline('E', 'Builtin Function Call w/o Paren', lnum, recDepth); + + let treeHead = bF._parseIdent(lnum, [headTkn], [headSta], recDepth + 1); + treeHead.astLeaves[0] = bF._parseExpr(lnum, + tokens.slice(1, tokens.length), + states.slice(1, states.length), + recDepth + 1 + ); + treeHead.astType = "function"; + + return treeHead; + } + + /*************************************************************************/ + // ## case for: // (* at this point, if OP is found in paren-level 0, skip function_call *) // | function_call ; - if (topmostOp === undefined) { + 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); @@ -2145,7 +2148,12 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); } else { - treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; + if (topmostOp === "-") treeHead.astValue = "UNARYMINUS" + else if (topmostOp === "+") treeHead.astValue = "UNARYPLUS" + else if (topmostOp === "NOT") treeHead.astValue = "UNARYLOGICNOT" + else if (topmostOp === "BNOT") treeHead.astValue = "UNARYBNOT" + else throw new ParserError(`Unknown unary op '${topmostOp}'`) + treeHead.astLeaves[0] = bF._parseExpr(lnum, tokens.slice(operatorPos + 1, tokens.length), states.slice(operatorPos + 1, states.length), @@ -2554,11 +2562,6 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) { bF._interpretLine = function(lnum, cmd) { var _debugprintHighestLevel = false; - if (TRACEON) { - //print(`[${lnum}]`); - serial.println("[BASIC] Line "+lnum); - } - if (cmd.toUpperCase().startsWith("REM")) { if (_debugprintHighestLevel) serial.println(lnum+" "+cmd); return undefined; @@ -2684,6 +2687,11 @@ bF.run = function(args) { // RUN function var oldnum = 1; do { if (cmdbuf[linenumber] !== undefined) { + if (TRACEON) { + //print(`[${lnum}]`); + serial.println("[BASIC] Line "+linenumber); + } + oldnum = linenumber; let trees = programTrees[linenumber]; diff --git a/assets/tbas/parser_wip.js b/assets/tbas/parser_wip.js index 9c2d1c3..8ba5f85 100644 --- a/assets/tbas/parser_wip.js +++ b/assets/tbas/parser_wip.js @@ -74,7 +74,6 @@ bF._parseTokens = function(lnum, tokens, states) { /** Parses following EBNF rule: stmt = "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] - | "FOR" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn} | "(" , stmt , ")" @@ -152,26 +151,6 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { if (!(e instanceof ParserError)) throw e; } - - /*************************************************************************/ - - // ## case for: - // | "FOR" , expr - if ("FOR" == headTkn && "lit" == headSta) { - bF.parserPrintdbgline('$', 'FOR Stmt', lnum, recDepth); - - treeHead.astValue = "FOR"; - treeHead.astType = "function"; - - treeHead.astLeaves[0] = bF._parseExpr(lnum, - tokens.slice(1, tokens.length), - states.slice(1, states.length), - recDepth + 1 - ); - - return treeHead; - } - /*************************************************************************/ // ## case for: @@ -298,6 +277,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f lit | "(" , expr , ")" | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | kywd , expr (* also deals with FOR statement; kywd = ? words that exists on the list of predefined function that are not operators ? ; *) | function_call | expr , op , expr | op_uni , expr ; @@ -307,11 +287,13 @@ 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); + let headTkn = tokens[0].toUpperCase(); + let headSta = states[0]; + /*************************************************************************/ // ## case for: // lit - let headTkn = tokens[0].toUpperCase(); if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) { bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth); return bF._parseLit(lnum, tokens, states, recDepth + 1); @@ -387,13 +369,33 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); if (!(e instanceof ParserError)) throw e; } - + + /*************************************************************************/ + + // ## case for: + // | kywd , expr (* kywd = ? words that exists on the list of predefined function that are not operators ? ; *) + if (bStatus.builtin[headTkn] && headSta == "lit" && !bF._opPrc[headTkn] && + states[1] != "paren" + ) { + bF.parserPrintdbgline('E', 'Builtin Function Call w/o Paren', lnum, recDepth); + + let treeHead = bF._parseIdent(lnum, [headTkn], [headSta], recDepth + 1); + treeHead.astLeaves[0] = bF._parseExpr(lnum, + tokens.slice(1, tokens.length), + states.slice(1, states.length), + recDepth + 1 + ); + treeHead.astType = "function"; + + return treeHead; + } + /*************************************************************************/ // ## case for: // (* at this point, if OP is found in paren-level 0, skip function_call *) // | function_call ; - if (topmostOp === undefined) { + 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); @@ -436,7 +438,12 @@ bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); } else { - treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; + if (topmostOp === "-") treeHead.astValue = "UNARYMINUS" + else if (topmostOp === "+") treeHead.astValue = "UNARYPLUS" + else if (topmostOp === "NOT") treeHead.astValue = "UNARYLOGICNOT" + else if (topmostOp === "BNOT") treeHead.astValue = "UNARYBNOT" + else throw new ParserError(`Unknown unary op '${topmostOp}'`) + treeHead.astLeaves[0] = bF._parseExpr(lnum, tokens.slice(operatorPos + 1, tokens.length), states.slice(operatorPos + 1, states.length), @@ -700,7 +707,7 @@ bF._opPrc = { "*":2,"/":2, "MOD":3, "+":4,"-":4, - //";":5, + "NOT":5,"BNOT":5, "<<":6,">>":6, "<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7, "==":8,"<>":8,"><":8, @@ -717,6 +724,9 @@ bF._opPrc = { "IN":1000 }; bF._opRh = {"^":1,"=":1,"!":1,"IN":1}; +let bStatus = {}; +bStatus.builtin = {}; +["PRINT","NEXT","SPC","CHR","ROUND","SQR","RND","GOTO","GOSUB","DEFUN","FOR"].forEach(w=>{ bStatus.builtin[w] = 1 }); let lnum = 10; // FIXME print's last (;) gets parsed but ignored @@ -740,16 +750,16 @@ let states4 = ["lit","paren","lit","lit","op","lit","lit","qot","lit","lit","lit let tokens5 = ["ON","6","*","SQR","(","X","-","3",")","GOTO","X","+","1",",","X","+","2",",","X","+","3"]; let states5 = ["lit","num","op","lit","paren","lit","op","num","paren","lit","lit","op","num","sep","lit","op","num","sep","lit","op","num"]; -// FOR K=1 TO 10 +// FOR K=10 TO 1 STEP -1 let tokens6 = ["FOR","K","=","10","TO","1","STEP","-","1"]; let states6 = ["lit","lit","op","num","op","num","op","op","num"]; -// FIXME print(chr(47+round(rnd(1))*45);) outputs bad tree +// print(chr(47+round(rnd(1))*45);) let tokens7 = ["PRINT","(","CHR","(","47","+","ROUND","(","RND","(","1",")",")","*","45",")",";",")"]; let states7 = ["lit","paren","lit","paren","num","op","lit","paren","lit","paren","num","paren","paren","op","num","paren","sep","paren"]; -// PRINT 4 + 5 * 9 -let tokens8 = ["PRINT","4","+","5","*","9"]; +// PRINT 4 - 5 * 9 +let tokens8 = ["PRINT","4","-","5","*","9"]; let states8 = ["lit","num","op","num","op","num"]; // NEXT @@ -762,12 +772,15 @@ let states10 = ["lit","op","num"]; // PRINT SPC(20-I); let tokens11 = ["PRINT","SPC","(","20","-","I",")",";"]; -let states11 = ["lit"],"lit","paren","num","op","lit","paren","sep"]; +let states11 = ["lit","lit","paren","num","op","lit","paren","sep"]; +// DEFUN FAC(N)=IF N==0 THEN 1 ELSE N*FAC(N-1) +let tokens12 = ["DEFUN","FAC","(","N",")","=","IF","N","==","0","THEN","1","ELSE","N","*","FAC","(","N","-","1",")"]; +let states12 = ["lit","lit","paren","lit","paren","op","lit","lit","op","num","lit","num","lit","lit","op","lit","paren","lit","op","num","paren"]; try { let trees = bF._parseTokens(lnum, - tokens6, - states6 + tokens12, + states12 ); trees.forEach((t,i) => { serial.println("\nParsed Statement #"+(i+1)); diff --git a/assets/tbas/syntax.txt b/assets/tbas/syntax.txt index 7dccee3..ff26050 100644 --- a/assets/tbas/syntax.txt +++ b/assets/tbas/syntax.txt @@ -7,7 +7,6 @@ linenumber = digits ; stmt = "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] - | "FOR" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "ON" , expr_sans_asgn , ("GOTO" | "GOSUB") , expr_sans_asgn , {"," , expr_sans_asgn} | "(" , stmt , ")" @@ -17,6 +16,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f lit | "(" , expr , ")" | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | kywd , expr - "(" (* also deals with FOR statement *) (* at this point, if OP is found in paren-level 0, skip function_call *) | function_call | expr , op , expr @@ -27,7 +27,7 @@ expr_sans_asgn = ? identical to expr except errors out whenever "=" is found ? ; function_call = ident , "(" , [expr , {argsep , expr} , [argsep]] , ")" | ident , expr , {argsep , expr} , [argsep] ; - +kywd = ? words that exists on the list of predefined function that are not operators ? ; (* don't bother looking at these, because you already know the stuff *)