forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreader.el
155 lines (137 loc) · 4.1 KB
/
reader.el
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
143
144
145
146
147
148
149
150
151
152
153
154
155
;; HACK: `text-quoting-style' prettifies quotes in error messages on
;; Emacs 25, but no longer does from 26 upwards...
(when (= emacs-major-version 25)
(setq text-quoting-style 'grave))
(defvar tokens nil)
(defun peek ()
(car tokens))
(defun next ()
(pop tokens))
(defun read-str (input)
(setq tokens (tokenizer input))
(read-form))
(defun tokenizer (input)
(let (output)
(with-temp-buffer
(insert input)
(goto-char (point-min))
(while (not (eobp))
(when (looking-at token-re)
(let ((token (match-string 1)))
(if (= (length token) 0)
(let ((remainder (buffer-substring (point) (point-max))))
(push remainder output)
(goto-char (point-max)))
(when (not (string-match-p comment-re token))
(push token output))
(goto-char (match-end 1))))))
(nreverse output))))
(defun read-form ()
(let ((token (peek)))
(cond
((string= token "'")
(read-quote))
((string= token "`")
(read-quasiquote))
((string= token "~")
(read-unquote))
((string= token "~@")
(read-splice-unquote))
((string= token "@")
(read-deref))
((string= token "^")
(read-with-meta))
((string= token "(")
(read-list))
((string= token "[")
(read-vector))
((string= token "{")
(read-map))
(t
;; assume anything else is an atom
(read-atom)))))
(defun read-simple-reader-macro (symbol)
(next) ; pop reader macro token
;; turn form into (symbol form)
(mal-list (list (mal-symbol symbol) (read-form))))
(defun read-quote ()
(read-simple-reader-macro 'quote))
(defun read-quasiquote ()
(read-simple-reader-macro 'quasiquote))
(defun read-unquote ()
(read-simple-reader-macro 'unquote))
(defun read-splice-unquote ()
(read-simple-reader-macro 'splice-unquote))
(defun read-deref ()
(read-simple-reader-macro 'deref))
(defun read-with-meta ()
(next) ; pop with-meta token
(let ((meta (read-form)))
(mal-list (list (mal-symbol 'with-meta) (read-form) meta))))
(defun read-list ()
(next) ; pop list start
(let (output end-of-list)
(while (not end-of-list)
(let ((token (peek)))
(cond
((string= token ")")
(next) ; pop list end
(setq end-of-list t))
((not token)
(signal 'unterminated-sequence '(list)))
(t
(push (read-form) output)))))
(mal-list (nreverse output))))
(defun read-vector ()
(next) ; pop vector start
(let (output end-of-vector)
(while (not end-of-vector)
(let ((token (peek)))
(cond
((string= token "]")
(next) ; pop vector end
(setq end-of-vector t))
((not token)
(signal 'unterminated-sequence '(vector)))
(t
(push (read-form) output)))))
(mal-vector (vconcat (nreverse output)))))
;; HACK overriden by core.el in later steps
(define-hash-table-test 'mal-= 'equal 'sxhash)
(defun read-map ()
(next) ; pop map start
(let ((output (make-hash-table :test 'mal-=))
end-of-map)
(while (not end-of-map)
(let ((token (peek)))
(cond
((string= token "}")
(next) ; pop map end
(setq end-of-map t))
((not token)
(signal 'unterminated-sequence '(map)))
(t
(puthash (read-form) (read-form) output)))))
(mal-map output)))
(defun read-atom ()
(let ((token (next)))
(if token
(cond
((string= token "nil")
mal-nil)
((string= token "true")
mal-true)
((string= token "false")
mal-false)
((string-match number-re token)
(mal-number (string-to-number token)))
((= (aref token 0) ?\")
(if (string-match string-re token)
(mal-string (read token))
(signal 'unterminated-sequence '(string))))
((= (aref token 0) ?:)
(mal-keyword (intern token)))
(t
;; assume anything else is a symbol
(mal-symbol (intern token))))
(signal 'end-of-token-stream nil))))