Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splicing Reader conditional breaks map literals when :read-cond not :allow #99

Closed
NoahTheDuke opened this issue Apr 13, 2023 · 3 comments

Comments

@NoahTheDuke
Copy link
Contributor

NoahTheDuke commented Apr 13, 2023

version

1.3.22

platform

Ubuntu

problem

Parsing a map literal that has a splicing reader conditional in it throws an exception.

repro

$ deps-try borkdude/edamame 1.3.22
[Rebel readline] Type :repl/help for online help info
user=> (require '[edamame.core :as e])
nil
user=> (e/parse-string-all "(def example {:a 1 #?@(:clj [:b 2 :c 3])})" {:read-cond :allow})
[(def example {:a 1})]
user=> (e/parse-string-all "(def example {:a 1 #?@(:clj [:b 2 :c 3])})" {:read-cond :preserve})
Execution error (ExceptionInfo) at edamame.impl.parser/throw-reader (parser.cljc:44).
The map literal starting with :a contains 3 form(s). Map literals must contain an even number of forms.
user=> (e/parse-string-all "(def example {:a 1 #?@(:clj [:b 2 :c 3])})" {:read-cond (fn [obj] (list 'read-cond obj))})
Execution error (ExceptionInfo) at edamame.impl.parser/throw-reader (parser.cljc:44).
The map literal starting with :a contains 3 form(s). Map literals must contain an even number of forms.

expected behavior

I'm honestly not sure how to handle it. Unlike clj-kondo (cf clj-kondo #2049), edamame doesn't produce multiple versions of a given string/file so it's not really feasible to expand in the same way.

Maybe an edamame keyword to vector that contains each splicing reader conditional? This would work with :allow, :preserve, and the function option.

{:a 1 #?@(:clj [:b 2 :c 3]) #?@(:clj [:d 4 :e 5])} read in as {:a 1 :edamame.impl.parser/read-cond [(:clj [:b 2 :c 3]) (:clj [:d 4 :e 5])]}. That doesn't warn when one of them will produce an incorrect map (odd number of entries in the spliced vector), but that feels outside of edamame's scope.

full stack trace

user=> *e
#error {
 :cause "The map literal starting with :a contains 3 form(s). Map literals must contain an even number of forms."
 :data {:type :edamame/error, :row 1, :col 14}
 :via
 [{:type clojure.lang.ExceptionInfo
   :message "The map literal starting with :a contains 3 form(s). Map literals must contain an even number of forms."
   :data {:type :edamame/error, :row 1, :col 14}
   :at [edamame.impl.parser$throw_reader invokeStatic "parser.cljc" 44]}]
 :trace
 [[edamame.impl.parser$throw_reader invokeStatic "parser.cljc" 44]
  [edamame.impl.parser$throw_reader invoke "parser.cljc" 31]
  [edamame.impl.parser$throw_odd_map invokeStatic "parser.cljc" 507]
  [edamame.impl.parser$throw_odd_map invoke "parser.cljc" 505]
  [edamame.impl.parser$parse_map invokeStatic "parser.cljc" 525]
  [edamame.impl.parser$parse_map invoke "parser.cljc" 517]
  [edamame.impl.parser$dispatch invokeStatic "parser.cljc" 643]
  [edamame.impl.parser$dispatch invoke "parser.cljc" 570]
  [edamame.impl.parser$parse_next invokeStatic "parser.cljc" 710]
  [edamame.impl.parser$parse_next invoke "parser.cljc" 696]
  [edamame.impl.parser$parse_next invokeStatic "parser.cljc" 697]
  [edamame.impl.parser$parse_next invoke "parser.cljc" 696]
  [edamame.impl.parser$parse_to_delimiter invokeStatic "parser.cljc" 202]
  [edamame.impl.parser$parse_to_delimiter invoke "parser.cljc" 189]
  [edamame.impl.parser$parse_to_delimiter invokeStatic "parser.cljc" 191]
  [edamame.impl.parser$parse_to_delimiter invoke "parser.cljc" 189]
  [edamame.impl.parser$parse_list invokeStatic "parser.cljc" 223]
  [edamame.impl.parser$parse_list invoke "parser.cljc" 222]
  [edamame.impl.parser$dispatch invokeStatic "parser.cljc" 641]
  [edamame.impl.parser$dispatch invoke "parser.cljc" 570]
  [edamame.impl.parser$parse_next invokeStatic "parser.cljc" 710]
  [edamame.impl.parser$parse_next invoke "parser.cljc" 696]
  [edamame.impl.parser$parse_next invokeStatic "parser.cljc" 697]
  [edamame.impl.parser$parse_next invoke "parser.cljc" 696]
  [edamame.impl.parser$parse_string_all invokeStatic "parser.cljc" 842]
  [edamame.impl.parser$parse_string_all invoke "parser.cljc" 836]
  [edamame.core$parse_string_all invokeStatic "core.cljc" 75]
  [edamame.core$parse_string_all invoke "core.cljc" 69]
@borkdude
Copy link
Owner

@NoahTheDuke

The above problem is the same with Clojure's own reader:

$ clj
Clojure 1.11.0
user=> (read-string {:read-cond :preserve} "(def example {:a 1 #?@(:clj [:b 2 :c 3])})")
Execution error at user/eval1 (REPL:1).
Map literal must contain an even number of forms

How clj-kondo does this: it first parses the file using the first reader conditional feature, then lints that and then does the same for the second reader conditional.

@borkdude
Copy link
Owner

borkdude commented Apr 13, 2023

What I'm saying is: parse the file twice:

user=> (e/parse-string "(def example {:a 1 #?@(:clj [:b 2 :c 3])})" {:read-cond :allow :features #{:clj}})
(def example {:a 1, :b 2, :c 3})
user=> (e/parse-string "(def example {:a 1 #?@(:clj [:b 2 :c 3])})" {:read-cond :allow :features #{:cljs}})
(def example {:a 1})

and then work with those results.

@NoahTheDuke
Copy link
Contributor Author

Hmm okay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants