this repo has no description
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);