fix other mandelbrot variants
[mu.git] / archive / 1.vm / lambda-to-mu.mu
bloba171b4ca165a2f79690efdff39be2f204a963cc9
1 ## experimental compiler to translate programs written in a generic
2 ## expression-oriented language called 'lambda' into Mu
4 # incomplete; code generator not done
5 # potential enhancements:
6 #   symbol table
7 #   poor man's macros
8 #     substitute one instruction with multiple, parameterized by inputs and products
10 scenario convert-lambda [
11   run [
12     local-scope
13     1:text/raw <- lambda-to-mu [(add a (multiply b c))]
14     2:@:char/raw <- copy *1:text/raw
15   ]
16   memory-should-contain [
17     2:array:character <- [t1 <- multiply b c
18 result <- add a t1]
19   ]
22 def lambda-to-mu in:text -> out:text [
23   local-scope
24   load-inputs
25   out <- copy null
26   cells:&:cell <- parse in
27   out <- to-mu cells
30 # 'parse' will turn lambda expressions into trees made of cells
31 exclusive-container cell [
32   atom:text
33   pair:pair
36 # printed below as < first | rest >
37 container pair [
38   first:&:cell
39   rest:&:cell
42 def new-atom name:text -> result:&:cell [
43   local-scope
44   load-inputs
45   result <- new cell:type
46   *result <- merge 0/tag:atom, name
49 def new-pair a:&:cell, b:&:cell -> result:&:cell [
50   local-scope
51   load-inputs
52   result <- new cell:type
53   *result <- merge 1/tag:pair, a/first, b/rest
56 def is-atom? x:&:cell -> result:bool [
57   local-scope
58   load-inputs
59   return-unless x, false
60   _, result <- maybe-convert *x, atom:variant
63 def is-pair? x:&:cell -> result:bool [
64   local-scope
65   load-inputs
66   return-unless x, false
67   _, result <- maybe-convert *x, pair:variant
70 scenario atom-is-not-pair [
71   local-scope
72   s:text <- new [a]
73   x:&:cell <- new-atom s
74   10:bool/raw <- is-atom? x
75   11:bool/raw <- is-pair? x
76   memory-should-contain [
77     10 <- 1
78     11 <- 0
79   ]
82 scenario pair-is-not-atom [
83   local-scope
84   # construct (a . nil)
85   s:text <- new [a]
86   x:&:cell <- new-atom s
87   y:&:cell <- new-pair x, null
88   10:bool/raw <- is-atom? y
89   11:bool/raw <- is-pair? y
90   memory-should-contain [
91     10 <- 0
92     11 <- 1
93   ]
96 def atom-match? x:&:cell, pat:text -> result:bool [
97   local-scope
98   load-inputs
99   s:text, is-atom?:bool <- maybe-convert *x, atom:variant
100   return-unless is-atom?, false
101   result <- equal pat, s
104 scenario atom-match [
105   local-scope
106   x:&:cell <- new-atom [abc]
107   10:bool/raw <- atom-match? x, [abc]
108   memory-should-contain [
109     10 <- 1
110   ]
113 def first x:&:cell -> result:&:cell [
114   local-scope
115   load-inputs
116   pair:pair, pair?:bool <- maybe-convert *x, pair:variant
117   return-unless pair?, null
118   result <- get pair, first:offset
121 def rest x:&:cell -> result:&:cell [
122   local-scope
123   load-inputs
124   pair:pair, pair?:bool <- maybe-convert *x, pair:variant
125   return-unless pair?, null
126   result <- get pair, rest:offset
129 def set-first base:&:cell, new-first:&:cell -> base:&:cell [
130   local-scope
131   load-inputs
132   pair:pair, is-pair?:bool <- maybe-convert *base, pair:variant
133   return-unless is-pair?
134   pair <- put pair, first:offset, new-first
135   *base <- merge 1/pair, pair
138 def set-rest base:&:cell, new-rest:&:cell -> base:&:cell [
139   local-scope
140   load-inputs
141   pair:pair, is-pair?:bool <- maybe-convert *base, pair:variant
142   return-unless is-pair?
143   pair <- put pair, rest:offset, new-rest
144   *base <- merge 1/pair, pair
147 scenario cell-operations-on-atom [
148   local-scope
149   s:text <- new [a]
150   x:&:cell <- new-atom s
151   10:&:cell/raw <- first x
152   11:&:cell/raw <- rest x
153   memory-should-contain [
154     10 <- 0  # first is nil
155     11 <- 0  # rest is nil
156   ]
159 scenario cell-operations-on-pair [
160   local-scope
161   # construct (a . nil)
162   s:text <- new [a]
163   x:&:cell <- new-atom s
164   y:&:cell <- new-pair x, null
165   x2:&:cell <- first y
166   10:bool/raw <- equal x, x2
167   11:&:cell/raw <- rest y
168   memory-should-contain [
169     10 <- 1  # first is correct
170     11 <- 0  # rest is nil
171   ]
174 ## convert lambda text to a tree of cells
176 def parse in:text -> out:&:cell [
177   local-scope
178   load-inputs
179   s:&:stream:char <- new-stream in
180   out, s <- parse s
181   trace 2, [app/parse], out
184 def parse in:&:stream:char -> out:&:cell, in:&:stream:char [
185   local-scope
186   load-inputs
187   # skip whitespace
188   in <- skip-whitespace in
189   c:char, eof?:bool <- peek in
190   return-if eof?, null
191   pair?:bool <- equal c, 40/open-paren
192   {
193     break-if pair?
194     # atom
195     buf:&:buffer:char <- new-buffer 30
196     {
197       done?:bool <- end-of-stream? in
198       break-if done?
199       # stop before close paren or space
200       c:char <- peek in
201       done? <- equal c, 41/close-paren
202       break-if done?
203       done? <- space? c
204       break-if done?
205       c <- read in
206       buf <- append buf, c
207       loop
208     }
209     s:text <- buffer-to-array buf
210     out <- new-atom s
211   }
212   {
213     break-unless pair?
214     # pair
215     read in  # skip the open-paren
216     out <- new cell:type  # start out with nil
217     # read in first element of pair
218     {
219       end?:bool <- end-of-stream? in
220       not-end?:bool <- not end?
221       assert not-end?, [unbalanced '(' in expression]
222       c <- peek in
223       close-paren?:bool <- equal c, 41/close-paren
224       break-if close-paren?
225       first:&:cell, in <- parse in
226       *out <- merge 1/pair, first, null
227     }
228     # read in any remaining elements
229     curr:&:cell <- copy out
230     {
231       in <- skip-whitespace in
232       end?:bool <- end-of-stream? in
233       not-end?:bool <- not end?
234       assert not-end?, [unbalanced '(' in expression]
235       # termination check: ')'
236       c <- peek in
237       {
238         close-paren?:bool <- equal c, 41/close-paren
239         break-unless close-paren?
240         read in  # skip ')'
241         break +end-pair
242       }
243       # still here? read next element of pair
244       next:&:cell, in <- parse in
245       is-dot?:bool <- atom-match? next, [.]
246       {
247         break-if is-dot?
248         next-curr:&:cell <- new-pair next, null
249         curr <- set-rest curr, next-curr
250         curr <- rest curr
251       }
252       {
253         break-unless is-dot?
254         # deal with dotted pair
255         in <- skip-whitespace in
256         c <- peek in
257         not-close-paren?:bool <- not-equal c, 41/close-paren
258         assert not-close-paren?, [')' cannot immediately follow '.']
259         final:&:cell <- parse in
260         curr <- set-rest curr, final
261         # we're not gonna update curr, so better make sure the next iteration
262         # is going to end the pair
263         in <- skip-whitespace in
264         c <- peek in
265         close-paren?:bool <- equal c, 41/close-paren
266         assert close-paren?, ['.' must be followed by exactly one expression before ')']
267       }
268       loop
269     }
270     +end-pair
271   }
274 def skip-whitespace in:&:stream:char -> in:&:stream:char [
275   local-scope
276   load-inputs
277   {
278     done?:bool <- end-of-stream? in
279     return-if done?, null
280     c:char <- peek in
281     space?:bool <- space? c
282     break-unless space?
283     read in  # skip
284     loop
285   }
288 def to-text x:&:cell -> out:text [
289   local-scope
290   load-inputs
291   buf:&:buffer:char <- new-buffer 30
292   buf <- to-buffer x, buf
293   out <- buffer-to-array buf
296 def to-buffer x:&:cell, buf:&:buffer:char -> buf:&:buffer:char [
297   local-scope
298   load-inputs
299   # base case: empty cell
300   {
301     break-if x
302     buf <- append buf, [<>]
303     return
304   }
305   # base case: atom
306   {
307     s:text, atom?:bool <- maybe-convert *x, atom:variant
308     break-unless atom?
309     buf <- append buf, s
310     return
311   }
312   # recursive case: pair
313   buf <- append buf, [< ]
314   first:&:cell <- first x
315   buf <- to-buffer first, buf
316   buf <- append buf, [ | ]
317   rest:&:cell <- rest x
318   buf <- to-buffer rest, buf
319   buf <- append buf, [ >]
322 scenario parse-single-letter-atom [
323   local-scope
324   s:text <- new [a]
325   x:&:cell <- parse s
326   s2:text, 10:bool/raw <- maybe-convert *x, atom:variant
327   11:@:char/raw <- copy *s2
328   memory-should-contain [
329     10 <- 1  # parse result is an atom
330     11:array:character <- [a]
331   ]
334 scenario parse-atom [
335   local-scope
336   s:text <- new [abc]
337   x:&:cell <- parse s
338   s2:text, 10:bool/raw <- maybe-convert *x, atom:variant
339   11:@:char/raw <- copy *s2
340   memory-should-contain [
341     10 <- 1  # parse result is an atom
342     11:array:character <- [abc]
343   ]
346 scenario parse-list-of-two-atoms [
347   local-scope
348   s:text <- new [(abc def)]
349   x:&:cell <- parse s
350   trace-should-contain [
351     app/parse: < abc | < def | <> > >
352   ]
353   10:bool/raw <- is-pair? x
354   x1:&:cell <- first x
355   x2:&:cell <- rest x
356   s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant
357   12:bool/raw <- is-pair? x2
358   x3:&:cell <- first x2
359   s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant
360   14:&:cell/raw <- rest x2
361   20:@:char/raw <- copy *s1
362   30:@:char/raw <- copy *s2
363   memory-should-contain [
364     10 <- 1  # parse result is a pair
365     11 <- 1  # result.first is an atom
366     12 <- 1  # result.rest is a pair
367     13 <- 1  # result.rest.first is an atom
368     14 <- 0  # result.rest.rest is nil
369     20:array:character <- [abc]  # result.first
370     30:array:character <- [def]  # result.rest.first
371   ]
374 scenario parse-list-with-extra-spaces [
375   local-scope
376   s:text <- new [ ( abc  def ) ]  # extra spaces
377   x:&:cell <- parse s
378   trace-should-contain [
379     app/parse: < abc | < def | <> > >
380   ]
381   10:bool/raw <- is-pair? x
382   x1:&:cell <- first x
383   x2:&:cell <- rest x
384   s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant
385   12:bool/raw <- is-pair? x2
386   x3:&:cell <- first x2
387   s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant
388   14:&:cell/raw <- rest x2
389   20:@:char/raw <- copy *s1
390   30:@:char/raw <- copy *s2
391   memory-should-contain [
392     10 <- 1  # parse result is a pair
393     11 <- 1  # result.first is an atom
394     12 <- 1  # result.rest is a pair
395     13 <- 1  # result.rest.first is an atom
396     14 <- 0  # result.rest.rest is nil
397     20:array:character <- [abc]  # result.first
398     30:array:character <- [def]  # result.rest.first
399   ]
402 scenario parse-list-of-more-than-two-atoms [
403   local-scope
404   s:text <- new [(abc def ghi)]
405   x:&:cell <- parse s
406   trace-should-contain [
407     app/parse: < abc | < def | < ghi | <> > > >
408   ]
409   10:bool/raw <- is-pair? x
410   x1:&:cell <- first x
411   x2:&:cell <- rest x
412   s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant
413   12:bool/raw <- is-pair? x2
414   x3:&:cell <- first x2
415   s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant
416   x4:&:cell <- rest x2
417   14:bool/raw <- is-pair? x4
418   x5:&:cell <- first x4
419   s3:text, 15:bool/raw <- maybe-convert *x5, atom:variant
420   16:&:cell/raw <- rest x4
421   20:@:char/raw <- copy *s1
422   30:@:char/raw <- copy *s2
423   40:@:char/raw <- copy *s3
424   memory-should-contain [
425     10 <- 1  # parse result is a pair
426     11 <- 1  # result.first is an atom
427     12 <- 1  # result.rest is a pair
428     13 <- 1  # result.rest.first is an atom
429     14 <- 1  # result.rest.rest is a pair
430     15 <- 1  # result.rest.rest.first is an atom
431     16 <- 0  # result.rest.rest.rest is nil
432     20:array:character <- [abc]  # result.first
433     30:array:character <- [def]  # result.rest.first
434     40:array:character <- [ghi]  # result.rest.rest
435   ]
438 scenario parse-nested-list [
439   local-scope
440   s:text <- new [((abc))]
441   x:&:cell <- parse s
442   trace-should-contain [
443     app/parse: < < abc | <> > | <> >
444   ]
445   10:bool/raw <- is-pair? x
446   x1:&:cell <- first x
447   11:bool/raw <- is-pair? x
448   x2:&:cell <- first x1
449   s1:text, 12:bool/raw <- maybe-convert *x2, atom:variant
450   13:&:cell/raw <- rest x1
451   14:&:cell/raw <- rest x
452   20:@:char/raw <- copy *s1
453   memory-should-contain [
454     10 <- 1  # parse result is a pair
455     11 <- 1  # result.first is a pair
456     12 <- 1  # result.first.first is an atom
457     13 <- 0  # result.first.rest is nil
458     14 <- 0  # result.rest is nil
459     20:array:character <- [abc]  # result.first.first
460   ]
463 scenario parse-nested-list-2 [
464   local-scope
465   s:text <- new [((abc) def)]
466   x:&:cell <- parse s
467   trace-should-contain [
468     app/parse: < < abc | <> > | < def | <> > >
469   ]
470   10:bool/raw <- is-pair? x
471   x1:&:cell <- first x
472   11:bool/raw <- is-pair? x
473   x2:&:cell <- first x1
474   s1:text, 12:bool/raw <- maybe-convert *x2, atom:variant
475   13:&:cell/raw <- rest x1
476   x3:&:cell <- rest x
477   x4:&:cell <- first x3
478   s2:text, 14:bool/raw <- maybe-convert *x4, atom:variant
479   15:&:cell/raw <- rest x3
480   20:@:char/raw <- copy *s1
481   30:@:char/raw <- copy *s2
482   memory-should-contain [
483     10 <- 1  # parse result is a pair
484     11 <- 1  # result.first is a pair
485     12 <- 1  # result.first.first is an atom
486     13 <- 0  # result.first.rest is nil
487     14 <- 1  # result.rest.first is an atom
488     15 <- 0  # result.rest.rest is nil
489     20:array:character <- [abc]  # result.first.first
490     30:array:character <- [def]  # result.rest.first
491   ]
494 # todo: uncomment these tests after we figure out how to continue tests after
495 # assertion failures
496 #? scenario parse-error [
497 #?   local-scope
498 #?   s:text <- new [(]
499 #? #?   hide-errors
500 #?   x:&:cell <- parse s
501 #? #?   show-errors
502 #?   trace-should-contain [
503 #?     error: unbalanced '(' in expression
504 #?   ]
505 #? ]
506 #? 
507 #? scenario parse-error-after-element [
508 #?   local-scope
509 #?   s:text <- new [(abc]
510 #? #?   hide-errors
511 #?   x:&:cell <- parse s
512 #? #?   show-errors
513 #?   trace-should-contain [
514 #?     error: unbalanced '(' in expression
515 #?   ]
516 #? ]
518 scenario parse-dotted-list-of-two-atoms [
519   local-scope
520   s:text <- new [(abc . def)]
521   x:&:cell <- parse s
522   trace-should-contain [
523     app/parse: < abc | def >
524   ]
525   10:bool/raw <- is-pair? x
526   x1:&:cell <- first x
527   x2:&:cell <- rest x
528   s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant
529   s2:text, 12:bool/raw <- maybe-convert *x2, atom:variant
530   20:@:char/raw <- copy *s1
531   30:@:char/raw <- copy *s2
532   memory-should-contain [
533     # parses to < abc | def >
534     10 <- 1  # parse result is a pair
535     11 <- 1  # result.first is an atom
536     12 <- 1  # result.rest is an atom
537     20:array:character <- [abc]  # result.first
538     30:array:character <- [def]  # result.rest
539   ]
542 scenario parse-dotted-list-of-more-than-two-atoms [
543   local-scope
544   s:text <- new [(abc def . ghi)]
545   x:&:cell <- parse s
546   trace-should-contain [
547     app/parse: < abc | < def | ghi > >
548   ]
549   10:bool/raw <- is-pair? x
550   x1:&:cell <- first x
551   x2:&:cell <- rest x
552   s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant
553   12:bool/raw <- is-pair? x2
554   x3:&:cell <- first x2
555   s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant
556   x4:&:cell <- rest x2
557   s3:text, 14:bool/raw <- maybe-convert *x4, atom:variant
558   20:@:char/raw <- copy *s1
559   30:@:char/raw <- copy *s2
560   40:@:char/raw <- copy *s3
561   memory-should-contain [
562     10 <- 1  # parse result is a pair
563     11 <- 1  # result.first is an atom
564     12 <- 1  # result.rest is a pair
565     13 <- 1  # result.rest.first is an atom
566     14 <- 1  # result.rest.rest is an atom
567     20:array:character <- [abc]  # result.first
568     30:array:character <- [def]  # result.rest.first
569     40:array:character <- [ghi]  # result.rest.rest
570   ]
573 ## convert tree of cells to Mu text
575 def to-mu in:&:cell -> out:text [
576   local-scope
577   load-inputs
578   buf:&:buffer:char <- new-buffer 30
579   buf <- to-mu in, buf
580   out <- buffer-to-array buf
583 def to-mu in:&:cell, buf:&:buffer:char -> buf:&:buffer:char, result-name:text [
584   local-scope
585   load-inputs
586   # null cell? no change.
587   # pair with all atoms? gensym a new variable
588   # pair containing other pairs? recurse
589   result-name <- copy null