diff --git a/src/jolt/clojure/edn.clj b/src/jolt/clojure/edn.clj index 505765e..6de5146 100644 --- a/src/jolt/clojure/edn.clj +++ b/src/jolt/clojure/edn.clj @@ -1,13 +1,27 @@ -; Jolt Standard Library: clojure.edn -; EDN reading and writing (stubs using the Jolt reader). +;; clojure.edn — reading EDN data. Delegates to the Jolt reader via +;; clojure.core/read-string (which parses, never evaluates — safe for EDN), and +;; adds the opts-map arity with :eof plus nil/blank-input handling. +(ns clojure.edn + "Reading EDN data." + (:require [clojure.string :as cstr])) + +;; Private helper, NOT named read-string: an unqualified (read-string …) call +;; dispatches the core read-string SPECIAL FORM (by name, regardless of ns), so +;; the 1-arity can't delegate to the 2-arity through that name. +(defn- read-edn [opts s] + (if (or (nil? s) (cstr/blank? s)) + (get opts :eof nil) + (clojure.core/read-string s))) (defn read-string - [s] - (let [ctx ((get (dyn :current-env) (symbol "init")))] - ((get (dyn :current-env) (symbol "eval-string")) ctx s))) + "Reads one object from the string s. Returns the :eof option value (default + nil) for nil or blank input. opts is an options map; :eof sets the value + returned at end of input." + ([s] (read-edn {} s)) + ([opts s] (read-edn opts s))) (defn read + "Reads the next line from reader and parses one EDN object from it." [reader] (let [line ((get (dyn :current-env) (symbol "file/read")) reader :line)] - (when line - (read-string line)))) + (when line (read-string line)))) diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index f64fe89..fdd90a6 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -611,9 +611,13 @@ (defn- eval-list [ctx bindings form] (def first-form (first form)) - # Safe name extraction: non-symbol heads (e.g. keywords) fall through to default + # Safe name extraction: non-symbol heads (e.g. keywords) fall through to default. + # A head qualified to a NON-core namespace (e.g. clojure.edn/read-string) must + # resolve to that var, not the like-named clojure.core special form — so only + # unqualified or clojure.core-qualified heads dispatch as special forms. (def name (if (and (struct? first-form) (= :symbol (first-form :jolt/type))) - (first-form :name) + (let [ns (first-form :ns)] + (if (or (nil? ns) (= ns "clojure.core")) (first-form :name) nil)) nil)) (match name "quote" (in form 1) diff --git a/test/integration/clojure-stdlib-suite-test.janet b/test/integration/clojure-stdlib-suite-test.janet index d8f675f..d50fc58 100644 --- a/test/integration/clojure-stdlib-suite-test.janet +++ b/test/integration/clojure-stdlib-suite-test.janet @@ -12,9 +12,10 @@ [["clojure/walk_test/walk.cljc" 34 true] ["clojure/zip_test/zip.cljc" 33 true] ["clojure/data_test/diff.cljc" 61 true] - # clojure.edn is still a stub (no opts/:eof arity, broken set/nil handling); - # tracked separately. Guard its current passing subset so it can't regress. - ["clojure/edn_test/read_string.cljc" 43 false]]) + # clojure.edn now reads via clojure.core/read-string with opts/:eof + nil/blank + # handling. The remaining 3 fails are sets (the reader yields a set FORM, not a + # constructed set) and #uuid — tracked in jolt-b7y. Guard the passing subset. + ["clojure/edn_test/read_string.cljc" 47 false]]) (def root "test/clojure-stdlib") (def per-file-timeout 6)