3 Imports System
.Collections
.Generic
5 Imports MalVal
= Mal
.types
.MalVal
6 Imports MalInt
= Mal
.types
.MalInt
7 Imports MalString
= Mal
.types
.MalString
8 Imports MalSymbol
= Mal
.types
.MalSymbol
9 Imports MalList
= Mal
.types
.MalList
10 Imports MalVector
= Mal
.types
.MalVector
11 Imports MalHashMap
= Mal
.types
.MalHashMap
12 Imports MalFunc
= Mal
.types
.MalFunc
13 Imports MalEnv
= Mal
.env
.Env
18 Shared
Function READ(str
As String) As MalVal
19 Return reader
.read_str(str
)
23 Shared
Function is_pair(x
As MalVal
) As Boolean
24 return TypeOf x Is MalList AndAlso _
25 DirectCast(x
,MalList
).size() > 0
28 Shared
Function quasiquote(ast
As MalVal
) As MalVal
29 If not is_pair(ast
) Then
30 return New MalList(New MalSymbol("quote"), ast
)
32 Dim a0
As MalVal
= DirectCast(ast
,MalList
)(0)
33 If TypeOf a0 Is MalSymbol AndAlso _
34 DirectCast(a0
,MalSymbol
).getName() = "unquote" Then
35 return DirectCast(ast
,MalList
)(1)
36 Else If is_pair(a0
) Then
37 Dim a00
As MalVal
= DirectCast(a0
,MalList
)(0)
38 If TypeOf a00 is MalSymbol AndAlso _
39 DirectCast(a00
,MalSymbol
).getName() = "splice-unquote" Then
40 return New MalList(New MalSymbol("concat"),
41 DirectCast(a0
,MalList
)(1),
42 quasiquote(DirectCast(ast
,MalList
).rest()))
45 return New MalList(New MalSymbol("cons"),
47 quasiquote(DirectCast(ast
,MalList
).rest()))
51 Shared
Function is_macro_call(ast
As MalVal
, env
As MalEnv
) As Boolean
52 If TypeOf ast Is MalList
Then
53 Dim a0
As MalVal
= DirectCast(ast
,MalList
)(0)
54 If TypeOf a0 Is MalSymbol AndAlso _
55 env
.find(DirectCast(a0
,MalSymbol
).getName()) IsNot
Nothing Then
56 Dim mac
As MalVal
= env
.do_get(DirectCast(a0
,MalSymbol
).getName())
57 If TypeOf mac Is MalFunc AndAlso _
58 DirectCast(mac
,MalFunc
).isMacro() Then
66 Shared
Function macroexpand(ast
As MalVal
, env
As MalEnv
) As MalVal
67 While is_macro_call(ast
, env
)
68 Dim a0
As MalSymbol
= DirectCast(DirectCast(ast
,MalList
)(0),MalSymbol
)
69 Dim mac
As MalFunc
= DirectCast(env
.do_get(a0
.getName()),MalFunc
)
70 ast
= mac
.apply(DirectCast(ast
,MalList
).rest())
75 Shared
Function eval_ast(ast
As MalVal
, env
As MalEnv
) As MalVal
76 If TypeOf ast Is MalSymbol
Then
77 Dim sym
As MalSymbol
= DirectCast(ast
, MalSymbol
)
78 return env
.do_get(sym
.getName())
79 Else If TypeOf ast Is MalList
Then
80 Dim old_lst
As MalList
= DirectCast(ast
, MalList
)
81 Dim new_lst
As MalList
85 new_lst
= DirectCast(New MalVector
, MalList
)
88 For Each mv
in old_lst
.getValue()
89 new_lst
.conj_BANG(EVAL(mv
, env
))
92 Else If TypeOf ast Is MalHashMap
Then
93 Dim new_dict
As New Dictionary(Of
String, MalVal
)
94 Dim entry
As KeyValuePair(Of
String, MalVal
)
95 For Each entry
in DirectCast(ast
,MalHashMap
).getValue()
96 new_dict
.Add(entry
.Key
, EVAL(DirectCast(entry
.Value
,MalVal
), env
))
98 return New MalHashMap(new_dict
)
105 ' TODO: move to types.vb when it is ported
108 Public params
As MalList
110 Function fn(args
as MalList
) As MalVal
111 return EVAL(ast
, new MalEnv(env
, params
, args
))
115 Shared
Function EVAL(orig_ast
As MalVal
, env
As MalEnv
) As MalVal
118 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
119 If not orig_ast
.list_Q() Then
120 return eval_ast(orig_ast
, env
)
124 Dim expanded
As MalVal
= macroexpand(orig_ast
, env
)
125 if not expanded
.list_Q() Then
128 Dim ast
As MalList
= DirectCast(expanded
, MalList
)
130 If ast
.size() = 0 Then
133 Dim a0
As MalVal
= ast(0)
135 If TypeOf a0 is MalSymbol
Then
136 a0sym
= DirectCast(a0
,MalSymbol
).getName()
143 Dim a1
As MalVal
= ast(1)
144 Dim a2
As MalVal
= ast(2)
145 Dim res
As MalVal
= EVAL(a2
, env
)
146 env
.do_set(DirectCast(a1
,MalSymbol
).getName(), res
)
149 Dim a1
As MalVal
= ast(1)
150 Dim a2
As MalVal
= ast(2)
153 Dim let_env
As new MalEnv(env
)
154 For i
As Integer = 0 To (DirectCast(a1
,MalList
)).size()-1 Step
2
155 key
= DirectCast(DirectCast(a1
,MalList
)(i
),MalSymbol
)
156 val
= DirectCast(a1
,MalList
)(i
+1)
157 let_env
.do_set(key
.getName(), EVAL(val
, let_env
))
164 orig_ast
= quasiquote(ast(1))
166 Dim a1
As MalVal
= ast(1)
167 Dim a2
As MalVal
= ast(2)
168 Dim res
As MalVal
= EVAL(a2
, env
)
169 DirectCast(res
,MalFunc
).setMacro()
170 env
.do_set(DirectCast(a1
,MalSymbol
).getName(), res
)
173 Dim a1
As MalVal
= ast(1)
174 return macroexpand(a1
, env
)
177 return EVAL(ast(1), env
)
179 If ast
.size() > 2 Then
181 Dim a2
As MalVal
= ast(2)
182 Dim a20
As MalVal
= DirectCast(a2
,MalList
)(0)
183 If DirectCast(a20
,MalSymbol
).getName() = "catch*" Then
184 If TypeOf e Is Mal
.types
.MalException
Then
185 exc
= DirectCast(e
,Mal
.types
.MalException
).getValue()
187 exc
= New MalString(e
.StackTrace
)
190 DirectCast(a2
,MalList
)(2),
192 DirectCast(a2
,MalList
).slice(1,2),
199 eval_ast(ast
.slice(1, ast
.size()-1), env
)
200 orig_ast
= ast(ast
.size()-1)
202 Dim a1
As MalVal
= ast(1)
203 Dim cond
As MalVal
= EVAL(a1
, env
)
204 If cond Is Mal
.types
.Nil
or cond Is Mal
.types
.MalFalse
Then
205 ' eval false slot form
206 If ast
.size() > 3 Then
212 ' eval true slot form
217 Dim fc
As New FClosure()
219 fc
.params
= DirectCast(ast(1),MalLIst
)
221 Dim f
As Func(Of MalList
, MalVal
) = AddressOf fc
.fn
222 Dim mf
As new MalFunc(ast(2), env
,
223 DirectCast(ast(1),MalList
), f
)
224 return DirectCast(mf
,MalVal
)
226 Dim el
As MalList
= DirectCast(eval_ast(ast
, env
), MalList
)
227 Dim f
As MalFunc
= DirectCast(el(0), MalFunc
)
228 Dim fnast
As MalVal
= f
.getAst()
229 If not fnast Is
Nothing
231 env
= f
.genEnv(el
.rest())
233 Return f
.apply(el
.rest())
241 Shared
Function PRINT(exp
As MalVal
) As String
242 return printer
._pr_str(exp
, TRUE)
246 Shared repl_env
As MalEnv
248 Shared
Function REP(str
As String) As String
249 Return PRINT(EVAL(READ(str
), repl_env
))
252 Shared
Function do_eval(args
As MalList
) As MalVal
253 Return EVAL(args(0), repl_env
)
256 Shared
Function Main
As Integer
257 Dim args
As String() = Environment
.GetCommandLineArgs()
259 repl_env
= New MalEnv(Nothing)
261 ' core.vb: defined using VB.NET
262 For Each entry
As KeyValuePair(Of
String,MalVal
) In core
.ns()
263 repl_env
.do_set(entry
.Key
, entry
.Value
)
265 repl_env
.do_set("eval", new MalFunc(AddressOf do_eval
))
266 Dim fileIdx
As Integer = 1
267 If args
.Length
> 1 AndAlso
args(1) = "--raw" Then
268 Mal
.readline
.SetMode(Mal
.readline
.Modes
.Raw
)
271 Dim argv
As New MalList()
272 For i
As Integer = fileIdx
+1 To args
.Length
-1
273 argv
.conj_BANG(new MalString(args(i
)))
275 repl_env
.do_set("*ARGV*", argv
)
277 ' core.mal: defined using the language itself
278 REP("(def! *host-language* ""VB.NET"")")
279 REP("(def! not (fn* (a) (if a false true)))")
280 REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
281 REP("(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)))))))")
282 REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
284 If args
.Length
> fileIdx
Then
285 REP("(load-file """ & args(fileIdx
) & """)")
291 REP("(println (str ""Mal ["" *host-language* ""]""))")
294 line
= Mal
.readline
.Readline("user> ")
295 If line is
Nothing Then
301 Catch e
As IOException
302 Console
.WriteLine("IOException: " & e
.Message
)
305 Console
.WriteLine(REP(line
))
306 Catch e
As Mal
.types
.MalException
307 Console
.WriteLine("Error: " & _
308 printer
._pr_str(e
.getValue(), False))
311 Console
.WriteLine("Error: " & e
.Message
)
312 Console
.WriteLine(e
.StackTrace
)