mirror of
				https://github.com/Baidicoot/rpncalc-v4
				synced 2025-10-31 15:43:00 +00:00 
			
		
		
		
	added builtins
This commit is contained in:
		
							
								
								
									
										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 + '"' | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Aidan K. Ewart
					Aidan K. Ewart