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