### Run JVM Tests Source: https://github.com/borkdude/edamame/blob/master/README.md Execute the JVM tests for the project. Leiningen must be installed. ```bash script/test/jvm ``` -------------------------------- ### Run Node.js Tests Source: https://github.com/borkdude/edamame/blob/master/README.md Execute the Node.js tests for the project. Ensure Clojure is installed as a command-line tool. ```bash script/test/node ``` -------------------------------- ### Parsing with Custom Data Readers Source: https://github.com/borkdude/edamame/blob/master/README.md Define custom data readers using the `:readers` option to handle non-standard literal syntax. This example defines a reader for `#js`. ```clojure (parse-string "#js [1 2 3]" {:readers {'js (fn [v] (list 'js v))}}) (js [1 2 3]) ``` -------------------------------- ### Override Map and Set Constructors Source: https://context7.com/borkdude/edamame/llms.txt Customize map and set constructors using `:map` and `:set` options, for example, to preserve insertion order with ordered maps/sets. ```clojure (require '[edamame.core :as e]) (require '[flatland.ordered.map :as omap]) (require '[flatland.ordered.set :as oset]) ;; Ordered map preserves key insertion order (e/parse-string "{:z 1 :a 2 :m 3}" {:map omap/ordered-map}) ``` ```clojure ;; Useful for JSON serialization where key order matters (require '[clojure.data.json :as json]) (json/write-str (e/parse-string "{:first 1 :second 2 :third 3}" {:map omap/ordered-map})) ``` ```clojure ;; Ordered set (e/parse-string "#{:z :a :m}" {:set oset/ordered-set}) ``` -------------------------------- ### Postprocess Parsed Values with Location Metadata Source: https://github.com/borkdude/edamame/blob/master/README.md Utilize the `:postprocess` option to modify parsed values. This example attaches location metadata to objects that don't natively support it, using a custom `Wrapper` record. ```clojure (defrecord Wrapper [obj loc]) (defn iobj? [x] #?(:clj (instance? clojure.lang.IObj x) :cljs (satisfies? IWithMeta x))) (parse-string "[1]" {:postprocess (fn [{:keys [:obj :loc]}] (if (iobj? obj) (vary-meta obj merge loc) (->Wrapper obj loc)))}) [#user.Wrapper{:obj 1, :loc {:row 1, :col 2, :end-row 1, :end-col 3}}] ``` -------------------------------- ### Syntax Quoting with Custom Symbol Resolution Source: https://github.com/borkdude/edamame/blob/master/README.md Customize symbol resolution within syntax quotes by providing a function to `:syntax-quote {:resolve-symbol ...}`. This example prefixes symbols with 'user'. ```clojure (parse-string "`(+ 1 2 3 ~x ~@y)" {:syntax-quote {:resolve-symbol #(symbol "user" (name %))}}) ;;=> (clojure.core/sequence (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/+)) (clojure.core/list 1) (clojure.core/list 2) (clojure.core/list 3) (clojure.core/list x) y))) ``` -------------------------------- ### Parse next form and its source string with edamame.core Source: https://context7.com/borkdude/edamame/llms.txt Use `parse-next+string` with a reader created by `source-reader` to get a vector containing the parsed form and its whitespace-trimmed original source string. This is useful for tasks requiring both the parsed data and its textual representation. ```clojure (require '[edamame.core :as e]) (let [rdr (e/source-reader " (defn foo [] 1) {:key \"value\"}") opts (e/normalize-opts {:all true})] (let [[form1 src1] (e/parse-next+string rdr opts) [form2 src2] (e/parse-next+string rdr opts)] {:first-form form1 :first-src src1 :second-form form2 :second-src src2})) ``` -------------------------------- ### Run All Tests Source: https://github.com/borkdude/edamame/blob/master/README.md Execute all tests for the project, including JVM, CLR, and Node.js. ```bash script/test/all ``` -------------------------------- ### Get Column Number from Reader Source: https://github.com/borkdude/edamame/blob/master/API.md Retrieves the current column number from a reader object. This is useful for error reporting or tracking parsing position. ```clojure (get-column-number reader) ``` -------------------------------- ### Get Line Number from Reader Source: https://github.com/borkdude/edamame/blob/master/API.md Retrieves the current line number from a reader object. This is useful for error reporting or tracking parsing position. ```clojure (get-line-number reader) ``` -------------------------------- ### continue Source: https://github.com/borkdude/edamame/blob/master/API.md Singleton value to be used as return value in `:read-cond` fn to indicate to continue parsing the next form. ```APIDOC ## `continue` ### Description Singleton value to be used as return value in `:read-cond` fn to indicate to continue parsing the next form. ### Type Singleton Value ``` -------------------------------- ### Create a source-logging reader with edamame.core Source: https://context7.com/borkdude/edamame/llms.txt Use `source-reader` to wrap a string or `java.io.Reader`. When combined with `parse-next` and `{:source true}` options, it captures the original source text for each parsed form in its metadata. ```clojure (require '[edamame.core :as e]) ;; Capture the original source text alongside each parsed value (let [rdr (e/source-reader "#( + 1 %)") opts (e/normalize-opts {:all true :source true})] (let [form (e/parse-next rdr opts)] {:form form :source (:source (meta form))})) ``` ```clojure ;; source-reader is required for parse-next+string (let [rdr (e/source-reader " 42 [1 2] {:k :v}") opts (e/normalize-opts {:all true})] [(e/parse-next+string rdr opts) (e/parse-next+string rdr opts) (e/parse-next+string rdr opts)]) ``` -------------------------------- ### Create an indexing pushback reader Source: https://context7.com/borkdude/edamame/llms.txt Use `reader` to create an indexing pushback reader from a string or `java.io.Reader`. This reader is suitable for use with `parse-next` and provides row/column tracking. You can check the reader's position during parsing. ```clojure (require '[edamame.core :as e]) ;; Create a reader and consume forms one at a time (let [rdr (e/reader "(+ 1 2) {:a 1} [3 4 5]") opts (e/normalize-opts {:all true})] (loop [forms []] (let [form (e/parse-next rdr opts)] (if (= ::e/eof form) forms (recur (conj forms form)))))) ;;=> [(+ 1 2) {:a 1} [3 4 5]] ;; Check reader position during parsing (let [rdr (e/reader "hello world")] (e/parse-next rdr) [(e/get-line-number rdr) (e/get-column-number rdr)]) ;;=> [1 6] ``` -------------------------------- ### Run CLR Tests Source: https://github.com/borkdude/edamame/blob/master/README.md Execute the CLR tests for the project. Requires .NET 8.0 or later and the cljr tool. ```bash script/test/clr ``` -------------------------------- ### Add Edamame to project.clj Source: https://context7.com/borkdude/edamame/llms.txt Add this to your `project.clj` file to include Edamame as a dependency. ```clojure [borkdude/edamame "1.5.39"] ``` -------------------------------- ### Create a source-logging-reader for parsing Source: https://github.com/borkdude/edamame/blob/master/API.md Coerces input into a source-logging-reader, useful with `parse-next` for tracking parsing origins. Accepts either a string or a `java.io.Reader`. ```clojure (source-reader x) ``` -------------------------------- ### Enable Syntax-Quote Expansion Source: https://context7.com/borkdude/edamame/llms.txt Use the `:syntax-quote` option to enable full syntax-quote expansion. Symbols can be resolved via a custom function or the live environment. ```clojure (require '[edamame.core :as e]) ;; Default — symbols resolved with identity (not qualified) (e/parse-string "`(+ 1 ~x ~@ys)" {:syntax-quote true}) ``` ```clojure ;; Qualify symbols via custom resolver (e/parse-string "`(inc x)" {:syntax-quote {:resolve-symbol #(symbol "user" (name %))}}) ``` ```clojure ;; Resolve from live environment using tools.reader (require '[clojure.tools.reader :refer [resolve-symbol]]) (require '[clojure.test :as t]) (e/parse-string "`t/run-tests" {:syntax-quote {:resolve-symbol resolve-symbol}}) ``` -------------------------------- ### Add Edamame to deps.edn Source: https://context7.com/borkdude/edamame/llms.txt Add this to your `deps.edn` file to include Edamame as a dependency. ```clojure ;; deps.edn {:deps {borkdude/edamame {:mvn/version "1.5.39"}}} ``` -------------------------------- ### Create a pushback-reader for parsing Source: https://github.com/borkdude/edamame/blob/master/API.md Coerces input into an indexing pushback-reader, suitable for use with `parse-next`. Accepts either a string or a `java.io.Reader`. ```clojure (reader x) ``` -------------------------------- ### Expand shorthand options with edamame.core Source: https://context7.com/borkdude/edamame/llms.txt Use `normalize-opts` to convert user-friendly shorthand options, like `:all true`, into the fully expanded internal format required by `parse-next`. This function should be called once, and its result reused across multiple `parse-next` calls for efficiency. ```clojure (require '[edamame.core :as e]) ;; :all true expands into each individual feature flag set to true (e/normalize-opts {:all true}) ``` ```clojure ;; Use in a tight loop — normalize once, reuse the result (let [opts (e/normalize-opts {:all true :read-cond :allow :features #{:clj}})] (doseq [source ["(+ 1 2)" "(defn f [] nil)"]] (println (e/parse-next (e/reader source) opts)))) ``` -------------------------------- ### Parse String with All Option for Function Literal Source: https://github.com/borkdude/edamame/blob/master/README.md Parses a string with both :all and :fn options, demonstrating a more complex function literal conversion. ```clojure (parse-string "#(alter-var-root #'foo %)" {:all true}) ``` -------------------------------- ### Normalize Parsing Options Source: https://github.com/borkdude/edamame/blob/master/API.md Expands shorthand options into a full set of normalized options for parsing functions. Ensure options are normalized before passing to parsing functions like `parse-next`. ```clojure (normalize-opts opts) ``` -------------------------------- ### Require Edamame Core Source: https://github.com/borkdude/edamame/blob/master/README.md Import the necessary functions from the edamame.core namespace for parsing. ```clojure (require '[edamame.core :as e :refer [parse-string]]) ``` -------------------------------- ### Auto-resolve Keywords from Running Environment Source: https://github.com/borkdude/edamame/blob/master/README.md Configure `:auto-resolve` to use a function that queries the running Clojure environment, allowing keywords to be resolved against the current namespace and its aliases. ```clojure (require '[clojure.test :as t]) (e/parse-string "::t/foo" {:auto-resolve (fn [x] (if (= :current x) *ns* (get (ns-aliases *ns*) x)))}) :clojure.test/foo ``` -------------------------------- ### normalize-opts Source: https://context7.com/borkdude/edamame/llms.txt Converts a user-facing options map into the fully expanded internal format required by `parse-next`. Must be called once and the result reused across many `parse-next` calls. ```APIDOC ## `normalize-opts` — Expand shorthand options Converts a user-facing options map (with shorthands like `:all true`) into the fully expanded internal format required by `parse-next`. Must be called once and the result reused across many `parse-next` calls to avoid redundant expansion. ```clojure (require '[edamame.core :as e]) ;; :all true expands into each individual feature flag set to true (e/normalize-opts {:all true}) ;;=> {:deref true, :fn true, :quote true, :read-eval true, ;; :regex true, :var true, :syntax-quote true, ...} ;; Use in a tight loop — normalize once, reuse the result (let [opts (e/normalize-opts {:all true :read-cond :allow :features #{:clj}})] (doseq [source ["(+ 1 2)" "(defn f [] nil)"]] (println (e/parse-next (e/reader source) opts)))) ``` ``` -------------------------------- ### Test if a value can carry metadata with iobj? Source: https://context7.com/borkdude/edamame/llms.txt Checks if an object implements IObj (JVM/CLR) or IWithMeta (CLJS), indicating support for metadata operations. Use this to determine if `with-meta` or `vary-meta` can be applied. ```clojure (require '[edamame.core :as e]) (e/iobj? [1 2 3]) ;;=> true (e/iobj? {:a 1}) ;;=> true (e/iobj? 'foo) ;;=> true (e/iobj? 42) ;;=> false (e/iobj? :keyword) ;;=> false (e/iobj? "string") ;;=> false ``` -------------------------------- ### normalize-opts Source: https://github.com/borkdude/edamame/blob/master/API.md Expands and normalizes parsing options. ```APIDOC ## `normalize-opts` ### Description Expands `opts` into normalized opts, e.g. `:all true` is expanded into explicit options. ### Function Signature ```clojure (normalize-opts opts) ``` ### Parameters * **opts** (map) - The options map to normalize. ``` -------------------------------- ### Parse Next Form from Reader Source: https://github.com/borkdude/edamame/blob/master/API.md Parses the next EDN form from a reader. Accepts normalized options. Use `normalize-opts` first if providing options. ```clojure (parse-next reader) (parse-next reader normalized-opts) ``` -------------------------------- ### Auto-Resolve Keywords with `:auto-resolve` Source: https://context7.com/borkdude/edamame/llms.txt Resolves `::alias/keyword` and `::keyword` forms to fully qualified keywords at parse time. Can use an explicit map, a function, or derive aliases from the environment. ```clojure (require '[edamame.core :as e]) ;; Explicit alias map (e/parse-string "[::foo ::str/foo]" {:auto-resolve '{:current user str clojure.string}}) ;;=> [:user/foo :clojure.string/foo] ``` ```clojure ;; Function fallback (use alias name as namespace when unknown) (e/parse-string "[::foo ::str/foo]" {:auto-resolve name}) ;;=> [:current/foo :str/foo] ``` ```clojure ;; Derive aliases from the live Clojure environment (defn live-auto-resolves [ns] (as-> (ns-aliases ns) $ (assoc $ :current (ns-name *ns*)) (zipmap (keys $) (map ns-name (vals $))))) (require '[clojure.string :as str]) (e/parse-string "[::foo ::str/upper-case]" {:auto-resolve (live-auto-resolves *ns*)}) ;;=> [:user/foo :clojure.string/upper-case] ``` ```clojure ;; :auto-resolve-ns true — resolve from the ns form found in the same string (e/parse-string-all "(ns app.core (:require [clojure.set :as set])) ::set/union" {:auto-resolve-ns true}) ;;=> [(ns app.core (:require [clojure.set :as set])) :clojure.set/union] ``` -------------------------------- ### parse-next Source: https://context7.com/borkdude/edamame/llms.txt Reads and returns the next form from a reader. Returns `::edamame.core/eof` when the input is exhausted. The options map must be pre-normalized with `normalize-opts`. ```APIDOC ## `parse-next` — Stream-parse the next form from a reader Reads and returns the next form from a reader created with `reader` or `source-reader`. Returns `::edamame.core/eof` when the input is exhausted. The options map **must** be pre-normalized with `normalize-opts`. ```clojure (require '[edamame.core :as e]) ;; Incremental parsing — useful for large files or streaming input (let [code "(defn greet [name] (str \"Hello \" name)) (greet \"World\")" rdr (e/reader code) opts (e/normalize-opts {:all true})] (loop [acc []] (let [form (e/parse-next rdr opts)] (if (= ::e/eof form) acc (recur (conj acc {:form form :loc (select-keys (meta form) [:row :col])})))))) ;;=> [{:form (defn greet [name] (str "Hello " name)), :loc {:row 1, :col 1}} ;; {:form (greet "World"), :loc {:row 2, :col 1}}] ;; Works with java.io.PushbackReader for file streaming (JVM only) #?(:clj (with-open [rdr (java.io.PushbackReader. (clojure.java.io/reader "src/my/ns.clj"))] (let [opts (e/normalize-opts {:all true :read-cond :allow :features #{:clj}})] (take-while #(not= ::e/eof %) (repeatedly #(e/parse-next rdr opts)))))) ``` ``` -------------------------------- ### Auto-resolve Keywords with Specific Mappings Source: https://github.com/borkdude/edamame/blob/master/README.md Use `:auto-resolve` with a map to specify how keywords should be resolved. This is useful when you need precise control over keyword expansion. ```clojure (parse-string "[::foo ::str/foo]" {:auto-resolve '{:current user str clojure.string}}) ;;=> [:user/foo :clojure.string/foo] ``` -------------------------------- ### Auto-resolve Namespaces with `:auto-resolve-ns` Source: https://github.com/borkdude/edamame/blob/master/README.md Enable automatic namespace resolution for keywords using `:auto-resolve-ns true`. This option is particularly useful when parsing code containing namespace-qualified keywords. ```clojure (= '[(ns foo (:require [clojure.set :as set])) :clojure.set/foo] (parse-string-all "(ns foo (:require [clojure.set :as set])) ::set/foo" {:auto-resolve-ns true})) (def rdr (p/reader "(ns foo (:require [clojure.set :as set])) ::set/foo")) (def opts (p/normalize-opts {:auto-resolve-ns true})) (= (ns foo (:require [clojure.set :as set])) (p/parse-next rdr opts)) (= :clojure.set/foo (p/parse-next rdr opts)) ``` -------------------------------- ### Parse Next Form and String from Reader Source: https://github.com/borkdude/edamame/blob/master/API.md Parses the next EDN form from a reader and returns both the parsed value and the trimmed string that was read. Accepts normalized options. Use `normalize-opts` first if providing options. ```clojure (parse-next+string reader) (parse-next+string reader normalized-opts) ``` -------------------------------- ### Parse String with Var Option Source: https://github.com/borkdude/edamame/blob/master/README.md Parses a string with the :var option enabled, converting "#'foo" into '(var foo)'. ```clojure (parse-string "#\'foo" {:var true}) ``` -------------------------------- ### Define Custom Auto-resolve Function Source: https://github.com/borkdude/edamame/blob/master/README.md Create a custom function to dynamically determine keyword resolutions based on the current namespace and its aliases. This function is then passed to the `:auto-resolve` option. ```clojure (defn auto-resolves [ns] (as-> (ns-aliases ns) $ (assoc $ :current (ns-name *ns*)) (zipmap (keys $) (map ns-name (vals $))))) (require '[clojure.string :as str]) ;; create example alias (auto-resolves *ns*) ;;=> {str clojure.string, :current user} (parse-string "[::foo ::str/foo]" {:auto-resolve (auto-resolves *ns*)}) ;;=> [:user/foo :clojure.string/foo] ``` -------------------------------- ### Parse String with Quote Option Source: https://github.com/borkdude/edamame/blob/master/README.md Parses a string with the :quote option enabled, converting ''bar' into '(quote bar)'. ```clojure (parse-string "'bar" {:quote true}) ``` -------------------------------- ### Parse String with Custom Regex Handler Source: https://github.com/borkdude/edamame/blob/master/README.md Demonstrates overriding the default regex parsing behavior by providing a custom function to handle '#"foo"'. ```clojure (parse-string "#\"foo\"" {:regex #(list 're-pattern %)}) ``` -------------------------------- ### Control Location Metadata with `:location?` Source: https://context7.com/borkdude/edamame/llms.txt Attaches `:row`, `:col`, `:end-row`, `:end-col` metadata to parsed nodes. Control which nodes receive metadata by specifying a predicate for `:location?`. ```clojure (require '[edamame.core :as e]) ;; All nested forms get location metadata (->> "{:a {:b {:c [x y z]}}}" e/parse-string (tree-seq coll? #(if (map? %) (vals %) %)) (map meta)) ;;=> ({:row 1, :col 1, :end-row 1, :end-col 23} ; outer map ;; {:row 1, :col 5, :end-row 1, :end-col 22} ; inner map ;; {:row 1, :col 9, :end-row 1, :end-col 21} ; innermost map ;; {:row 1, :col 13, :end-row 1, :end-col 20} ; vector ;; {:row 1, :col 14, :end-row 1, :end-col 15} ; x ;; {:row 1, :col 16, :end-row 1, :end-col 17} ; y ;; {:row 1, :col 18, :end-row 1, :end-col 19}) ; z ``` ```clojure ;; Only attach metadata to sequences (Clojure-reader default style) (e/parse-string "(defn f [x] x) :keyword 42" {:location? seq?}) ;; :keyword and 42 will have no metadata; (defn ...) will ``` ```clojure ;; Use Clojure-compatible key names (meta (e/parse-string "(+ 1 2)" {:row-key :line :col-key :column})) ;;=> {:line 1, :column 1, :end-row 1, :end-col 8} ``` -------------------------------- ### parse-string Source: https://context7.com/borkdude/edamame/llms.txt Parses the first EDN/Clojure form from a string and returns it with location metadata. Options can be provided to configure reader features. ```APIDOC ## parse-string ### Description Parses the first EDN/Clojure form from `s` and returns it as a Clojure data structure with location metadata attached. Accepts an optional options map to enable/configure reader features. When a feature option is set to `true` the default behavior is used; when set to a function, that function receives the parsed form and its return value is used instead. ### Method (e/parse-string s options?) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```clojure (require '[edamame.core :as e]) ;; Basic EDN parsing — all standard types work out of the box (e/parse-string "\"hello\"") ;=> "hello" (e/parse-string ":foo/bar") ;=> :foo/bar (e/parse-string "(1 2 3)") ;=> (1 2 3) (e/parse-string "[1 2 3]") ;=> [1 2 3] (e/parse-string "#{1 2 3}") ;=> #{1 2 3} (e/parse-string "{:a 1 :b 2}") ;=> {:a 1, :b 2} ;; Location metadata is attached automatically (meta (e/parse-string "{:a 1}")) ;;=> {:row 1, :col 1, :end-row 1, :end-col 7} ;; Enable Clojure reader features individually (e/parse-string "@foo" {:deref true}) ;=> (deref foo) (e/parse-string "'bar" {:quote true}) ;=> (quote bar) (e/parse-string "#(* % %2)" {:fn true}) ;=> (fn* [%1 %2] (* %1 %2)) (e/parse-string "#=(+ 1 2)" {:read-eval true}) ;;=> (read-eval (+ 1 2)) (e/parse-string "#\"\\d+\"" {:regex true}) ;=> #"\\d+" (e/parse-string "#\'clojure.core/+" {:var true}) ;=> (var clojure.core/+) ;; :all true enables all of the above at once (closest to Clojure defaults) (e/parse-string "#(alter-var-root #'foo %)" {:all true}) ;;=> (fn* [%1] (alter-var-root (var foo) %1)) ;; Replace a feature with a custom function instead of the default behavior (e/parse-string "#\"foo\"" {:regex #(list 're-pattern %)}) ;;=> (re-pattern "foo") ;; Error handling — exceptions carry location data (try (e/parse-string "{:a :b :c}") (catch clojure.lang.ExceptionInfo e (ex-data e))) ;;=> {:type :edamame/error, :row 1, :col 1, ...} ``` ``` -------------------------------- ### iobj? Source: https://github.com/borkdude/edamame/blob/master/API.md Checks if an object can carry metadata. ```APIDOC ## `iobj?` ### Description Returns true if obj can carry metadata. ### Function Signature ```clojure (iobj? obj) ``` ### Parameters * **obj** (any) - The object to check. ``` -------------------------------- ### Parse String with Regex Option Source: https://github.com/borkdude/edamame/blob/master/README.md Parses a string with the :regex option enabled, converting '#"foo"' into a regex literal. ```clojure (parse-string "#\"foo\"" {:regex true}) ``` -------------------------------- ### parse-string-all Source: https://context7.com/borkdude/edamame/llms.txt Reads every top-level form in a string and returns them collected into a vector. Accepts the same options map as `parse-string`. ```APIDOC ## parse-string-all ### Description Like `parse-string` but reads every top-level form in `s` and returns them collected into a vector. Accepts the same options map. ### Method (e/parse-string-all s options?) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```clojure (require '[edamame.core :as e]) ;; Multiple top-level forms (e/parse-string-all "1 2 3") ;;=> [1 2 3] (e/parse-string-all ":a :b :c") ;;=> [:a :b :c] ;; Parse an entire Clojure namespace string (e/parse-string-all "(ns my.ns) (defn add [a b] (+ a b)) (add 1 2)" {:all true}) ;;=> [(ns my.ns) (defn add [a b] (+ a b)) (add 1 2)] ;; Use with :read-cond to handle .cljc files (e/parse-string-all "1 2 #?(:clj 3 :cljs 4) 5" {:features #{:clj} :read-cond :allow}) ;;=> [1 2 3 5] ;; Parse source with :source true to capture original text per form (map (comp :source meta) (e/parse-string-all "(+ 1 2) [a b c]" {:all true :source true})) ;;=> ("(+ 1 2)" "[a b c]") ``` ``` -------------------------------- ### Streaming Incremental Parsing with Auto-Resolve Source: https://context7.com/borkdude/edamame/llms.txt Processes a Clojure/ClojureScript file incrementally, resolving namespace aliases on-the-fly. This is useful for analyzing code where `ns` declarations define aliases that are used later in the file. ```clojure (require '[edamame.core :as e]) (defn parse-ns-aliases [ns-form] (let [ns-name (second ns-form) clauses (filter seq? ns-form) requires (mapcat rest (filter #(= :require (first %)) clauses))] (reduce (fn [m [lib & {:keys [as as-alias]}]] (if-let [a (or as as-alias)] (assoc m a lib) m)) {:current ns-name} requires))) (defn parse-all-with-auto-resolve [source] (let [rdr (e/reader source) opts (atom (e/normalize-opts {:auto-resolve #(get @(atom {}) % 'unknown) :read-cond :allow :features #{:clj} :all true})) aliases (atom {})] (loop [results []] (let [form (e/parse-next rdr @opts)] (if (= ::e/eof form) results (do (when (and (seq? form) (= 'ns (first form))) (reset! aliases (parse-ns-aliases form)) (reset! opts (e/normalize-opts {:auto-resolve #(get @aliases % 'unknown) :read-cond :allow :features #{:clj} :all true}))) (recur (conj results form)))))))) (parse-all-with-auto-resolve "(ns myapp (:require [clojure.set :as s])) ::s/union ::myapp/thing") ;;=> [(ns myapp (:require [clojure.set :as s])) ;; :clojure.set/union ;; :myapp/thing] ``` -------------------------------- ### parse-next Source: https://github.com/borkdude/edamame/blob/master/API.md Parses the next EDN form from a reader. ```APIDOC ## `parse-next` ### Description Parses next form from reader. Accepts same opts as [`parse-string`](#edamame.core/parse-string), but must be normalized with [`normalize-opts`](#edamame.core/normalize-opts) first. ### Function Signature ```clojure (parse-next reader) (parse-next reader normalized-opts) ``` ### Parameters * **reader** (object) - The reader object to parse from. * **normalized-opts** (map, optional) - Normalized options for parsing. ``` -------------------------------- ### parse-next+string Source: https://github.com/borkdude/edamame/blob/master/API.md Parses the next EDN form from a reader and returns the form along with its string representation. ```APIDOC ## `parse-next+string` ### Description Parses next form from reader. Accepts same opts as [`parse-string`](#edamame.core/parse-string), but must be normalized with [`normalize-opts`](#edamame.core/normalize-opts) first. Returns read value + string read (whitespace-trimmed). ### Function Signature ```clojure (parse-next+string reader) (parse-next+string reader normalized-opts) ``` ### Parameters * **reader** (object) - The reader object to parse from. * **normalized-opts** (map, optional) - Normalized options for parsing. ``` -------------------------------- ### Parse EDN string with options Source: https://github.com/borkdude/edamame/blob/master/API.md Parses the first EDN value from a string, supporting various options to customize parsing behavior for features like deref, function literals, quotes, regex, vars, maps, sets, syntax quotes, reader conditionals, and more. Use this for single EDN value parsing. ```clojure (parse-string s) (parse-string s opts) ``` ```clojure (parse-string "`x" {:syntax-quote {:resolve-symbol #(symbol "user" (str %))}}) ;;=> (quote user/x) ``` -------------------------------- ### Apply Postprocess Hook for Transformations Source: https://context7.com/borkdude/edamame/llms.txt Use the `:postprocess` hook to apply transformations to parsed values. The hook receives the parsed object and its location, and is responsible for attaching location metadata if needed. ```clojure (require '[edamame.core :as e]) ;; Wrap non-IObj values (e.g. numbers) in a record to preserve location (defrecord Located [value loc]) (defn iobj? [x] #?(:clj (instance? clojure.lang.IObj x) :cljs (satisfies? IWithMeta x))) (e/parse-string "[1 :kw \"str\"]" {:postprocess (fn [{:keys [obj loc]}] (if (iobj? obj) (vary-meta obj merge loc) (->Located obj loc)))}) ``` -------------------------------- ### Syntax Quoting with Environment Symbol Resolution Source: https://github.com/borkdude/edamame/blob/master/README.md Resolve symbols within syntax quotes against the running Clojure environment by setting `:syntax-quote {:resolve-symbol resolve-symbol}`. This requires importing `resolve-symbol`. ```clojure (require '[clojure.tools.reader :refer [resolve-symbol]]) (require '[clojure.test :as t]) (e/parse-string "`t/run-tests" {:syntax-quote {:resolve-symbol resolve-symbol}}) ;;=> (quote clojure.test/run-tests) ``` -------------------------------- ### source-reader Source: https://context7.com/borkdude/edamame/llms.txt Creates a source-logging reader that captures the original source text for each parsed form when used with `parse-next` and `{:source true}` options. ```APIDOC ## `source-reader` — Create a source-logging reader Coerces a string or `java.io.Reader` into a source-logging reader. When used with `parse-next` and `{:source true}` in opts, the original source text for each form is stored under `:source` in the form's metadata. ```clojure (require '[edamame.core :as e]) ;; Capture the original source text alongside each parsed value (let [rdr (e/source-reader "#( + 1 %)") opts (e/normalize-opts {:all true :source true})] (let [form (e/parse-next rdr opts)] {:form form :source (:source (meta form))})) ;;=> {:form (fn* [%1] (+ 1 %1)), :source "#( + 1 %)"} ;; source-reader is required for parse-next+string (let [rdr (e/source-reader " 42 [1 2] {:k :v}") opts (e/normalize-opts {:all true})] [(e/parse-next+string rdr opts) (e/parse-next+string rdr opts) (e/parse-next+string rdr opts)]) ;;=> [[42 "42"] [[1 2] "[1 2]"] [{:k :v} "{:k :v}"]] ``` ``` -------------------------------- ### Auto-resolve Keywords with a Generic Function Source: https://github.com/borkdude/edamame/blob/master/README.md When the exact resolution doesn't matter, you can use a generic function like `name` with `:auto-resolve` to simplify keyword parsing. ```clojure (parse-string "[::foo ::str/foo]" {:auto-resolve name}) ;;=> [:current/foo :str/foo] ``` -------------------------------- ### Fix Incomplete Expressions by Inferring Delimiters Source: https://github.com/borkdude/edamame/blob/master/README.md Handle `clojure.lang.ExceptionInfo` during parsing to fix incomplete expressions. This function recursively adds expected delimiters until the expression is valid. ```clojure (def incomplete "{:a (let [x 5") (defn fix-expression [expr] (try (when (parse-string expr) expr) (catch clojure.lang.ExceptionInfo e (if-let [expected-delimiter (:edamame/expected-delimiter (ex-data e))] (fix-expression (str expr expected-delimiter)) (throw e))))) (fix-expression incomplete) ;; => "{:a (let [x 5])}" ``` -------------------------------- ### Parse Namespace Metadata with `parse-ns-form` Source: https://context7.com/borkdude/edamame/llms.txt Extracts namespace information like name, aliases, requires, and imports from a Clojure ns form. Useful for analyzing project dependencies and structure. ```clojure (require '[edamame.core :as e]) (e/parse-ns-form '(ns my.app (:require [clojure.string :as str] [clojure.set :as set] clojure.walk) (:import java.lang.Thread [java.util Date List]))) ;;=> {:current my.app ;; :meta nil ;; :requires ({:lib clojure.string, :as str, :require true} ;; {:lib clojure.set, :as set, :require true} ;; {:lib clojure.walk, :require true}) ;; :aliases {str clojure.string, set clojure.set} ;; :imports ({:full-classname java.lang.Thread, :package java.lang, :classname Thread} ;; {:full-classname java.util.Date, :package java.util, :classname Date} ;; {:full-classname java.util.List, :package java.util, :classname List})} ``` ```clojure ;; Practical: auto-resolve keywords based on a parsed ns form (let [ns-str "(ns foo (:require [clojure.set :as set])) ::set/union" forms (e/parse-string-all ns-str {:auto-resolve-ns true})] (second forms)) ;;=> :clojure.set/union ``` -------------------------------- ### Parse EDN/Clojure forms with location metadata Source: https://context7.com/borkdude/edamame/llms.txt Use `parse-string` to parse the first EDN/Clojure form from a string. Location metadata (row/column) is automatically attached. Options can enable specific reader features or replace default behavior with custom functions. ```clojure (require '[edamame.core :as e]) ;; Basic EDN parsing — all standard types work out of the box (e/parse-string "\"hello\"") ;;=> "hello" (e/parse-string ":foo/bar") ;;=> :foo/bar (e/parse-string "(1 2 3)") ;;=> (1 2 3) (e/parse-string "[1 2 3]") ;;=> [1 2 3] (e/parse-string "#{1 2 3}") ;;=> #{1 2 3} (e/parse-string "{:a 1 :b 2}") ;;=> {:a 1, :b 2} ;; Location metadata is attached automatically (meta (e/parse-string "{:a 1}")) ;;=> {:row 1, :col 1, :end-row 1, :end-col 7} ;; Enable Clojure reader features individually (e/parse-string "@foo" {:deref true}) ;;=> (deref foo) (e/parse-string "'bar" {:quote true}) ;;=> (quote bar) (e/parse-string "#(* % %2)" {:fn true}) ;;=> (fn* [%1 %2] (* %1 %2)) (e/parse-string "#=(+ 1 2)" {:read-eval true}) ;;=> (read-eval (+ 1 2)) (e/parse-string "#\"\\d+\"" {:regex true}) ;;=> #"\\d+" (e/parse-string "#\'clojure.core/+" {:var true}) ;;=> (var clojure.core/+) ;; :all true enables all of the above at once (closest to Clojure defaults) (e/parse-string "#(alter-var-root #'foo %)" {:all true}) ;;=> (fn* [%1] (alter-var-root (var foo) %1)) ;; Replace a feature with a custom function instead of the default behavior (e/parse-string "#\"foo\"" {:regex #(list 're-pattern %)}) ;;=> (re-pattern "foo") ;; Error handling — exceptions carry location data (try (e/parse-string "{:a :b :c}") (catch clojure.lang.ExceptionInfo e (ex-data e))) ;;=> {:type :edamame/error, :row 1, :col 1, ...} ``` -------------------------------- ### Supply Custom Data Readers Source: https://context7.com/borkdude/edamame/llms.txt Use the `:readers` option to provide custom tag handler functions. This can be a map of symbols to functions or a single function that receives the tag symbol. ```clojure (require '[edamame.core :as e]) ;; Map of tag → handler function (e/parse-string "#js [1 2 3]" {:readers {'js (fn [v] (list 'js v))}}) ``` ```clojure ;; Function fallback — called with unknown tag, returns handler or nil (e/parse-string "#foo/bar [1 2 3]" {:readers (constantly identity)}) ``` ```clojure ;; Tagged literals preserved as-is with tagged-literal (e/parse-string "#myapp/point [1.0 2.0]" {:readers {'myapp/point (fn [v] (tagged-literal 'myapp/point v))}}) ``` -------------------------------- ### Process Reader Conditionals with `:read-cond` Source: https://context7.com/borkdude/edamame/llms.txt Handles `#?()` and `#?@()` reader conditionals. Specify `:features` to control which branches are evaluated, or use a function for custom logic. ```clojure (require '[edamame.core :as e]) ;; :allow — evaluate and keep only the matching branch (e/parse-string "[1 #?(:clj 2 :cljs 3) 4]" {:features #{:clj} :read-cond :allow}) ;;=> [1 2 4] ``` ```clojure ;; :preserve — keep the conditional as-is (e/parse-string "[1 #?@(:clj [2 3] :cljs [4 5]) 6]" {:features #{:clj} :read-cond :preserve}) ;;=> [1 #?@(:clj [2 3] :cljs [4 5]) 6] ``` ```clojure ;; Function — full control; return `e/continue` to skip a form entirely (e/parse-string "[1 #?(:cljs 2) 3]" {:features #{:clj} :read-cond (fn [pairs] (let [m (apply hash-map pairs)] (get m :clj (get m :default e/continue))))}) ;;=> [1 3] ``` ```clojure ;; Inspect the splicing flag via metadata (let [form (e/parse-string "#?@(:bb 1 :clj 2)" {:read-cond identity})] {:form form :splicing? (:edamame/read-cond-splicing (meta form))}) ;;=> {:form (:bb 1 :clj 2), :splicing? true} ``` -------------------------------- ### parse-ns-form Source: https://github.com/borkdude/edamame/blob/master/API.md Parses an ns-form into a map containing namespace information. ```APIDOC ## `parse-ns-form` ### Description Parses `ns-form`, an s-expression, into map with: - `:name`: the name of the namespace - `:aliases`: a map of aliases to lib names ### Function Signature ```clojure (parse-ns-form ns-form) ``` ### Parameters * **ns-form** (any) - The ns-form to parse. ``` -------------------------------- ### Parse String with Location Metadata Source: https://github.com/borkdude/edamame/blob/master/README.md Parses a string and includes location metadata (row, column, etc.) for each parsed element. Useful for providing feedback on file locations. ```clojure (def s " [{:a 1} {:b 2}]") (map meta (parse-string s)) ``` ```clojure (->> "{:a {:b {:c [a b c]}}}" parse-string (tree-seq coll? #(if (map? %) (vals %) %)) (map meta)) ``` -------------------------------- ### source-reader Source: https://github.com/borkdude/edamame/blob/master/API.md Coerces input into a source-logging-reader for use with `parse-next`. ```APIDOC ## `source-reader` ### Description Coerces input `x` into a source-logging-reader to be used with `parse-next`. Accepts a string or `java.io.Reader`. ### Function Signature ```clojure (source-reader x) ``` ```