tests: make throw of non-strings optional/soft.
[jackhill/mal.git] / vb / step6_file.vb
1 Imports System
2 Imports System.IO
3 Imports System.Collections.Generic
4 Imports Mal
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
14
15 Namespace Mal
16 Class step6_file
17 ' read
18 Shared Function READ(str As String) As MalVal
19 Return reader.read_str(str)
20 End Function
21
22 ' eval
23 Shared Function eval_ast(ast As MalVal, env As MalEnv) As MalVal
24 If TypeOf ast Is MalSymbol Then
25 return env.do_get(DirectCast(ast, MalSymbol))
26 Else If TypeOf ast Is MalList Then
27 Dim old_lst As MalList = DirectCast(ast, MalList)
28 Dim new_lst As MalList
29 If ast.list_Q() Then
30 new_lst = New MalList
31 Else
32 new_lst = DirectCast(New MalVector, MalList)
33 End If
34 Dim mv As MalVal
35 For Each mv in old_lst.getValue()
36 new_lst.conj_BANG(EVAL(mv, env))
37 Next
38 return new_lst
39 Else If TypeOf ast Is MalHashMap Then
40 Dim new_dict As New Dictionary(Of String, MalVal)
41 Dim entry As KeyValuePair(Of String, MalVal)
42 For Each entry in DirectCast(ast,MalHashMap).getValue()
43 new_dict.Add(entry.Key, EVAL(DirectCast(entry.Value,MalVal), env))
44 Next
45 return New MalHashMap(new_dict)
46 Else
47 return ast
48 End If
49 return ast
50 End Function
51
52 ' TODO: move to types.vb when it is ported
53 Class FClosure
54 Public ast As MalVal
55 Public params As MalList
56 Public env As MalEnv
57 Function fn(args as MalList) As MalVal
58 return EVAL(ast, new MalEnv(env, params, args))
59 End Function
60 End Class
61
62 Shared Function EVAL(orig_ast As MalVal, env As MalEnv) As MalVal
63 Do
64
65 'Console.WriteLine("EVAL: {0}", printer._pr_str(orig_ast, true))
66 If not orig_ast.list_Q() Then
67 return eval_ast(orig_ast, env)
68 End If
69
70 ' apply list
71 Dim ast As MalList = DirectCast(orig_ast, MalList)
72 If ast.size() = 0 Then
73 return ast
74 End If
75 Dim a0 As MalVal = ast(0)
76 Dim a0sym As String
77 If TypeOf a0 is MalSymbol Then
78 a0sym = DirectCast(a0,MalSymbol).getName()
79 Else
80 a0sym = "__<*fn*>__"
81 End If
82
83 Select a0sym
84 Case "def!"
85 Dim a1 As MalVal = ast(1)
86 Dim a2 As MalVal = ast(2)
87 Dim res As MalVal = EVAL(a2, env)
88 env.do_set(DirectCast(a1,MalSymbol), res)
89 return res
90 Case "let*"
91 Dim a1 As MalVal = ast(1)
92 Dim a2 As MalVal = ast(2)
93 Dim key As MalSymbol
94 Dim val as MalVal
95 Dim let_env As new MalEnv(env)
96 For i As Integer = 0 To (DirectCast(a1,MalList)).size()-1 Step 2
97 key = DirectCast(DirectCast(a1,MalList)(i),MalSymbol)
98 val = DirectCast(a1,MalList)(i+1)
99 let_env.do_set(key, EVAL(val, let_env))
100 Next
101 orig_ast = a2
102 env = let_env
103 Case "do"
104 eval_ast(ast.slice(1, ast.size()-1), env)
105 orig_ast = ast(ast.size()-1)
106 Case "if"
107 Dim a1 As MalVal = ast(1)
108 Dim cond As MalVal = EVAL(a1, env)
109 If cond Is Mal.types.Nil or cond Is Mal.types.MalFalse Then
110 ' eval false slot form
111 If ast.size() > 3 Then
112 orig_ast = ast(3)
113 Else
114 return Mal.types.Nil
115 End If
116 Else
117 ' eval true slot form
118 orig_ast = ast(2)
119
120 End If
121 Case "fn*"
122 Dim fc As New FClosure()
123 fc.ast = ast(2)
124 fc.params = DirectCast(ast(1),MalLIst)
125 fc.env = env
126 Dim f As Func(Of MalList, MalVal) = AddressOf fc.fn
127 Dim mf As new MalFunc(ast(2), env,
128 DirectCast(ast(1),MalList), f)
129 return DirectCast(mf,MalVal)
130 Case Else
131 Dim el As MalList = DirectCast(eval_ast(ast, env), MalList)
132 Dim f As MalFunc = DirectCast(el(0), MalFunc)
133 Dim fnast As MalVal = f.getAst()
134 If not fnast Is Nothing
135 orig_ast = fnast
136 env = f.genEnv(el.rest())
137 Else
138 Return f.apply(el.rest())
139 End If
140 End Select
141
142 Loop While True
143 End Function
144
145 ' print
146 Shared Function PRINT(exp As MalVal) As String
147 return printer._pr_str(exp, TRUE)
148 End Function
149
150 ' repl
151 Shared repl_env As MalEnv
152
153 Shared Function REP(str As String) As String
154 Return PRINT(EVAL(READ(str), repl_env))
155 End Function
156
157 Shared Function do_eval(args As MalList) As MalVal
158 Return EVAL(args(0), repl_env)
159 End Function
160
161 Shared Function Main As Integer
162 Dim args As String() = Environment.GetCommandLineArgs()
163
164 repl_env = New MalEnv(Nothing)
165
166 ' core.vb: defined using VB.NET
167 For Each entry As KeyValuePair(Of String,MalVal) In core.ns()
168 repl_env.do_set(new MalSymbol(entry.Key), entry.Value)
169 Next
170 repl_env.do_set(new MalSymbol("eval"), new MalFunc(AddressOf do_eval))
171 Dim fileIdx As Integer = 1
172 If args.Length > 1 AndAlso args(1) = "--raw" Then
173 Mal.readline.SetMode(Mal.readline.Modes.Raw)
174 fileIdx = 2
175 End If
176 Dim argv As New MalList()
177 For i As Integer = fileIdx+1 To args.Length-1
178 argv.conj_BANG(new MalString(args(i)))
179 Next
180 repl_env.do_set(new MalSymbol("*ARGV*"), argv)
181
182 ' core.mal: defined using the language itself
183 REP("(def! not (fn* (a) (if a false true)))")
184 REP("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))")
185
186 If args.Length > fileIdx Then
187 REP("(load-file """ & args(fileIdx) & """)")
188 return 0
189 End If
190
191 ' repl loop
192 Dim line As String
193 Do
194 Try
195 line = Mal.readline.Readline("user> ")
196 If line is Nothing Then
197 Exit Do
198 End If
199 If line = "" Then
200 Continue Do
201 End If
202 Catch e As IOException
203 Console.WriteLine("IOException: " & e.Message)
204 End Try
205 Try
206 Console.WriteLine(REP(line))
207 Catch e as Exception
208 Console.WriteLine("Error: " & e.Message)
209 Console.WriteLine(e.StackTrace)
210 Continue Do
211 End Try
212 Loop While True
213 End function
214 End Class
215 End Namespace