use (name; value) to define something. the definition can be recursive. value is executed and name is set to the final state of the stack, i.e. (name; 1 3) is possible
-
use ' to push instead of apply to the stack, e.g. '(a -> a). This is useful for lazyness, i.e. '(->lazy evaluated thing)
-
-
+, -, *, /, ^, sqrt: mathematical operations
-
==: equality (automatically derived for all types); returns a b -> a if true, a b -> b if false
-
typeof: returns the type of the object
-
pair, fst, snd: pairs two objects, gets first or second item of pair
-
tuple: used like ... 3 tuple; creates an n tuple of n items on the stack
-
!!: index into a tuple
-
len: length of a tuple
-
unsafeCoerce: unsafely transforms one type into another. useage: 1 "type unsafeCoerce
-
true, false: true/false used in ==
-
stop: equivalent to "stop
-
inv: equivalent to 1 x /
-
fold: equivalent to x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==
-
-
\ No newline at end of file
diff --git a/new/main.js b/new/main.js
deleted file mode 100644
index e75a75e..0000000
--- a/new/main.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import {execRPN} from './shiny.mjs';
-import {parseExprs} from './parse.mjs';
-import {tokenize} from './token.mjs';
-import {scope, customHandler} from './builtin.mjs';
-
-const inbox = document.getElementById("inbox")
-const outbox = document.getElementById("outbox")
-const submit = document.getElementById("submit")
-
-const show = (elem) => {
- if (elem.type === "pair") {
- return "{" + show(elem.val.fst) + ", " + show(elem.val.snd) + "}"
- } else if (elem.type === "closure") {
- return "(needs: " + elem.val.args.join(", ") + ")"
- } else if (elem.type === "array") {
- return "[" + prettyprint(elem.val) + "]"
- } else {
- return "(" + elem.val + ": " + elem.type + ")"
- }
-}
-
-const prettyprint = (out) => {
- let str = "";
- for (let i = 0; i < out.length; i++) {
- str += show(out[i]);
- if (i < out.length - 1) {
- str += " ";
- }
- }
- return str;
-}
-
-submit.onclick = (event) => {
- const input = inbox.value;
- let toks = tokenize(input);
- if (!toks) {
- outbox.innerHTML = "could not parse input: " + input;
- return;
- }
- let ast = parseExprs(toks);
- if (ast.stream.length !== 0) {
- outbox.innerHTML = "unexpected token: " + ((e)=>{if(e.type==="ident"){return e.name}else{return e.val}})(ast.stream[0]);
- return;
- }
- let out = execRPN(scope, ast.parsed, customHandler);
- if (!out) {
- outbox.innerHTML = "failed to execute";
- return;
- }
- console.log(out);
- outbox.innerHTML = prettyprint(out.stacks[0]);
-}
\ No newline at end of file
diff --git a/new/token.mjs b/new/token.mjs
deleted file mode 100644
index 6276cd9..0000000
--- a/new/token.mjs
+++ /dev/null
@@ -1,40 +0,0 @@
-export function tokenize(input) {
- let i;
- let inputWithAddedSpaces = "";
- const syntax = /['";()]/;
- for(i = 0; i"){
- if(input.charAt(i-1) != " "){
- inputWithAddedSpaces += " ";
- }
- if(input.charAt(i+2) != " "){
- inputWithAddedSpaces += "->" + " ";
- } else {
- inputWithAddedSpaces += "->";
- }
- } else if(input.charAt(i) != ">") {
- inputWithAddedSpaces += input.charAt(i);
- }
- }
- let splitInput = inputWithAddedSpaces.split(" ").filter((s) => s !== "");
- let output = [];
- for(i = 0; i"){
- output.push({type: "syntax", val:splitInput[i]});
- } else if(splitInput[i] != '') {
- output.push({type: "ident", name:splitInput[i]});
- }
- }
- return(output)
-}
\ No newline at end of file
diff --git a/npm-debug.log b/npm-debug.log
deleted file mode 100644
index 2f67d2c..0000000
--- a/npm-debug.log
+++ /dev/null
@@ -1,96 +0,0 @@
-0 info it worked if it ends with ok
-1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'i', 'rationals' ]
-2 info using npm@3.5.2
-3 info using node@v8.10.0
-4 verbose config Skipping project config: /home/aidan/.npmrc. (matches userconfig)
-5 silly loadCurrentTree Starting
-6 silly install loadCurrentTree
-7 silly install readLocalPackageData
-8 silly fetchPackageMetaData rationals
-9 silly fetchNamedPackageData rationals
-10 silly mapToRegistry name rationals
-11 silly mapToRegistry using default registry
-12 silly mapToRegistry registry https://registry.npmjs.org/
-13 silly mapToRegistry uri https://registry.npmjs.org/rationals
-14 verbose request uri https://registry.npmjs.org/rationals
-15 verbose request no auth needed
-16 info attempt registry request try #1 at 19:23:48
-17 verbose request id 1f9e752341fc452f
-18 verbose etag W/"e0fb616d3d75eb1a38936789fa99e693"
-19 verbose lastModified Sat, 12 Oct 2019 22:31:44 GMT
-20 http request GET https://registry.npmjs.org/rationals
-21 http 304 https://registry.npmjs.org/rationals
-22 verbose headers { date: 'Mon, 01 Jun 2020 18:23:48 GMT',
-22 verbose headers connection: 'keep-alive',
-22 verbose headers 'set-cookie':
-22 verbose headers [ '__cfduid=da1ea1de35cfdbd41bc4da4edd15edf091591035828; expires=Wed, 01-Jul-20 18:23:48 GMT; path=/; domain=.npmjs.org; HttpOnly; SameSite=Lax' ],
-22 verbose headers 'cf-ray': '59caf7cabb5ad210-MAN',
-22 verbose headers age: '10',
-22 verbose headers 'cache-control': 'public, max-age=300',
-22 verbose headers etag: '"e0fb616d3d75eb1a38936789fa99e693"',
-22 verbose headers 'last-modified': 'Sat, 12 Oct 2019 22:31:44 GMT',
-22 verbose headers vary: 'Accept-Encoding',
-22 verbose headers 'cf-cache-status': 'HIT',
-22 verbose headers 'cf-request-id': '0312b932ae0000d2108792b200000001',
-22 verbose headers 'expect-ct': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
-22 verbose headers server: 'cloudflare' }
-23 silly get cb [ 304,
-23 silly get { date: 'Mon, 01 Jun 2020 18:23:48 GMT',
-23 silly get connection: 'keep-alive',
-23 silly get 'set-cookie':
-23 silly get [ '__cfduid=da1ea1de35cfdbd41bc4da4edd15edf091591035828; expires=Wed, 01-Jul-20 18:23:48 GMT; path=/; domain=.npmjs.org; HttpOnly; SameSite=Lax' ],
-23 silly get 'cf-ray': '59caf7cabb5ad210-MAN',
-23 silly get age: '10',
-23 silly get 'cache-control': 'public, max-age=300',
-23 silly get etag: '"e0fb616d3d75eb1a38936789fa99e693"',
-23 silly get 'last-modified': 'Sat, 12 Oct 2019 22:31:44 GMT',
-23 silly get vary: 'Accept-Encoding',
-23 silly get 'cf-cache-status': 'HIT',
-23 silly get 'cf-request-id': '0312b932ae0000d2108792b200000001',
-23 silly get 'expect-ct': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
-23 silly get server: 'cloudflare' } ]
-24 verbose etag https://registry.npmjs.org/rationals from cache
-25 verbose get saving rationals to /home/aidan/.npm/registry.npmjs.org/rationals/.cache.json
-26 silly install normalizeTree
-27 silly loadCurrentTree Finishing
-28 silly loadIdealTree Starting
-29 silly install loadIdealTree
-30 silly cloneCurrentTree Starting
-31 silly install cloneCurrentTreeToIdealTree
-32 silly cloneCurrentTree Finishing
-33 silly loadShrinkwrap Starting
-34 silly install loadShrinkwrap
-35 silly loadShrinkwrap Finishing
-36 silly loadAllDepsIntoIdealTree Starting
-37 silly install loadAllDepsIntoIdealTree
-38 verbose stack Error: Missing required argument #1
-38 verbose stack at andLogAndFinish (/usr/share/npm/lib/fetch-package-metadata.js:31:3)
-38 verbose stack at fetchPackageMetadata (/usr/share/npm/lib/fetch-package-metadata.js:51:22)
-38 verbose stack at resolveWithNewModule (/usr/share/npm/lib/install/deps.js:456:12)
-38 verbose stack at /usr/share/npm/lib/install/deps.js:190:5
-38 verbose stack at /usr/share/npm/node_modules/slide/lib/async-map.js:52:35
-38 verbose stack at Array.forEach ()
-38 verbose stack at /usr/share/npm/node_modules/slide/lib/async-map.js:52:11
-38 verbose stack at Array.forEach ()
-38 verbose stack at asyncMap (/usr/share/npm/node_modules/slide/lib/async-map.js:51:8)
-38 verbose stack at exports.loadRequestedDeps (/usr/share/npm/lib/install/deps.js:188:3)
-39 verbose cwd /home/aidan/Desktop/js-projects/rpncalc-v4
-40 error Linux 5.3.0-53-generic
-41 error argv "/usr/bin/node" "/usr/bin/npm" "i" "rationals"
-42 error node v8.10.0
-43 error npm v3.5.2
-44 error code EMISSINGARG
-45 error typeerror Error: Missing required argument #1
-45 error typeerror at andLogAndFinish (/usr/share/npm/lib/fetch-package-metadata.js:31:3)
-45 error typeerror at fetchPackageMetadata (/usr/share/npm/lib/fetch-package-metadata.js:51:22)
-45 error typeerror at resolveWithNewModule (/usr/share/npm/lib/install/deps.js:456:12)
-45 error typeerror at /usr/share/npm/lib/install/deps.js:190:5
-45 error typeerror at /usr/share/npm/node_modules/slide/lib/async-map.js:52:35
-45 error typeerror at Array.forEach ()
-45 error typeerror at /usr/share/npm/node_modules/slide/lib/async-map.js:52:11
-45 error typeerror at Array.forEach ()
-45 error typeerror at asyncMap (/usr/share/npm/node_modules/slide/lib/async-map.js:51:8)
-45 error typeerror at exports.loadRequestedDeps (/usr/share/npm/lib/install/deps.js:188:3)
-46 error typeerror This is an error with npm itself. Please report this error at:
-46 error typeerror
-47 verbose exit [ 1, true ]
diff --git a/new/builtin.mjs b/script/builtin.mjs
similarity index 62%
rename from new/builtin.mjs
rename to script/builtin.mjs
index 4999dcf..df3ac7a 100644
--- a/new/builtin.mjs
+++ b/script/builtin.mjs
@@ -1,6 +1,4 @@
-import {defnOp, makeOp, defn} from './shiny.mjs';
-import {parseExprs} from './parse.mjs';
-import {tokenize} from './token.mjs';
+import {defnOp, makeOp} from './shiny.mjs';
const objEq = (a, b) => {
if (typeof a !== 'object' && typeof b !== 'object') {
@@ -27,29 +25,13 @@ const objEq = (a, b) => {
export let scope = {};
-export const customHandler = (ins) => {
- return [ins];
-}
-
-const addRPNDefn = (name, def) => {
- let toks = tokenize(def);
- if (!toks) {
- throw 'could not load builtin'
- }
- let ast = parseExprs(toks);
- if (!ast.parsed) {
- throw 'could not load builtin'
- }
- scope = defn(name, ast.parsed, scope);
-}
-
-const assertType = (type) => (elem) => {
+export const assertType = (type) => (elem) => {
if (elem.type !== type) {
throw 'typeerror'
}
}
-const addDefn = (name, args, fn) => {
+export const addDefn = (name, args, fn) => {
if (Array.isArray(args)) {
const nargs = [...Array(args.length).keys()];
const liftFn = (scope) => {
@@ -83,12 +65,25 @@ const mult = (args) => [{type:"num", val:args[0] * args[1]}];
const pow = (args) => [{type:"num", val:Math.pow(args[0], args[1])}];
const root = (args) => [{type:"num", val:Math.sqrt(args[0])}];
const type = (args) => [{type:"type", val:args[0].type}];
-const pair = (args) => [{type:"pair", val:{fst:args[0], snd:args[1]}}];
-const fst = (args) => [args[0].fst];
-const snd = (args) => [args[0].snd];
-const tuple = (args) => makeOp([...Array(args[0]).keys()], (args) => {return [{type:"tuple", val:args}]});
-const index = (args) => args[0][args[1]];
+const index = (args) => [args[0][args[1]]];
const len = (args) => [{type:"num", val:args[0].length}];
+const untuple = (args) => args[0];
+
+const coerce = (args) => {
+ if (args[1].type === "type") {
+ return [{type:args[1].val, val:args[0].val}];
+ } else {
+ throw 'typeerror'
+ }
+}
+
+const tuple = (args) => makeOp([...Array(args[0]).keys()], (iargs) => {
+ let arr = [];
+ for (let i = 0; i < args[0]; i++) {
+ arr.push(iargs[i][0]);
+ }
+ return [{type:"tuple", val:arr}]
+});
const eq = (args) => {
console.log(args[2], args[3])
@@ -106,19 +101,9 @@ addDefn("*", ["num", "num"], mult);
addDefn("^", ["num", "num"], pow);
addDefn("sqrt", ["num"], root);
addDefn("==", 4, eq);
-addDefn("pair", 2, pair);
-addDefn("fst", ["pair"], fst);
-addDefn("snd", ["pair"], snd);
addDefn("tuple", ["num"], tuple);
addDefn("!!", ["tuple", "num"], index);
addDefn("len", ["tuple"], len);
+addDefn("untuple", ["tuple"], untuple);
addDefn("typeof", 1, type);
-addRPNDefn("stop", "\"stop");
-addRPNDefn("inv", "(x -> 1 x /)");
-addRPNDefn("fold", "(x acc fn -> acc '(-> x acc fn 'fn fold) 'x stop ==)");
-addRPNDefn("range", "(x y -> x '(->x x 1 + y range) 'x y ==)");
-addRPNDefn("listthen", "(fn -> (internal; x acc -> '(->acc fn) '(->x acc pair internal) x stop ==) 0 tuple internal)");
-addRPNDefn("list", "'(a -> a) listthen");
-addRPNDefn("lmap", "(list fn -> list '(->list fst fn list snd 'fn lmap pair) list 0 tuple ==)");
-addRPNDefn("unlist", "(l -> (internal; list -> '(->) '(->list fst list snd internal) list 0 tuple ==) stop l internal)");
-addRPNDefn("map", "fn -> '(l->l 'fn lmap unlist) listthen");
\ No newline at end of file
+addDefn("coerce", 2, coerce);
\ No newline at end of file
diff --git a/script/dom.mjs b/script/dom.mjs
new file mode 100644
index 0000000..33312dc
--- /dev/null
+++ b/script/dom.mjs
@@ -0,0 +1,33 @@
+import {assertType, addDefn} from './builtin.mjs';
+import {execFn} from './shiny.mjs';
+
+const getElem = (args) => [{type:"domNode", val:document.getElementById(args[0])}];
+const setHTML = (args) => args[0].innerHTML = args[1];
+const mutref = (args) => [{type:"&mut", val:args[0]}];
+const readmut = (args) => [args[0]];
+
+const log = (args) => {
+ console.log(args);
+ return [];
+}
+
+const writemut = (args) => {
+ assertType("&mut", args[1]);
+ args[1].val = args[0];
+ return [];
+}
+
+const onclick = (args) => {
+ args[0].onclick = (_) => {
+ execFn(args[1]);
+ }
+ return [];
+}
+
+addDefn("log", 1, log);
+addDefn("getId", ["string"], getElem);
+addDefn("setHTML", ["domNode", "string"], setHTML);
+addDefn("mutref", 1, mutref);
+addDefn("readmut", ["&mut"], readmut);
+addDefn("writemut", 2, writemut);
+addDefn("onclick", ["domNode", "closure"], onclick);
\ No newline at end of file
diff --git a/script/index.html b/script/index.html
new file mode 100644
index 0000000..12184f7
--- /dev/null
+++ b/script/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+ Meta Calculator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script/main.js b/script/main.js
new file mode 100644
index 0000000..7fdea40
--- /dev/null
+++ b/script/main.js
@@ -0,0 +1,31 @@
+import {execRPN} from './shiny.mjs';
+import {parseExprs} from './parse.mjs';
+import {tokenize} from './token.mjs';
+import {scope} from './builtin.mjs';
+import './dom.mjs';
+
+const tags = document.getElementsByTagName("script");
+let scripts = []
+for (let i = 0; i < tags.length; i++) {
+ scripts.push(tags.item(i));
+}
+
+scripts = scripts.filter(script => script.type === "text/rpncalc");
+
+const dispatch = (src) => {
+ setTimeout(() => {
+ let http = new XMLHttpRequest();
+ http.onreadystatechange = async(e) => {
+ if (http.readyState == 4 && http.status == 200) {
+ execRPN(scope, parseExprs(tokenize(http.responseText)).parsed);
+ }
+ };
+ http.open("get", src);
+ http.responseType = "text";
+ http.send();
+ }, 0);
+}
+
+for (let i = 0; i < scripts.length; i++) {
+ dispatch(scripts[i].src);
+}
\ No newline at end of file
diff --git a/new/parse.mjs b/script/parse.mjs
similarity index 92%
rename from new/parse.mjs
rename to script/parse.mjs
index b576f1a..0f4a3b5 100644
--- a/new/parse.mjs
+++ b/script/parse.mjs
@@ -115,6 +115,19 @@ const parseType = (stream) => {
//return {parsed:null, stream:stream};
}
+const parseString = (stream) => {
+ let e = stream[0];
+ if (e === undefined) {
+ return {parsed:null, stream:stream};
+ }
+ if (e.type !== "string") {
+ return {parsed:null, stream:stream};
+ } else {
+ stream.shift();
+ return {parsed:{type:e.type, val:e.val}, stream:stream};
+ }
+}
+
/* takes in stream, outputs parsed item or null - FAILABLE */
const parsePush = (stream) => {
let syn = attempt(parseSyntax("'"))(stream);
@@ -161,9 +174,10 @@ const parseExpr = or(
attempt(parseLambda), or(
parseType, or(
parseIdent, or(
- parseNum,
+ parseNum, or(
+ parseString,
parsePush
- ))))));
+ )))))));
/* takes in stream, outputs parsed items */
export const parseExprs = many(parseExpr);
\ No newline at end of file
diff --git a/new/shiny.mjs b/script/shiny.mjs
similarity index 88%
rename from new/shiny.mjs
rename to script/shiny.mjs
index e017ba5..09fa782 100644
--- a/new/shiny.mjs
+++ b/script/shiny.mjs
@@ -99,7 +99,7 @@ const applyMany = (elems, state) => {
}
}
-const makeStackElems = (ins, state, handler) => {
+const makeStackElems = (ins, state) => {
if (ins.type === "push") {
throw 'nested push error'
} else if (ins.type === "ident") {
@@ -107,21 +107,21 @@ const makeStackElems = (ins, state, handler) => {
} else if (ins.type === "func") {
return [{type:"closure", val:{scope:state.scopes[state.scopes.length-1], args:ins.args, defn:{type:"ins", ins:ins.body}}}];
} else {
- return handler(ins);
+ return [ins];
}
}
-const doIns = (ins, state, handler) => {
+const doIns = (ins, state) => {
if (ins.type === "push") {
- state.stacks[state.stacks.length-1] = state.stacks[state.stacks.length-1].concat(makeStackElems(ins.elem, state, handler));
+ state.stacks[state.stacks.length-1] = state.stacks[state.stacks.length-1].concat(makeStackElems(ins.elem, state));
} else if (ins.type === "defn") {
state.scopes[state.scopes.length-1] = defn(ins.ident, ins.defn, state.scopes[state.scopes.length-1]);
} else {
- applyMany(makeStackElems(ins, state, handler), state);
+ applyMany(makeStackElems(ins, state), state);
}
}
-const step = (state, handler) => {
+const step = (state) => {
if (state.calls[state.calls.length-1].length === 0) {
if (state.calls.length === 1) {
throw 'finished execution'
@@ -136,17 +136,26 @@ const step = (state, handler) => {
} else {
let ins = state.calls[state.calls.length-1][0];
state.calls[state.calls.length-1] = state.calls[state.calls.length-1].slice(1);
- doIns(ins, state, handler);
+ doIns(ins, state);
}
}
-export const execRPN = (scope, ins, handler=(x)=>[x]) => {
- let state = {scopes:[scope], stacks:[[]], calls:[ins]};
+export const exec = (state) => {
while (state.calls[0].length > 0 || state.calls.length > 1) {
- step(state, handler);
+ step(state);
if (state.stacks.length > 4096) {
throw 'max recursion depth exceeded'
}
}
return state;
+}
+
+export const execFn = (fn) => {
+ let state = {stacks:[[]], scopes:[fn.scope], calls:[fn.defn.ins]};
+ return exec(state);
+}
+
+export const execRPN = (scope, ins) => {
+ let state = {scopes:[scope], stacks:[[]], calls:[ins]};
+ return exec(state);
}
\ No newline at end of file
diff --git a/script/test.rpn b/script/test.rpn
new file mode 100644
index 0000000..efacb33
--- /dev/null
+++ b/script/test.rpn
@@ -0,0 +1,45 @@
+(output; `out` getId)
+
+(acc; 0 mutref)
+(stack; 0 tuple mutref)
+
+(show; '(->
+ output acc readmut "string coerce setHTML))
+
+(writeDigit; x ->
+ acc readmut 10 * x + acc writemut show)
+
+(spush; stack val ->
+ stack untuple val stack len 1 + tuple)
+
+(push; '(->
+ stack readmut acc readmut spush stack writemut))
+
+(spop; stack ->
+ stack untuple (a ->) stack len 1 - tuple)
+
+(last; stack ->
+ stack stack len 1 - !!)
+
+(pop; '(->
+ stack readmut last stack readmut spop stack writemut))
+
+(b1; `1` getId) b1 '(->1 writeDigit) onclick
+(b2; `2` getId) b2 '(->2 writeDigit) onclick
+(b3; `3` getId) b3 '(->3 writeDigit) onclick
+(b4; `4` getId) b4 '(->4 writeDigit) onclick
+(b5; `5` getId) b5 '(->5 writeDigit) onclick
+(b6; `6` getId) b6 '(->6 writeDigit) onclick
+(b7; `7` getId) b7 '(->7 writeDigit) onclick
+(b8; `8` getId) b8 '(->8 writeDigit) onclick
+(b9; `9` getId) b9 '(->9 writeDigit) onclick
+(b0; `0` getId) b0 '(->0 writeDigit) onclick
+
+(bpush; `push` getId) bpush '(->push 0 acc writemut show) onclick
+(bpop; `pop` getId) bpop '(->pop acc writemut show) onclick
+(bzero; `zero` getId) bzero '(->0 acc writemut show) onclick
+
+(badd; `add` getId) badd '(->pop pop + acc writemut show) onclick
+(bsub; `sub` getId) bsub '(->pop pop (a b -> b a) - acc writemut show) onclick
+(bmul; `mul` getId) bmul '(->pop pop * acc writemut show) onclick
+(bdiv; `div` getId) bdiv '(->pop pop (a b -> b a) / acc writemut show) onclick
\ No newline at end of file
diff --git a/script/token.mjs b/script/token.mjs
new file mode 100644
index 0000000..036e8c1
--- /dev/null
+++ b/script/token.mjs
@@ -0,0 +1,68 @@
+const tokens = (stream) => {
+ let toks = [];
+ let currTok = {val:"", type:"str"};
+ let stringmode = false;
+ for (let i = 0; i < stream.length; i++) {
+ if (stringmode) {
+ if (stream[i] === '`') {
+ toks.push(currTok);
+ stringmode = false;
+ currTok = {val:"", type:"str"};
+ } else {
+ currTok.val += stream[i];
+ }
+ } else if (stream[i] === '`') {
+ if (currTok.val !== "") {
+ toks.push(currTok);
+ }
+ stringmode = true;
+ currTok = {val:"", type:"string"};
+ } else if ("()';\"".includes(stream[i])) {
+ if (currTok.val !== "") {
+ toks.push(currTok);
+ }
+ toks.push({val:stream[i], type:"syntax"});
+ currTok = {val:"", type:"str"};
+ } else if (stream[i] === "-") {
+ if (stream[i+1] === ">") {
+ if (currTok.val !== "") {
+ toks.push(currTok);
+ }
+ toks.push({val:"->", type:"syntax"});
+ i++;
+ currTok = {val:"", type:"str"};
+ } else {
+ currTok.val += "-";
+ }
+ } else if (/\s/.test(stream[i])) {
+ if (currTok.val !== "") {
+ toks.push(currTok);
+ }
+ currTok = {val:"", type:"str"};
+ } else {
+ currTok.val += stream[i];
+ }
+ }
+ if (currTok.val !== "") {
+ toks.push(currTok);
+ }
+ return toks;
+}
+
+const classify = (tokens) => {
+ return tokens.map(tok => {
+ if (tok.type === "str") {
+ if (!isNaN(tok.val)) {
+ return {val:Number(tok.val), type:"num"};
+ } else {
+ return {name:tok.val, type:"ident"};
+ }
+ } else {
+ return tok;
+ }
+ });
+}
+
+export const tokenize = (stream) => {
+ return classify(tokens(stream));
+}
\ No newline at end of file
diff --git a/tracer/index.html b/tracer/index.html
index 33e1363..ce63583 100644
--- a/tracer/index.html
+++ b/tracer/index.html
@@ -18,7 +18,7 @@ code {
-
Documentation
+
documentation
use (name; value) to define something. the definition can be recursive. value is executed and name is set to the final state of the stack, i.e. (name; 1 3) is possible
use ' to push instead of apply to the stack, e.g. '(a -> a). This is useful for lazyness, i.e. '(->lazy evaluated thing)
@@ -29,10 +29,17 @@ code {
tuple: used like ... 3 tuple; creates an n tuple of n items on the stack
!!: index into a tuple
len: length of a tuple
-
unsafeCoerce: unsafely transforms one type into another. useage: 1 "type unsafeCoerce
-
true, false: true/false used in ==
-
stop: equivalent to "stop
-
inv: equivalent to 1 x /
-
fold: equivalent to x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==
+
+
stdlib
+
+
stop; "stop
+
inv; x -> 1 x /
+
fold; x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==
+
range; x y -> x '(->x x 1 + y range) 'x y ==
+
listthen; fn -> (internal; x acc -> '(->acc fn) '(->x acc pair internal) x stop ==) 0 tuple internal
+
list; (a -> a) listthen
+
lmap; list fn -> list '(->list fst fn list snd 'fn lmap pair) list 0 tuple ==
+
unlist; l -> (internal; list -> '(->) '(->list fst list snd internal) list 0 tuple ==) stop l internal