mirror of
https://github.com/Baidicoot/rpncalc-v4
synced 2024-12-11 18:50:26 +00:00
added meta
This commit is contained in:
parent
2ec9cf6a0c
commit
c3c39b1fff
@ -1,33 +0,0 @@
|
||||
<html>
|
||||
<style>
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
}
|
||||
code {
|
||||
background-color:rgb(230, 230, 230);
|
||||
padding: 0 0.125rem;
|
||||
}
|
||||
</style>
|
||||
<textarea id="inbox"></textarea>
|
||||
<button id="submit">execute</button>
|
||||
<pre id="outbox"></pre>
|
||||
<script src="./main.js" type="module"></script>
|
||||
<h3>Documentation</h3>
|
||||
<p>use <code>(name; value)</code> to define something. the definition can be recursive. <code>value</code> is executed and <code>name</code> is set to the final state of the stack, i.e. <code>(name; 1 3)</code> is possible</p>
|
||||
<p>use <code>'</code> to push instead of apply to the stack, e.g. <code>'(a -> a)</code>. This is useful for lazyness, i.e. <code>'(->lazy evaluated thing)</code></p>
|
||||
<ul>
|
||||
<li><code>+, -, *, /, ^, sqrt</code>: mathematical operations</li>
|
||||
<li><code>==</code>: equality (automatically derived for all types); returns <code>a b -> a</code> if true, <code>a b -> b</code> if false</li>
|
||||
<li><code>typeof</code>: returns the type of the object</li>
|
||||
<li><code>pair, fst, snd</code>: pairs two objects, gets first or second item of pair</li>
|
||||
<li><code>tuple</code>: used like <code>... 3 tuple</code>; creates an n tuple of n items on the stack</li>
|
||||
<li><code>!!</code>: index into a tuple</li>
|
||||
<li><code>len</code>: length of a tuple</li>
|
||||
<li><code>unsafeCoerce</code>: unsafely transforms one type into another. useage: <code>1 "type unsafeCoerce</code></li>
|
||||
<li><code>true, false</code>: true/false used in <code>==</code></li>
|
||||
<li><code>stop</code>: equivalent to <code>"stop</code></li>
|
||||
<li><code>inv</code>: equivalent to <code>1 x /</code></li>
|
||||
<li><code>fold</code>: equivalent to <code>x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==</code></li>
|
||||
</ul>
|
||||
</html>
|
52
new/main.js
52
new/main.js
@ -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]);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
export function tokenize(input) {
|
||||
let i;
|
||||
let inputWithAddedSpaces = "";
|
||||
const syntax = /['";()]/;
|
||||
for(i = 0; i<input.length; i++){
|
||||
if(syntax.test(input.charAt(i))){
|
||||
if(input.charAt(i-1) != " "){
|
||||
inputWithAddedSpaces += " ";
|
||||
}
|
||||
if(input.charAt(i+1) != " "){
|
||||
inputWithAddedSpaces += input.charAt(i) + " ";
|
||||
} else {
|
||||
inputWithAddedSpaces += input.charAt(i);
|
||||
}
|
||||
} else if(input.charAt(i) === "-" && input.charAt(i+1) === ">"){
|
||||
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<splitInput.length; i++){
|
||||
if(!isNaN(splitInput[i])){
|
||||
output.push({type: "num", val:Number(splitInput[i])});
|
||||
} else if(syntax.test(splitInput[i]) || splitInput[i] === "->"){
|
||||
output.push({type: "syntax", val:splitInput[i]});
|
||||
} else if(splitInput[i] != '') {
|
||||
output.push({type: "ident", name:splitInput[i]});
|
||||
}
|
||||
}
|
||||
return(output)
|
||||
}
|
@ -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 (<anonymous>)
|
||||
38 verbose stack at /usr/share/npm/node_modules/slide/lib/async-map.js:52:11
|
||||
38 verbose stack at Array.forEach (<anonymous>)
|
||||
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 (<anonymous>)
|
||||
45 error typeerror at /usr/share/npm/node_modules/slide/lib/async-map.js:52:11
|
||||
45 error typeerror at Array.forEach (<anonymous>)
|
||||
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 <http://github.com/npm/npm/issues>
|
||||
47 verbose exit [ 1, true ]
|
@ -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");
|
||||
addDefn("coerce", 2, coerce);
|
33
script/dom.mjs
Normal file
33
script/dom.mjs
Normal file
@ -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);
|
31
script/index.html
Normal file
31
script/index.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Meta Calculator</title>
|
||||
</head>
|
||||
<body>
|
||||
<p id="out"></p>
|
||||
<button id="1">1</button>
|
||||
<button id="2">2</button>
|
||||
<button id="3">3</button>
|
||||
<button id="4">4</button>
|
||||
<button id="5">5</button>
|
||||
<button id="6">6</button>
|
||||
<button id="7">7</button>
|
||||
<button id="8">8</button>
|
||||
<button id="9">9</button>
|
||||
<button id="0">0</button>
|
||||
<button id="push">push</button>
|
||||
<button id="pop">pop</button>
|
||||
<button id="zero">zero</button>
|
||||
<br>
|
||||
<button id="add">+</button>
|
||||
<button id="sub">-</button>
|
||||
<button id="mul">*</button>
|
||||
<button id="div">/</button>
|
||||
</body>
|
||||
<script src="./test.rpn" type="text/rpncalc"></script>
|
||||
<script src="./main.js" type="module"></script>
|
||||
</html>
|
31
script/main.js
Normal file
31
script/main.js
Normal file
@ -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);
|
||||
}
|
@ -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);
|
@ -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);
|
||||
}
|
45
script/test.rpn
Normal file
45
script/test.rpn
Normal file
@ -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
|
68
script/token.mjs
Normal file
68
script/token.mjs
Normal file
@ -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));
|
||||
}
|
@ -18,7 +18,7 @@ code {
|
||||
<pre id="insbox"></pre>
|
||||
<pre id="outbox"></pre>
|
||||
<script src="./main.js" type="module"></script>
|
||||
<h3>Documentation</h3>
|
||||
<h3>documentation</h3>
|
||||
<p>use <code>(name; value)</code> to define something. the definition can be recursive. <code>value</code> is executed and <code>name</code> is set to the final state of the stack, i.e. <code>(name; 1 3)</code> is possible</p>
|
||||
<p>use <code>'</code> to push instead of apply to the stack, e.g. <code>'(a -> a)</code>. This is useful for lazyness, i.e. <code>'(->lazy evaluated thing)</code></p>
|
||||
<ul>
|
||||
@ -29,10 +29,17 @@ code {
|
||||
<li><code>tuple</code>: used like <code>... 3 tuple</code>; creates an n tuple of n items on the stack</li>
|
||||
<li><code>!!</code>: index into a tuple</li>
|
||||
<li><code>len</code>: length of a tuple</li>
|
||||
<li><code>unsafeCoerce</code>: unsafely transforms one type into another. useage: <code>1 "type unsafeCoerce</code></li>
|
||||
<li><code>true, false</code>: true/false used in <code>==</code></li>
|
||||
<li><code>stop</code>: equivalent to <code>"stop</code></li>
|
||||
<li><code>inv</code>: equivalent to <code>1 x /</code></li>
|
||||
<li><code>fold</code>: equivalent to <code>x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==</code></li>
|
||||
</ul>
|
||||
<h3>stdlib</h3>
|
||||
<ul>
|
||||
<li><code>stop; "stop</code></li>
|
||||
<li><code>inv; x -> 1 x /</code></li>
|
||||
<li><code>fold; x acc fn -> acc '(-> x acc fn 'fn fold) 'x \"stop ==</code></li>
|
||||
<li><code>range; x y -> x '(->x x 1 + y range) 'x y ==</code></li>
|
||||
<li><code>listthen; fn -> (internal; x acc -> '(->acc fn) '(->x acc pair internal) x stop ==) 0 tuple internal</code></li>
|
||||
<li><code>list; (a -> a) listthen</code></li>
|
||||
<li><code>lmap; list fn -> list '(->list fst fn list snd 'fn lmap pair) list 0 tuple ==</code></li>
|
||||
<li><code>unlist; l -> (internal; list -> '(->) '(->list fst list snd internal) list 0 tuple ==) stop l internal</code></li>
|
||||
<li><code>map; fn -> '(l->l 'fn lmap unlist) listthen</code></li>
|
||||
</ul>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user