added builtins

This commit is contained in:
Aidan K. Ewart 2020-06-01 11:58:56 +01:00
parent 5f7a6f0516
commit ea7836145b
3 changed files with 118 additions and 67 deletions

View File

@ -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)");

View File

@ -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]);
}

View File

@ -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 + '"'
}
}
}