1515d277aa1d37a37d75615c3b6f4517c435b0fe
[jackhill/mal.git] / gst / reader.st
1 Object subclass: Reader [
2 | storage index |
3
4 TokenRegex := '[\s,]*(~@|[\[\]{}()''`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}(''"`,;)]*)'.
5 CommentRegex := ';.*'.
6 NumberRegex := '-?[0-9]+(?:\.[0-9]+)?'.
7
8 Reader class >> tokenizer: input [
9 | tokens token hit pos done |
10 tokens := OrderedCollection new.
11 pos := 1.
12 done := false.
13
14 [done] whileFalse: [
15 hit := input searchRegex: TokenRegex startingAt: pos.
16 token := hit at: 1.
17 token size = 0 ifTrue: [
18 tokens add: (input copyFrom: pos to: input size) trimSeparators.
19 done := true.
20 ].
21 (token size = 0 or: [token matchRegex: CommentRegex]) ifFalse: [
22 tokens add: token
23 ].
24 pos := pos + (hit match size).
25 pos > input size ifTrue: [
26 done := true.
27 ].
28 ].
29 ^tokens
30 ]
31
32 Reader class >> readStr: input [
33 | tokens reader form |
34 tokens := self tokenizer: input.
35 reader := self new: tokens.
36 tokens isEmpty ifTrue: [
37 ^MALEmptyInput new signal
38 ].
39 ^self readForm: reader.
40 ]
41
42 Reader class >> readForm: reader [
43 | token |
44 token := reader peek.
45 token = '(' ifTrue: [
46 ^self readList: reader class: MALList ender: ')'
47 ].
48 token = '[' ifTrue: [
49 ^self readList: reader class: MALVector ender: ']'
50 ].
51 token = '{' ifTrue: [
52 ^self readList: reader class: MALMap ender: '}'
53 ].
54
55 (token matchRegex: '[])}]') ifTrue: [
56 ^MALUnexpectedToken new signal: token
57 ].
58
59 token = '''' ifTrue: [
60 ^self readSimpleMacro: reader name: #quote
61 ].
62 token = '`' ifTrue: [
63 ^self readSimpleMacro: reader name: #quasiquote
64 ].
65 token = '~' ifTrue: [
66 ^self readSimpleMacro: reader name: #unquote
67 ].
68 token = '~@' ifTrue: [
69 ^self readSimpleMacro: reader name: #'splice-unquote'
70 ].
71 token = '@' ifTrue: [
72 ^self readSimpleMacro: reader name: #deref
73 ].
74
75 token = '^' ifTrue: [
76 ^self readWithMetaMacro: reader
77 ].
78
79 ^self readAtom: reader
80 ]
81
82 Reader class >> readList: reader class: aClass ender: ender [
83 | storage token |
84 storage := OrderedCollection new.
85 "pop opening token"
86 reader next.
87 [ token := reader peek. token isNil ] whileFalse: [
88 token = ender ifTrue: [
89 ender = '}' ifTrue: [
90 storage := storage asDictionary.
91 ].
92 "pop closing token"
93 reader next.
94 ^aClass new: storage
95 ].
96 storage add: (self readForm: reader).
97 ].
98 ^MALUnterminatedSequence new signal: ender
99 ]
100
101 Reader class >> readAtom: reader [
102 | token |
103 token := reader next.
104
105 token = 'true' ifTrue: [ ^MALObject True ].
106 token = 'false' ifTrue: [ ^MALObject False ].
107 token = 'nil' ifTrue: [ ^MALObject Nil ].
108
109 (token first = $") ifTrue: [
110 (token last = $") ifTrue: [
111 ^MALString new: token parse
112 ] ifFalse: [
113 ^MALUnterminatedSequence new signal: '"'
114 ]
115 ].
116
117 (token matchRegex: NumberRegex) ifTrue: [
118 ^MALNumber new: token asNumber
119 ].
120
121 (token first = $:) ifTrue: [
122 ^MALKeyword new: token allButFirst asSymbol
123 ].
124
125 ^MALSymbol new: token asSymbol
126 ]
127
128 Reader class >> readSimpleMacro: reader name: name [
129 | form list |
130 "pop reader macro token"
131 reader next.
132 form := self readForm: reader.
133 list := OrderedCollection from: { MALSymbol new: name. form }.
134 ^MALList new: list
135 ]
136
137 Reader class >> readWithMetaMacro: reader [
138 | form meta list |
139 "pop reader macro token"
140 reader next.
141 meta := self readForm: reader.
142 form := self readForm: reader.
143 list := OrderedCollection from:
144 { MALSymbol new: #'with-meta'. form. meta }.
145 ^MALList new: list
146 ]
147
148 Reader class >> new: tokens [
149 | reader |
150 reader := super new.
151 reader init: tokens.
152 ^reader
153 ]
154
155 init: tokens [
156 storage := tokens.
157 index := 1.
158 ]
159
160 peek [
161 ^storage at: index ifAbsent: [ nil ]
162 ]
163
164 next [
165 | token |
166 token := self peek.
167 index := index + 1.
168 ^token
169 ]
170 ]