├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── leiningen.xml
├── libraries
│ ├── Clojure_1_7_0.xml
│ ├── Leiningen__clojure_complete_0_2_3.xml
│ ├── Leiningen__com_datomic_datomic_lucene_core_3_3_0.xml
│ ├── Leiningen__com_datomic_datomic_pro_0_9_5344.xml
│ ├── Leiningen__com_google_guava_guava_18_0.xml
│ ├── Leiningen__com_h2database_h2_1_3_171.xml
│ ├── Leiningen__commons_codec_1_5.xml
│ ├── Leiningen__io_netty_netty_3_6_7_Final.xml
│ ├── Leiningen__net_spy_spymemcached_2_11_4.xml
│ ├── Leiningen__org_apache_httpcomponents_httpclient_4_2.xml
│ ├── Leiningen__org_apache_httpcomponents_httpcore_4_2.xml
│ ├── Leiningen__org_apache_tomcat_tomcat_jdbc_7_0_27.xml
│ ├── Leiningen__org_apache_tomcat_tomcat_juli_7_0_27.xml
│ ├── Leiningen__org_clojure_clojure_1_8_0_beta1.xml
│ ├── Leiningen__org_clojure_tools_cli_0_2_2.xml
│ ├── Leiningen__org_clojure_tools_nrepl_0_2_10.xml
│ ├── Leiningen__org_codehaus_janino_commons_compiler_2_6_1.xml
│ ├── Leiningen__org_codehaus_janino_commons_compiler_jdk_2_6_1.xml
│ ├── Leiningen__org_fressian_fressian_0_6_5.xml
│ ├── Leiningen__org_hornetq_hornetq_commons_2_3_17_Final.xml
│ ├── Leiningen__org_hornetq_hornetq_core_client_2_3_17_Final.xml
│ ├── Leiningen__org_hornetq_hornetq_journal_2_3_17_Final.xml
│ ├── Leiningen__org_hornetq_hornetq_server_2_3_17_Final.xml
│ ├── Leiningen__org_jboss_logging_jboss_logging_3_1_0_GA.xml
│ ├── Leiningen__org_jgroups_jgroups_3_2_12_Final.xml
│ ├── Leiningen__org_slf4j_jcl_over_slf4j_1_7_7.xml
│ ├── Leiningen__org_slf4j_jul_to_slf4j_1_7_7.xml
│ ├── Leiningen__org_slf4j_log4j_over_slf4j_1_7_7.xml
│ ├── Leiningen__org_slf4j_slf4j_api_1_7_7.xml
│ └── Leiningen__org_slf4j_slf4j_nop_1_7_7.xml
├── misc.xml
├── modules.xml
├── replstate.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── .lein-failures
├── .nrepl-port
├── README.md
├── cast.iml
├── project.clj
├── src
├── clj
│ ├── .DS_Store
│ └── cst
│ │ ├── database.clj
│ │ ├── path.clj
│ │ ├── reader.clj
│ │ ├── schema.clj
│ │ └── test.clj
└── java
│ └── cst
│ ├── LispReader.java
│ └── SyntaxElement.java
└── test
└── clj
├── cst
├── database_test.clj
└── reader_test.clj
└── util
└── macro.clj
/.idea/.name:
--------------------------------------------------------------------------------
1 | cast
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/leiningen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/Clojure_1_7_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__clojure_complete_0_2_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__com_datomic_datomic_lucene_core_3_3_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__com_datomic_datomic_pro_0_9_5344.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__com_google_guava_guava_18_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__com_h2database_h2_1_3_171.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__commons_codec_1_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__io_netty_netty_3_6_7_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__net_spy_spymemcached_2_11_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_apache_httpcomponents_httpclient_4_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_apache_httpcomponents_httpcore_4_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_apache_tomcat_tomcat_jdbc_7_0_27.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_apache_tomcat_tomcat_juli_7_0_27.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_clojure_clojure_1_8_0_beta1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_clojure_tools_cli_0_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_clojure_tools_nrepl_0_2_10.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_codehaus_janino_commons_compiler_2_6_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_codehaus_janino_commons_compiler_jdk_2_6_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_fressian_fressian_0_6_5.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_hornetq_hornetq_commons_2_3_17_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_hornetq_hornetq_core_client_2_3_17_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_hornetq_hornetq_journal_2_3_17_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_hornetq_hornetq_server_2_3_17_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_jboss_logging_jboss_logging_3_1_0_GA.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_jgroups_jgroups_3_2_12_Final.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_slf4j_jcl_over_slf4j_1_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_slf4j_jul_to_slf4j_1_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_slf4j_log4j_over_slf4j_1_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_slf4j_slf4j_api_1_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Leiningen__org_slf4j_slf4j_nop_1_7_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/replstate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {:repl-history {:ide [], :local ["(map #(.name %) (SyntaxElement$Macro/values))" "(require '[clojure.string :as str])" "(map #(str/lower-case (.name %)) (SyntaxElement$Macro/values))" "(cst-read-string \"#{:bar :foo}\")" "(import '[java.io PushbackReader StringReader])" "(LispReader/read io nil)" "(LispReader/read io {:eof :end})" "(def end (Object.))" "(def io (PushbackReader. (StringReader. \"#{:bar :foo} (x)\")))" "(LispReader/read io {:eof end})" "SyntaxElement$Macro" "SyntaxElement$Macro$FN" "SyntaxElement$Macro/FN" "(use 'cst.reader)" "(use '[cst.reader] :reload-all)" "(cst-read-string \"#{:bar :foo} (x)\")" "(cst-read-all-string \"#{:bar :foo} (x)\")" "(def rdr (slurp \"/Users/pag/src/IdeaProjects/cast/src/clj/cst/reader.clj\"))" "(take 20 rdr)" "(cst-read-all-string rdr)" "(map #(keyword (str/lower-case (.name %))) (SyntaxElement$Macro/values))" "(require 'cst.test)" "(require '[cst.test])" "(require '[cst.database])" "(require '[cst.data])" "cst.data.reader-macros" "(require 'cst.data)" "(use 'cst.data)" "cst.data/reader-macros" "(use 'cst.test)" "(require '[cst.database] :reload-all)" "(main-)" "(cst.test/main-)" "(require '[cst.test] :reload-all)" "(list-data [:a :b] :vector)" "(def n (:db/id (first *1)))" "n" "(type n)" "(import '[datomic.db DbId])" "(instance? DbId n)" "() (list-data '(list 1 2 3) :list)" "(use '[cst.test-database] :reload-all)" "(list-data '(list 1 2 3) :list)" "(use '[cst.database-test] :reload-all)" "(map blankify-nodes (list-data '(list 1 2 3) :list))" "simple-test" "(tx-data '(list 1 2 3) :list)" "(tx-data '(list 1 2 3))" "(tx-data [1 2])" "(tx-data [{:a 0 :b 1}])" "(tx-data #{0 1})" "(def setd (reader/cst-read-all-string \"#{1 2}\"))" "s" "setd" "(type setd)" "(.data setd)" "(type (.data setd))" "(.get (.data setd) 0)" "(.emit (.get (.data setd) 0))" "(.emit setd)" "(.emit (reader/cst-read-all-string \"#{1 2\"))" "(.emit (reader/cst-read-all-string \"#{1 2}\"))" "(.emit (reader/cst-read-all-string \"[1 2]\"))" "(.emit (reader/cst-read-all-string \"{1 2}\"))" "(.emit (reader/cst-read-all-string \"(1 2)\"))" "(.emit (reader/cst-read-all-string \"1 2\"))" "(.emit (reader/cst-read-all-string \"symbol\"))" "f" "(def d (reader/cst-read-all-string f))" "(reader/cst-read-all-string \"1\")" "(reader/cst-read-string \"1\")" "(require '[cst.reader :as reader] :reload-all)" "two" "e" "(LispReader/read io {:eof :eof})" "(import '[cst LispReader])" "(import '[java.io PushbackReader StringReader Writer])" "(def io (PushbackReader. (StringReader. f)))" "(def one (LispReader/read io {:eof :eof}))" "(def two (LispReader/read io {:eof :eof}))" "(def three (LispReader/read io {:eof :eof}))" "three" "(.emit three)" "(import '[java.util.regex Matcher Pattern])" "(Pattern/compile \"a\")" "(.toString (Pattern/compile \"a\"))" "(def f (slurp \"/Users/pag/src/IdeaProjects/cast/src/clj/cst/test.clj\"))" "(take 10 f)" "(def c (cst-read-all-string f))" "c" "(.emit c)" "(tx-data c)" "(.printStackTrace *e)" "short-ns" "(use '[cst.database-test])" "(use '[cst.reader])" "(use '[cst.database])" "(def fhello \"(ns cst.test-hello)\\n(println \\\"Hello world\\\")\")" "(def chello (cst-read-all-string fhello))" "(use '[cst.database] :reload-all)" "(def tx (tx-data chello))"], :remote []}}
4 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | Abstraction issuesJava
176 |
177 |
178 | Assignment issuesGroovy
179 |
180 |
181 | Assignment issuesJava
182 |
183 |
184 | Concurrency annotation issuesJava
185 |
186 |
187 | Control FlowGroovy
188 |
189 |
190 | Control flow issuesJava
191 |
192 |
193 | Data flow issuesJava
194 |
195 |
196 | Declaration redundancyJava
197 |
198 |
199 | Dependency issuesJava
200 |
201 |
202 | Encapsulation issuesJava
203 |
204 |
205 | GeneralJava
206 |
207 |
208 | Groovy
209 |
210 |
211 | J2ME issuesJava
212 |
213 |
214 | JUnit issuesJava
215 |
216 |
217 | Java
218 |
219 |
220 | Javadoc issuesJava
221 |
222 |
223 | Kotlin
224 |
225 |
226 | Numeric issuesJava
227 |
228 |
229 | Performance issuesJava
230 |
231 |
232 | Plugin DevKit
233 |
234 |
235 | Potentially confusing code constructsGroovy
236 |
237 |
238 | Probable bugsGroovy
239 |
240 |
241 | Probable bugsJava
242 |
243 |
244 | Serialization issuesJava
245 |
246 |
247 | TestNGJava
248 |
249 |
250 | Threading issuesGroovy
251 |
252 |
253 | Threading issuesJava
254 |
255 |
256 | Visibility issuesJava
257 |
258 |
259 | XML
260 |
261 |
262 |
263 |
264 | Ant inspections
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 | 1436933044218
797 |
798 | 1436933044218
799 |
800 |
801 | 1444546857770
802 |
803 |
804 | 1444546857770
805 |
806 |
807 | 1445232454797
808 |
809 |
810 | 1445232454797
811 |
812 |
813 | 1445232839808
814 |
815 |
816 | 1445232839808
817 |
818 |
819 | 1445833034367
820 |
821 |
822 | 1445833034367
823 |
824 |
825 | 1446086579417
826 |
827 |
828 | 1446086579417
829 |
830 |
831 | 1446118601458
832 |
833 |
834 | 1446118601458
835 |
836 |
837 | 1446463689271
838 |
839 |
840 | 1446463689271
841 |
842 |
843 | 1446791644338
844 |
845 |
846 | 1446791644338
847 |
848 |
849 | 1447055998929
850 |
851 |
852 | 1447055998929
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 | file://$PROJECT_DIR$/src/java/cst/SyntaxElement.java
926 | 58
927 |
928 |
929 |
930 |
931 | file://$PROJECT_DIR$/src/java/cst/SyntaxElement.java
932 | 79
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
1116 |
1117 |
1118 |
1119 |
1120 |
1121 |
1122 |
1123 |
1124 |
1125 |
1126 |
1127 |
1128 |
1129 |
1130 |
1131 |
1132 |
1133 |
1134 |
1135 |
1136 |
1137 |
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 |
1146 |
1147 |
1148 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 |
1196 |
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 |
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 |
1234 |
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 |
1247 |
1248 |
1249 |
1250 |
1251 |
1252 |
1253 |
1254 |
1255 |
1256 |
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 |
1264 |
1265 |
1266 |
1267 |
1268 |
1269 |
1270 |
1271 |
1272 |
1273 |
1274 |
1275 |
1276 |
1277 |
1278 |
1279 |
1280 |
1281 |
1282 |
1283 |
1284 |
1285 |
1286 |
1287 |
1288 |
1289 |
1290 |
1291 |
1292 |
1293 |
1294 |
1295 |
1296 |
1297 |
1298 |
1299 |
1300 |
1301 |
1302 |
1303 |
1304 |
1305 |
1306 |
1307 |
1308 |
1309 |
1310 |
1311 |
1312 |
1313 |
1314 |
1315 |
1316 |
1317 |
1318 |
1319 |
1320 |
1321 |
1322 |
1323 |
1324 |
1325 |
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 |
1334 |
1335 |
1336 |
1337 |
1338 |
1339 |
1340 |
1341 |
1342 |
1343 |
1344 |
1345 |
1346 |
1347 |
1348 |
1349 |
1350 |
1351 |
1352 |
1353 |
1354 |
1355 |
1356 |
1357 |
1358 |
1359 |
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 |
1368 |
1369 |
1370 |
1371 |
1372 |
1373 |
1374 |
1375 |
1376 |
1377 |
1378 |
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 |
1386 |
1387 |
1388 |
1389 |
1390 |
1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 |
1400 |
1401 |
1402 |
1403 |
1404 |
1405 |
1406 |
1407 |
1408 |
1409 |
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 |
1418 |
1419 |
1420 |
1421 |
1422 |
1423 |
1424 |
1425 |
1426 |
1427 |
1428 |
1429 |
1430 |
1431 |
1432 |
1433 |
1434 |
1435 |
1436 |
1437 |
1438 |
1439 |
1440 |
1441 |
1442 |
1443 |
1444 |
1445 |
1446 |
1447 |
1448 |
1449 |
1450 |
1451 |
1452 |
1453 |
1454 |
1455 |
1456 |
1457 |
1458 |
1459 |
1460 |
1461 |
1462 |
1463 |
1464 |
1465 |
1466 |
1467 |
1468 |
1469 |
1470 |
1471 |
1472 |
1473 |
1474 |
1475 |
1476 |
1477 |
1478 |
1479 |
1480 |
1481 |
1482 |
1483 |
1484 |
1485 |
1486 |
1487 |
1488 |
1489 |
1490 |
1491 |
1492 |
1493 |
1494 |
1495 |
1496 |
1497 |
1498 |
1499 |
1500 |
1501 |
1502 |
1503 |
1504 |
1505 |
1506 |
1507 |
1508 |
1509 |
1510 |
1511 |
1512 |
1513 |
1514 |
1515 |
1516 |
1517 |
1518 |
1519 |
1520 |
1521 |
1522 |
1523 |
1524 |
1525 |
1526 |
1527 |
1528 |
1529 |
1530 |
1531 |
1532 |
1533 |
1534 |
1535 |
1536 |
1537 |
1538 |
1539 |
1540 |
1541 |
1542 |
1543 |
1544 |
1545 |
1546 |
1547 |
1548 |
1549 |
1550 |
1551 |
1552 |
1553 |
1554 |
1555 |
1556 |
1557 |
1558 |
1559 |
1560 |
1561 |
1562 |
1563 |
1564 |
1565 |
1566 |
1567 |
1568 |
1569 |
1570 |
1571 |
1572 |
1573 |
1574 |
1575 |
1576 |
1577 |
1578 |
1579 |
1580 |
1581 |
1582 |
1583 |
1584 |
1585 |
1586 |
1587 | 1.8
1588 |
1589 |
1590 |
1591 |
1592 |
1593 |
1594 |
1595 |
1596 |
1597 |
1598 |
1599 | cast
1600 |
1601 |
1602 |
1603 |
1604 |
1605 |
1606 |
1607 |
1608 |
1609 |
1610 |
1611 |
1612 | 1.8
1613 |
1614 |
1615 |
1616 |
1617 |
1618 |
1619 |
1620 |
1621 |
1622 |
1623 |
1624 | Clojure-1.7.0
1625 |
1626 |
1627 |
1628 |
1629 |
1630 |
1631 |
1632 |
1633 |
1634 |
1635 |
1636 |
1637 |
1638 |
1639 |
1640 |
1641 |
1642 |
1643 |
1644 |
1645 |
1646 |
1647 |
--------------------------------------------------------------------------------
/.lein-failures:
--------------------------------------------------------------------------------
1 | {"cst.database-test" ["simple-list" "rt-hello" "rt-hello" "rt-hello"]}
--------------------------------------------------------------------------------
/.nrepl-port:
--------------------------------------------------------------------------------
1 | 54291
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cast
2 | **Concrete/Abstract Syntax Trees for Clojure**
3 |
4 | The idea of this code is to convert Clojure into a Concrete Syntax Tree (CST) suitable for storage in a database, Datomic in this case. From there it can be loaded back into the CST, and then either emitted back to file format, or "read" into the Clojure Abstract Syntax Tree (AST). Once read, the AST should be identical to having read Clojure code from the original file.
5 |
6 | The CST contains elements that are not in the AST, such as comments and commas. Whitespace is dropped: we're not doing XML here. So emiting a file from the CST will only match the input file if the input had minimal whitespace. Also, interpretation of certain reader macros is not done in the CST, and macro expansion has not occurred.
7 |
8 | The idea of storing the CST is to enable an editor to modify source code in a database, rather than in a flat text file. This is an idea that keeps coming up, but a nice writeup was done by Kent Beck talking about the Prune Editor:
9 |
10 | https://www.facebook.com/notes/kent-beck/prune-a-code-editor-that-is-not-a-text-editor/1012061842160013
11 |
12 | This is my first attempt at using Cursive, so some files for supporting IntelliJ IDEA are also included. These may be ignored.
13 |
14 | The Parser is a simple adaptation of the parsing portion of clojure.lang.LispReader, which means that it this part is written in Java. To make for easier integration (due to compilation order) and to allow for protocol dispatch on type, the syntax structure are also written in Java. The rest is in Clojure.
15 |
16 | The code still has a long way to go, but it's doing basic things now.
17 |
--------------------------------------------------------------------------------
/cast.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject cast "0.1.0-SNAPSHOT"
2 | :description "Clojure AST reader"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.8.0-beta1"]
7 | [com.datomic/datomic-pro "0.9.5344" :exclusions [joda-time]]]
8 | :source-paths ["src/clj"]
9 | :java-source-paths ["src/java"]
10 | :prep-tasks ["javac" "compile"]
11 | :test-paths ["test/clj"]
12 | :main cst.reader
13 | :repositories {"my.datomic.com" {:url "https://my.datomic.com/repo"
14 | :creds :gpg}} )
15 |
--------------------------------------------------------------------------------
/src/clj/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quoll/cast/367e96500e9c35301f59941e75633520eb9bbba5/src/clj/.DS_Store
--------------------------------------------------------------------------------
/src/clj/cst/database.clj:
--------------------------------------------------------------------------------
1 | (ns cst.database
2 | (:require [cst.schema :as data]
3 | [cst.path :as path]
4 | [datomic.api :refer [q] :as d])
5 | (:import [datomic Peer]
6 | [datomic.db DbId]
7 | [clojure.lang Keyword Symbol IPersistentList IPersistentVector IPersistentMap]
8 | [java.util Date UUID Map]
9 | [java.net URI]
10 | [java.math BigInteger BigDecimal]
11 | [cst SyntaxElement SyntaxElement$Type]))
12 |
13 | (def dburl "datomic:dev://localhost:4334/source")
14 |
15 | (defn load-schema
16 | "Loads the cast schema into a database connection"
17 | [c]
18 | (d/transact c data/schema)
19 | (d/transact c data/reader-macros))
20 |
21 | (defn database
22 | "Creates and initializes a database, returning a connection"
23 | ([] (database dburl))
24 | ([uri]
25 | (let [newdb (d/create-database uri)
26 | c (d/connect uri)]
27 | (when newdb (load-schema c))
28 | c)))
29 |
30 | (defn node [] (Peer/tempid :db.part/cst))
31 |
32 | (defprotocol WithProperty
33 | (data-property [x] "Return the correct property to use for x"))
34 |
35 | (extend-protocol WithProperty
36 | Keyword
37 | (data-property [_] :cst.value/keyword)
38 | Symbol
39 | (data-property [_] :cst.value/symbol)
40 | String
41 | (data-property [_] :cst.value/string)
42 | Boolean
43 | (data-property [_] :cst.value/boolean)
44 | Long
45 | (data-property [_] :cst.value/long)
46 | BigInteger
47 | (data-property [_] :cst.value/bigint)
48 | Float
49 | (data-property [_] :cst.value/float)
50 | Double
51 | (data-property [_] :cst.value/double)
52 | BigDecimal
53 | (data-property [_] :cst.value/bigdec)
54 | Date
55 | (data-property [_] :cst.value/instant)
56 | UUID
57 | (data-property [_] :cst.value/uuid)
58 | URI
59 | (data-property [_] :cst.value/uri)
60 | Object
61 | (data-property [_] :cst.value/object))
62 |
63 | (defprotocol ConvertedToDb
64 | (smb [x] "Converts the parameter to a type that can be stored in the database"))
65 |
66 | (extend-protocol ConvertedToDb
67 | Symbol
68 | (smb [x] (name x))
69 | Object
70 | (smb [x] x))
71 |
72 | (defprotocol Data
73 | (object-data [x] "Returns a single value suitable for a transaction,
74 | paired with a seq of any supporting transaction data"))
75 |
76 | (declare list-data)
77 |
78 | (extend-protocol Data
79 | Object
80 | (object-data [x] [x []])
81 | SyntaxElement
82 | (object-data [^SyntaxElement x]
83 | (let [etype (. x id)
84 | data (. x data)
85 | node-id (node)]
86 | (cond
87 | (= :cst/file etype) (let [location (or (path/to-uri (:location data))
88 | (URI. (str "uuid:" (UUID/randomUUID))))]
89 | (list-data (:data data) :file node-id location)) ;; list structure for the file contents
90 | (= :cst/conditional etype) (let [[o auxo] (object-data (:form data))
91 | splice? (:splice data)
92 | condo {:db/id node-id
93 | :cst/type etype
94 | :cst.cond/splice splice?
95 | :cst.cond/form o}]
96 | [node-id (concat auxo [condo])])
97 | data (let [[d auxd] (object-data data)]
98 | [node-id (concat auxd [(assoc {:db/id node-id, :cst/type etype}
99 | (data-property d) (smb d))])])
100 | :default [node-id []])))
101 | IPersistentList
102 | (object-data [^IPersistentList x] (list-data x :list (node)))
103 | IPersistentVector
104 | (object-data [^IPersistentVector x] (list-data x :vector (node)))
105 | IPersistentMap
106 | (object-data [^IPersistentMap x] (list-data (seq x) :map (node))))
107 |
108 |
109 | (defn- list-struct
110 | [[head & tail] pre-node]
111 | (let [pre-node (or pre-node (node))
112 | [d aux] (object-data head)
113 | list-elt {:db/id pre-node, (data-property d) (smb d)}]
114 | ;; if d is an object, then drop it in as a replacement at the end, or add rest to it
115 | (if-not (seq tail)
116 | [[list-elt aux]]
117 | (let [next-node (node)]
118 | (lazy-seq (cons [(assoc list-elt :cst/rest next-node) aux]
119 | (list-struct tail next-node)))))))
120 |
121 | (defn list-data
122 | "Converts a seq into a transaction seq, of a provided type and using a given function for seq elements.
123 | s - sequence
124 | t - type
125 | l - location
126 | The provided function returns a pair: [list-element, auxiliary-data]
127 | Returns a pair: [list-ID, tx-sequence]."
128 | ([s t n] (list-data s t n nil))
129 | ([s t n l]
130 | (let [elt-data (list-struct s n)
131 | aux (apply concat (map second elt-data))
132 | [{list-id :db/id :as head} & srest] (map first elt-data)
133 | head (assoc head :cst/type t)
134 | head (if l (assoc head :cst/location l) head)]
135 | [list-id (concat aux (cons head srest))])))
136 |
137 | (defn tx-data
138 | "Convert an object into transaction data. The final item is always the Object."
139 | [obj]
140 | (let [[element aux] (object-data obj)]
141 | (if (instance? DbId element)
142 | aux
143 | (concat aux
144 | [(if (map? element)
145 | element
146 | {:db/id (node)
147 | :cst/type :native
148 | (data-property element) (smb element)})]))))
149 |
150 | (declare reconstruct)
151 |
152 | ; "Creates a value from v based on the type associated with the property p"
153 | (defmulti value-of (fn [p v] p))
154 |
155 | (defmethod value-of :cst.value/symbol
156 | [p ^String v]
157 | (symbol v))
158 |
159 | (defmethod value-of :cst.value/object
160 | [p m]
161 | (reconstruct m))
162 |
163 | (defmethod value-of :default [p m] m)
164 |
165 | (defn value-fn
166 | "Retrieves the value from a structure, "
167 | [e]
168 | (some (fn [[k v]] (if (= (namespace k) "cst.value")
169 | (value-of k v)))
170 | e))
171 |
172 | (defn rebuild-list*
173 | [l]
174 | (map value-fn (sort-by :cst/index l)))
175 |
176 | (defn rebuild-list
177 | [{next :cst/rest :as e}]
178 | (let [v (value-fn e)]
179 | (if-not (seq next)
180 | [v]
181 | (cons v (rebuild-list next)))))
182 |
183 | (defmulti reconstruct :cst/type)
184 |
185 | (defmethod reconstruct :file
186 | [f]
187 | (let [elements (rebuild-list f)]
188 | (SyntaxElement. SyntaxElement$Type/FILE (map reconstruct elements))))
189 |
190 | (defmethod reconstruct :vector
191 | [v]
192 | (let [elements (rebuild-list v)]
193 | (SyntaxElement. SyntaxElement$Type/VECTOR (apply vector (map reconstruct elements)))))
194 |
195 | (defmethod reconstruct :list
196 | [l]
197 | (let [elements (rebuild-list l)]
198 | (SyntaxElement. SyntaxElement$Type/LIST (map reconstruct elements))))
199 |
200 | (defmethod reconstruct :map
201 | [m]
202 | (let [elements (rebuild-list m)]
203 | (SyntaxElement. SyntaxElement$Type/MAP (map reconstruct elements))))
204 |
205 | (defmethod reconstruct :conditional
206 | [c]
207 | (let [form (reconstruct (:cst.cond/form c))
208 | splice? (:cst.cond/splice c)]
209 | (SyntaxElement. SyntaxElement$Type/CONDITIONAL {:splice splice?, :form form})))
210 |
211 | (defmethod reconstruct :default [v] v)
212 |
213 | (defn get-filenames
214 | "Retrieves the locations (or paths) for each file stored in the database."
215 | [db]
216 | (q '[:find [?l ...] :where [?e :cst/type :file] [?e :cst/location ?l]] db))
217 |
218 | (defn get-cst
219 | "Retrieves the Concrete Syntax Tree for a file location. Returns nil if the location is unknown."
220 | [db location]
221 | (when location
222 | (when-let [eid (q '[:find ?e . :in $ ?l :where [?e :cst/location ?l] [?e :cst/type :file]]
223 | db
224 | (path/to-uri location))]
225 | (let [fdata (d/pull db '[*] eid)]
226 | (reconstruct fdata)))))
227 |
228 |
--------------------------------------------------------------------------------
/src/clj/cst/path.clj:
--------------------------------------------------------------------------------
1 | (ns cst.path
2 | (:require [clojure.string :as str])
3 | (:import [java.net URL URI]
4 | [java.io File]))
5 |
6 | (def file-schema "file:")
7 |
8 | (defn has-schema
9 | "Simple test for a schema (RFC 3986)"
10 | [s]
11 | (re-find #"^[a-zA-Z][a-zA-Z+\-.]*:" s))
12 |
13 | (defprotocol URIable
14 | (to-uri [x] "Converts the value to a URI"))
15 |
16 | (extend-protocol URIable
17 | nil
18 | (to-uri [_] nil)
19 | String
20 | (to-uri [^String x]
21 | (if (has-schema x)
22 | (URI. x)
23 | (URI. (str file-schema x))))
24 | URL
25 | (to-uri [^URL x] (.toURI x))
26 | URI
27 | (to-uri [x] x))
28 |
--------------------------------------------------------------------------------
/src/clj/cst/reader.clj:
--------------------------------------------------------------------------------
1 | (ns cst.reader
2 | (require [clojure.java.io :as io])
3 | (import [java.io PushbackReader StringReader Writer]
4 | [java.util UUID]
5 | [java.net URI]
6 | [cst LispReader SyntaxElement SyntaxElement$Type]))
7 |
8 | (defmethod print-method SyntaxElement [^SyntaxElement o ^Writer w]
9 | (let [writer-print (fn [^String s]
10 | (dotimes [n (count s)]
11 | (.write w (int (.charAt s n)))))]
12 | (writer-print (str o))))
13 |
14 | (defn new-location [] (URI. (str "uuid:" (UUID/randomUUID))))
15 |
16 | (defn cst-read-all
17 | "Reads an entire string into a sequence of elements"
18 | ([io] (cst-read-all io (new-location)))
19 | ([io location] (cst-read-all io location nil))
20 | ([io location external-opts]
21 | (let [eof (Object.)
22 | opts (merge external-opts {:eof eof})]
23 | (loop [element (LispReader/read io opts) context []]
24 | (if (= eof element)
25 | (SyntaxElement. SyntaxElement$Type/FILE {:data context :location location})
26 | (recur (LispReader/read io opts) (conj context element)))))))
27 |
28 | (defn cst-read-all-string
29 | "Read all forms from a string into a seq of CST structures"
30 | ([^String s] (cst-read-all-string s (new-location) nil))
31 | ([^String s location] (cst-read-all-string s location nil))
32 | ([^String s location opts]
33 | (with-open [io (PushbackReader. (StringReader. s))]
34 | (cst-read-all io location opts))))
35 |
36 | (defn cst-read
37 | "Reads the first element parsed from the stream into a single cst structure form"
38 | ([^PushbackReader io] (cst-read io nil))
39 | ([^PushbackReader io opts] (LispReader/read io opts)))
40 |
41 | (defn cst-read-string
42 | "Read the first element parsed from the a string in a single cst structure form"
43 | ([^String s] (cst-read-string s nil))
44 | ([^String s opts]
45 | (with-open [io (PushbackReader. (StringReader. s))]
46 | (cst-read io opts))))
47 |
48 |
--------------------------------------------------------------------------------
/src/clj/cst/schema.clj:
--------------------------------------------------------------------------------
1 | (ns cst.schema
2 | (:require [clojure.string :as str])
3 | (:import [datomic Peer]
4 | [cst SyntaxElement$Type]))
5 |
6 | (def std-types [:keyword :string :boolean :long :bigint :float :double :bigdec :instant :uuid :uri])
7 |
8 | (def types (concat [{:db/id (Peer/tempid :db.part/db)
9 | :db/ident :cst.value/object
10 | :db/valueType :db.type/ref
11 | :db/cardinality :db.cardinality/one
12 | :db/isComponent true
13 | :db.install/_attribute :db.part/db}
14 | {:db/id (Peer/tempid :db.part/db)
15 | :db/ident :cst.value/symbol
16 | :db/valueType :db.type/string
17 | :db/cardinality :db.cardinality/one
18 | :db/fulltext true
19 | :db.install/_attribute :db.part/db}]
20 | (map (fn [a]
21 | (let [nm (name a)
22 | attr {:db/id (Peer/tempid :db.part/db)
23 | :db/ident (keyword "cst.value" nm)
24 | :db/valueType (keyword "db.type" nm)
25 | :db/cardinality :db.cardinality/one
26 | :db.install/_attribute :db.part/db}]
27 | (if (= :string a)
28 | (assoc attr :db/fulltext true)
29 | attr)))
30 | std-types)))
31 |
32 | (def basic-schema
33 | [{:db/id (Peer/tempid :db.part/db)
34 | :db/ident :cst/type
35 | :db/valueType :db.type/keyword ;; TODO: change to ref
36 | :db/cardinality :db.cardinality/one
37 | :db.install/_attribute :db.part/db}
38 | {:db/id (Peer/tempid :db.part/db)
39 | :db/ident :cst/element
40 | :db/valueType :db.type/ref
41 | :db/isComponent true
42 | :db/cardinality :db.cardinality/many
43 | :db.install/_attribute :db.part/db}
44 | {:db/id (Peer/tempid :db.part/db)
45 | :db/ident :cst/index
46 | :db/valueType :db.type/long
47 | :db/cardinality :db.cardinality/one
48 | :db.install/_attribute :db.part/db}
49 | {:db/id (Peer/tempid :db.part/db)
50 | :db/ident :cst/location
51 | :db/valueType :db.type/uri
52 | :db/cardinality :db.cardinality/one
53 | :db/unique :db.unique/identity
54 | :db.install/_attribute :db.part/db}
55 | {:db/id (Peer/tempid :db.part/db)
56 | :db/ident :cst.cond/form
57 | :db/valueType :db.type/ref
58 | :db/cardinality :db.cardinality/one
59 | :db.install/_attribute :db.part/db}
60 | {:db/id (Peer/tempid :db.part/db)
61 | :db/ident :cst.cond/splice
62 | :db/valueType :db.type/boolean
63 | :db/cardinality :db.cardinality/one
64 | :db.install/_attribute :db.part/db}
65 | {:db/id (Peer/tempid :db.part/db)
66 | :db/ident :cst/rest
67 | :db/valueType :db.type/ref
68 | :db/isComponent true
69 | :db/cardinality :db.cardinality/one
70 | :db.install/_attribute :db.part/db}])
71 |
72 | (def partitions
73 | [{:db/id (Peer/tempid :db.part/db)
74 | :db/ident :db.part/cst
75 | :db.install/_partition :db.part/db}])
76 |
77 | (def schema (concat partitions basic-schema types))
78 |
79 | (def reader-macros
80 | (conj
81 | (map (fn [e]
82 | {:db/id (Peer/tempid :db.part/cst)
83 | :db/ident (keyword (str/lower-case (.name e)))})
84 | (SyntaxElement$Type/values))
85 | {:db/id (Peer/tempid :db.part/cst)
86 | :db/ident :native}))
87 |
88 |
--------------------------------------------------------------------------------
/src/clj/cst/test.clj:
--------------------------------------------------------------------------------
1 | (ns cst.test
2 | (:require [cst.reader :as reader]
3 | [cst.database :as cdb]
4 | [datomic.api :refer [q] :as db]))
5 |
6 | (def test-data "(list 1 2 3) [:a :b]")
7 |
8 | (defn main- [& args]
9 | (let [db (cdb/database cdb/dburl)
10 | cst-data (reader/cst-read-all-string test-data)
11 | tx-data (map cdb/tx-data (.data cst-data))]
12 | (println "TX:")
13 | (println tx-data)
14 | (println "----")
15 | (println (db/transact db tx-data))
16 | )
17 | )
18 |
--------------------------------------------------------------------------------
/src/java/cst/LispReader.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Rich Hickey. All rights reserved.
3 | * The use and distribution terms for this software are covered by the
4 | * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 | * which can be found in the file epl-v10.html at the root of this distribution.
6 | * By using this software in any fashion, you are agreeing to be bound by
7 | * the terms of this license.
8 | * You must not remove this notice, or any other, from this software.
9 | **/
10 |
11 | package cst;
12 |
13 | import clojure.lang.*;
14 |
15 | import java.io.IOException;
16 | import java.io.PushbackReader;
17 | import java.io.Reader;
18 | import java.lang.reflect.Constructor;
19 | import java.math.BigDecimal;
20 | import java.math.BigInteger;
21 | import java.util.ArrayList;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 | import java.util.regex.Matcher;
25 | import java.util.regex.Pattern;
26 | import static cst.SyntaxElement.Type;
27 |
28 | public class LispReader {
29 |
30 | static final Symbol THE_VAR = Symbol.intern("var");
31 | static final Symbol UNQUOTE = Symbol.intern("clojure.core", "unquote");
32 | static final Symbol UNQUOTE_SPLICING = Symbol.intern("clojure.core", "unquote-splicing");
33 | static final Symbol LIST = Symbol.intern("clojure.core", "list");
34 | static final Symbol WITH_META = Symbol.intern("clojure.core", "with-meta");
35 | static final Keyword UNKNOWN = Keyword.intern(null, "unknown");
36 |
37 | static IFn[] macros = new IFn[256];
38 | static IFn[] dispatchMacros = new IFn[256];
39 | static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
40 | static Pattern intPat =
41 | Pattern.compile(
42 | "([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
43 | static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
44 | static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
45 |
46 | //symbol->gensymbol
47 | static Var GENSYM_ENV = Var.create(null).setDynamic();
48 | //sorted-map num->gensymbol
49 | static Var ARG_ENV = Var.create(null).setDynamic();
50 | static IFn ctorReader = new CtorReader();
51 |
52 | // Dynamic var set to true in a read-cond context
53 | static Var READ_COND_ENV = Var.create(null).setDynamic();
54 |
55 | static Symbol resolveSymbol(Symbol sym) {
56 | //already qualified or classname?
57 | if (sym.getName().indexOf('.') > 0) return sym;
58 | if (sym.getNamespace() != null) {
59 | Namespace ns = namespaceFor(sym);
60 | if (ns == null || (ns.name.getName() == null ? sym.getNamespace() == null : ns.name.getName().equals(sym.getNamespace())))
61 | return sym;
62 | return Symbol.intern(ns.name.getName(), sym.getName());
63 | }
64 | Object o = currentNS().getMapping(sym);
65 | if (o == null) {
66 | return Symbol.intern(currentNS().name.getName(), sym.getName());
67 | } else if (o instanceof Class) {
68 | return Symbol.intern(null, ((Class) o).getName());
69 | } else if (o instanceof Var) {
70 | Var v = (Var) o;
71 | return Symbol.intern(v.ns.name.getName(), v.sym.getName());
72 | }
73 | return null;
74 | }
75 |
76 | // Extracted elements from the Compiler specials
77 | final static Keyword TAG_KEY = Keyword.intern(null, "tag");
78 | final static Keyword KEYWORD_KEY = Keyword.intern(null, "keyword");
79 | final static Keyword MAP_KEY = Keyword.intern(null, "map");
80 | final static Keyword META_KEY = Keyword.intern(null, "meta");
81 | final static Keyword OBJECT_KEY = Keyword.intern(null, "object");
82 | final static Keyword LINE_KEY = Keyword.intern(null, "line");
83 | final static Keyword COLUMN_KEY = Keyword.intern(null, "column");
84 |
85 | static {
86 | macros['"'] = new StringReader();
87 | macros[';'] = new SyntaxCommentReader();
88 | macros[','] = new CommaReader();
89 | macros['\''] = new WrappingReader(SyntaxElement.Type.QUOTE);
90 | macros['@'] = new WrappingReader(SyntaxElement.Type.DEREF);//new DerefReader();
91 | macros['^'] = new MetaReader();
92 | macros['`'] = new SyntaxQuoteReader();
93 | macros['~'] = new UnquoteReader();
94 | macros['('] = new ListReader();
95 | macros[')'] = new UnmatchedDelimiterReader();
96 | macros['['] = new VectorReader();
97 | macros[']'] = new UnmatchedDelimiterReader();
98 | macros['{'] = new MapReader();
99 | macros['}'] = new UnmatchedDelimiterReader();
100 | macros['\\'] = new CharacterReader();
101 | macros['%'] = new ArgReader();
102 | macros['#'] = new DispatchReader();
103 |
104 | dispatchMacros['^'] = new MetaReader();
105 | dispatchMacros['\''] = new VarReader();
106 | dispatchMacros['"'] = new RegexReader();
107 | dispatchMacros['('] = new FnReader();
108 | dispatchMacros['{'] = new SetReader();
109 | dispatchMacros['='] = new EvalReader();
110 | dispatchMacros['!'] = new MacroCommentReader();
111 | dispatchMacros['<'] = new UnreadableReader();
112 | dispatchMacros['_'] = new DiscardReader();
113 | dispatchMacros['?'] = new ConditionalReader();
114 | }
115 |
116 | static Namespace currentNS() { return (Namespace)RT.CURRENT_NS.deref(); }
117 |
118 | static Namespace namespaceFor(Symbol sym) { return namespaceFor(currentNS(), sym); }
119 |
120 | static Namespace namespaceFor(Namespace inns, Symbol sym) {
121 | // note, presumes non-nil sym.ns
122 | // first check against currentNS' aliases...
123 | Symbol nsSym = Symbol.intern(sym.getNamespace());
124 | Namespace ns = inns.lookupAlias(nsSym);
125 | if (ns == null) {
126 | // ...otherwise check the Namespaces map.
127 | ns = Namespace.find(nsSym);
128 | }
129 | return ns;
130 | }
131 |
132 | static boolean isWhitespace(int ch) {
133 | return Character.isWhitespace(ch);
134 | }
135 |
136 | static void unread(PushbackReader r, int ch) {
137 | if (ch != -1) {
138 | try {
139 | r.unread(ch);
140 | } catch (IOException e) {
141 | throw Util.sneakyThrow(e);
142 | }
143 | }
144 | }
145 |
146 | public static class ReaderException extends RuntimeException {
147 | final int line;
148 | final int column;
149 |
150 | public ReaderException(int line, int column, Throwable cause) {
151 | super(cause);
152 | this.line = line;
153 | this.column = column;
154 | }
155 | }
156 |
157 | static public int read1(Reader r) {
158 | try {
159 | return r.read();
160 | } catch(IOException e) {
161 | throw Util.sneakyThrow(e);
162 | }
163 | }
164 |
165 | // Reader opts
166 | static public final Keyword OPT_EOF = Keyword.intern(null, "eof");
167 | static public final Keyword OPT_FEATURES = Keyword.intern(null, "features");
168 | static public final Keyword OPT_READ_COND = Keyword.intern(null, "read-cond");
169 |
170 | // EOF special value to throw on eof
171 | static public final Keyword EOFTHROW = Keyword.intern(null, "eofthrow");
172 |
173 | // Platform features - always installed
174 | static private final Keyword PLATFORM_KEY = Keyword.intern(null, "clj");
175 | static private final Object PLATFORM_FEATURES = PersistentHashSet.create(PLATFORM_KEY);
176 |
177 | // Reader conditional options - use with :read-cond
178 | static public final Keyword COND_ALLOW = Keyword.intern(null, "allow");
179 | static public final Keyword COND_PRESERVE = Keyword.intern(null, "preserve");
180 |
181 | static public Object read(PushbackReader r, Object opts) {
182 | boolean eofIsError = true;
183 | Object eofValue = null;
184 | if (opts != null && opts instanceof IPersistentMap) {
185 | Object eof = ((IPersistentMap)opts).valAt(OPT_EOF, EOFTHROW);
186 | if (!EOFTHROW.equals(eof)) {
187 | eofIsError = false;
188 | eofValue = eof;
189 | }
190 | }
191 | return read(r, eofIsError, eofValue, false, opts);
192 | }
193 |
194 | static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive) {
195 | return read(r, eofIsError, eofValue, isRecursive, PersistentHashMap.EMPTY);
196 | }
197 |
198 | static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts) {
199 | // start with pendingForms null as reader conditional splicing is not allowed at top level
200 | return read(r, eofIsError, eofValue, null, null, isRecursive, opts, null);
201 | }
202 |
203 | static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts, Object pendingForms) {
204 | return read(r, eofIsError, eofValue, null, null, isRecursive, opts, ensurePending(pendingForms));
205 | }
206 |
207 | static private Object ensurePending(Object pendingForms) {
208 | if (pendingForms == null) {
209 | return new LinkedList();
210 | } else {
211 | return pendingForms;
212 | }
213 | }
214 |
215 | static private Object installPlatformFeature(Object opts) {
216 | if (opts == null) {
217 | return RT.mapUniqueKeys(clojure.lang.LispReader.OPT_FEATURES, PLATFORM_FEATURES);
218 | } else {
219 | IPersistentMap mopts = (IPersistentMap) opts;
220 | Object features = mopts.valAt(OPT_FEATURES);
221 | if (features == null) {
222 | return mopts.assoc(clojure.lang.LispReader.OPT_FEATURES, PLATFORM_FEATURES);
223 | } else {
224 | return mopts.assoc(clojure.lang.LispReader.OPT_FEATURES, RT.conj((IPersistentSet) features, PLATFORM_KEY));
225 | }
226 | }
227 | }
228 |
229 | static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, Character returnOn,
230 | Object returnOnValue, boolean isRecursive, Object opts, Object pendingForms) {
231 | if (RT.READEVAL.deref() == UNKNOWN) {
232 | throw Util.runtimeException("Reading disallowed - *read-eval* bound to :unknown");
233 | }
234 |
235 | opts = installPlatformFeature(opts);
236 |
237 | try {
238 | for(;;) {
239 |
240 | if (pendingForms instanceof List && !((List)pendingForms).isEmpty()) {
241 | return ((List) pendingForms).remove(0);
242 | }
243 |
244 | int ch = read1(r);
245 |
246 | while (isWhitespace(ch)) ch = read1(r);
247 |
248 | if (ch == -1) {
249 | if (eofIsError) throw Util.runtimeException("EOF while reading");
250 | return eofValue;
251 | }
252 |
253 | if (returnOn != null && (returnOn.charValue() == ch)) {
254 | return returnOnValue;
255 | }
256 |
257 | if (Character.isDigit(ch)) {
258 | Object n = readNumber(r, (char)ch);
259 | return n;
260 | }
261 |
262 | IFn macroFn = getMacro(ch);
263 | if (macroFn != null) {
264 | Object ret = macroFn.invoke(r, (char)ch, opts, pendingForms);
265 | // no op macros return the reader
266 | if (ret == r) continue;
267 | return ret;
268 | }
269 |
270 | if (ch == '+' || ch == '-') {
271 | int ch2 = read1(r);
272 | if (Character.isDigit(ch2)) {
273 | unread(r, ch2);
274 | Object n = readNumber(r, (char)ch);
275 | return n;
276 | }
277 | unread(r, ch2);
278 | }
279 |
280 | String token = readToken(r, (char) ch);
281 | return interpretToken(token);
282 | }
283 | } catch(Exception e) {
284 | if (isRecursive || !(r instanceof LineNumberingPushbackReader)) {
285 | throw Util.sneakyThrow(e);
286 | }
287 | LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
288 | throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
289 | }
290 | }
291 |
292 | static private String readToken(PushbackReader r, char initch) {
293 | StringBuilder sb = new StringBuilder();
294 | sb.append(initch);
295 |
296 | for (;;) {
297 | int ch = read1(r);
298 | if (ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch)) {
299 | unread(r, ch);
300 | return sb.toString();
301 | }
302 | sb.append((char)ch);
303 | }
304 | }
305 |
306 | static private Object readNumber(PushbackReader r, char initch) {
307 | StringBuilder sb = new StringBuilder();
308 | sb.append(initch);
309 |
310 | for (;;) {
311 | int ch = read1(r);
312 | if (ch == -1 || isWhitespace(ch) || isMacro(ch)) {
313 | unread(r, ch);
314 | break;
315 | }
316 | sb.append((char) ch);
317 | }
318 |
319 | String s = sb.toString();
320 | Object n = matchNumber(s);
321 | if (n == null) throw new NumberFormatException("Invalid number: " + s);
322 | return n;
323 | }
324 |
325 | static private int readUnicodeChar(String token, int offset, int length, int base) {
326 | if (token.length() != offset + length) {
327 | throw new IllegalArgumentException("Invalid unicode character: \\" + token);
328 | }
329 | int uc = 0;
330 | for (int i = offset; i < offset + length; ++i) {
331 | int d = Character.digit(token.charAt(i), base);
332 | if (d == -1) throw new IllegalArgumentException("Invalid digit: " + token.charAt(i));
333 | uc = uc * base + d;
334 | }
335 | return (char) uc;
336 | }
337 |
338 | static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) {
339 | int uc = Character.digit(initch, base);
340 | if (uc == -1) {
341 | throw new IllegalArgumentException("Invalid digit: " + (char) initch);
342 | }
343 | int i = 1;
344 | for (; i < length; ++i) {
345 | int ch = read1(r);
346 | if (ch == -1 || isWhitespace(ch) || isMacro(ch)) {
347 | unread(r, ch);
348 | break;
349 | }
350 | int d = Character.digit(ch, base);
351 | if (d == -1) throw new IllegalArgumentException("Invalid digit: " + (char) ch);
352 | uc = uc * base + d;
353 | }
354 | if (i != length && exact) {
355 | throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
356 | }
357 | return uc;
358 | }
359 |
360 | static private Object interpretToken(String s) {
361 | if (s.equals("nil")) {
362 | return null;
363 | } else if (s.equals("true")) {
364 | return RT.T;
365 | } else if (s.equals("false")) {
366 | return RT.F;
367 | }
368 | Object ret = null;
369 |
370 | ret = matchSymbol(s);
371 | if (ret != null) return ret;
372 |
373 | throw Util.runtimeException("Invalid token: " + s);
374 | }
375 |
376 |
377 | private static Object matchSymbol(String s){
378 | Matcher m = symbolPat.matcher(s);
379 | if (m.matches()) {
380 | int gc = m.groupCount();
381 | String ns = m.group(1);
382 | String name = m.group(2);
383 | if (ns != null && ns.endsWith(":/")
384 | || name.endsWith(":")
385 | || s.indexOf("::", 1) != -1) {
386 | return null;
387 | }
388 | if (s.startsWith("::")) {
389 | Symbol ks = Symbol.intern(s.substring(2));
390 | Namespace kns;
391 | if (ks.getNamespace() != null) {
392 | kns = namespaceFor(currentNS(), ks);
393 | } else {
394 | kns = currentNS();
395 | }
396 | //auto-resolving keyword
397 | if (kns != null) {
398 | return Keyword.intern(kns.name.getName(), ks.getName());
399 | } else {
400 | return null;
401 | }
402 | }
403 | boolean isKeyword = s.charAt(0) == ':';
404 | Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
405 | if (isKeyword) return Keyword.intern(sym);
406 | return sym;
407 | }
408 | return null;
409 | }
410 |
411 |
412 | private static Object matchNumber(String s){
413 | Matcher m = intPat.matcher(s);
414 | if (m.matches()) {
415 | if (m.group(2) != null) {
416 | if (m.group(8) != null) return BigInt.ZERO;
417 | return Numbers.num(0);
418 | }
419 | boolean negate = (m.group(1).equals("-"));
420 | String n;
421 | int radix = 10;
422 | if ((n = m.group(3)) != null) {
423 | radix = 10;
424 | } else if ((n = m.group(4)) != null) {
425 | radix = 16;
426 | } else if ((n = m.group(5)) != null) {
427 | radix = 8;
428 | } else if ((n = m.group(7)) != null) {
429 | radix = Integer.parseInt(m.group(6));
430 | }
431 | if (n == null) return null;
432 | BigInteger bn = new BigInteger(n, radix);
433 | if (negate) bn = bn.negate();
434 | if (m.group(8) != null) return BigInt.fromBigInteger(bn);
435 | return bn.bitLength() < 64 ?
436 | Numbers.num(bn.longValue())
437 | : BigInt.fromBigInteger(bn);
438 | }
439 | m = floatPat.matcher(s);
440 | if (m.matches()) {
441 | if (m.group(4) != null) return new BigDecimal(m.group(1));
442 | return Double.parseDouble(s);
443 | }
444 | m = ratioPat.matcher(s);
445 | if (m.matches()) {
446 | String numerator = m.group(1);
447 | if (numerator.startsWith("+")) numerator = numerator.substring(1);
448 |
449 | return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
450 | Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
451 | }
452 | return null;
453 | }
454 |
455 | static private IFn getMacro(int ch) {
456 | if (ch < macros.length) return macros[ch];
457 | return null;
458 | }
459 |
460 | static private boolean isMacro(int ch){
461 | return (ch < macros.length && macros[ch] != null);
462 | }
463 |
464 | static private boolean isTerminatingMacro(int ch){
465 | return (ch != '#' && ch != '\'' && ch != '%' && isMacro(ch));
466 | }
467 |
468 | public static class RegexReader extends AFn {
469 | static StringReader stringrdr = new StringReader();
470 |
471 | public Object invoke(Object reader, Object doublequote, Object opts, Object pendingForms) {
472 | StringBuilder sb = new StringBuilder();
473 | Reader r = (Reader) reader;
474 | for (int ch = read1(r); ch != '"'; ch = read1(r)) {
475 | if (ch == -1) throw Util.runtimeException("EOF while reading regex");
476 | sb.append( (char) ch );
477 | if (ch == '\\') { //escape
478 | ch = read1(r);
479 | if (ch == -1) throw Util.runtimeException("EOF while reading regex");
480 | sb.append( (char) ch ) ;
481 | }
482 | }
483 | return Pattern.compile(sb.toString());
484 | }
485 | }
486 |
487 | public static class CommaReader extends AFn {
488 | public Object invoke(Object reader, Object doublequote, Object opts, Object pendingForms) {
489 | return SyntaxElement.COMMA_SYNTAX;
490 | }
491 | }
492 |
493 | public static class StringReader extends AFn {
494 | public Object invoke(Object reader, Object doublequote, Object opts, Object pendingForms) {
495 | StringBuilder sb = new StringBuilder();
496 | Reader r = (Reader)reader;
497 |
498 | for (int ch = read1(r); ch != '"'; ch = read1(r)) {
499 | if (ch == -1) throw Util.runtimeException("EOF while reading string");
500 | if (ch == '\\') { //escape
501 | ch = read1(r);
502 | if (ch == -1) throw Util.runtimeException("EOF while reading string");
503 | switch(ch) {
504 | case 't':
505 | ch = '\t';
506 | break;
507 | case 'r':
508 | ch = '\r';
509 | break;
510 | case 'n':
511 | ch = '\n';
512 | break;
513 | case '\\':
514 | break;
515 | case '"':
516 | break;
517 | case 'b':
518 | ch = '\b';
519 | break;
520 | case 'f':
521 | ch = '\f';
522 | break;
523 | case 'u': {
524 | ch = read1(r);
525 | if (Character.digit(ch, 16) == -1) {
526 | throw Util.runtimeException("Invalid unicode escape: \\u" + (char)ch);
527 | }
528 | ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
529 | break;
530 | }
531 | default: {
532 | if (Character.isDigit(ch)) {
533 | ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
534 | if (ch > 0377) {
535 | throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
536 | }
537 | } else {
538 | throw Util.runtimeException("Unsupported escape character: \\" + (char)ch);
539 | }
540 | }
541 | }
542 | }
543 | sb.append((char)ch);
544 | }
545 | return sb.toString();
546 | }
547 | }
548 |
549 | private static abstract class CommentReader extends AFn {
550 | public Object invoke(Object reader, Object semicolon, Object opts, Object pendingForms) {
551 | Reader r = (Reader) reader;
552 | StringBuilder sb = new StringBuilder();
553 | for (int ch = read1(r); ch != -1 && ch != '\n' && ch != '\r'; ch = read1(r)) {
554 | sb.append((char)ch);
555 | }
556 | return sb.toString();
557 | }
558 | }
559 |
560 | public static class SyntaxCommentReader extends CommentReader {
561 | public Object invoke(Object reader, Object semicolon, Object opts, Object pendingForms) {
562 | return new SyntaxElement(SyntaxElement.Type.COMMENT, super.invoke(reader, semicolon, opts, pendingForms));
563 | }
564 | }
565 |
566 | public static class MacroCommentReader extends CommentReader {
567 | public Object invoke(Object reader, Object semicolon, Object opts, Object pendingForms) {
568 | return new SyntaxElement(Type.M_COMMENT, super.invoke(reader, semicolon, opts, pendingForms));
569 | }
570 | }
571 |
572 | public static class DiscardReader extends AFn {
573 | public Object invoke(Object reader, Object underscore, Object opts, Object pendingForms) {
574 | PushbackReader r = (PushbackReader) reader;
575 | Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
576 | return new SyntaxElement(SyntaxElement.Type.DISCARD, form);
577 | }
578 | }
579 |
580 | public static class WrappingReader extends AFn {
581 | final SyntaxElement.Type t;
582 |
583 | public WrappingReader(SyntaxElement.Type t) { this.t = t; }
584 |
585 | public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
586 | PushbackReader r = (PushbackReader) reader;
587 | Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
588 | return new SyntaxElement(t, o);
589 | }
590 | }
591 |
592 | public static class DeprecatedWrappingReader extends AFn {
593 | final Symbol sym;
594 | final String macro;
595 |
596 | public DeprecatedWrappingReader(Symbol sym, String macro){
597 | this.sym = sym;
598 | this.macro = macro;
599 | }
600 |
601 | public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
602 | System.out.println("WARNING: reader macro " + macro +
603 | " is deprecated; use " + sym.getName() +
604 | " instead");
605 | PushbackReader r = (PushbackReader) reader;
606 | Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
607 | return RT.list(sym, o);
608 | }
609 |
610 | }
611 |
612 | public static class VarReader extends AFn {
613 | public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
614 | PushbackReader r = (PushbackReader) reader;
615 | Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
616 | return new SyntaxElement(SyntaxElement.Type.VAR, RT.list(THE_VAR, o));
617 | }
618 | }
619 |
620 | public static class DispatchReader extends AFn {
621 | public Object invoke(Object reader, Object hash, Object opts, Object pendingForms) {
622 | int ch = read1((Reader)reader);
623 | if (ch == -1) throw Util.runtimeException("EOF while reading character");
624 | IFn fn = dispatchMacros[ch];
625 |
626 | // Try the ctor reader first
627 | if (fn == null) {
628 | unread((PushbackReader) reader, ch);
629 | pendingForms = ensurePending(pendingForms);
630 | Object result = ctorReader.invoke(reader, ch, opts, pendingForms);
631 |
632 | if (result != null) {
633 | return result;
634 | } else {
635 | throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
636 | }
637 | }
638 | return fn.invoke(reader, ch, opts, pendingForms);
639 | }
640 | }
641 |
642 | static Symbol garg(int n){
643 | return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID() + "#");
644 | }
645 |
646 | public static class FnReader extends AFn {
647 | public Object invoke(Object reader, Object lparen, Object opts, Object pendingForms) {
648 | PushbackReader r = (PushbackReader) reader;
649 | if (ARG_ENV.deref() != null) {
650 | throw new IllegalStateException("Nested #()s are not allowed");
651 | }
652 | try {
653 | Var.pushThreadBindings(RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
654 | unread(r, '(');
655 | Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
656 | return new SyntaxElement(SyntaxElement.Type.FN, form);
657 | } finally {
658 | Var.popThreadBindings();
659 | }
660 | }
661 | }
662 |
663 | static Symbol registerArg(int n){
664 | PersistentTreeMap argsyms = (PersistentTreeMap)ARG_ENV.deref();
665 | if (argsyms == null) {
666 | throw new IllegalStateException("arg literal not in #()");
667 | }
668 | Symbol ret = (Symbol)argsyms.valAt(n);
669 | if (ret == null) {
670 | ret = garg(n);
671 | ARG_ENV.set(argsyms.assoc(n, ret));
672 | }
673 | return ret;
674 | }
675 |
676 | static class ArgReader extends AFn {
677 | public Object invoke(Object reader, Object pct, Object opts, Object pendingForms) {
678 | PushbackReader r = (PushbackReader) reader;
679 | return new SyntaxElement(SyntaxElement.Type.ARG, interpretToken(readToken(r, '%')));
680 | }
681 | }
682 |
683 | public static class MetaReader extends AFn {
684 | public Object invoke(Object reader, Object caret, Object opts, Object pendingForms) {
685 | PushbackReader r = (PushbackReader)reader;
686 | int line = -1;
687 | int column = -1;
688 | if (r instanceof LineNumberingPushbackReader) {
689 | line = ((LineNumberingPushbackReader) r).getLineNumber();
690 | column = ((LineNumberingPushbackReader) r).getColumnNumber() - 1;
691 | }
692 | pendingForms = ensurePending(pendingForms);
693 | Object meta = read(r, true, null, true, opts, pendingForms);
694 | if (meta instanceof Symbol || meta instanceof String) {
695 | meta = RT.map(TAG_KEY, meta);
696 | } else if (meta instanceof Keyword) {
697 | meta = RT.map(KEYWORD_KEY, meta);
698 | } else if (meta instanceof IPersistentMap) {
699 | meta = RT.map(MAP_KEY, meta);
700 | } else {
701 | throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
702 | }
703 |
704 | Object o = read(r, true, null, true, opts, pendingForms);
705 | if (o instanceof IMeta) {
706 | if (line != -1 && o instanceof ISeq) {
707 | meta = ((IPersistentMap) meta).assoc(LINE_KEY, line).assoc(COLUMN_KEY, column);
708 | }
709 | return new SyntaxElement(SyntaxElement.Type.META, RT.map(META_KEY, meta, OBJECT_KEY, o));
710 | } else {
711 | throw new IllegalArgumentException("Metadata can only be applied to IMetas");
712 | }
713 | }
714 | }
715 |
716 | public static class SyntaxQuoteReader extends AFn {
717 | public Object invoke(Object reader, Object backquote, Object opts, Object pendingForms) {
718 | PushbackReader r = (PushbackReader) reader;
719 | try {
720 | Var.pushThreadBindings( RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
721 |
722 | Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
723 | return syntaxQuote(form);
724 | } finally {
725 | Var.popThreadBindings();
726 | }
727 | }
728 |
729 | static Object syntaxQuote(Object form) {
730 | if (isUnquoteSplicing(form)) {
731 | throw new IllegalStateException("splice not in list");
732 | }
733 | Object ret;
734 | if (form instanceof Keyword
735 | || form instanceof Number
736 | || form instanceof Character
737 | || form instanceof String) {
738 | ret = form;
739 | } else {
740 | ret = new SyntaxElement(SyntaxElement.Type.SYNTAX_QUOTE, form);
741 | }
742 |
743 | if (form instanceof IObj && RT.meta(form) != null) {
744 | //filter line and column numbers
745 | IPersistentMap newMeta = ((IObj)form).meta().without(LINE_KEY).without(COLUMN_KEY);
746 | if (newMeta.count() > 0) return RT.list(WITH_META, ret, syntaxQuote(((IObj)form).meta()));
747 | }
748 | return ret;
749 | }
750 |
751 | private static ISeq sqExpandList(ISeq seq) {
752 | PersistentVector ret = PersistentVector.EMPTY;
753 | for (; seq != null; seq = seq.next()) {
754 | Object item = seq.first();
755 | if (isUnquote(item)) {
756 | ret = ret.cons(RT.list(LIST, RT.second(item)));
757 | } else if (isUnquoteSplicing(item)) {
758 | ret = ret.cons(RT.second(item));
759 | } else {
760 | ret = ret.cons(RT.list(LIST, syntaxQuote(item)));
761 | }
762 | }
763 | return ret.seq();
764 | }
765 |
766 | }
767 |
768 | static boolean isUnquoteSplicing(Object form){
769 | return form instanceof ISeq && Util.equals(RT.first(form), UNQUOTE_SPLICING);
770 | }
771 |
772 | static boolean isUnquote(Object form){
773 | return form instanceof ISeq && Util.equals(RT.first(form), UNQUOTE);
774 | }
775 |
776 | static class UnquoteReader extends AFn {
777 | public Object invoke(Object reader, Object comma, Object opts, Object pendingForms) {
778 | PushbackReader r = (PushbackReader)reader;
779 | int ch = read1(r);
780 | if (ch == -1) throw Util.runtimeException("EOF while reading character");
781 | pendingForms = ensurePending(pendingForms);
782 | if (ch == '@') {
783 | Object o = read(r, true, null, true, opts, pendingForms);
784 | return new SyntaxElement(SyntaxElement.Type.UNQUOTE_SPLICING, o);
785 | } else {
786 | unread(r, ch);
787 | Object o = read(r, true, null, true, opts, pendingForms);
788 | return new SyntaxElement(Type.UNQUOTE, o);
789 | }
790 | }
791 | }
792 |
793 | public static class CharacterReader extends AFn {
794 | private SyntaxElement cse(char c) { return new SyntaxElement(SyntaxElement.Type.CHAR, c); }
795 |
796 | public Object invoke(Object reader, Object backslash, Object opts, Object pendingForms) {
797 | PushbackReader r = (PushbackReader) reader;
798 | int ch = read1(r);
799 | if (ch == -1) throw Util.runtimeException("EOF while reading character");
800 | String token = readToken(r, (char) ch);
801 | if (token.length() == 1) {
802 | return cse(Character.valueOf(token.charAt(0)));
803 | } else if (token.equals("newline")) {
804 | return cse('\n');
805 | } else if (token.equals("space")) {
806 | return cse(' ');
807 | } else if (token.equals("tab")) {
808 | return cse('\t');
809 | } else if (token.equals("backspace")) {
810 | return cse('\b');
811 | } else if (token.equals("formfeed")) {
812 | return cse('\f');
813 | } else if (token.equals("return")) {
814 | return cse('\r');
815 | } else if (token.startsWith("u")) {
816 | char c = (char) readUnicodeChar(token, 1, 4, 16);
817 | if (c >= '\uD800' && c <= '\uDFFF') { // surrogate code unit?
818 | throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
819 | }
820 | return cse(c);
821 | } else if (token.startsWith("o")) {
822 | int len = token.length() - 1;
823 | if (len > 3) throw Util.runtimeException("Invalid octal escape sequence length: " + len);
824 | int uc = readUnicodeChar(token, 1, len, 8);
825 | if (uc > 0377) throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
826 | return cse((char)uc);
827 | }
828 | throw Util.runtimeException("Unsupported character: \\" + token);
829 | }
830 | }
831 |
832 | public static class ListReader extends AFn {
833 | public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
834 | PushbackReader r = (PushbackReader) reader;
835 | int line = -1;
836 | int column = -1;
837 | if (r instanceof LineNumberingPushbackReader) {
838 | line = ((LineNumberingPushbackReader)r).getLineNumber();
839 | column = ((LineNumberingPushbackReader)r).getColumnNumber() - 1;
840 | }
841 | List list = readDelimitedList(')', r, true, opts, ensurePending(pendingForms));
842 | if (list.isEmpty()) return PersistentList.EMPTY;
843 | IObj s = (IObj) PersistentList.create(list);
844 | if (line != -1) {
845 | return s.withMeta(RT.map(LINE_KEY, line, COLUMN_KEY, column));
846 | } else {
847 | return s;
848 | }
849 | }
850 |
851 | }
852 |
853 | public static class EvalReader extends AFn {
854 | public Object invoke(Object reader, Object eq, Object opts, Object pendingForms) {
855 | if (!RT.booleanCast(RT.READEVAL.deref())) {
856 | throw Util.runtimeException("EvalReader not allowed when *read-eval* is false.");
857 | }
858 |
859 | PushbackReader r = (PushbackReader) reader;
860 | Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
861 | return new SyntaxElement(SyntaxElement.Type.EVAL, o);
862 | }
863 | }
864 |
865 | public static class VectorReader extends AFn {
866 | public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
867 | PushbackReader r = (PushbackReader)reader;
868 | return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts, ensurePending(pendingForms)));
869 | }
870 | }
871 |
872 | public static class MapReader extends AFn {
873 | public Object invoke(Object reader, Object leftparen, Object opts, Object pendingForms) {
874 | PushbackReader r = (PushbackReader)reader;
875 | List a = readDelimitedList('}', r, true, opts, ensurePending(pendingForms));
876 | int skipped = 0;
877 | for (Object e: a) {
878 | if (e instanceof SyntaxElement && ((SyntaxElement)e).skippable()) skipped++;
879 | }
880 | if (((a.size() - skipped) & 1) == 1) {
881 | throw Util.runtimeException("Map literal must contain an even number of forms");
882 | }
883 | return new SyntaxElement(SyntaxElement.Type.MAP, a);
884 | }
885 | }
886 |
887 | public static class SetReader extends AFn {
888 | public Object invoke(Object reader, Object leftbracket, Object opts, Object pendingForms) {
889 | PushbackReader r = (PushbackReader)reader;
890 | return new SyntaxElement(SyntaxElement.Type.SET, readDelimitedList('}', r, true, opts, ensurePending(pendingForms)));
891 | }
892 | }
893 |
894 | public static class UnmatchedDelimiterReader extends AFn {
895 | public Object invoke(Object reader, Object rightdelim, Object opts, Object pendingForms) {
896 | throw Util.runtimeException("Unmatched delimiter: " + rightdelim);
897 | }
898 |
899 | }
900 |
901 | public static class UnreadableReader extends AFn {
902 | public Object invoke(Object reader, Object leftangle, Object opts, Object pendingForms) {
903 | throw Util.runtimeException("Unreadable form");
904 | }
905 | }
906 |
907 | // Sentinel values for reading lists
908 | private static final Object READ_EOF = new Object();
909 | private static final Object READ_FINISHED = new Object();
910 |
911 | public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts, Object pendingForms) {
912 | final int firstline =
913 | (r instanceof LineNumberingPushbackReader) ?
914 | ((LineNumberingPushbackReader)r).getLineNumber() : -1;
915 |
916 | ArrayList a = new ArrayList();
917 |
918 | for (;;) {
919 |
920 | Object form = read(r, false, READ_EOF, delim, READ_FINISHED, isRecursive, opts, pendingForms);
921 |
922 | if (form == READ_EOF) {
923 | if (firstline < 0) {
924 | throw Util.runtimeException("EOF while reading");
925 | } else {
926 | throw Util.runtimeException("EOF while reading, starting at line " + firstline);
927 | }
928 | } else if (form == READ_FINISHED) {
929 | return a;
930 | }
931 |
932 | a.add(form);
933 | }
934 | }
935 |
936 | public static class CtorReader extends AFn {
937 | public Object invoke(Object reader, Object firstChar, Object opts, Object pendingForms){
938 | PushbackReader r = (PushbackReader) reader;
939 | pendingForms = ensurePending(pendingForms);
940 | Object name = read(r, true, null, false, opts, pendingForms);
941 | if (!(name instanceof Symbol))
942 | throw new RuntimeException("Reader tag must be a symbol");
943 | Symbol sym = (Symbol)name;
944 | Object form = read(r, true, null, true, opts, pendingForms);
945 |
946 | if (isPreserveReadCond(opts) || RT.suppressRead()) {
947 | return TaggedLiteral.create(sym, form);
948 | } else {
949 | return sym.getName().contains(".") ? readRecord(form, sym, opts, pendingForms) : readTagged(form, sym, opts, pendingForms);
950 | }
951 |
952 | }
953 |
954 | private Object readTagged(Object o, Symbol tag, Object opts, Object pendingForms) {
955 | ILookup data_readers = (ILookup)RT.DATA_READERS.deref();
956 | IFn data_reader = (IFn)RT.get(data_readers, tag);
957 | if (data_reader == null) {
958 | data_readers = (ILookup)RT.DEFAULT_DATA_READERS.deref();
959 | data_reader = (IFn)RT.get(data_readers, tag);
960 | if (data_reader == null) {
961 | IFn default_reader = (IFn)RT.DEFAULT_DATA_READER_FN.deref();
962 | if (default_reader != null) {
963 | return default_reader.invoke(tag, o);
964 | } else {
965 | throw new RuntimeException("No reader function for tag " + tag.toString());
966 | }
967 | }
968 | }
969 |
970 | return data_reader.invoke(o);
971 | }
972 |
973 | private Object readRecord(Object form, Symbol recordName, Object opts, Object pendingForms) {
974 | boolean readeval = RT.booleanCast(RT.READEVAL.deref());
975 |
976 | if (!readeval) {
977 | throw Util.runtimeException("Record construction syntax can only be used when *read-eval* == true");
978 | }
979 |
980 | Class recordClass = RT.classForNameNonLoading(recordName.toString());
981 |
982 |
983 | boolean shortForm = true;
984 |
985 | if (form instanceof IPersistentMap) {
986 | shortForm = false;
987 | } else if (form instanceof IPersistentVector) {
988 | shortForm = true;
989 | } else {
990 | throw Util.runtimeException("Unreadable constructor form starting with \"#" + recordName + "\"");
991 | }
992 |
993 | Object ret = null;
994 | Constructor[] allctors = recordClass.getConstructors();
995 |
996 | if (shortForm) {
997 | IPersistentVector recordEntries = (IPersistentVector)form;
998 | boolean ctorFound = false;
999 | for (Constructor ctor : allctors) {
1000 | if (ctor.getParameterTypes().length == recordEntries.count()) ctorFound = true;
1001 | }
1002 |
1003 | if (!ctorFound) {
1004 | throw Util.runtimeException("Unexpected number of constructor arguments to " + recordClass.toString() + ": got " + recordEntries.count());
1005 | }
1006 |
1007 | ret = Reflector.invokeConstructor(recordClass, RT.toArray(recordEntries));
1008 |
1009 | } else {
1010 |
1011 | IPersistentMap vals = (IPersistentMap)form;
1012 | for (ISeq s = RT.keys(vals); s != null; s = s.next()) {
1013 | if (!(s.first() instanceof Keyword)) {
1014 | throw Util.runtimeException("Unreadable defrecord form: key must be of type clojure.lang.Keyword, got " + s.first().toString());
1015 | }
1016 | }
1017 | ret = Reflector.invokeStaticMethod(recordClass, "create", new Object[]{vals});
1018 | }
1019 |
1020 | return ret;
1021 | }
1022 | }
1023 |
1024 | static boolean isPreserveReadCond(Object opts) {
1025 | if (RT.booleanCast(READ_COND_ENV.deref()) && opts instanceof IPersistentMap) {
1026 | Object readCond = ((IPersistentMap)opts).valAt(OPT_READ_COND);
1027 | return COND_PRESERVE.equals(readCond);
1028 | } else {
1029 | return false;
1030 | }
1031 | }
1032 |
1033 | public static class ConditionalReader extends AFn {
1034 |
1035 | final static public IPersistentSet RESERVED_FEATURES =
1036 | RT.set(Keyword.intern(null, "else"), Keyword.intern(null, "none"));
1037 |
1038 | private static void checkConditionalAllowed(Object opts) {
1039 | IPersistentMap mopts = (IPersistentMap)opts;
1040 | if (! (opts != null && (COND_ALLOW.equals(mopts.valAt(OPT_READ_COND)) ||
1041 | COND_PRESERVE.equals(mopts.valAt(OPT_READ_COND))))) {
1042 | throw Util.runtimeException("Conditional read not allowed");
1043 | }
1044 | }
1045 |
1046 | public Object invoke(Object reader, Object mode, Object opts, Object pendingForms) {
1047 | checkConditionalAllowed(opts);
1048 |
1049 | PushbackReader r = (PushbackReader)reader;
1050 | int ch = read1(r);
1051 | if (ch == -1) throw Util.runtimeException("EOF while reading character");
1052 |
1053 | boolean splicing = false;
1054 |
1055 | if (ch == '@') {
1056 | splicing = true;
1057 | ch = read1(r);
1058 | }
1059 |
1060 | while (isWhitespace(ch)) ch = read1(r);
1061 |
1062 | if (ch == -1) throw Util.runtimeException("EOF while reading character");
1063 |
1064 | if (ch != '(') throw Util.runtimeException("read-cond body must be a list");
1065 |
1066 | int line = -1;
1067 | int column = -1;
1068 | if (r instanceof LineNumberingPushbackReader) {
1069 | line = ((LineNumberingPushbackReader)r).getLineNumber();
1070 | column = ((LineNumberingPushbackReader)r).getColumnNumber() - 1;
1071 | }
1072 |
1073 | try {
1074 | Var.pushThreadBindings(RT.map(READ_COND_ENV, RT.T));
1075 |
1076 | List list = readDelimitedList(')', r, true, opts, ensurePending(pendingForms));
1077 | if (list.size() % 2 != 0) throw Util.runtimeException("conditional macros require type/form pairs");
1078 | for (int i = 0; i < list.size(); i++) {
1079 | Object k = list.get(i);
1080 | if (0 == i % 2) {
1081 | if (!(k instanceof Keyword)) throw Util.runtimeException("conditional macro conditions must be a keyword");
1082 | if (RESERVED_FEATURES.contains(k)) throw Util.runtimeException("Feature name " + k + " is reserved.");
1083 | } else {
1084 | if (splicing && !(k instanceof List)) throw Util.runtimeException("Spliced macro conditionals must be a list");
1085 | }
1086 | }
1087 | IObj s = (IObj)PersistentList.create(list);
1088 | Object result;
1089 | if (line != -1) {
1090 | result = s.withMeta(RT.map(LINE_KEY, line, COLUMN_KEY, column));
1091 | } else {
1092 | result = s;
1093 | }
1094 | return new SyntaxElement(SyntaxElement.Type.CONDITIONAL,
1095 | RT.map(SyntaxElement.SPLICE_KEY, splicing,
1096 | SyntaxElement.FORM_KEY, result));
1097 | } finally {
1098 | Var.popThreadBindings();
1099 | }
1100 | }
1101 | }
1102 |
1103 | /*
1104 | public static void main(String[] args) throws Exception{
1105 | //RT.init();
1106 | PushbackReader rdr = new PushbackReader( new java.io.StringReader( "(+ 21 21)" ) );
1107 | Object input = LispReader.read(rdr, false, new Object(), false );
1108 | System.out.println(Compiler.eval(input));
1109 | }
1110 |
1111 | public static void main(String[] args){
1112 | LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in));
1113 | OutputStreamWriter w = new OutputStreamWriter(System.out);
1114 | Object ret = null;
1115 | try
1116 | {
1117 | for(; ;)
1118 | {
1119 | ret = LispReader.read(r, true, null, false);
1120 | RT.print(ret, w);
1121 | w.write('\n');
1122 | if (ret != null)
1123 | w.write(ret.getClass().toString());
1124 | w.write('\n');
1125 | w.flush();
1126 | }
1127 | }
1128 | catch(Exception e)
1129 | {
1130 | e.printStackTrace();
1131 | }
1132 | }
1133 | */
1134 |
1135 | }
1136 |
--------------------------------------------------------------------------------
/src/java/cst/SyntaxElement.java:
--------------------------------------------------------------------------------
1 | package cst;
2 |
3 | import clojure.lang.*;
4 |
5 | import java.io.IOException;
6 | import java.io.StringWriter;
7 | import java.util.Collection;
8 | import java.util.List;
9 |
10 | /**
11 | * Indicates an element of syntax, without an exact corollory in the AST structure.
12 | * Includes functions for conversion to macro text, based on the macro type.
13 | */
14 | public class SyntaxElement {
15 |
16 | final static Keyword TAG_KEY = Keyword.intern(null, "tag");
17 | final static Keyword KEYWORD_KEY = Keyword.intern(null, "keyword");
18 | final static Keyword MAP_KEY = Keyword.intern(null, "map");
19 | final static Keyword META_KEY = Keyword.intern(null, "meta");
20 | final static Keyword OBJECT_KEY = Keyword.intern(null, "object");
21 | final static Keyword SPLICE_KEY = Keyword.intern(null, "splice");
22 | final static Keyword FORM_KEY = Keyword.intern(null, "form");
23 |
24 | static final SyntaxElement COMMA_SYNTAX = new SyntaxElement(SyntaxElement.Type.COMMA);
25 |
26 | static private String join(String separator, Collection c) {
27 | StringBuffer sb = new StringBuffer();
28 | boolean first = true;
29 | for (Object o: c) {
30 | if (first) first = false;
31 | else if (o != COMMA_SYNTAX) sb.append(separator);
32 | sb.append(emit(o));
33 | }
34 | return sb.toString();
35 | }
36 | static private String spaceJoin(Collection c) {
37 | return join(" ", c);
38 | }
39 |
40 | public enum Type {
41 | SET {
42 | public String str(Object e) { return "#{" + spaceJoin((List) e) + "}"; }
43 | },
44 | COMMA {
45 | public String str(Object e) { return ","; }
46 | public boolean skippable() { return true; }
47 | },
48 | COMMENT {
49 | public String str(Object e) { return ";" + e + "\n"; }
50 | public boolean skippable() { return true; }
51 | },
52 | QUOTE{
53 | public String str(Object e) { return "'" + emit(e); }
54 | },
55 | DEREF {
56 | public String str(Object e) { return "@" + emit(e); }
57 | },
58 | META{
59 | public String str(Object e) {
60 | Object form = ((IPersistentMap)e).valAt(OBJECT_KEY);
61 | IPersistentMap meta = (IPersistentMap)((IPersistentMap)e).valAt(META_KEY);
62 |
63 | Object tag = meta.valAt(TAG_KEY);
64 | if (tag != null) return "^" + emit(tag) + " " + emit(form);
65 |
66 | Keyword keyword = (Keyword)meta.valAt(KEYWORD_KEY);
67 | if (keyword != null) return "^" + keyword + " " + emit(form);
68 |
69 | IPersistentMap map = (IPersistentMap)meta.valAt(MAP_KEY);
70 | if (map != null) return "^" + emit(map) + " " + emit(form);
71 |
72 | throw new IllegalStateException("Structure for Meta is unknown: " + e); }
73 | },
74 | SYNTAX_QUOTE {
75 | public String str(Object e) { return "`" + emit(e); }
76 | },
77 | UNQUOTE {
78 | public String str(Object e) { return "~" + emit(e); }
79 | },
80 | UNQUOTE_SPLICING {
81 | public String str(Object e) { return "~@" + emit(e); }
82 | },
83 | CHAR {
84 | public String str(Object e) {
85 | String d = (String) e;
86 | if (d.length() == 1) {
87 | switch (d.charAt(0)) {
88 | case '\n': return "\\newline";
89 | case ' ': return "\\space";
90 | case '\t': return "\\tab";
91 | case '\b': return "\\backspace";
92 | case '\f': return "\\formfeed";
93 | case '\r': return "\\return";
94 | default: return d;
95 | }
96 | } else {
97 | return (d.length() == 3) ? "o" + d : "u" + d;
98 | }
99 | }
100 | },
101 | ARG{
102 | public String str(Object e) {
103 | if (null == e) return "%";
104 | else return emit(e);
105 | }
106 | },
107 | EVAL {
108 | public String str(Object e) { return "#=" + emit(e);}
109 | },
110 | VAR {
111 | public String str(Object e) { return "#'" + emit(e); }
112 | },
113 | FN {
114 | public String str(Object e) { return "#" + emit(e); }
115 | },
116 | M_COMMENT {
117 | public String str(Object e) { return "#!" + emit(e); }
118 | public boolean skippable() { return true; }
119 | },
120 | DISCARD {
121 | public String str(Object e) { return "#_" + emit(e); }
122 | public boolean skippable() { return true; }
123 | },
124 | CONDITIONAL {
125 | public String str(Object e) {
126 | Object form = ((IPersistentMap)e).valAt(FORM_KEY);
127 | Boolean splicing = (Boolean)((IPersistentMap)e).valAt(SPLICE_KEY);
128 | return "#?" + (splicing ? "@" : "") + emit(form);
129 | }
130 | },
131 | VECTOR {
132 | public String str(Object e) { return "[" + spaceJoin((List) e) + "]"; }
133 | },
134 | LIST {
135 | public String str(Object e) { return "(" + spaceJoin((List) e) + ")"; }
136 | },
137 | MAP {
138 | public String str(Object e) { return "{" + spaceJoin((List) e) + "}"; }
139 | },
140 | FILE {
141 | public String str(Object e) { return join("\n", (Collection)e); }
142 | };
143 | public abstract String str(Object e);
144 | public boolean skippable() { return false; };
145 | public final Keyword id;
146 | Type() { id = Keyword.intern("cst", name().toLowerCase()); }
147 | };
148 |
149 | public final Type type;
150 | public final Object data;
151 |
152 | public SyntaxElement(Type type) {
153 | this.type = type;
154 | this.data = null;
155 | }
156 |
157 | public SyntaxElement(Type type, Object data) {
158 | this.type = type;
159 | this.data = data;
160 | }
161 |
162 | public Keyword id() { return type.id; }
163 |
164 | public boolean skippable() {
165 | return type.skippable();
166 | }
167 |
168 | public String emit() {
169 | return type.str(data);
170 | }
171 |
172 | public static String emit(Object o) {
173 | if (o instanceof IPersistentVector) return Type.VECTOR.str(o);
174 | if (o instanceof IPersistentList) return Type.LIST.str(o);
175 | if (o instanceof IPersistentSet) return Type.SET.str(o);
176 | if (o instanceof SyntaxElement) return ((SyntaxElement)o).emit();
177 | StringWriter w = new StringWriter();
178 | try {
179 | clojure.lang.RT.print(o, w);
180 | } catch (IOException e) {
181 | throw new ExceptionInfo("Error in string output", RT.map(), e);
182 | }
183 | return w.toString();
184 | }
185 |
186 | public String toString() {
187 | return "<" + type.name() + ": " + data + ">";
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/test/clj/cst/database_test.clj:
--------------------------------------------------------------------------------
1 | (ns cst.database-test
2 | (:use [clojure.test]
3 | [clojure.pprint]
4 | [util.macro]
5 | [cst.database]
6 | [cst.reader]
7 | [datomic.api :refer [q] :as d])
8 | (:require [clojure.string :as str]
9 | [cst.path :as path])
10 | (:import [datomic.db DbId]
11 | [datomic.query EntityMap]
12 | (java.util Map)))
13 |
14 | (defn blankify-nodes ;; TODO Skolemize for more detailed comparisons
15 | [m]
16 | (letfn [(to-blank [n]
17 | (cond
18 | (instance? DbId n) :blank
19 | (sequential? n) (map to-blank n)
20 | :default n))]
21 | (into {} (keep (fn [[k v]]
22 | (if (not= k :cst/location)
23 | [k (to-blank v)]
24 | (if-not (is (str/starts-with? (str v) "uuid:"))
25 | [k v])))
26 | m))))
27 |
28 | (deftest simple-list
29 | (let [[id-node the-data] (list-data [1 2] :vector nil)
30 | elts (map blankify-nodes the-data)
31 | node (first the-data)
32 | bnode (first elts)]
33 | (is (= id-node (:db/id node)))
34 | (is (= {:db/id :blank
35 | :cst/type :vector
36 | :cst.value/long 1
37 | :cst/rest :blank}
38 | bnode))
39 | (is (every? #(= :blank (:db/id %)) elts))
40 | (is (= #{1 2} (set (map :cst.value/long the-data))))
41 | (is (= (count elts) 2))))
42 |
43 | (deftest natives
44 | (let [tx (tx-data (cst-read-all-string "5"))
45 | btx (map blankify-nodes tx)]
46 | (is (= [{:db/id :blank
47 | :cst/type :file
48 | :cst.value/long 5}] btx)))
49 | (let [tx (tx-data (cst-read-all-string "5\n:foo"))
50 | btx (map blankify-nodes tx)]
51 | (is (= [{:db/id :blank
52 | :cst/type :file
53 | :cst.value/long 5
54 | :cst/rest :blank}
55 | {:db/id :blank
56 | :cst.value/keyword :foo}] btx))))
57 |
58 | (deftest short-ns
59 | (let [fhello "(ns cst.test-hello)\n(println \"Hello world\")"
60 | chello (cst-read-all-string fhello)
61 | tx (tx-data chello)
62 | btx (->> tx (map blankify-nodes) (map #(dissoc % :cst/location)))]
63 | (is (= #{{:db/id :blank, :cst/type :file, :cst.value/object :blank, :cst/rest :blank}
64 | {:db/id :blank, :cst/type :list, :cst.value/symbol "ns", :cst/rest :blank}
65 | {:db/id :blank, :cst.value/symbol "cst.test-hello"}
66 | {:db/id :blank, :cst/type :list, :cst.value/symbol "println", :cst/rest :blank}
67 | {:db/id :blank, :cst.value/string "Hello world"}
68 | {:db/id :blank, :cst.value/object :blank}}
69 | (set btx)))
70 | (println "TX: " tx)))
71 |
72 | (deftest save-program
73 | (let [fhello "(ns cst.test-hello)\n(println \"Hello world\")"
74 | chello (cst-read-all-string fhello)
75 | tx (tx-data chello)
76 | cnx (database "datomic:mem://src")
77 | _ (d/transact cnx tx)
78 | db (d/db cnx)
79 | pid (q '[:find ?pid . :where [?pid :cst/type :file]] db)
80 | prog (d/touch (d/entity db pid))
81 | p (d/pull db '[*] pid)]
82 | (pprint p)
83 | ))
84 |
85 | (defmacro with-connection [bindings & body]
86 | (assert-args
87 | (vector? bindings) "a vector for its binding"
88 | (even? (count bindings)) "an even number of forms in binding vector")
89 | (cond
90 | (= (count bindings) 0) `(do ~@body)
91 | (symbol? (bindings 0))
92 | (let [s# (bindings 0)
93 | uri# (bindings 1)]
94 | `(if (d/create-database ~uri#)
95 | (let [~s# (d/connect ~uri#)]
96 | (try
97 | (load-schema ~s#)
98 | (with-connection ~(subvec bindings 2) ~@body)
99 | (finally (d/release ~s#)
100 | (d/delete-database ~uri#))))
101 | (throw (ex-info (str "Unable to create database: " ~uri#) {:uri ~uri#}))))
102 | :else (throw (IllegalArgumentException. "with-db only allows Symbols to be bound"))))
103 |
104 | (def hello-program "(ns cst.test-hello)\n(println \"Hello world\")")
105 |
106 | (deftest rt-hello
107 | (with-connection [c "datomic:mem://source"]
108 | (let [chello (cst-read-all-string hello-program "foo")
109 | tx (tx-data chello)
110 | _ (d/transact c tx)
111 | location (first (keep :cst/location tx))
112 | db (d/db c)
113 | reloaded (get-cst db "foo")]
114 | (is (= location (path/to-uri "foo")))
115 | (is (= hello-program (.emit reloaded))))
116 |
117 | (let [chello (cst-read-all-string hello-program)
118 | tx (tx-data chello)
119 | _ (d/transact c tx)
120 | location (first (keep :cst/location tx))
121 | db (d/db c)
122 | reloaded (get-cst db location)]
123 | (is (= hello-program (.emit reloaded))))))
124 |
125 |
--------------------------------------------------------------------------------
/test/clj/cst/reader_test.clj:
--------------------------------------------------------------------------------
1 | (ns cst.reader-test
2 | (:use [clojure.test]
3 | [cst.reader])
4 | (:import [cst SyntaxElement]))
5 |
6 | (defn roundtrip
7 | [s]
8 | (let [cst (cst-read-string s)]
9 | (is (= s (SyntaxElement/emit cst)))))
10 |
11 | (deftest simple-roundtrip
12 | (roundtrip "[1 2 3]")
13 | (roundtrip "(1 2 3)")
14 | (roundtrip "#{1 2 3}")
15 | (roundtrip "\"1 2 3\"")
16 | (roundtrip "\"1 \\\"2 3\"")
17 | (roundtrip "{:a 1 :b 2 :c 3}")
18 | (roundtrip "{:a 1, :b 2, :c 3}")
19 | (roundtrip "a")
20 | (roundtrip "'a")
21 | (roundtrip "\"a\"")
22 | (roundtrip "\"a\"")
23 | (roundtrip "#\"a\"")
24 | (roundtrip "#\"a\\(\"")
25 | (roundtrip "(.toString [1 2])")
26 | (roundtrip "#(= 5 %)")
27 | (roundtrip "#(= %1 %2)"))
28 |
29 | (deftest nested-roundtrip
30 | (roundtrip "[[1] [2]]")
31 | (roundtrip "[{:a 1 :b 2} {:a 1 :c 3}]")
32 | (roundtrip "[{:a 1 :b 2} '(1 3)]")
33 | (roundtrip "(let [^String x (.toString y)] x)"))
34 |
--------------------------------------------------------------------------------
/test/clj/util/macro.clj:
--------------------------------------------------------------------------------
1 | (ns util.macro)
2 |
3 | (defmacro assert-args
4 | [& pairs]
5 | `(do (when-not ~(first pairs)
6 | (throw (IllegalArgumentException.
7 | (str (first ~'&form) " requires " ~(second pairs) " in " ~'*ns* ":" (:line (meta ~'&form))))))
8 | ~(let [more (nnext pairs)]
9 | (when more
10 | (list* `assert-args more)))))
11 |
12 |
--------------------------------------------------------------------------------