|
4400 | 4400 | [fmt & args]
|
4401 | 4401 | (print (apply format fmt args)))
|
4402 | 4402 |
|
4403 |
| -;;;;;;;;;;;;;;;;;;;; |
4404 |
| -;; REPL Utilities ;; |
4405 |
| -;;;;;;;;;;;;;;;;;;;; |
| 4403 | +;;;;;;;;;;;;;;;;;;;;;;;;; |
| 4404 | +;; Reading and Loading ;; |
| 4405 | +;;;;;;;;;;;;;;;;;;;;;;;;; |
4406 | 4406 |
|
4407 | 4407 | (def
|
4408 | 4408 | ^{:doc "The default data readers used in reader macros. Overriding or
|
|
4428 | 4428 | *resolver*
|
4429 | 4429 | basilisp.lang.runtime/resolve-alias)
|
4430 | 4430 |
|
| 4431 | +(def ^{:doc "When no data reader is found for a tag and ``*default-data-reader-fn*`` |
| 4432 | + is non-``nil``, it will be called with two arguments, the tag and the value. |
| 4433 | + If ``*default-data-reader-fn*`` is ``nil`` (the default), raise a |
| 4434 | + ``basilisp.lang.compiler.SyntaxError``." |
| 4435 | + :dynamic true} |
| 4436 | + *default-data-reader-fn* |
| 4437 | + nil) |
| 4438 | + |
4431 | 4439 | (defn- read-iterator
|
4432 | 4440 | [opts x]
|
4433 | 4441 | (let [read (:read opts basilisp.lang.reader/read)]
|
|
4457 | 4465 | "Read the next form from the ``stream``\\. If no stream is specified, uses the value
|
4458 | 4466 | currently bound to :lpy:var:`*in*`.
|
4459 | 4467 |
|
4460 |
| - Callers may bind a map of readers to :lpy:var:`*data-readers*` to customize the data |
4461 |
| - readers used reading this string. |
| 4468 | + Callers may bind a map of readers to :lpy:var:`*data-readers*` or a default data |
| 4469 | + reader function to :lpy:var::`*default-data-reader-fn*` to customize the data |
| 4470 | + readers used reading this string |
4462 | 4471 |
|
4463 | 4472 | The stream must satisfy the interface of :external:py:class:`io.TextIOBase`\\, but
|
4464 | 4473 | does not require any pushback capabilities. The default
|
|
4477 | 4486 | "Eagerly read all forms from the ``stream``\\. If no stream is specified, uses the
|
4478 | 4487 | value currently bound to :lpy:var:`*in*`.
|
4479 | 4488 |
|
4480 |
| - Callers may bind a map of readers to :lpy:var:`*data-readers*` to customize |
4481 |
| - the data readers used reading this string |
| 4489 | + Callers may bind a map of readers to :lpy:var:`*data-readers*` or a default data |
| 4490 | + reader function to :lpy:var::`*default-data-reader-fn*` to customize the data |
| 4491 | + readers used reading this string |
4482 | 4492 |
|
4483 | 4493 | The stream must satisfy the interface of :external:py:class:`io.TextIOBase`\\, but
|
4484 | 4494 | does not require any pushback capabilities. The default
|
|
7024 | 7034 | (.write writer content)
|
7025 | 7035 | nil))
|
7026 | 7036 |
|
| 7037 | +;;;;;;;;;;;;;;;;;;;;;;;;; |
| 7038 | +;; Custom Data Readers ;; |
| 7039 | +;;;;;;;;;;;;;;;;;;;;;;;;; |
| 7040 | + |
| 7041 | +(defmulti ^:private make-custom-data-readers |
| 7042 | + (fn [obj metadata] (type obj))) |
| 7043 | + |
| 7044 | +(defmethod make-custom-data-readers :default |
| 7045 | + [obj mta] |
| 7046 | + (throw (ex-info "Not a valid data-reader map" (assoc mta :object obj)))) |
| 7047 | + |
| 7048 | +(defmethod make-custom-data-readers basilisp.lang.interfaces/IPersistentMap |
| 7049 | + [mappings mta] |
| 7050 | + (reduce (fn [m [k v]] |
| 7051 | + (let [v' (if (qualified-symbol? v) |
| 7052 | + (intern (create-ns (symbol (namespace v))) |
| 7053 | + (symbol (name v))) |
| 7054 | + v)] |
| 7055 | + (cond |
| 7056 | + (not (qualified-symbol? k)) |
| 7057 | + (throw |
| 7058 | + (ex-info "Invalid tag in data-readers. Expected qualified symbol." |
| 7059 | + (merge mta {:form k}))) |
| 7060 | + |
| 7061 | + (not (ifn? v')) |
| 7062 | + (throw (ex-info "Invalid reader function in data-readers" |
| 7063 | + (merge mta {:form v}))) |
| 7064 | + |
| 7065 | + :else |
| 7066 | + (assoc m (with-meta k mta) v')))) |
| 7067 | + mappings |
| 7068 | + mappings)) |
| 7069 | + |
| 7070 | +(defmethod make-custom-data-readers importlib.metadata/EntryPoint |
| 7071 | + [entry-point mta] |
| 7072 | + (make-custom-data-readers (.load entry-point) |
| 7073 | + (assoc mta |
| 7074 | + :basilisp.entry-point/name (.-name entry-point) |
| 7075 | + :basilisp.entry-point/group (.-group entry-point)))) |
| 7076 | + |
| 7077 | +(defmethod make-custom-data-readers pathlib/Path |
| 7078 | + [file mta] |
| 7079 | + (make-custom-data-readers |
| 7080 | + (with-open [rdr (basilisp.io/reader file)] |
| 7081 | + (read (if (.endswith (name file) "cljc") |
| 7082 | + {:eof nil :read-cond :allow} |
| 7083 | + {:eof nil}) |
| 7084 | + rdr)) |
| 7085 | + (assoc mta :file (str file)))) |
| 7086 | + |
| 7087 | +(defn- data-readers-entry-points [] |
| 7088 | + (when (#{"true" "t" "1" "yes" "y"} (.lower |
| 7089 | + (os/getenv |
| 7090 | + "BASILISP_USE_DATA_READERS_ENTRY_POINT" |
| 7091 | + "true"))) |
| 7092 | + (#?@(:lpy39- [get (.entry_points importlib/metadata)] |
| 7093 | + :lpy310+ [.entry_points importlib/metadata ** :group]) |
| 7094 | + "basilisp_data_readers"))) |
| 7095 | + |
| 7096 | +(defn- data-readers-files [] |
| 7097 | + (->> sys/path |
| 7098 | + (mapcat file-seq) |
| 7099 | + (filter (comp #{"data_readers.lpy" "data_readers.cljc"} name)) |
| 7100 | + (group-by #(.-parent %)) |
| 7101 | + vals |
| 7102 | + ;; Only load one data readers file per directory and prefer |
| 7103 | + ;; `data_readers.lpy` to `data_readers.cljc` |
| 7104 | + (map #(first (sort-by name > %))))) |
| 7105 | + |
| 7106 | +(defn- load-data-readers [] |
| 7107 | + (alter-var-root |
| 7108 | + #'*data-readers* |
| 7109 | + (fn [mappings additional-mappings] |
| 7110 | + (reduce (fn [m [k v]] |
| 7111 | + (if (not= (get m k v) v) |
| 7112 | + (throw (ex-info "Conflicting data-reader mapping" |
| 7113 | + (merge (meta k) {:conflict k, :mappings m}))) |
| 7114 | + (assoc m k v))) |
| 7115 | + mappings |
| 7116 | + additional-mappings)) |
| 7117 | + ;; Can't use `read` when altering `*data-readers*` so do reads ahead of time |
| 7118 | + (->> (concat (data-readers-files) |
| 7119 | + (data-readers-entry-points)) |
| 7120 | + (mapcat #(make-custom-data-readers % nil)) |
| 7121 | + doall))) |
| 7122 | + |
| 7123 | +(load-data-readers) |
| 7124 | + |
7027 | 7125 | ;;;;;;;;;;;;;;;;;
|
7028 | 7126 | ;; Transducers ;;
|
7029 | 7127 | ;;;;;;;;;;;;;;;;;
|
|
0 commit comments