mirror of
https://github.com/Baidicoot/rpncalc-v4
synced 2024-12-04 23:39:56 +00:00
added builtins
This commit is contained in:
parent
5f7a6f0516
commit
ea7836145b
143
new/builtin.mjs
143
new/builtin.mjs
@ -1,7 +1,9 @@
|
|||||||
import {addDefn, addRPNASTDefn, makeFn, cloneElem} from './shiny.mjs';
|
import {defnOp, makeOp, defn} from './shiny.mjs';
|
||||||
import {parseExprs} from './parse.mjs';
|
import {parseExprs} from './parse.mjs';
|
||||||
import {tokenize} from './token.mjs';
|
import {tokenize} from './token.mjs';
|
||||||
|
|
||||||
|
export let scope = {};
|
||||||
|
|
||||||
const addRPNDefn = (name, def) => {
|
const addRPNDefn = (name, def) => {
|
||||||
let toks = tokenize(def);
|
let toks = tokenize(def);
|
||||||
if (!toks) {
|
if (!toks) {
|
||||||
@ -11,71 +13,110 @@ const addRPNDefn = (name, def) => {
|
|||||||
if (!ast.parsed) {
|
if (!ast.parsed) {
|
||||||
throw 'could not load builtin'
|
throw 'could not load builtin'
|
||||||
}
|
}
|
||||||
addRPNASTDefn(name, ast.parsed[0]);
|
scope = defn(name, ast.parsed, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
const add = (_, args) => {
|
const ASTs = { // should export makeStackElems or eq. to allow for this kind of thing ('ast'?)
|
||||||
return [{type:"int", val:args[0] + args[1]}];
|
"true":parseExprs(tokenize("(a b -> a)")).parsed,
|
||||||
|
"false":parseExprs(tokenize("(a b -> b)")).parsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
const sub = (_, args) => {
|
const assertType = (type) => (elem) => {
|
||||||
return [{type:"int", val:args[1] - args[0]}];
|
if (elem.type !== type) {
|
||||||
}
|
throw 'typeerror'
|
||||||
|
|
||||||
const div = (_, args) => {
|
|
||||||
return [{type:"int", val:args[1] / args[0]}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const mult = (_, args) => {
|
|
||||||
return [{type:"int", val:args[0] * args[1]}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const pow = (_, args) => {
|
|
||||||
return [{type:"int", val:Math.pow(args[1], args[0])}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = (_, args) => {
|
|
||||||
return [{type:"int", val:Math.sqrt(args[0])}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = (_, args) => {
|
|
||||||
return [{type:"type", val:args[0].type}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const pair = (_, args) => {
|
|
||||||
return [{type:"pair", val:{fst:args[1], snd:args[0]}}];
|
|
||||||
}
|
|
||||||
|
|
||||||
const fst = (_, args) => [args[0].fst];
|
|
||||||
|
|
||||||
const snd = (_, args) => [args[0].snd];
|
|
||||||
|
|
||||||
const eq = (_, args) => {
|
|
||||||
if (args[0].type === args[1].type && args[0].val === args[1].val) {
|
|
||||||
console.log(args[0], args[1])
|
|
||||||
return [{type:"ident", val:"true"}];
|
|
||||||
} else {
|
|
||||||
return [{type:"ident", val:"false"}];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tuple = (_, args) => {
|
const addDefn = (name, args, fn) => {
|
||||||
|
if (Array.isArray(args)) {
|
||||||
|
const nargs = [...Array(args.length).keys()];
|
||||||
|
const liftFn = (scope) => {
|
||||||
|
let newscope = Object.create(scope);
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
assertType(args[i])(scope[i][0]);
|
||||||
|
newscope[i] = scope[i][0].val;
|
||||||
|
}
|
||||||
|
return fn(newscope);
|
||||||
|
}
|
||||||
|
const op = makeOp(nargs, liftFn);
|
||||||
|
defnOp(name, op);
|
||||||
|
} else {
|
||||||
|
const nargs = [...Array(args).keys()];
|
||||||
|
const liftFn = (scope) => {
|
||||||
|
let newscope = Object.create(scope);
|
||||||
|
for (let i = 0; i < args; i++) {
|
||||||
|
newscope[i] = scope[i][0];
|
||||||
|
}
|
||||||
|
return fn(newscope);
|
||||||
|
}
|
||||||
|
const op = makeOp(nargs, liftFn);
|
||||||
|
defnOp(name, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const add = (args) => {
|
||||||
|
return [{type:"int", val:args[0] + args[1]}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const sub = (args) => {
|
||||||
|
return [{type:"int", val:args[1] - args[0]}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const div = (args) => {
|
||||||
|
return [{type:"int", val:args[1] / args[0]}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const mult = (args) => {
|
||||||
|
return [{type:"int", val:args[0] * args[1]}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const pow = (args) => {
|
||||||
|
return [{type:"int", val:Math.pow(args[1], args[0])}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = (args) => {
|
||||||
|
return [{type:"int", val:Math.sqrt(args[0])}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = (args) => {
|
||||||
|
return [{type:"type", val:args[0].type}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const pair = (args) => {
|
||||||
|
return [{type:"pair", val:{fst:args[0], snd:args[1]}}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fst = (args) => [args[0].fst];
|
||||||
|
|
||||||
|
const snd = (args) => [args[0].snd];
|
||||||
|
|
||||||
|
const eq = (args) => {
|
||||||
|
args = defn("true", ASTs["true"], args);
|
||||||
|
args = defn("false", ASTs["false"], args);
|
||||||
|
if (args[0].type === args[1].type && args[0].val === args[1].val) {
|
||||||
|
console.log(args[0], args[1])
|
||||||
|
return args["true"];
|
||||||
|
} else {
|
||||||
|
return args["false"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tuple = (args) => {
|
||||||
return [makeFn(args[0], (_, args) => {return [{type:"tuple", val:args}]})];
|
return [makeFn(args[0], (_, args) => {return [{type:"tuple", val:args}]})];
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = (_, args) => {
|
const index = (args) => {
|
||||||
return [args[1][args[0]]];
|
return [args[0][args[1]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
const len = (_, args) => {
|
const len = (args) => {
|
||||||
return [{type:"int", val:args[0].length}];
|
return [{type:"int", val:args[0].length}];
|
||||||
}
|
}
|
||||||
|
|
||||||
const coerce = (_, args) => {
|
const coerce = (args) => {
|
||||||
if (args[0].type === "type") {
|
if (args[0].type === "type") {
|
||||||
let o = cloneElem(args[1]);
|
let o = {type:args[1].val, val:args[0].val};
|
||||||
o.type = args[0].val;
|
return [o];
|
||||||
return o;
|
|
||||||
} else {
|
} else {
|
||||||
throw 'typeerror'
|
throw 'typeerror'
|
||||||
}
|
}
|
||||||
@ -93,7 +134,7 @@ addDefn("pair", 2, pair);
|
|||||||
addDefn("fst", ["pair"], fst);
|
addDefn("fst", ["pair"], fst);
|
||||||
addDefn("snd", ["pair"], snd);
|
addDefn("snd", ["pair"], snd);
|
||||||
addDefn("tuple", ["int"], tuple);
|
addDefn("tuple", ["int"], tuple);
|
||||||
addDefn("!!", ["int", "tuple"], index);
|
addDefn("!!", ["tuple", "int"], index);
|
||||||
addDefn("len", ["tuple"], len);
|
addDefn("len", ["tuple"], len);
|
||||||
addDefn("unsafeCoerce", 2, coerce);
|
addDefn("unsafeCoerce", 2, coerce);
|
||||||
//addRPNDefn("unit", "(-> 0 arr)");
|
//addRPNDefn("unit", "(-> 0 arr)");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {execRPN} from './shiny.mjs';
|
import {execRPN} from './shiny.mjs';
|
||||||
import {parseExprs} from './parse.mjs';
|
import {parseExprs} from './parse.mjs';
|
||||||
import {tokenize} from './token.mjs';
|
import {tokenize} from './token.mjs';
|
||||||
//import './builtin.mjs';
|
import {scope} from './builtin.mjs';
|
||||||
|
|
||||||
const inbox = document.getElementById("inbox")
|
const inbox = document.getElementById("inbox")
|
||||||
const outbox = document.getElementById("outbox")
|
const outbox = document.getElementById("outbox")
|
||||||
@ -19,7 +19,7 @@ const show = (elem) => {
|
|||||||
} else if (elem.type === "array") {
|
} else if (elem.type === "array") {
|
||||||
return "[" + prettyprint(elem.val) + "]"
|
return "[" + prettyprint(elem.val) + "]"
|
||||||
} else if (elem.val) {
|
} else if (elem.val) {
|
||||||
return "(" + elem.type + ": " + elem.val + ")"
|
return "(" + elem.val + ": " + elem.type + ")"
|
||||||
} else {
|
} else {
|
||||||
return elem.type
|
return elem.type
|
||||||
}
|
}
|
||||||
@ -48,11 +48,11 @@ submit.onclick = (event) => {
|
|||||||
outbox.innerHTML = "incorrect syntax somewhere";
|
outbox.innerHTML = "incorrect syntax somewhere";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let out = execRPN({}, ast.parsed);
|
let out = execRPN(scope, ast.parsed);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
outbox.innerHTML = "failed to execute";
|
outbox.innerHTML = "failed to execute";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//console.log(out.stacks);
|
console.log(out);
|
||||||
outbox.innerHTML = prettyprint(out.stacks[0]);
|
outbox.innerHTML = prettyprint(out.stacks[0]);
|
||||||
}
|
}
|
@ -17,6 +17,8 @@ TYPE VAL
|
|||||||
|
|
||||||
exported functionality:
|
exported functionality:
|
||||||
defnOp(name, function) - define a built-in operator
|
defnOp(name, function) - define a built-in operator
|
||||||
|
defn(name, ins, scope) - extend scope with AST
|
||||||
|
makeOp(name, args, fn) - lift a function to an operator
|
||||||
evalRPN(scope, ast)
|
evalRPN(scope, ast)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -26,12 +28,25 @@ export const defnOp = (name, data) => {
|
|||||||
builtins[name] = data;
|
builtins[name] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defn = (name, ins, scope) => {
|
||||||
|
let defscope = Object.create(scope);
|
||||||
|
let fnForm = {type:"closure", val:{scope:defscope, args:[], defn:{type:"ins", ins:ins}}};
|
||||||
|
defscope[name] = [fnForm];
|
||||||
|
let out = execRPN(defscope, ins);
|
||||||
|
defscope[name] = out.stacks[0];
|
||||||
|
return defscope;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeOp = (args, fn) => {
|
||||||
|
return [{type:"closure", val:{scope:{}, args:args, defn:{type:"builtin", fn:fn}}}];
|
||||||
|
}
|
||||||
|
|
||||||
const lookup = (name, scope) => {
|
const lookup = (name, scope) => {
|
||||||
let n = scope[name];
|
let n = scope[name];
|
||||||
if (n) {
|
if (n) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
n = builtins[n];
|
n = builtins[name];
|
||||||
if (n) {
|
if (n) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@ -100,16 +115,7 @@ const doIns = (ins, state) => {
|
|||||||
if (ins.type === "push") {
|
if (ins.type === "push") {
|
||||||
state.stacks[state.stacks.length-1] = state.stacks[state.stacks.length-1].concat(makeStackElems(ins.elem, state));
|
state.stacks[state.stacks.length-1] = state.stacks[state.stacks.length-1].concat(makeStackElems(ins.elem, state));
|
||||||
} else if (ins.type === "defn") {
|
} else if (ins.type === "defn") {
|
||||||
// initialize definition scope
|
state.scopes[state.scopes.length-1] = defn(ins.ident, ins.defn, state.scopes[state.scopes.length-1]);
|
||||||
let defscope = Object.create(state.scopes[state.scopes.length-1]);
|
|
||||||
// parse into closure for definition's scope (to allow for recursion)
|
|
||||||
let fnForm = {type:"closure", val:{scope:defscope, args:[], defn:{type:"ins", ins:ins.defn}}};
|
|
||||||
// add fnForm to definition scope
|
|
||||||
defscope[ins.ident] = [fnForm];
|
|
||||||
// evaluate fnForm
|
|
||||||
let out = execRPN(defscope, ins.defn);
|
|
||||||
defscope[ins.ident] = out.stacks[0];
|
|
||||||
state.scopes[state.scopes.length-1] = defscope;
|
|
||||||
} else {
|
} else {
|
||||||
applyMany(makeStackElems(ins, state), state);
|
applyMany(makeStackElems(ins, state), state);
|
||||||
}
|
}
|
||||||
@ -129,7 +135,11 @@ const step = (state) => {
|
|||||||
} else {
|
} else {
|
||||||
let ins = state.calls[state.calls.length-1][0];
|
let ins = state.calls[state.calls.length-1][0];
|
||||||
state.calls[state.calls.length-1] = state.calls[state.calls.length-1].slice(1);
|
state.calls[state.calls.length-1] = state.calls[state.calls.length-1].slice(1);
|
||||||
doIns(ins, state);
|
try {
|
||||||
|
doIns(ins, state);
|
||||||
|
} catch (error) {
|
||||||
|
throw error + ' while executing "' + ins.val + '"'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user