basic: CURRY, TYPEOF, LEN

This commit is contained in:
minjaesong
2020-12-21 14:12:09 +09:00
parent f81d28dfc7
commit e11038254a
4 changed files with 142 additions and 35 deletions

View File

@@ -27,7 +27,7 @@ let TRACEON = true;
let DBGON = true; let DBGON = true;
let DATA_CURSOR = 0; let DATA_CURSOR = 0;
let DATA_CONSTS = []; let DATA_CONSTS = [];
let DEFUNS_BUILD_DEFUNS = false; let DEFUNS_BUILD_DEFUNS = true;
if (system.maxmem() < 8192) { if (system.maxmem() < 8192) {
println("Out of memory. BASIC requires 8K or more User RAM"); println("Out of memory. BASIC requires 8K or more User RAM");
@@ -238,7 +238,7 @@ let BasicAST = function() {
this.astValue = undefined; this.astValue = undefined;
this.astType = "null"; // lit, op, string, num, array, function, null, defun_args (! NOT usrdefun !) this.astType = "null"; // lit, op, string, num, array, function, null, defun_args (! NOT usrdefun !)
} }
let literalTypes = ["string", "num", "bool", "array", "generator"]; let literalTypes = ["string", "num", "bool", "array", "generator", "usrdefun"];
/* /*
@param variable SyntaxTreeReturnObj, of which the 'troType' is defined in BasicAST. @param variable SyntaxTreeReturnObj, of which the 'troType' is defined in BasicAST.
@return a value, if the input type if string or number, its literal value will be returned. Otherwise will search the @return a value, if the input type if string or number, its literal value will be returned. Otherwise will search the
@@ -271,6 +271,22 @@ let resolve = function(variable) {
else else
throw Error("BasicIntpError: unknown variable with type "+variable.troType+", with value "+variable.troValue); throw Error("BasicIntpError: unknown variable with type "+variable.troType+", with value "+variable.troValue);
} }
let curryDefun = function(exprTree, value) {
bF._recurseApplyAST(exprTree, it => {
if (it.astType == "defun_args") {
// apply arg0 into the tree
if (it.astValue == 0) {
it.astType = JStoBASICtype(value);
it.astValue = value;
}
// shift down arg index
else {
it.astValue -= 1;
}
}
});
return exprTree;
}
let argCheckErr = function(lnum, o) { let argCheckErr = function(lnum, o) {
if (o === undefined || o.troType == "null") throw lang.refError(lnum, o); if (o === undefined || o.troType == "null") throw lang.refError(lnum, o);
if (o.troType == "lit" && bStatus.vars[o.troValue] === undefined) throw lang.refError(lnum, o); if (o.troType == "lit" && bStatus.vars[o.troValue] === undefined) throw lang.refError(lnum, o);
@@ -503,11 +519,10 @@ bStatus.getDefunThunk = function(lnum, stmtnum, exprTree) {
let argsIndex = it.astValue; let argsIndex = it.astValue;
let theArg = argsMap[argsIndex]; // instanceof theArg == resolved version of SyntaxTreeReturnObj let theArg = argsMap[argsIndex]; // instanceof theArg == resolved version of SyntaxTreeReturnObj
if (theArg === undefined) if (theArg !== undefined) { // this "forgiveness" is required to implement currying
throw lang.badFunctionCallFormat(lang.ord(argsIndex)+" argument was not given"); it.astValue = theArg;
it.astType = JStoBASICtype(theArg);
it.astValue = theArg; }
it.astType = JStoBASICtype(theArg);
} }
}); });
@@ -1175,14 +1190,34 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
bStatus.vars[varname] = new BasicVar(keys, "array"); bStatus.vars[varname] = new BasicVar(keys, "array");
return {asgnVarName: varname, asgnValue: keys}; return {asgnVarName: varname, asgnValue: keys};
}, },
"CURRY" : function(lnum, stmtnum, args) {
return twoArg(lnum, stmtnum, args, (fn, arg0) => {
if (fn.astLeaves === undefined) throw lang.badFunctionCallFormat("Not a Function");
curryDefun(fn, arg0);
return fn;
});
},
"TYPEOF" : function(lnum, stmtnum, args) {
return oneArg(lnum, stmtnum, args, bv => {
if (bv.Type === undefined || !(bv instanceof BasicVar))
return JStoBASICtype(bv);
return bv.bvType;
});
},
"LEN" : function(lnum, stmtnum, args) {
return oneArg(lnum, stmtnum, args, lh => {
if (lh.length === undefined) throw lang.illegalType();
return lh.length;
});
},
"OPTIONDEBUG" : function(lnum, stmtnum, args) { "OPTIONDEBUG" : function(lnum, stmtnum, args) {
return 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);
DBGON = (1 == lh|0); DBGON = (1 == lh|0);
}); });
}, },
"OPTIONTRACE" : function(lnum, stmtnum, args) { "OPTIONTRACE" : function(lnum, stmtnum, args) {
return 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);
TRACEON = (1 == lh|0); TRACEON = (1 == lh|0);
}); });
@@ -1197,10 +1232,12 @@ if no arg text were given (e.g. "10 NEXT"), args will have zero length
throw lang.syntaxfehler(lnum); throw lang.syntaxfehler(lnum);
} }
}, },
"RESOLVE0" : function(lnum, stmtnum, args) { "RESOLVEVAR" : function(lnum, stmtnum, args) {
if (DBGON) { if (DBGON) {
return oneArg(lnum, stmtnum, args, (it) => { return oneArg(lnum, stmtnum, args, (it) => {
println(Object.entries(it)); let v = bStatus.vars[args[0].troValue];
if (v === undefined) println("Undefined variable: "+args[0].troValue);
else println(`type: ${v.bvType}, value: ${v.bvLiteral}`);
}); });
} }
else { else {
@@ -2477,6 +2514,7 @@ let JStoBASICtype = function(object) {
else if (object.arrName !== undefined) return "internal_arrindexing_lazy"; else if (object.arrName !== undefined) return "internal_arrindexing_lazy";
else if (object.asgnVarName !== undefined) return "internal_assignment_object"; else if (object.asgnVarName !== undefined) return "internal_assignment_object";
else if (object instanceof ForGen) return "generator"; else if (object instanceof ForGen) return "generator";
else if (object instanceof BasicAST) return "usrdefun";
else if (Array.isArray(object)) return "array"; else if (Array.isArray(object)) return "array";
else if (!isNaN(object)) return "num"; else if (!isNaN(object)) return "num";
else if (typeof object === "string" || object instanceof String) return "string"; else if (typeof object === "string" || object instanceof String) return "string";
@@ -2708,22 +2746,24 @@ bF._interpretLine = function(lnum, cmd) {
// syntax tree pruning // syntax tree pruning
// turn UNARYMINUS(num) to -num // turn UNARYMINUS(num) to -num
syntaxTrees.forEach(syntaxTree => { syntaxTrees.forEach(syntaxTree => {
bF._recurseApplyAST(syntaxTree, tree => { if (syntaxTree !== undefined) {
if (tree.astValue == "UNARYMINUS" && tree.astType == "op" && bF._recurseApplyAST(syntaxTree, tree => {
tree.astLeaves[1] === undefined && tree.astLeaves[0] !== undefined && tree.astLeaves[0].astType == "num" if (tree.astValue == "UNARYMINUS" && tree.astType == "op" &&
) { tree.astLeaves[1] === undefined && tree.astLeaves[0] !== undefined && tree.astLeaves[0].astType == "num"
tree.astValue = -(tree.astLeaves[0].astValue); ) {
tree.astType = JStoBASICtype(tree.astValue); tree.astValue = -(tree.astLeaves[0].astValue);
tree.astLeaves = []; tree.astType = JStoBASICtype(tree.astValue);
} tree.astLeaves = [];
else if (tree.astValue == "UNARYPLUS" && tree.astType == "op" && }
tree.astLeaves[1] === undefined && tree.astLeaves[0] !== undefined && tree.astLeaves[0].astType == "num" else if (tree.astValue == "UNARYPLUS" && tree.astType == "op" &&
) { tree.astLeaves[1] === undefined && tree.astLeaves[0] !== undefined && tree.astLeaves[0].astType == "num"
tree.astValue = +(tree.astLeaves[0].astValue); ) {
tree.astType = JStoBASICtype(tree.astValue); tree.astValue = +(tree.astLeaves[0].astValue);
tree.astLeaves = []; tree.astType = JStoBASICtype(tree.astValue);
} tree.astLeaves = [];
}); }
});
}
}); });
if (_debugprintHighestLevel) { if (_debugprintHighestLevel) {

6
assets/currytest.bas Normal file
View File

@@ -0,0 +1,6 @@
10 DEFUN F(K,T)=ABS(T)==K
11 CF=CURRY(F,32)
20 PRINT TYPEOF(F):REM must be usrdefun
21 PRINT TYPEOF(CF):REM also must be usrdefun
30 PRINT CF(24):PRINT CF(-32)
31 REM Expected printout: false true

View File

@@ -161,6 +161,8 @@
\input{intro} \input{intro}
\openany \openany
\part{Language}
\chapter{Basic Concepts} \chapter{Basic Concepts}
\input{concepts} \input{concepts}
@@ -176,8 +178,10 @@
\chapter{Functions} \chapter{Functions}
\input{functions} \input{functions}
\part{Implementation}
\chapter{Technical Reference} \chapter{Technical Reference}
\input{implementation} \input{technical}
\chapter{Index} \chapter{Index}

View File

@@ -0,0 +1,57 @@
\section{Resolving Variables}
This section describes all use cases of \code{BasicVar}.
When a variable is \code{resolve}d, an object with instance of \code{BasicVar} is returned. A \code{bvType} of Javascript value is determined using \code{JStoBASICtype}.
\begin{tabulary}{\textwidth}{RLL}
Typical User Input & TYPEOF(\textbf{Q}) & Instanceof \\
\hline
\code{\textbf{Q}=42.195} & {\ttfamily num} & \emph{primitive} \\
\code{\textbf{Q}=42>21} & {\ttfamily boolean} & \emph{primitive} \\
\code{\textbf{Q}="BASIC!"} & {\ttfamily string} & \emph{primitive} \\
\code{\textbf{Q}=DIM(12)} & {\ttfamily array} & Array (JS) \\
\code{\textbf{Q}=1 TO 9 STEP 2} & {\ttfamily generator} & ForGen \\
\code{DEFUN \textbf{Q}(X)=X+3} & {\ttfamily usrdefun} & BasicAST \\
\end{tabulary}
\subsection*{Notes}
\begin{itemlist}
\item \code{TYPEOF(\textbf{Q})} is identical to the variable's bvType; the function simply returns \code{BasicVar.bvType}.
\item Do note that all resolved variables have \code{troType} of \code{Lit}, see next section for more information.
\end{itemlist}
\section{Unresolved Values}
Unresolved variables has JS-object of \code{troType}, with \emph{instanceof} \code{SyntaxTeeReturnObj}. Its properties are defined as follows:
\begin{tabulary}{\textwidth}{RL}
Properties & Description \\
\hline
{\ttfamily troType} & Type of the TRO (Tree Return Object) \\
{\ttfamily troValue} & Value of the TRO \\
{\ttfamily troNextLine} & Pointer to next instruction, array of: [\#line, \#statement] \\
\end{tabulary}
Following table shows which BASIC object can have which \code{troType}:
\begin{tabulary}{\textwidth}{RLL}
BASIC Type & troType \\
\hline
Any Variable & {\ttfamily lit} \\
DEFUN'd Function & {\ttfamily function} \\
Boolean & {\ttfamily bool} \\
Generator & {\ttfamily generator} \\
Array & {\ttfamily array} \\
Number & {\ttfamily num} \\
String & {\ttfamily string} \\
Array Indexing & {\ttfamily internal\_arrindexing\_lazy} \\
Assignment & {\ttfamily internal\_assignment\_object} \\
\end{tabulary}
\subsection*{Notes}
\begin{itemlist}
\item All type that is not \code{lit} only appear when the statement returns such values, e.g. \code{function} only get returned by DEFUN statements as the statement itself returns defined function as well as assign them to given BASIC variable.
\item As all variables will have \code{troType} of \code{lit} when they are not resolved, the property must not be used to determine the type of the variable; you must \code{resolve} it first.
\item The type string \code{function} should not appear outside of TRO and \code{astType}; if you do see them in the wild, please check your JS code because you probably meant \code{usrdefun}.
\end{itemlist}