From 0d68c667f00169421d94d50c9d24c76d8e4fd8ad Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 12 Dec 2020 17:05:59 +0900 Subject: [PATCH] basic: integration test on master branch lol --- assets/basic.js | 352 +++++++++++++++++++------------------- assets/rmaze.bas | 1 - assets/tbas/parser_wip.js | 331 +++++++++++++++++------------------ assets/tbas/syntax.txt | 4 +- 4 files changed, 349 insertions(+), 339 deletions(-) diff --git a/assets/basic.js b/assets/basic.js index cd51dd1..863a494 100644 --- a/assets/basic.js +++ b/assets/basic.js @@ -1708,16 +1708,16 @@ bF.isSemanticLiteral = function(token, state) { "qot" == state || "num" == state || "bool" == state || "lit" == state; } bF.parserDoDebugPrint = false; -bF.parserPrintdbg = any => if (parserDoDebugPrint) serial.println(any); +bF.parserPrintdbg = any => { if (bF.parserDoDebugPrint) serial.println(any) }; bF.parserPrintdbg2 = function(icon, lnum, tokens, states, recDepth) { - if (parserDoDebugPrint) { + if (bF.parserDoDebugPrint) { let treeHead = String.fromCharCode(0x2502,32).repeat(recDepth); bF.parserPrintdbg(`${icon}${lnum} ${treeHead}${tokens.join(' ')}`); bF.parserPrintdbg(`${icon}${lnum} ${treeHead}${states.join(' ')}`); } } bF.parserPrintdbgline = function(icon, msg, lnum, recDepth) { - if (parserDoDebugPrint) { + if (bF.parserDoDebugPrint) { let treeHead = String.fromCharCode(0x2502,32).repeat(recDepth); bF.parserPrintdbg(`${icon}${lnum} ${treeHead}${msg}`); } @@ -1772,6 +1772,7 @@ bF._parseTokens = function(lnum, tokens, states) { }); } + /** Parses following EBNF rule: stmt = "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] @@ -1959,9 +1960,167 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { /*************************************************************************/ - throw new ParserError("Statement cannot be parsed: "+e.stack); + throw new ParserError("Statement cannot be parsed"); } // END of STMT + +/** Parses following EBNF rule: +expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *) + lit + | "(" , expr , ")" + | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | function_call + | expr , op , expr + | op_uni , expr ; + + * @return: BasicAST + */ +bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { + bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); + + /*************************************************************************/ + + // ## 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); + } + + /*************************************************************************/ + + // scan for operators with highest precedence, use rightmost one if multiple were found + let topmostOp; + let topmostOpPrc = 0; + let operatorPos = -1; + + // find and mark position of parentheses + // properly deal with the nested function calls + let parenDepth = 0; + let parenStart = -1; + let parenEnd = -1; + + // Scan for unmatched parens and mark off the right operator we must deal with + // every function_call need to re-scan because it is recursively called + for (let k = 0; k < tokens.length; k++) { + // increase paren depth and mark paren start position + if (tokens[k] == "(" && states[k] != "qot") { + parenDepth += 1; + if (parenStart == -1 && parenDepth == 1) parenStart = k; + } + // decrease paren depth + else if (tokens[k] == ")" && states[k] != "qot") { + if (parenEnd == -1 && parenDepth == 1) parenEnd = k; + parenDepth -= 1; + } + + // determine the right operator to deal with + if (parenDepth == 0) { + if (states[k] == "op" && bF.isSemanticLiteral(tokens[k-1], states[k-1]) && + ((bF._opPrc[tokens[k].toUpperCase()] > topmostOpPrc) || + (!bF._opRh[tokens[k].toUpperCase()] && bF._opPrc[tokens[k].toUpperCase()] == topmostOpPrc)) + ) { + topmostOp = tokens[k].toUpperCase(); + topmostOpPrc = bF._opPrc[tokens[k].toUpperCase()]; + operatorPos = k; + } + } + } + + // unmatched brackets, duh! + if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets); + + /*************************************************************************/ + + // ## case for: + // | "(" , expr , ")" + if (parenStart == 0 && parenEnd == tokens.length - 1) { + bF.parserPrintdbgline('E', '( Expr )', lnum, recDepth); + + return bF._parseExpr(lnum, + tokens.slice(parenStart + 1, parenEnd), + states.slice(parenStart + 1, parenEnd), + recDepth + 1 + ); + } + + /*************************************************************************/ + + // ## case for: + // | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + try { + bF.parserPrintdbgline('E', "Trying IF Expression...", lnum, recDepth); + return bF._parseIfMode(lnum, tokens, states, recDepth + 1, false); + } + // if ParserError is raised, continue to apply other rules + catch (e) { + bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); + if (!(e instanceof ParserError)) throw e; + } + + /*************************************************************************/ + + // ## case for: + // | function_call ; + try { + bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); + return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); + } + // if ParserError is raised, continue to apply other rules + catch (e) { + bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); + if (!(e instanceof ParserError)) throw e; + } + + /*************************************************************************/ + + // ## case for: + // | expr , op, expr + // | op_uni , expr + // if operator is found, split by the operator and recursively parse the LH and RH + if (topmostOp !== undefined) { + bF.parserPrintdbgline('E', 'Operators', lnum, recDepth); + + if (ifMode && topmostOp == "=") throw lang.syntaxfehler(lnum, "'=' used on IF, did you mean '=='?"); + if (ifMode && topmostOp == ":") throw lang.syntaxfehler(lnum, "':' used on IF"); + + + // this is the AST we're going to build up and return + // (other IF clauses don't use this) + let treeHead = new BasicAST(); + treeHead.astLnum = lnum; + treeHead.astValue = topmostOp; + treeHead.astType = "op"; + + // BINARY_OP? + if (operatorPos > 0) { + let subtknL = tokens.slice(0, operatorPos); + let substaL = states.slice(0, operatorPos); + let subtknR = tokens.slice(operatorPos + 1, tokens.length); + let substaR = states.slice(operatorPos + 1, tokens.length); + + treeHead.astLeaves[0] = bF._parseExpr(lnum, subtknL, substaL, recDepth + 1); + treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); + } + else { + treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; + treeHead.astLeaves[0] = bF._parseExpr(lnum, + tokens.slice(operatorPos + 1, tokens.length), + states.slice(operatorPos + 1, states.length), + recDepth + 1 + ); + } + + return treeHead; + } + + /*************************************************************************/ + + throw new ParserError("Expression cannot be parsed"); +} // END of EXPR + + /** Parses following EBNF rule: "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] @@ -2047,165 +2206,6 @@ bF._parseIfMode = function(lnum, tokens, states, recDepth, exprMode) { } // END of IF -/** Parses following EBNF rule: -expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *) - lit - | "(" , expr , ")" - | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] - | expr , op , expr - | op_uni , expr - | function_call ; //TODO - - * @return: BasicAST - */ -bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { - bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); - - /*************************************************************************/ - - // ## 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); - } - - /*************************************************************************/ - - // scan for operators with highest precedence, use rightmost one if multiple were found - let topmostOp; - let topmostOpPrc = 0; - let operatorPos = -1; - - // find and mark position of parentheses - // properly deal with the nested function calls - let parenDepth = 0; - let parenStart = -1; - let parenEnd = -1; - - // Scan for unmatched parens and mark off the right operator we must deal with - // every function_call need to re-scan because it is recursively called - for (let k = 0; k < tokens.length; k++) { - // increase paren depth and mark paren start position - if (tokens[k] == "(" && states[k] != "qot") { - parenDepth += 1; - if (parenStart == -1 && parenDepth == 1) parenStart = k; - } - // decrease paren depth - else if (tokens[k] == ")" && states[k] != "qot") { - if (parenEnd == -1 && parenDepth == 1) parenEnd = k; - parenDepth -= 1; - } - - // determine the right operator to deal with - if (parenDepth == 0) { - if (states[k] == "op" && bF.isSemanticLiteral(tokens[k-1], states[k-1]) && - ((bF._opPrc[tokens[k].toUpperCase()] > topmostOpPrc) || - (!bF._opRh[tokens[k].toUpperCase()] && bF._opPrc[tokens[k].toUpperCase()] == topmostOpPrc)) - ) { - topmostOp = tokens[k].toUpperCase(); - topmostOpPrc = bF._opPrc[tokens[k].toUpperCase()]; - operatorPos = k; - } - } - } - - // unmatched brackets, duh! - if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets); - if (_debugSyntaxAnalysis) serial.println("Equation NEW Paren position: "+parenStart+", "+parenEnd); - - /*************************************************************************/ - - // ## case for: - // | "(" , expr , ")" - if (parenStart == 0 && parenEnd == tokens.length - 1) { - bF.parserPrintdbgline('E', '( Expr )', lnum, recDepth); - - return bF._parseEquation(lnum, - tokens.slice(parenStart + 1, parenEnd), - states.slice(parenStart + 1, parenEnd), - recDepth + 1 - ); - } - - /*************************************************************************/ - - // ## case for: - // | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] - try { - bF.parserPrintdbgline('E', "Trying IF Expression...", lnum, recDepth); - return bF._parseIfMode(lnum, tokens, states, recDepth + 1, false); - } - // if ParserError is raised, continue to apply other rules - catch (e) { - bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); - if (!(e instanceof ParserError)) throw e; - } - - /*************************************************************************/ - - // ## case for: - // | expr , op, expr - // | op_uni , expr - // if operator is found, split by the operator and recursively parse the LH and RH - if (topmostOp !== undefined) { - bF.parserPrintdbgline('E', 'Operators', lnum, recDepth); - - if (_debugSyntaxAnalysis) serial.println("operator: "+topmostOp+", pos: "+operatorPos); - - if (ifMode && topmostOp == "=") throw lang.syntaxfehler(lnum, "'=' used on IF, did you mean '=='?"); - if (ifMode && topmostOp == ":") throw lang.syntaxfehler(lnum, "':' used on IF"); - - - // this is the AST we're going to build up and return - // (other IF clauses don't use this) - let treeHead = new BasicAST(); - treeHead.astLnum = lnum; - treeHead.astValue = topmostOp; - treeHead.astType = "op"; - - // BINARY_OP? - if (operatorPos > 0) { - let subtknL = tokens.slice(0, operatorPos); - let substaL = states.slice(0, operatorPos); - let subtknR = tokens.slice(operatorPos + 1, tokens.length); - let substaR = states.slice(operatorPos + 1, tokens.length); - - treeHead.astLeaves[0] = bF._parseExpr(lnum, subtknL, substaL, recDepth + 1); - treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); - } - else { - treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; - treeHead.astLeaves[0] = bF._parseExpr(lnum, - tokens.slice(operatorPos + 1, tokens.length), - states.slice(operatorPos + 1, states.length), - recDepth + 1 - ); - } - - return treeHead; - } - - /*************************************************************************/ - - // ## case for: - // | function_call ; - try { - bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); - return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); - } - catch (e) { - throw new ParserError("Expression cannot be parsed: "+e.stack); - } - - /*************************************************************************/ - - throw new ParserError("Expression cannot be parsed: "+e.stack); -} // END of EXPR - - - /** Parses following EBNF rule: function_call = ident , "(" , [expr , {argsep , expr} , [argsep]] , ")" @@ -2316,7 +2316,6 @@ bF._parseLit = function(lnum, tokens, states, recDepth) { let treeHead = new BasicAST(); treeHead.astLnum = lnum; - if (_debugSyntaxAnalysis) serial.println("literal/number: "+tokens[0]); treeHead.astValue = ("qot" == states[0]) ? tokens[0] : tokens[0].toUpperCase(); treeHead.astType = ("qot" == states[0]) ? "string" : ("num" == states[0]) ? "num" : "lit"; @@ -2509,7 +2508,7 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) { throw Error("Parse error"); } }; -// @returns: line number for the next command, normally (lnum + 1); if GOTO or GOSUB was met, returns its line number +// @return ARRAY of BasicAST bF._interpretLine = function(lnum, cmd) { var _debugprintHighestLevel = false; @@ -2533,12 +2532,17 @@ bF._interpretLine = function(lnum, cmd) { bF._parserElaboration(lnum, tokens, states); // PARSING (SYNTAX ANALYSIS) - var syntaxTree = bF._parseTokens(lnum, tokens, states, 0); - if (_debugprintHighestLevel) serial.println("Final syntax tree:"); - if (_debugprintHighestLevel) serial.println(astToString(syntaxTree)); + var syntaxTrees = bF._parseTokens(lnum, tokens, states); + if (_debugprintHighestLevel) { + syntaxTrees.forEach((t,i) => { + serial.println("\nParsed Statement #"+(i+1)); + serial.println(astToString(t)); + }); + } - return syntaxTree; + return syntaxTrees; }; // end INTERPRETLINE +// @return next line number bF._executeAndGet = function(lnum, syntaxTree) { // EXECUTE try { @@ -2625,9 +2629,9 @@ bF.troff = function(args) { }; bF.run = function(args) { // RUN function // pre-build the trees - let programTree = []; + let programTrees = []; cmdbuf.forEach((linestr, linenum) => { - programTree[linenum] = bF._interpretLine(linenum, linestr.trim()); + programTrees[linenum] = bF._interpretLine(linenum, linestr.trim()); }); // actually execute the program @@ -2636,7 +2640,11 @@ bF.run = function(args) { // RUN function do { if (cmdbuf[linenumber] !== undefined) { oldnum = linenumber; - linenumber = bF._executeAndGet(linenumber, programTree[linenumber]); + + let trees = programTrees[linenumber]; + trees.forEach((t,i) => { + linenumber = bF._executeAndGet(linenumber, t); + }); } else { linenumber += 1; diff --git a/assets/rmaze.bas b/assets/rmaze.bas index 7c5f368..48c4f15 100644 --- a/assets/rmaze.bas +++ b/assets/rmaze.bas @@ -1,3 +1,2 @@ -1 rem RANDOM MAZE 10 print(chr(47+round(rnd(1))*45);) 20 goto 10 diff --git a/assets/tbas/parser_wip.js b/assets/tbas/parser_wip.js index 0c6a75f..4c8aec6 100644 --- a/assets/tbas/parser_wip.js +++ b/assets/tbas/parser_wip.js @@ -64,6 +64,7 @@ bF._parseTokens = function(lnum, tokens, states) { }); } + /** Parses following EBNF rule: stmt = "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] @@ -251,9 +252,167 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) { /*************************************************************************/ - throw new ParserError("Statement cannot be parsed: "+e.stack); + throw new ParserError("Statement cannot be parsed"); } // END of STMT + +/** Parses following EBNF rule: +expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *) + lit + | "(" , expr , ")" + | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | function_call + | expr , op , expr + | op_uni , expr ; + + * @return: BasicAST + */ +bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { + bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); + + /*************************************************************************/ + + // ## 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); + } + + /*************************************************************************/ + + // scan for operators with highest precedence, use rightmost one if multiple were found + let topmostOp; + let topmostOpPrc = 0; + let operatorPos = -1; + + // find and mark position of parentheses + // properly deal with the nested function calls + let parenDepth = 0; + let parenStart = -1; + let parenEnd = -1; + + // Scan for unmatched parens and mark off the right operator we must deal with + // every function_call need to re-scan because it is recursively called + for (let k = 0; k < tokens.length; k++) { + // increase paren depth and mark paren start position + if (tokens[k] == "(" && states[k] != "qot") { + parenDepth += 1; + if (parenStart == -1 && parenDepth == 1) parenStart = k; + } + // decrease paren depth + else if (tokens[k] == ")" && states[k] != "qot") { + if (parenEnd == -1 && parenDepth == 1) parenEnd = k; + parenDepth -= 1; + } + + // determine the right operator to deal with + if (parenDepth == 0) { + if (states[k] == "op" && bF.isSemanticLiteral(tokens[k-1], states[k-1]) && + ((bF._opPrc[tokens[k].toUpperCase()] > topmostOpPrc) || + (!bF._opRh[tokens[k].toUpperCase()] && bF._opPrc[tokens[k].toUpperCase()] == topmostOpPrc)) + ) { + topmostOp = tokens[k].toUpperCase(); + topmostOpPrc = bF._opPrc[tokens[k].toUpperCase()]; + operatorPos = k; + } + } + } + + // unmatched brackets, duh! + if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets); + + /*************************************************************************/ + + // ## case for: + // | "(" , expr , ")" + if (parenStart == 0 && parenEnd == tokens.length - 1) { + bF.parserPrintdbgline('E', '( Expr )', lnum, recDepth); + + return bF._parseExpr(lnum, + tokens.slice(parenStart + 1, parenEnd), + states.slice(parenStart + 1, parenEnd), + recDepth + 1 + ); + } + + /*************************************************************************/ + + // ## case for: + // | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + try { + bF.parserPrintdbgline('E', "Trying IF Expression...", lnum, recDepth); + return bF._parseIfMode(lnum, tokens, states, recDepth + 1, false); + } + // if ParserError is raised, continue to apply other rules + catch (e) { + bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); + if (!(e instanceof ParserError)) throw e; + } + + /*************************************************************************/ + + // ## case for: + // | function_call ; + try { + bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); + return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); + } + // if ParserError is raised, continue to apply other rules + catch (e) { + bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); + if (!(e instanceof ParserError)) throw e; + } + + /*************************************************************************/ + + // ## case for: + // | expr , op, expr + // | op_uni , expr + // if operator is found, split by the operator and recursively parse the LH and RH + if (topmostOp !== undefined) { + bF.parserPrintdbgline('E', 'Operators', lnum, recDepth); + + if (ifMode && topmostOp == "=") throw lang.syntaxfehler(lnum, "'=' used on IF, did you mean '=='?"); + if (ifMode && topmostOp == ":") throw lang.syntaxfehler(lnum, "':' used on IF"); + + + // this is the AST we're going to build up and return + // (other IF clauses don't use this) + let treeHead = new BasicAST(); + treeHead.astLnum = lnum; + treeHead.astValue = topmostOp; + treeHead.astType = "op"; + + // BINARY_OP? + if (operatorPos > 0) { + let subtknL = tokens.slice(0, operatorPos); + let substaL = states.slice(0, operatorPos); + let subtknR = tokens.slice(operatorPos + 1, tokens.length); + let substaR = states.slice(operatorPos + 1, tokens.length); + + treeHead.astLeaves[0] = bF._parseExpr(lnum, subtknL, substaL, recDepth + 1); + treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); + } + else { + treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; + treeHead.astLeaves[0] = bF._parseExpr(lnum, + tokens.slice(operatorPos + 1, tokens.length), + states.slice(operatorPos + 1, states.length), + recDepth + 1 + ); + } + + return treeHead; + } + + /*************************************************************************/ + + throw new ParserError("Expression cannot be parsed"); +} // END of EXPR + + /** Parses following EBNF rule: "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] @@ -339,165 +498,6 @@ bF._parseIfMode = function(lnum, tokens, states, recDepth, exprMode) { } // END of IF -/** Parses following EBNF rule: -expr = (* this basically blocks some funny attemps such as using DEFUN as anon function because everything is global in BASIC *) - lit - | "(" , expr , ")" - | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] - | expr , op , expr - | op_uni , expr - | function_call ; //TODO - - * @return: BasicAST - */ -bF._parseExpr = function(lnum, tokens, states, recDepth, ifMode) { - bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); - - /*************************************************************************/ - - // ## 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); - } - - /*************************************************************************/ - - // scan for operators with highest precedence, use rightmost one if multiple were found - let topmostOp; - let topmostOpPrc = 0; - let operatorPos = -1; - - // find and mark position of parentheses - // properly deal with the nested function calls - let parenDepth = 0; - let parenStart = -1; - let parenEnd = -1; - - // Scan for unmatched parens and mark off the right operator we must deal with - // every function_call need to re-scan because it is recursively called - for (let k = 0; k < tokens.length; k++) { - // increase paren depth and mark paren start position - if (tokens[k] == "(" && states[k] != "qot") { - parenDepth += 1; - if (parenStart == -1 && parenDepth == 1) parenStart = k; - } - // decrease paren depth - else if (tokens[k] == ")" && states[k] != "qot") { - if (parenEnd == -1 && parenDepth == 1) parenEnd = k; - parenDepth -= 1; - } - - // determine the right operator to deal with - if (parenDepth == 0) { - if (states[k] == "op" && bF.isSemanticLiteral(tokens[k-1], states[k-1]) && - ((bF._opPrc[tokens[k].toUpperCase()] > topmostOpPrc) || - (!bF._opRh[tokens[k].toUpperCase()] && bF._opPrc[tokens[k].toUpperCase()] == topmostOpPrc)) - ) { - topmostOp = tokens[k].toUpperCase(); - topmostOpPrc = bF._opPrc[tokens[k].toUpperCase()]; - operatorPos = k; - } - } - } - - // unmatched brackets, duh! - if (parenDepth != 0) throw lang.syntaxfehler(lnum, lang.unmatchedBrackets); - if (_debugSyntaxAnalysis) serial.println("Equation NEW Paren position: "+parenStart+", "+parenEnd); - - /*************************************************************************/ - - // ## case for: - // | "(" , expr , ")" - if (parenStart == 0 && parenEnd == tokens.length - 1) { - bF.parserPrintdbgline('E', '( Expr )', lnum, recDepth); - - return bF._parseEquation(lnum, - tokens.slice(parenStart + 1, parenEnd), - states.slice(parenStart + 1, parenEnd), - recDepth + 1 - ); - } - - /*************************************************************************/ - - // ## case for: - // | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] - try { - bF.parserPrintdbgline('E', "Trying IF Expression...", lnum, recDepth); - return bF._parseIfMode(lnum, tokens, states, recDepth + 1, false); - } - // if ParserError is raised, continue to apply other rules - catch (e) { - bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth); - if (!(e instanceof ParserError)) throw e; - } - - /*************************************************************************/ - - // ## case for: - // | expr , op, expr - // | op_uni , expr - // if operator is found, split by the operator and recursively parse the LH and RH - if (topmostOp !== undefined) { - bF.parserPrintdbgline('E', 'Operators', lnum, recDepth); - - if (_debugSyntaxAnalysis) serial.println("operator: "+topmostOp+", pos: "+operatorPos); - - if (ifMode && topmostOp == "=") throw lang.syntaxfehler(lnum, "'=' used on IF, did you mean '=='?"); - if (ifMode && topmostOp == ":") throw lang.syntaxfehler(lnum, "':' used on IF"); - - - // this is the AST we're going to build up and return - // (other IF clauses don't use this) - let treeHead = new BasicAST(); - treeHead.astLnum = lnum; - treeHead.astValue = topmostOp; - treeHead.astType = "op"; - - // BINARY_OP? - if (operatorPos > 0) { - let subtknL = tokens.slice(0, operatorPos); - let substaL = states.slice(0, operatorPos); - let subtknR = tokens.slice(operatorPos + 1, tokens.length); - let substaR = states.slice(operatorPos + 1, tokens.length); - - treeHead.astLeaves[0] = bF._parseExpr(lnum, subtknL, substaL, recDepth + 1); - treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1); - } - else { - treeHead.astValue = (topmostOp === "-") ? "UNARYMINUS" : "UNARYPLUS"; - treeHead.astLeaves[0] = bF._parseExpr(lnum, - tokens.slice(operatorPos + 1, tokens.length), - states.slice(operatorPos + 1, states.length), - recDepth + 1 - ); - } - - return treeHead; - } - - /*************************************************************************/ - - // ## case for: - // | function_call ; - try { - bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); - return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); - } - catch (e) { - throw new ParserError("Expression cannot be parsed: "+e.stack); - } - - /*************************************************************************/ - - throw new ParserError("Expression cannot be parsed: "+e.stack); -} // END of EXPR - - - /** Parses following EBNF rule: function_call = ident , "(" , [expr , {argsep , expr} , [argsep]] , ")" @@ -608,7 +608,6 @@ bF._parseLit = function(lnum, tokens, states, recDepth) { let treeHead = new BasicAST(); treeHead.astLnum = lnum; - if (_debugSyntaxAnalysis) serial.println("literal/number: "+tokens[0]); treeHead.astValue = ("qot" == states[0]) ? tokens[0] : tokens[0].toUpperCase(); treeHead.astType = ("qot" == states[0]) ? "string" : ("num" == states[0]) ? "num" : "lit"; @@ -698,12 +697,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"]; -let _debugSyntaxAnalysis = false; +// FOR K=1 TO 10 +let tokens6 = ["FOR","K","=","1","TO","10"]; +let states6 = ["lit","lit","op","num","op","num"]; + +// FIXME print(chr(47+round(rnd(1))*45);) outputs bad tree try { let trees = bF._parseTokens(lnum, - tokens5.concat([":"], tokens4), - states5.concat(["seq"], states4) + tokens5, + states5 ); trees.forEach((t,i) => { serial.println("\nParsed Statement #"+(i+1)); diff --git a/assets/tbas/syntax.txt b/assets/tbas/syntax.txt index b7c6139..f5d94ed 100644 --- a/assets/tbas/syntax.txt +++ b/assets/tbas/syntax.txt @@ -16,9 +16,9 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f lit | "(" , expr , ")" | "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , expr] + | function_call | expr , op , expr - | op_uni , expr - | function_call ; + | op_uni , expr ; expr_sans_asgn = ? identical to expr except errors out whenever "=" is found ? ;