basic: making sure 'PRINT -3' to work

This commit is contained in:
minjaesong
2020-12-14 11:01:16 +09:00
parent 1b6f768f9d
commit f1365ed34c
3 changed files with 85 additions and 64 deletions

View File

@@ -1193,7 +1193,7 @@ bF._opPrc = {
"*":2,"/":2, "*":2,"/":2,
"MOD":3, "MOD":3,
"+":4,"-":4, "+":4,"-":4,
//";":5, "NOT":5,"BNOT":5,
"<<":6,">>":6, "<<":6,">>":6,
"<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7, "<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7,
"==":8,"<>":8,"><":8, "==":8,"<>":8,"><":8,
@@ -1784,7 +1784,6 @@ bF._parseTokens = function(lnum, tokens, states) {
/** Parses following EBNF rule: /** Parses following EBNF rule:
stmt = stmt =
"IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt]
| "FOR" , expr
| "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr
| "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn} | "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn}
| "(" , stmt , ")" | "(" , 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: // ## case for:
// | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr // | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr
if ("DEFUN" == headTkn && "lit" == headSta && if ("DEFUN" == headTkn && "lit" == headSta &&
@@ -2007,6 +1987,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f
lit lit
| "(" , expr , ")" | "(" , expr , ")"
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , 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 | function_call
| expr , op , expr | expr , op , expr
| op_uni , 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._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); bF.parserPrintdbg2('E', lnum, tokens, states, recDepth);
let headTkn = tokens[0].toUpperCase();
let headSta = states[0];
/*************************************************************************/ /*************************************************************************/
// ## case for: // ## case for:
// lit // lit
let headTkn = tokens[0].toUpperCase();
if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) { if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) {
bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth); bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth);
return bF._parseLit(lnum, tokens, states, recDepth + 1); 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: // ## case for:
// (* at this point, if OP is found in paren-level 0, skip function_call *) // (* at this point, if OP is found in paren-level 0, skip function_call *)
// | function_call ; // | function_call ;
if (topmostOp === undefined) { if (topmostOp === undefined) { // don't remove this IF statement!
try { try {
bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth);
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); 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); treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1);
} }
else { 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, treeHead.astLeaves[0] = bF._parseExpr(lnum,
tokens.slice(operatorPos + 1, tokens.length), tokens.slice(operatorPos + 1, tokens.length),
states.slice(operatorPos + 1, states.length), states.slice(operatorPos + 1, states.length),
@@ -2554,11 +2562,6 @@ bF._executeSyntaxTree = function(lnum, syntaxTree, recDepth) {
bF._interpretLine = function(lnum, cmd) { bF._interpretLine = function(lnum, cmd) {
var _debugprintHighestLevel = false; var _debugprintHighestLevel = false;
if (TRACEON) {
//print(`[${lnum}]`);
serial.println("[BASIC] Line "+lnum);
}
if (cmd.toUpperCase().startsWith("REM")) { if (cmd.toUpperCase().startsWith("REM")) {
if (_debugprintHighestLevel) serial.println(lnum+" "+cmd); if (_debugprintHighestLevel) serial.println(lnum+" "+cmd);
return undefined; return undefined;
@@ -2684,6 +2687,11 @@ bF.run = function(args) { // RUN function
var oldnum = 1; var oldnum = 1;
do { do {
if (cmdbuf[linenumber] !== undefined) { if (cmdbuf[linenumber] !== undefined) {
if (TRACEON) {
//print(`[${lnum}]`);
serial.println("[BASIC] Line "+linenumber);
}
oldnum = linenumber; oldnum = linenumber;
let trees = programTrees[linenumber]; let trees = programTrees[linenumber];

View File

@@ -74,7 +74,6 @@ bF._parseTokens = function(lnum, tokens, states) {
/** Parses following EBNF rule: /** Parses following EBNF rule:
stmt = stmt =
"IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt]
| "FOR" , expr
| "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr
| "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn} | "ON" , expr_sans_asgn , ident , expr_sans_asgn , {"," , expr_sans_asgn}
| "(" , stmt , ")" | "(" , stmt , ")"
@@ -152,26 +151,6 @@ bF._parseStmt = function(lnum, tokens, states, recDepth) {
if (!(e instanceof ParserError)) throw e; 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: // ## case for:
@@ -298,6 +277,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f
lit lit
| "(" , expr , ")" | "(" , expr , ")"
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , 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 | function_call
| expr , op , expr | expr , op , expr
| op_uni , 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._parseExpr = function(lnum, tokens, states, recDepth, ifMode) {
bF.parserPrintdbg2('E', lnum, tokens, states, recDepth); bF.parserPrintdbg2('E', lnum, tokens, states, recDepth);
let headTkn = tokens[0].toUpperCase();
let headSta = states[0];
/*************************************************************************/ /*************************************************************************/
// ## case for: // ## case for:
// lit // lit
let headTkn = tokens[0].toUpperCase();
if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) { if (!bF._EquationIllegalTokens.includes(headTkn) && tokens.length == 1) {
bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth); bF.parserPrintdbgline('E', 'Literal Call', lnum, recDepth);
return bF._parseLit(lnum, tokens, states, recDepth + 1); 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); bF.parserPrintdbgline('E', 'It was NOT!', lnum, recDepth);
if (!(e instanceof ParserError)) throw e; 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: // ## case for:
// (* at this point, if OP is found in paren-level 0, skip function_call *) // (* at this point, if OP is found in paren-level 0, skip function_call *)
// | function_call ; // | function_call ;
if (topmostOp === undefined) { if (topmostOp === undefined) { // don't remove this IF statement!
try { try {
bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth); bF.parserPrintdbgline('E', "Trying Function Call...", lnum, recDepth);
return bF._parseFunctionCall(lnum, tokens, states, recDepth + 1); 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); treeHead.astLeaves[1] = bF._parseExpr(lnum, subtknR, substaR, recDepth + 1);
} }
else { 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, treeHead.astLeaves[0] = bF._parseExpr(lnum,
tokens.slice(operatorPos + 1, tokens.length), tokens.slice(operatorPos + 1, tokens.length),
states.slice(operatorPos + 1, states.length), states.slice(operatorPos + 1, states.length),
@@ -700,7 +707,7 @@ bF._opPrc = {
"*":2,"/":2, "*":2,"/":2,
"MOD":3, "MOD":3,
"+":4,"-":4, "+":4,"-":4,
//";":5, "NOT":5,"BNOT":5,
"<<":6,">>":6, "<<":6,">>":6,
"<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7, "<":7,">":7,"<=":7,"=<":7,">=":7,"=>":7,
"==":8,"<>":8,"><":8, "==":8,"<>":8,"><":8,
@@ -717,6 +724,9 @@ bF._opPrc = {
"IN":1000 "IN":1000
}; };
bF._opRh = {"^":1,"=":1,"!":1,"IN":1}; 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; let lnum = 10;
// FIXME print's last (;) gets parsed but ignored // 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 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 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 tokens6 = ["FOR","K","=","10","TO","1","STEP","-","1"];
let states6 = ["lit","lit","op","num","op","num","op","op","num"]; 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 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"]; let states7 = ["lit","paren","lit","paren","num","op","lit","paren","lit","paren","num","paren","paren","op","num","paren","sep","paren"];
// PRINT 4 + 5 * 9 // PRINT 4 - 5 * 9
let tokens8 = ["PRINT","4","+","5","*","9"]; let tokens8 = ["PRINT","4","-","5","*","9"];
let states8 = ["lit","num","op","num","op","num"]; let states8 = ["lit","num","op","num","op","num"];
// NEXT // NEXT
@@ -762,12 +772,15 @@ let states10 = ["lit","op","num"];
// PRINT SPC(20-I); // PRINT SPC(20-I);
let tokens11 = ["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 { try {
let trees = bF._parseTokens(lnum, let trees = bF._parseTokens(lnum,
tokens6, tokens12,
states6 states12
); );
trees.forEach((t,i) => { trees.forEach((t,i) => {
serial.println("\nParsed Statement #"+(i+1)); serial.println("\nParsed Statement #"+(i+1));

View File

@@ -7,7 +7,6 @@ linenumber = digits ;
stmt = stmt =
"IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt] "IF" , expr_sans_asgn , "THEN" , stmt , ["ELSE" , stmt]
| "FOR" , expr
| "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr | "DEFUN" , [ident] , "(" , [ident , {" , " , ident}] , ")" , "=" , expr
| "ON" , expr_sans_asgn , ("GOTO" | "GOSUB") , expr_sans_asgn , {"," , expr_sans_asgn} | "ON" , expr_sans_asgn , ("GOTO" | "GOSUB") , expr_sans_asgn , {"," , expr_sans_asgn}
| "(" , stmt , ")" | "(" , stmt , ")"
@@ -17,6 +16,7 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f
lit lit
| "(" , expr , ")" | "(" , expr , ")"
| "IF" , expr_sans_asgn , "THEN" , expr , ["ELSE" , 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 *) (* at this point, if OP is found in paren-level 0, skip function_call *)
| function_call | function_call
| expr , op , expr | expr , op , expr
@@ -27,7 +27,7 @@ expr_sans_asgn = ? identical to expr except errors out whenever "=" is found ? ;
function_call = function_call =
ident , "(" , [expr , {argsep , expr} , [argsep]] , ")" ident , "(" , [expr , {argsep , expr} , [argsep]] , ")"
| 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 *) (* don't bother looking at these, because you already know the stuff *)