mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
basic: CURRY, TYPEOF, LEN
This commit is contained in:
108
assets/basic.js
108
assets/basic.js
@@ -27,7 +27,7 @@ let TRACEON = true;
|
||||
let DBGON = true;
|
||||
let DATA_CURSOR = 0;
|
||||
let DATA_CONSTS = [];
|
||||
let DEFUNS_BUILD_DEFUNS = false;
|
||||
let DEFUNS_BUILD_DEFUNS = true;
|
||||
|
||||
if (system.maxmem() < 8192) {
|
||||
println("Out of memory. BASIC requires 8K or more User RAM");
|
||||
@@ -238,7 +238,7 @@ let BasicAST = function() {
|
||||
this.astValue = undefined;
|
||||
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.
|
||||
@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
|
||||
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) {
|
||||
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);
|
||||
@@ -503,11 +519,10 @@ bStatus.getDefunThunk = function(lnum, stmtnum, exprTree) {
|
||||
let argsIndex = it.astValue;
|
||||
let theArg = argsMap[argsIndex]; // instanceof theArg == resolved version of SyntaxTreeReturnObj
|
||||
|
||||
if (theArg === undefined)
|
||||
throw lang.badFunctionCallFormat(lang.ord(argsIndex)+" argument was not given");
|
||||
|
||||
it.astValue = theArg;
|
||||
it.astType = JStoBASICtype(theArg);
|
||||
if (theArg !== undefined) { // this "forgiveness" is required to implement currying
|
||||
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");
|
||||
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) {
|
||||
return oneArgNum(lnum, stmtnum, args, (lh) => {
|
||||
oneArgNum(lnum, stmtnum, args, (lh) => {
|
||||
if (lh != 0 && lh != 1) throw lang.syntaxfehler(line);
|
||||
DBGON = (1 == lh|0);
|
||||
});
|
||||
},
|
||||
"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);
|
||||
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);
|
||||
}
|
||||
},
|
||||
"RESOLVE0" : function(lnum, stmtnum, args) {
|
||||
"RESOLVEVAR" : function(lnum, stmtnum, args) {
|
||||
if (DBGON) {
|
||||
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 {
|
||||
@@ -1700,7 +1737,7 @@ stmt =
|
||||
| "(" , stmt , ")"
|
||||
| expr ; (* if the statement is 'lit' and contains only one word, treat it as function_call
|
||||
e.g. NEXT for FOR loop *)
|
||||
|
||||
|
||||
expr = (* this basically blocks some funny attemps such as using DEFUN as anon function
|
||||
because everything is global in BASIC *)
|
||||
lit
|
||||
@@ -1711,16 +1748,16 @@ expr = (* this basically blocks some funny attemps such as using DEFUN as anon f
|
||||
| function_call
|
||||
| expr , op , expr
|
||||
| op_uni , expr ;
|
||||
|
||||
|
||||
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 *)
|
||||
|
||||
|
||||
(* don't bother looking at these, because you already know the stuff *)
|
||||
|
||||
argsep = "," | ";" ;
|
||||
ident = alph , [digits] ; (* variable and function names *)
|
||||
lit = alph , [digits] | num | string ; (* ident + numbers and string literals *)
|
||||
@@ -1734,7 +1771,7 @@ digits = digit | digit , digits ;
|
||||
hexdigits = hexdigit | hexdigit , hexdigits ;
|
||||
bindigits = bindigit | bindigit , bindigits ;
|
||||
num = digits | digits , "." , [digits] | "." , digits
|
||||
| ("0x"|"0X") , hexdigits
|
||||
| ("0x"|"0X") , hexdigits
|
||||
| ("0b"|"0B") , bindigits ; (* sorry, no e-notation! *)
|
||||
visible = ? ASCII 0x20 to 0x7E ? ;
|
||||
string = '"' , (visible | visible , stringlit) , '"' ;
|
||||
@@ -2477,6 +2514,7 @@ let JStoBASICtype = function(object) {
|
||||
else if (object.arrName !== undefined) return "internal_arrindexing_lazy";
|
||||
else if (object.asgnVarName !== undefined) return "internal_assignment_object";
|
||||
else if (object instanceof ForGen) return "generator";
|
||||
else if (object instanceof BasicAST) return "usrdefun";
|
||||
else if (Array.isArray(object)) return "array";
|
||||
else if (!isNaN(object)) return "num";
|
||||
else if (typeof object === "string" || object instanceof String) return "string";
|
||||
@@ -2708,22 +2746,24 @@ bF._interpretLine = function(lnum, cmd) {
|
||||
// syntax tree pruning
|
||||
// turn UNARYMINUS(num) to -num
|
||||
syntaxTrees.forEach(syntaxTree => {
|
||||
bF._recurseApplyAST(syntaxTree, tree => {
|
||||
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.astLeaves = [];
|
||||
}
|
||||
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.astLeaves = [];
|
||||
}
|
||||
});
|
||||
if (syntaxTree !== undefined) {
|
||||
bF._recurseApplyAST(syntaxTree, tree => {
|
||||
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.astLeaves = [];
|
||||
}
|
||||
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.astLeaves = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (_debugprintHighestLevel) {
|
||||
|
||||
6
assets/currytest.bas
Normal file
6
assets/currytest.bas
Normal 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
|
||||
@@ -161,6 +161,8 @@
|
||||
\input{intro}
|
||||
|
||||
\openany
|
||||
\part{Language}
|
||||
|
||||
\chapter{Basic Concepts}
|
||||
\input{concepts}
|
||||
|
||||
@@ -176,8 +178,10 @@
|
||||
\chapter{Functions}
|
||||
\input{functions}
|
||||
|
||||
\part{Implementation}
|
||||
|
||||
\chapter{Technical Reference}
|
||||
\input{implementation}
|
||||
\input{technical}
|
||||
|
||||
\chapter{Index}
|
||||
|
||||
|
||||
57
assets/tbas/doc/technical.tex
Normal file
57
assets/tbas/doc/technical.tex
Normal 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}
|
||||
Reference in New Issue
Block a user