this repo has no description
at trunk 253 lines 10 kB view raw
1let input = ` 2 t1 = 123 |> await ! 3 t2 = 456 |> await ! 4 1 + f a * b 5 ? 1 + f 2 == 3 6 ? 1.0 + 2.0 == 3.0 7 ? a == 2 8 ? [ ] == f [ ] 9 ? ( ) == ( ) 10 ? 1 >+ [ f 2 , 3 , 4 ] == [ 1 , 2 , f 3 , 4 ] 11 ? ( 1 + 2 ) == ( 1 + ( 1 + 1 ) ) 12 ? ~~aGVsbG8gd29ybGQ= == ~~aGVsbG8gd29ybGQ= 13 ? ~~64'aGVsbG8gd29ybGQ= == ~~64'aGVsbG8gd29ybGQ= 14 . a : $$int = 2 15 . a : $$int 16 . a = 2 17 . b = 4 - f c 18 . c = 1 19 . _ = f "hello" ++ "!" 20 . f = x -> x 21 . _ = h 1 2 22 . h = a -> b -> a + b 23 . _ = { a = 1 , b = "x" } 24 . _ = { r = _ } -> ( ) 25 . _ = | { r = _ } -> ( ) 26 . _ = { r = _ , ... } -> ( ) 27 . { r = r , ... } = { r = 123 , .. k } 28 . _ : { q : int , ... } 29 . _ : { q : int } 30 . _ = { a = 2 , b = "y" , .. k } 31 . k = { a = 1 , b = "x" , c = 3 } 32 . _ = | "a" -> 1 | "b" -> 2 | "c" -> 3 | x -> 0 33 . _ = g 6 34 . g = | 1 -> 1 | n -> n * g ( n - 1 ) 35 . greet = x -> "hello\`x\`!" 36 . _ = scoop :: chocolate ( ) 37 . scoop : # vanilla ( ) # chocolate ( ) # strawberry ( ) 38 . _ = p :: point { x = 3 , y = 4 } |> # point _ -> 999 39 . _ : p -> ( ) = # point _ -> ( ) 40 . _ : p -> ( ) = | # point _ -> ( ) 41 . _ : p = p :: point { x = 3 , y = 4 } 42 . p : # point { x : int , y : int } 43 . _ = tuple :: triplet { x = 1.0 , y = "A" , z = ~2B } |> | # pair _ -> "B" | # triplet { y = y , ... } -> y 44 . _ = { z = 888 } |> { z = z , ... } -> z 45 . tuple : x => y => z => # pair { x : x , y : y } # triplet { x : x , y : y , z : z } 46 . _ = $123456 1 2 47 . _ = $sha1'123456 1 2 48 . _ = $$add 1 2 49 . _ : $$int 50`; 51 52// input = `scoop :: chocolate |> | # vanilla _ -> 111 | # chocolate _ -> 222 | # strawberry _ -> 333 . scoop : # vanilla ( ) # chocolate ( ) # strawberry ( )`; 53 54// input = `( | # vanilla _ -> 111 | # chocolate _ -> 222 | # strawberry _ -> 333 ) scoop :: chocolate . scoop : # vanilla ( ) # chocolate ( ) # strawberry ( )`; 55 56// input = `scoop :: chocolate ( ) . scoop : # vanilla ( ) # chocolate ( ) # strawberry ( )`; 57 58// input = `r . { r = r , ... } = { r = 123 , .. k } . k = { }`; 59 60// input = `f { r = 234 } . f = { r = _ } -> ( )`; 61 62// input = `f x . f = | 123 -> 456 | y -> y . x = 0`; 63 64// input = `f x . f = | 1 -> 1 | n -> n * f ( n - 1 ) . x = 6`; 65 66// input = `f 1 2 . f = a -> b -> a + b`; 67 68// TODO: Make this a proper tokenizer that handles strings with blankspace. 69const tokenize = x => x.replace(/ *--[^\n]*/g, '').trim().split(/[\s\n]+/g) 70 71const tokens = tokenize(input); 72 73const lp = n => ({pl: n, pr: n - 0.1}); 74const rp = n => ({pl: n, pr: n + 0.1}); 75const np = n => ({pl: n, pr: n + 0}); 76const xp = n => ({pl: n, pr: 0}); 77const ps = { 78 "::": lp(2000), 79 "": rp(1000), 80 ">>": lp(14), 81 "^": rp(13), 82 "*": lp(12), "/": lp(12), "//": lp(12), "%": lp(12), 83 "+": lp(11), "-": lp(11), 84 "**": rp(10), ">*": rp(10), "++": rp(10), ">+": rp(10), 85 "==": np(9), "/=": np(9), "<": np(9), ">": np(9), "<=": np(9), ">=": np(9), 86 "&&": rp(8), 87 "||": rp(7), 88 "#": lp(5.5), 89 "=>": lp(5.11), 90 "->": lp(5), 91 "|": rp(4.5), ":": lp(4.5), 92 "|>": lp(4.11), 93 "=": rp(4), 94 "!": lp(3), ".": rp(3), "?": rp(3), 95 ",": xp(1), "]": xp(1), "}": xp(1), 96} 97function parse(ts, p = 0) { 98 const x = ts.shift(); 99 if (x === undefined) throw new Error("unexpected end of input"); 100 let l; if (false) {} 101 else if (x === "|") { 102 const expr = parse(ts, 5); // TODO: make this work for larger arities 103 if (expr.op !== "->") throw new Error("must be function"); 104 l = new Fun([[expr.l, expr.r]]); 105 while(ts[0] === "|") { 106 ts.shift() 107 const expr_ = parse(ts, 5); // TODO: make this work for larger arities 108 if (expr_.op !== "->") throw new Error("must be function"); 109 l.branches.push([expr_.l, expr_.r]); 110 } 111 } 112 else if (x === "#") { 113 l = new Uni({}); 114 do { 115 const {l:l_,op,r} = parse(ts, 6); 116 if (op !== '') throw new Error(`TODO: parsing error`); 117 l.types[l_.label] = r; 118 } while (ts[0] === "#" && ts.shift()) 119 } 120 else if (ps[x]) {} 121 else if (x === "(") { 122 l = ts[0] === ")" ? new Hole() : parse(ts, 0); 123 ts.shift(); 124 } 125 else if (x === "[") { 126 l = []; 127 if (ts[0] === "]") ts.shift(); 128 else do { 129 l.push(parse(ts, 2)); 130 } while(ts.shift() !== "]"); 131 } 132 else if (x === "{") { 133 l = new Rec(); 134 if (ts[0] === "}") ts.shift(); 135 else do { 136 const {l:l_,op,r} = parse(ts, 2); 137 if (l_?.label === "...") {} // TODO 138 else if (op === "=") l.data[l_.label ?? ".."] = r; 139 else if (op === "..") l.fills.push(r); 140 } while(ts.shift() !== "}"); 141 } 142 else if (x === "...") { l = new Var("..."); } 143 else if (x === "..") { l = new Expr({}, "..", parse(ts, 2)); } 144 else if (x.match(/^[0-9]+$/)) l = parseInt(x); 145 else if (x.match(/^[0-9]+[0-9.]*$/)) l = parseFloat(x); 146 else if (x.match(/^".+"$/)) l = JSON.parse(x); 147 else if (x.match(/^[_a-z][a-z0-9]*$/)) l = new Var(x); 148 else if (x.match(/^\$(sha1')?[a-z0-9]+$/)) l = new Var(x); 149 else if (x.match(/^\$\$[a-z0-9]+$/)) l = new Var(x); 150 else if (x.match(/^~[^~ ]+$/)) l = new Bytes([x]); 151 else if (x.match(/^~~[^~ ]+$/)) l = new Bytes(x); 152 else if (x.match(/^\$::[a-z]+$/)) l = new Rock(x); 153 else throw new Error(`bad token: ${x}`); 154 while (true) { 155 let op = ts[0]; 156 if (!op || op === ")" || op === "]") break; 157 if (!ps[op]) op = ""; 158 const {pl, pr} = ps[op]; 159 // console.log(l, op, p, pl, pr); 160 if (pl < p) break; 161 if (op !== "") ts.shift(); 162 l = new Expr(l, op, parse(ts, pr)); 163 } 164 return l; 165}; 166 167const ast = parse(tokens); 168 169function Var(label) {this.label = label;} 170function Expr(l, op, r) {this.l = l; this.op = op; this.r = r;} 171function Hole() {} 172function Bytes(x) {this.x = x;} 173function Fun(branches, ctx = {}, xs = []) {this.branches = [...branches]; this.ctx = {...ctx}; this.xs = [...xs];} 174function Rec(data = {}, fills = []) {this.data = {...data}; this.fills = [...fills];} 175function Uni(types, t = null, x = null) {this.types = {...types}; this.t = t; this.x = x;} 176function Rock(label) {this.label = label;} 177 178// TODO: null matches need to bubble up 179const match = (arg,x) => { 180 const type = Object.getPrototypeOf(arg).constructor.name 181 if (type === "Number") return arg === x ? {} : null; 182 else if (type === "Var") return { [arg.label]: x }; 183 else if (type === "Rec") { const envs = Object.entries(arg.data).map(([k,v]) => match(v,x.data[k])); if (envs.some(x => x === null)) return null; else return Object.assign({}, ...envs); } 184 else if (type === "Uni") return arg.types[x.t] ? match(arg.types[x.t],x.x) : null; 185 else if (type === "Expr") if (arg.op === ":") return match(arg.l,x); else throw new Error("TODO: unexpected expression"); 186 // TODO: return null if no match 187 else throw new Error(`TODO: match ${type}`); 188}; 189const ops = { 190 "!": (env,l,r) => eval(env, r), // TODO: skipping left eval for now 191 ".": (env,l,r) => eval({ ...env, ...eval(env,r).env }, l), 192 "?": (env,l,r) => {if (eval(env,r).y !== true) throw new Error(`bad assertion: ${JSON.stringify(r)}`); return eval(env, l);}, 193 "=": (env,l,r) => ({ env: { ...env, ...Object.fromEntries(Object.entries(match(l,r)).map(([k,v])=>[k,eval(env,v).y])) } }), 194 "+": (env,l,r) => ({ y: eval(env,l).y + eval(env,r).y }), 195 "-": (env,l,r) => ({ y: eval(env,l).y - eval(env,r).y }), 196 "*": (env,l,r) => ({ y: eval(env,l).y * eval(env,r).y }), 197 ":": (env,l,r) => ({ env: { ...env, [l.label]: eval(env,r).y } }), 198 "::": (env,l,r) => ({ y: new Uni(eval(env,l).y.types, r.label) }), 199 "==": (env,l,r) => ({ y: JSON.stringify(eval(env,l).y) === JSON.stringify(eval(env,r).y) }), 200 ">+": (env,l,r) => ({ y: [eval(env,l).y].concat(eval(env,r).y) }), 201 "++": (env,l,r) => ({ y: eval(env,l).y.concat(eval(env,r).y) }), 202 "->": (env,l,r) => ({ y: new Fun([[l,r]], env) }), 203 "=>": (env,l,r) => eval(env,r), 204 "|>": (env,l,r) => ops[""](env,r,l), 205 "": (env,l,r) => { 206 const type = Object.getPrototypeOf(l).constructor.name 207 if (false) {} 208 else if (["Var","Expr"].includes(type)) 209 return ops[""](env, eval(env, l).y, r); 210 else if (type === "Uni") 211 return { y: new Uni(l.types, l.t, eval(env,r).y) }; 212 else if (type === "Fun") { 213 l = new Fun(l.branches, l.ctx, l.xs.concat([r])); 214 if (l.branches[0].length - 1 > l.xs.length) return l; 215 for (const branch of l.branches) { 216 const envs = l.xs.map((x,i) => match(branch[i],eval(env,x).y)); 217 if (envs.some(x => !x)) continue; 218 return eval(Object.assign({}, env, l.ctx, ...envs), branch[branch.length - 1]); 219 } 220 throw new Error(`no match found`); 221 } else throw new Error(`TODO: APPLY ${type} ${l} ${r} ${env}`); 222 }, 223}; 224const eval = (env,x) => { 225 console.log(x); 226 const type = Object.getPrototypeOf(x).constructor.name 227 if (false) {} 228 else if (["Number","String","Boolean","Bytes","Hole","Rock"].includes(type)) 229 return {y:x}; 230 else if (type === "Var") 231 if (env[x.label]) return {y:env[x.label]}; 232 else throw new Error(`TODO: ${x.label} not found`); 233 else if (type === "Fun") 234 return {y:x}; // TODO: anything else? 235 else if (type === "Uni") 236 return {y:x}; // TODO: eval all the sub data? 237 else if (type === "Expr") 238 if (!ops[x.op]) throw new Error(`TODO: op ${x.op}`); 239 else return ops[x.op](env,x.l,x.r); 240 else if (type === "Array") 241 return {y:x.map(x_=>eval(env,x_).y)}; 242 else if (type === "Rec") 243 return { y: new Rec(Object.fromEntries(Object.entries(x.data).map(([k,v]) => [k,eval(env,v).y])), x.fills.map(v => eval(env,v).y)) }; 244 else throw new Error(`TODO: EVAL ${type} ${x} ${env}`); 245}; 246 247const env = { 248 "$123456": eval({}, parse(tokenize("a -> b -> a + b"))).y, 249 "$sha1'123456": eval({}, parse(tokenize("a -> b -> a + b"))).y, 250 "$$add": eval({}, parse(tokenize("a -> b -> a + b"))).y, 251 "$$int": eval({}, parse(tokenize("# int ( )"))).y, 252}; 253console.log(eval(env,ast).y);