Change quasiquote algorithm
[jackhill/mal.git] / impls / factor / step7_quote / step7_quote.factor
CommitLineData
199b1ce7
JB
1! Copyright (C) 2015 Jordan Lewis.
2! See http://factorcode.org/license.txt for BSD license.
3USING: accessors arrays assocs combinators
4combinators.short-circuit command-line continuations fry
8e2d4a4c
JM
5grouping hashtables io kernel lists locals lib.core lib.env
6lib.printer lib.reader lib.types math namespaces quotations
fbfe6784 7readline sequences splitting vectors ;
199b1ce7
JB
8IN: step7_quote
9
10SYMBOL: repl-env
11
12DEFER: EVAL
13
fbfe6784
NB
14GENERIC# eval-ast 1 ( ast env -- ast )
15M: malsymbol eval-ast env-get ;
16M: sequence eval-ast '[ _ EVAL ] map ;
17M: assoc eval-ast '[ _ EVAL ] assoc-map ;
18M: object eval-ast drop ;
199b1ce7
JB
19
20:: eval-def! ( key value env -- maltype )
21 value env EVAL [ key env env-set ] keep ;
22
23: eval-let* ( bindings body env -- maltype env )
24 [ swap 2 group ] [ new-env ] bi* [
25 dup '[ first2 _ EVAL swap _ env-set ] each
26 ] keep ;
27
28:: eval-do ( exprs env -- lastform env/f )
29 exprs [
30 { } f
31 ] [
32 unclip-last [ env eval-ast drop ] dip env
33 ] if-empty ;
34
35:: eval-if ( params env -- maltype env/f )
36 params first env EVAL { f +nil+ } index not [
37 params second env
38 ] [
39 params length 2 > [ params third env ] [ nil f ] if
40 ] if ;
41
42:: eval-fn* ( params env -- maltype )
43 env params first [ name>> ] map params second <malfn> ;
44
45: args-split ( bindlist -- bindlist restbinding/f )
46 { "&" } split1 ?first ;
47
48: make-bindings ( args bindlist restbinding/f -- bindingshash )
49 swapd [ over length cut [ zip ] dip ] dip
50 [ swap 2array suffix ] [ drop ] if* >hashtable ;
51
fbfe6784 52GENERIC# apply 0 ( args fn -- maltype newenv/f )
199b1ce7
JB
53
54M: malfn apply
55 [ exprs>> nip ]
56 [ env>> nip ]
57 [ binds>> args-split make-bindings ] 2tri <malenv> ;
58
59M: callable apply call( x -- y ) f ;
60
fbfe6784 61DEFER: quasiquote
199b1ce7 62
fbfe6784
NB
63: qq_loop ( elt acc -- maltype )
64 [
65 { [ dup array? ]
66 [ dup length 2 = ]
67 [ "splice-unquote" over first symeq? ] } 0&& [
68 second "concat"
69 ] [
70 quasiquote "cons"
71 ] if
72 <malsymbol> swap
73 ]
74 dip 3array ;
75
76: qq_foldr ( xs -- maltype )
77 dup length 0 = [
78 drop { }
79 ] [
80 unclip swap qq_foldr qq_loop
81 ] if ;
82
83GENERIC: quasiquote ( maltype -- maltype )
84M: array quasiquote
85 { [ dup length 2 = ] [ "unquote" over first symeq? ] } 0&&
86 [ second ] [ qq_foldr ] if ;
87M: vector quasiquote qq_foldr "vec" <malsymbol> swap 2array ;
88M: malsymbol quasiquote "quote" <malsymbol> swap 2array ;
89M: assoc quasiquote "quote" <malsymbol> swap 2array ;
90M: object quasiquote ;
199b1ce7
JB
91
92: READ ( str -- maltype ) read-str ;
93
94: EVAL ( maltype env -- maltype )
3339bb6a 95 over { [ array? ] [ empty? not ] } 1&& [
199b1ce7
JB
96 over first dup malsymbol? [ name>> ] when {
97 { "def!" [ [ rest first2 ] dip eval-def! f ] }
fbfe6784 98 { "let*" [ [ rest first2 ] dip eval-let* ] }
199b1ce7
JB
99 { "do" [ [ rest ] dip eval-do ] }
100 { "if" [ [ rest ] dip eval-if ] }
101 { "fn*" [ [ rest ] dip eval-fn* f ] }
102 { "quote" [ drop second f ] }
fbfe6784 103 { "quasiquoteexpand" [ drop second quasiquote f ] }
199b1ce7
JB
104 { "quasiquote" [ [ second quasiquote ] dip ] }
105 [ drop '[ _ EVAL ] map unclip apply ]
106 } case [ EVAL ] when*
107 ] [
108 eval-ast
109 ] if ;
110
26afafb2
DM
111[ apply [ EVAL ] when* ] mal-apply set-global
112
199b1ce7
JB
113: PRINT ( maltype -- str ) pr-str ;
114
115: REP ( str -- str )
dd7a4f55
JM
116 [
117 READ repl-env get EVAL PRINT
118 ] [
119 nip pr-str "Error: " swap append
120 ] recover ;
199b1ce7
JB
121
122: REPL ( -- )
123 [
124 "user> " readline [
125 [ REP print flush ] unless-empty
126 ] keep
127 ] loop ;
128
b85c07fd
DM
129: main ( -- )
130 command-line get
131 [ REPL ]
132 [ first "(load-file \"" "\")" surround REP drop ]
133 if-empty ;
134
199b1ce7
JB
135f ns clone
136[ first repl-env get EVAL ] "eval" pick set-at
b85c07fd 137command-line get dup empty? [ rest ] unless "*ARGV*" pick set-at
199b1ce7
JB
138<malenv> repl-env set-global
139
140"
141(def! not (fn* (a) (if a false true)))
e6d41de4 142(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))
199b1ce7
JB
143" string-lines harvest [ REP drop ] each
144
b85c07fd 145MAIN: main