Commit | Line | Data |
---|---|---|
ee7cd585 JM |
1 | Imports System |
2 | Imports System.Collections | |
3 | Imports System.Collections.Generic | |
4 | Imports System.Text.RegularExpressions | |
5 | Imports Mal | |
6 | Imports MalVal = Mal.types.MalVal | |
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 MalThrowable = Mal.types.MalThrowable | |
12 | Imports MalContinue = Mal.types.MalContinue | |
13 | ||
14 | Namespace Mal | |
15 | Public Class reader | |
16 | Public Class ParseError | |
17 | Inherits MalThrowable | |
18 | Public Sub New(msg As String) | |
19 | MyBase.New(msg) | |
20 | End Sub | |
21 | End Class | |
22 | ||
23 | Public Class Reader | |
24 | Private tokens As New List(Of String) | |
25 | Private position As Int32 = 0 | |
26 | Sub New(t As List(Of String)) | |
27 | tokens = t | |
28 | position = 0 | |
29 | End Sub | |
30 | ||
31 | Public Function peek() As String | |
32 | If position >= tokens.Count Then | |
33 | return Nothing | |
34 | Else | |
35 | return tokens(position) | |
36 | End If | |
37 | End Function | |
38 | ||
39 | Public Function get_next() As String | |
40 | If position >= tokens.Count Then | |
41 | return Nothing | |
42 | Else | |
43 | position += 1 | |
44 | return tokens(position-1) | |
45 | End If | |
46 | End Function | |
47 | End Class | |
48 | ||
49 | Shared Function tokenize(str As String) As List(Of String) | |
50 | Dim tokens As New List(Of String) | |
4aa0ebdf | 51 | Dim pattern As String = "[\s ,]*(~@|[\[\]{}()'`~@]|""(?:[\\].|[^\\""])*""?|;.*|[^\s \[\]{}()'""`~@,;]*)" |
ee7cd585 JM |
52 | Dim regex As New Regex(pattern) |
53 | For Each match As Match In regex.Matches(str) | |
54 | Dim token As String = match.Groups(1).Value | |
55 | If Not token Is Nothing _ | |
56 | AndAlso Not token = "" _ | |
57 | AndAlso Not token(0) = ";" Then | |
58 | 'Console.WriteLine("match: ^" & match.Groups[1] & "$") | |
59 | tokens.Add(token) | |
60 | End If | |
61 | Next | |
62 | return tokens | |
63 | End Function | |
64 | ||
65 | Shared Function read_atom(rdr As Reader) As MalVal | |
66 | Dim token As String = rdr.get_next() | |
c1982f1a | 67 | Dim pattern As String = "(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|(^""(?:[\\].|[^\\""])*""$)|^("".*)|^:(.*)|(^[^""]*$)" |
ee7cd585 JM |
68 | Dim regex As Regex = New Regex(pattern) |
69 | Dim match As Match = regex.Match(token) | |
70 | 'Console.WriteLine("token: ^" + token + "$") | |
71 | If not match.Success Then | |
72 | throw New ParseError("unrecognized token '" & token & "'") | |
73 | End If | |
74 | If match.Groups(1).Value <> String.Empty Then | |
75 | return New Mal.types.MalInt(Integer.Parse(match.Groups(1).Value)) | |
76 | Else If match.Groups(3).Value <> String.Empty Then | |
77 | return Mal.types.Nil | |
78 | Else If match.Groups(4).Value <> String.Empty Then | |
79 | return Mal.types.MalTrue | |
80 | Else If match.Groups(5).Value <> String.Empty Then | |
81 | return Mal.types.MalFalse | |
82 | Else If match.Groups(6).Value <> String.Empty Then | |
83 | Dim str As String = match.Groups(6).Value | |
84 | return New Mal.types.MalString( | |
85 | str.Substring(1, str.Length-2) _ | |
42aecee6 JM |
86 | .Replace("\\", ChrW(&H029e)) _ |
87 | .Replace("\""", """") _ | |
88 | .Replace("\n", Environment.NewLine) _ | |
89 | .Replace(ChrW(&H029e), "\")) | |
ee7cd585 | 90 | Else If match.Groups(7).Value <> String.Empty Then |
c1982f1a | 91 | throw New ParseError("expected '""', got EOF") |
b8ee29b2 | 92 | Else If match.Groups(8).Value <> String.Empty Then |
c1982f1a BH |
93 | return New Mal.types.MalString(ChrW(&H029e) & match.Groups(8).Value) |
94 | Else If match.Groups(9).Value <> String.Empty Then | |
95 | return New Mal.types.MalSymbol(match.Groups(9).Value) | |
ee7cd585 JM |
96 | Else |
97 | throw New ParseError("unrecognized '" & match.Groups(0).Value & "'") | |
98 | End If | |
99 | End Function | |
100 | ||
101 | Shared Function read_list(rdr As Reader, lst As MalList, | |
102 | start As String, last As String) As MalVal | |
103 | Dim token As String = rdr.get_next() | |
104 | If token(0) <> start Then | |
105 | throw New ParseError("expected '" & start & "'") | |
106 | End If | |
107 | ||
108 | token = rdr.peek() | |
109 | While token IsNot Nothing AndAlso token(0) <> last | |
110 | lst.conj_BANG(read_form(rdr)) | |
111 | token = rdr.peek() | |
112 | End While | |
113 | ||
114 | If token Is Nothing Then | |
115 | throw New ParseError("expected '" & last & "', got EOF") | |
116 | End If | |
117 | rdr.get_next() | |
118 | ||
119 | return lst | |
120 | End Function | |
121 | ||
122 | Shared Function read_hash_map(rdr As Reader) As MalVal | |
123 | Dim lst As MalList = DirectCast(read_list(rdr, new MalList(), | |
124 | "{", "}"),MalList) | |
125 | return New MalHashMap(lst) | |
126 | End Function | |
127 | ||
128 | ||
129 | Shared Function read_form(rdr As Reader) As MalVal | |
130 | Dim token As String = rdr.peek() | |
131 | If token Is Nothing Then | |
132 | throw New MalContinue() | |
133 | End If | |
134 | Dim form As MalVal = Nothing | |
135 | ||
136 | Select token | |
137 | Case "'" | |
138 | rdr.get_next() | |
139 | return New MalList(New MalSymbol("quote"), | |
140 | read_form(rdr)) | |
141 | Case "`" | |
142 | rdr.get_next() | |
143 | return New MalList(New MalSymbol("quasiquote"), | |
144 | read_form(rdr)) | |
145 | Case "~" | |
146 | rdr.get_next() | |
147 | return New MalList(New MalSymbol("unquote"), | |
148 | read_form(rdr)) | |
149 | Case "~@" | |
150 | rdr.get_next() | |
151 | return new MalList(New MalSymbol("splice-unquote"), | |
152 | read_form(rdr)) | |
153 | Case "^" | |
154 | rdr.get_next() | |
155 | Dim meta As MalVal = read_form(rdr) | |
156 | return new MalList(New MalSymbol("with-meta"), | |
157 | read_form(rdr), | |
158 | meta) | |
159 | Case "@" | |
160 | rdr.get_next() | |
161 | return new MalList(New MalSymbol("deref"), | |
162 | read_form(rdr)) | |
163 | ||
164 | Case "(" | |
165 | form = read_list(rdr, New MalList(), "(" , ")") | |
166 | Case ")" | |
167 | throw New ParseError("unexpected ')'") | |
168 | Case "[" | |
169 | form = read_list(rdr, New MalVector(), "[" , "]") | |
170 | Case "]" | |
171 | throw New ParseError("unexpected ']'") | |
172 | Case "{" | |
173 | form = read_hash_map(rdr) | |
174 | Case "}" | |
175 | throw New ParseError("unexpected '}'") | |
176 | Case Else | |
177 | form = read_atom(rdr) | |
178 | End Select | |
179 | return form | |
180 | End Function | |
181 | ||
182 | ||
183 | Shared Function read_str(str As string) As MalVal | |
184 | return read_form(New Reader(tokenize(str))) | |
185 | End Function | |
186 | End Class | |
187 | End Namespace |