1 source mal_readline.tcl
12 proc starts_with
{lst sym
} {
13 if {[llength $lst] != 2} {
16 lassign
[lindex $lst 0] a0
17 return [symbol_q
$a0] && [expr {[obj_val
$a0] == $sym}]
19 proc qq_loop
{elt acc
} {
20 if {[list_q
$elt] && [starts_with
[obj_val
$elt] "splice-unquote"]} {
21 return [list_new
[list [symbol_new
"concat"] [lindex [obj_val
$elt] 1] $acc]]
23 return [list_new
[list [symbol_new
"cons"] [quasiquote
$elt] $acc]]
28 for {set i
[expr {[llength $xs] - 1}]} {0 <= $i} {incr i
-1} {
29 set acc
[qq_loop
[lindex $xs $i] $acc]
34 proc quasiquote
{ast
} {
35 switch [obj_type
$ast] {
37 return [list_new
[list [symbol_new
"quote"] $ast]]
40 return [list_new
[list [symbol_new
"quote"] $ast]]
43 return [list_new
[list [symbol_new
"vec"] [qq_foldr
[obj_val
$ast]]]]
46 if {[starts_with
[obj_val
$ast] "unquote"]} {
47 return [lindex [obj_val
$ast] 1]
49 return [qq_foldr
[obj_val
$ast]]
58 proc is_macro_call
{ast env
} {
62 set a0
[lindex [obj_val
$ast] 0]
63 if {$a0 == "" ||
![symbol_q
$a0]} {
66 set varname
[obj_val
$a0]
67 set foundenv
[$env find
$varname]
71 macro_q
[$env get
$varname]
74 proc macroexpand
{ast env
} {
75 while {[is_macro_call
$ast $env]} {
76 set a0
[mal_first
[list $ast]]
77 set macro_name
[obj_val
$a0]
78 set macro_obj
[$env get
$macro_name]
79 set macro_args
[obj_val
[mal_rest
[list $ast]]]
81 set funcdict
[obj_val
$macro_obj]
82 set body
[dict get
$funcdict body
]
83 set env
[dict get
$funcdict env
]
84 set binds
[dict get
$funcdict binds
]
85 set funcenv
[Env new
$env $binds $macro_args]
86 set ast
[EVAL
$body $funcenv]
91 proc eval_ast
{ast env
} {
92 switch [obj_type
$ast] {
94 set varname
[obj_val
$ast]
95 return [$env get
$varname]
99 foreach element
[obj_val
$ast] {
100 lappend res
[EVAL
$element $env]
102 return [list_new
$res]
106 foreach element
[obj_val
$ast] {
107 lappend res
[EVAL
$element $env]
109 return [vector_new
$res]
112 set res
[dict create
]
113 dict
for {k v
} [obj_val
$ast] {
114 dict
set res
$k [EVAL
$v $env]
116 return [hashmap_new
$res]
118 default { return $ast }
122 proc EVAL
{ast env
} {
124 if {![list_q
$ast]} {
125 return [eval_ast
$ast $env]
128 set ast
[macroexpand
$ast $env]
129 if {![list_q
$ast]} {
130 return [eval_ast
$ast $env]
133 lassign
[obj_val
$ast] a0 a1 a2 a3
137 switch [obj_val
$a0] {
139 set varname
[obj_val
$a1]
140 set value
[EVAL
$a2 $env]
141 return [$env set $varname $value]
144 set letenv
[Env new
$env]
145 set bindings_list
[obj_val
$a1]
146 foreach {varnameobj varvalobj
} $bindings_list {
147 $letenv set [obj_val
$varnameobj] [EVAL
$varvalobj $letenv]
157 return [quasiquote
$a1]
160 set ast
[quasiquote
$a1]
163 set varname
[obj_val
$a1]
164 set value
[EVAL
$a2 $env]
165 return [$env set $varname [macro_new
$value]]
168 return [macroexpand
$a1 $env]
171 set el
[list_new
[lrange [obj_val
$ast] 1 end-1
]]
173 set ast
[lindex [obj_val
$ast] end
]
177 set condval
[EVAL
$a1 $env]
178 if {[false_q
$condval] ||
[nil_q
$condval]} {
190 foreach v
[obj_val
$a1] {
191 lappend binds
[obj_val
$v]
193 return [function_new
$a2 $env $binds]
196 set lst_obj
[eval_ast
$ast $env]
197 set lst
[obj_val
$lst_obj]
198 set f
[lindex $lst 0]
199 set call_args
[lrange $lst 1 end
]
200 switch [obj_type
$f] {
203 set ast
[dict get
$fn body
]
204 set env
[Env new
[dict get
$fn env
] [dict get
$fn binds
] $call_args]
208 set body
[concat [list [obj_val
$f]] {$a}]
209 set lambda
[list {a
} $body]
210 return [apply
$lambda $call_args]
213 error "Not a function"
226 PRINT
[EVAL
[READ
$str] $env]
230 EVAL
[READ
$str] $env
235 EVAL
[lindex $a 0] $repl_env
238 set repl_env
[Env new
]
239 dict
for {k v
} $core_ns {
243 $repl_env set "eval" [nativefunction_new mal_eval
]
246 foreach arg
[lrange $argv 1 end
] {
247 lappend argv_list
[string_new
$arg]
249 $repl_env set "*ARGV*" [list_new
$argv_list]
251 # core.mal: defined using the language itself
252 RE
"(def! not (fn* (a) (if a false true)))" $repl_env
253 RE
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))" $repl_env
254 RE
"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" $repl_env
256 fconfigure stdout
-translation binary
259 if { [array names env DEBUG
] != "" && $env(DEBUG
) != "0" } {
264 REP
"(load-file \"[lindex $argv 0]\")" $repl_env
270 set res
[_readline
"user> "]
271 if {[lindex $res 0] == "EOF"} {
274 set line
[lindex $res 1]
278 if { [catch { puts [REP
$line $repl_env] } exception
] } {
279 puts "Error: $exception"