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 {tokenize} from './token.mjs';
|
||||
|
||||
export let scope = {};
|
||||
|
||||
const addRPNDefn = (name, def) => {
|
||||
let toks = tokenize(def);
|
||||
if (!toks) {
|
||||
@ -11,71 +13,110 @@ const addRPNDefn = (name, def) => {
|
||||
if (!ast.parsed) {
|
||||
throw 'could not load builtin'
|
||||
}
|
||||
addRPNASTDefn(name, ast.parsed[0]);
|
||||
scope = defn(name, ast.parsed, scope);
|
||||
}
|
||||
|
||||
const add = (_, args) => {
|
||||
return [{type:"int", val:args[0] + args[1]}];
|
||||
const ASTs = { // should export makeStackElems or eq. to allow for this kind of thing ('ast'?)
|
||||
"true":parseExprs(tokenize("(a b -> a)")).parsed,
|
||||
"false":parseExprs(tokenize("(a b -> b)")).parsed,
|
||||
}
|
||||
|
||||
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[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 assertType = (type) => (elem) => {
|
||||
if (elem.type !== type) {
|
||||
throw 'typeerror'
|
||||
}
|
||||
}
|
||||
|
||||
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}]})];
|
||||
}
|
||||
|
||||
const index = (_, args) => {
|
||||
return [args[1][args[0]]];
|
||||
const index = (args) => {
|
||||
return [args[0][args[1]]];
|
||||
}
|
||||
|
||||
const len = (_, args) => {
|
||||
const len = (args) => {
|
||||
return [{type:"int", val:args[0].length}];
|
||||
}
|
||||
|
||||
const coerce = (_, args) => {
|
||||
const coerce = (args) => {
|
||||
if (args[0].type === "type") {
|
||||
let o = cloneElem(args[1]);
|
||||
o.type = args[0].val;
|
||||
return o;
|
||||
let o = {type:args[1].val, val:args[0].val};
|
||||
return [o];
|
||||
} else {
|
||||
throw 'typeerror'
|
||||
}
|
||||
@ -93,7 +134,7 @@ addDefn("pair", 2, pair);
|
||||
addDefn("fst", ["pair"], fst);
|
||||
addDefn("snd", ["pair"], snd);
|
||||
addDefn("tuple", ["int"], tuple);
|
||||
addDefn("!!", ["int", "tuple"], index);
|
||||
addDefn("!!", ["tuple", "int"], index);
|
||||
addDefn("len", ["tuple"], len);
|
||||
addDefn("unsafeCoerce", 2, coerce);
|
||||
//addRPNDefn("unit", "(-> 0 arr)");
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {execRPN} from './shiny.mjs';
|
||||
import {parseExprs} from './parse.mjs';
|
||||
import {tokenize} from './token.mjs';
|
||||
//import './builtin.mjs';
|
||||
import {scope} from './builtin.mjs';
|
||||
|
||||
const inbox = document.getElementById("inbox")
|
||||
const outbox = document.getElementById("outbox")
|
||||
@ -19,7 +19,7 @@ const show = (elem) => {
|
||||
} else if (elem.type === "array") {
|
||||
return "[" + prettyprint(elem.val) + "]"
|
||||
} else if (elem.val) {
|
||||
return "(" + elem.type + ": " + elem.val + ")"
|
||||
return "(" + elem.val + ": " + elem.type + ")"
|
||||
} else {
|
||||
return elem.type
|
||||
}
|
||||
@ -48,11 +48,11 @@ submit.onclick = (event) => {
|
||||
outbox.innerHTML = "incorrect syntax somewhere";
|
||||
return;
|
||||
}
|
||||
let out = execRPN({}, ast.parsed);
|
||||
let out = execRPN(scope, ast.parsed);
|
||||
if (!out) {
|
||||
outbox.innerHTML = "failed to execute";
|
||||
return;
|
||||
}
|
||||
//console.log(out.stacks);
|
||||
console.log(out);
|
||||
outbox.innerHTML = prettyprint(out.stacks[0]);
|
||||
}
|
@ -17,6 +17,8 @@ TYPE VAL
|
||||
|
||||
exported functionality:
|
||||
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)
|
||||
*/
|
||||
|
||||
@ -26,12 +28,25 @@ export const defnOp = (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) => {
|
||||
let n = scope[name];
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
n = builtins[n];
|
||||
n = builtins[name];
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
@ -100,16 +115,7 @@ 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));
|
||||
} else if (ins.type === "defn") {
|
||||
// initialize definition scope
|
||||
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;
|
||||
state.scopes[state.scopes.length-1] = defn(ins.ident, ins.defn, state.scopes[state.scopes.length-1]);
|
||||
} else {
|
||||
applyMany(makeStackElems(ins, state), state);
|
||||
}
|
||||
@ -129,7 +135,11 @@ const step = (state) => {
|
||||
} 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);
|
||||
try {
|
||||
doIns(ins, state);
|
||||
} catch (error) {
|
||||
throw error + ' while executing "' + ins.val + '"'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user