diff --git a/new/builtin.mjs b/new/builtin.mjs index 7b65afd..4819d69 100644 --- a/new/builtin.mjs +++ b/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)"); diff --git a/new/main.js b/new/main.js index 9ba0fd3..c233e00 100644 --- a/new/main.js +++ b/new/main.js @@ -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]); } \ No newline at end of file diff --git a/new/shiny.mjs b/new/shiny.mjs index 89ec0ef..d059b5e 100644 --- a/new/shiny.mjs +++ b/new/shiny.mjs @@ -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 + '"' + } } }