├── .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 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 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 | 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 | 54 | 55 | 56 | 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 | 113 | 114 | 115 | 120 | 123 | 124 | 127 | 128 | 129 | 153 | 154 | 155 | 160 | 161 | 162 | 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 | 302 | 303 | 304 | 305 | 308 | 309 | 312 | 313 | 314 | 315 | 318 | 319 | 322 | 323 | 326 | 327 | 330 | 331 | 334 | 335 | 336 | 337 | 340 | 341 | 344 | 345 | 348 | 349 | 352 | 353 | 356 | 357 | 358 | 359 | 362 | 363 | 366 | 367 | 370 | 371 | 374 | 375 | 378 | 379 | 380 | 381 | 384 | 385 | 388 | 389 | 392 | 393 | 396 | 397 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 411 | 412 | 415 | 416 | 419 | 420 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 433 | 434 | 437 | 438 | 439 | 440 | 443 | 444 | 447 | 448 | 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 | 493 | 494 | 495 | 522 | 523 | 524 | 552 | 553 | 559 | 560 | 561 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 601 | 604 | 606 | 607 | 608 | 609 | 610 | 611 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 671 | 672 | 673 | 684 | 685 | 686 | 696 | 697 | 704 | 705 | 706 | 707 | 725 | 732 | 733 | 734 | 735 | 753 | 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 | 800 | 801 | 1444546857770 802 | 806 | 807 | 1445232454797 808 | 812 | 813 | 1445232839808 814 | 818 | 819 | 1445833034367 820 | 824 | 825 | 1446086579417 826 | 830 | 831 | 1446118601458 832 | 836 | 837 | 1446463689271 838 | 842 | 843 | 1446791644338 844 | 848 | 849 | 1447055998929 850 | 854 | 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 | 901 | 904 | 905 | 906 | 908 | 909 | 921 | 922 | 923 | 924 | 925 | file://$PROJECT_DIR$/src/java/cst/SyntaxElement.java 926 | 58 927 | 928 | 930 | 931 | file://$PROJECT_DIR$/src/java/cst/SyntaxElement.java 932 | 79 933 | 934 | 936 | 937 | 938 | 939 | 940 | 942 | 943 | 944 | 945 | 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 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1.8 1588 | 1589 | 1594 | 1595 | 1596 | 1597 | 1598 | 1599 | cast 1600 | 1601 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | 1.8 1613 | 1614 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | Clojure-1.7.0 1625 | 1626 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 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 | --------------------------------------------------------------------------------