-
Notifications
You must be signed in to change notification settings - Fork 6
/
reader.mal
142 lines (124 loc) · 4.08 KB
/
reader.mal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
; mal-to-llvm is responsible for loading tokenizer.mal before this file
; (load-file "./tokenizer.mal")
(def! make_reader
(fn* [tokens]
(atom {:tokens tokens :pos 0})))
(def! reader_peek
(fn* [rdr]
(let* [tokens (get @rdr :tokens)
pos (get @rdr :pos)]
(if (>= pos (count tokens))
nil
(nth tokens pos)))))
(def! reader_next
(fn* [rdr]
(let* [tokens (get @rdr :tokens)
pos (get @rdr :pos)]
(do
(reset! rdr {:tokens tokens :pos (+ 1 pos)})
(nth tokens pos)))))
(def! *digits-values* {"0" 0 "1" 1 "2" 2 "3" 3 "4" 4 "5" 5 "6" 6 "7" 7 "8" 8 "9" 9})
(def! digit-char? (fn* [c] (contains? *digits-values* c)))
(def! string->integer-helper
(fn* [val chars]
(if (empty? chars)
val
(string->integer-helper (+ (* 10 val) (get *digits-values* (first chars))) (rest chars)))))
(def! string->integer
(fn* [token-chars]
(string->integer-helper 0 token-chars)))
(def! read_negative_integer
(fn* [token-chars]
(- 0 (string->integer-helper 0 (rest token-chars)))))
(def! negative_number?
(fn* [token-chars]
(if (> (count token-chars) 1)
(if (= "-" (first token-chars))
(digit-char? (nth token-chars 1))
false)
false)))
(def! unescape_char
(fn* [c1]
(cond
(= "n" c1) "\n"
(= "\"" c1) c1
(= "\\" c1) c1
:else (throw "Illegal escaped char in string"))))
(def! unescape_string
(fn* [chars res]
(if (empty? chars)
(throw "Expected '\"', got EOF")
(if (= '("\"") chars)
(apply str res)
(let* [c0 (first chars)]
(if (= "\\" c0)
(let* [c1 (first (rest chars))]
(unescape_string (rest (rest chars)) (concat res [(unescape_char c1)])))
(unescape_string (rest chars) (concat res [c0]))))))))
(def! read_atom
(fn* [rdr]
(let* [token (reader_next rdr)
token-chars (seq token)
tc0 (first token-chars)]
(cond
(digit-char? tc0) (string->integer token-chars)
(negative_number? token-chars) (read_negative_integer token-chars)
(= ":" tc0) (keyword (apply str (rest token-chars)))
(= "\"" tc0) (unescape_string (rest token-chars) [])
(= "true" token) true
(= "false" token) false
(= "nil" token) nil
:else (symbol token)))))
(def! read_sequence_entries
(fn* [rdr end res]
(let* [token (reader_peek rdr)]
(if (nil? token)
(throw (str "Expected '" end "', got EOF"))
(if (= end token)
res
(read_sequence_entries rdr end (concat res [(read_form rdr)])))))))
(def! read_list
(fn* [rdr start end]
(let* [token (reader_next rdr)]
(if (= start token)
(let* [lst (read_sequence_entries rdr end (list))]
(do
(reader_next rdr) ; skip the closing paren
lst))
(throw (str "Expected '" start "', got EOF"))))))
(def! read_macro
(fn* [rdr sym]
(do
(reader_next rdr)
(list sym (read_form rdr)))))
(def! read_with_meta
(fn* [rdr]
(do
(reader_next rdr)
(let* [meta-obj (read_form rdr)]
(list 'with-meta (read_form rdr) meta-obj)))))
(def! read_form
(fn* [rdr]
(let* [token (reader_peek rdr)]
(cond
(= "'" token) (read_macro rdr 'quote)
(= "`" token) (read_macro rdr 'quasiquote)
(= "~" token) (read_macro rdr 'unquote)
(= "~@" token) (read_macro rdr 'splice-unquote)
(= "^" token) (read_with_meta rdr)
(= "@" token) (read_macro rdr 'deref)
(= "(" token) (read_list rdr "(" ")")
(= ")" token) (throw "unexpected ')'")
(= "[" token) (apply vector (read_list rdr "[" "]"))
(= "]" token) (throw "unexpected ']'")
(= "{" token) (apply hash-map (read_list rdr "{" "}"))
(= "}" token) (throw "unexpected '}'")
:else (read_atom rdr)))))
(def! read_str
(fn* [code-string]
(let* [tokens (tokenize code-string)]
(if (empty? tokens)
nil
(read_form (make_reader tokens))))))
; Mal core read-string function
(def! read-string read_str)