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

Add more type related helpers #114

Open
lread opened this issue Jan 23, 2021 · 10 comments
Open

Add more type related helpers #114

lread opened this issue Jan 23, 2021 · 10 comments

Comments

@lread
Copy link
Collaborator

lread commented Jan 23, 2021

Originally raised by me as lread/rewrite-cljc-playground#5

Related to #113 - capturing valuable feedback from @sogaiu so I don't lose it. Need to review and evaluate more type related helpers.

Snippit from @sogaiu

(defn string-value
  "Return the string value for a node."
  [node]
  (when-let [lines (:lines node)]
    (cs/join "\n" lines)))
(defn string-node?
  "Returns true if node represents a string, else false."
  [node]
  (string-value node))
(defn string?
  "Returns true if zipper represents a string, else false."
  [zloc]
  (some-> zloc rz/node string-node?))
(defn symbol-value
  "Return the symbol value for a node."
  [node]
  (:value node))
(defn symbol-node?
  "Returns true if node represents a symbol, else false."
  [node]
  (clojure.core/symbol? (symbol-value node)))
(defn symbol?
  "Returns true if zipper represents a symbol, else false."
  [zloc]
  (some-> zloc rz/node symbol-node?))

Also: pointer to similar can be found within clj-kondo fork of rewrite-clj.

@lread
Copy link
Collaborator Author

lread commented Jan 23, 2021

Issue comments from rewrite-cljc-playground:

@lread:
Was chatting with @borkdude on Slack and type came up again. Perhaps a (type node) might a good addition. @borkdude said that such a feature would allow him to avoid code like this.

@lread:
More chats on Slack, @SevereOverfl0w was wondering about querying node types.

We agreed the existing granularity provided by (rewrite-cljc.node/tag n) is coarser than we'd like.

@borkdude chimed in with examples of what he uses for his custom version of rewrite-clj in clj-kondo:

(defn boolean-token? [node]
  (boolean? (:value node)))

(defn char-token? [node]
  (char? (:value node)))

(defn string-from-token [node]
  (when-let [lines (:lines node)]
    (str/join "\n" lines)))

(defn number-token? [node]
  (number? (:value node)))

(defn symbol-token? [node]
  (symbol? (:value node)))

(defn symbol-from-token [node]
  (when-let [?sym (:value node)]
    (when (symbol? ?sym)
      ?sym)))

And then @borkdude also shared another idea:

a function that returned some keyword could also be handy if you want to dispatch on something:

(defn tag+ [node] (if (symbol? (:value node)) :symbol) ...)
(case (tag+ node) :symbol ...)

I think I have something like that too in clj-kondo
this could also go into some extra utils lib
or utils ns

@lread lread changed the title https://github.com/lread/rewrite-cljc-playground/issues/5 Add more type related helpers Jan 23, 2021
@lread
Copy link
Collaborator Author

lread commented Apr 6, 2021

I think I like the idea of a tag+ (or maybe node-type) to avoid littering our already wide node and zip APIs with a bunch of new little functions.

@borkdude
Copy link
Collaborator

One idea is to also use (isa? :rewrite-clj/symbol :rewrite-clj/token), possibly.

@lread
Copy link
Collaborator Author

lread commented May 23, 2022

One idea is to also use (isa? :rewrite-clj/symbol :rewrite-clj/token), possibly.

Directly related: #131 (comment)

@borkdude
Copy link
Collaborator

I already wondered where I left that comment, thanks.

@lread
Copy link
Collaborator Author

lread commented May 23, 2022

Ha! Yeah, I was searching for it yesterday, oddly enough!

@lread
Copy link
Collaborator Author

lread commented Jul 7, 2024

Draft 0 of hierarchy: tag+ name, example/desc, current tag, node record

  • rewrite-clj.tag/forms top-level forms holder :forms FormsNode
  • rewrite-clj.tag/token (classifier)
    • rewrite-clj.tag/symbol foo :token SymbolNode
    • rewrite-clj.tag/string "foo" :token StringNode
      • rewrite-clj.tag/string-multi-line "foo
        bar" :multi-line StringNode
    • rewrite-clj.tag/keyword :mykw :token KeywordNode
    • rewrite-clj.tag/boolean false :token TokenNode
    • rewrite-clj.tag/number 42 :token TokenNode
    • rewrite-clj.tag/character-literal \newline :token TokenNode
    • rewrite-clj.tag/regex #"\d+" :regex RegexNode
  • rewrite-clj.tag/clojure-whitespace (classifier)
    • rewrite-clj.tag/horizontal-whitespace tab(s), space(s) :whitespace WhitespaceNode
    • rewrite-clj.tag/newline carriage return(s), linefeed(s) :newline NewlineNode
    • rewrite-clj.tag/comma , :comma CommaNode
  • rewrite-clj.tag/sequence (classifier)
    • rewrite-clj.tag/map {:a 1 :b 2} :map SeqNode
    • rewrite-clj.tag/namespace-map #:prefix{:a 1 :b 2} :namespaced-map NamespacedMapNode
    • rewrite-clj.tag/vector [1 2 3] :vector SeqNode
    • rewrite-clj.tag/set #{1 2 3} :set SeqNode
  • rewrite-clj.tag/map-qualifier qualifier for namespace map :map-qualifier MapQualifierNode
  • rewrite-clj.tag/meta ^foo [] :meta MetaNode
    • rewrite.clj.tag/legacy-meta #^foo []``:meta* MetaNode
  • rewrite-clj.tag/comment ; foo :comment CommentNode
  • rewrite-clj.tag/uneval #_ ignore-me :uneval UnevalNode
  • rewrite-clj.tag/reader-macro #my-macro 42 :reader-macro ReaderMacroNode|
  • rewrite-clj.tag/var-quote #'my-var :var ReaderNode
  • rewrite-clj.tag/eval #=(inc 1) :eval ReaderNode
  • rewrite-clj.tag/quote 'sym :quote QuoteNode
  • rewrite-clj.tag/syntax-quote `map :syntax-quote QuoteNode
  • rewrite-clj.tag/unquote ~mysym :unquote QuoteNode
  • rewrite-clj.tag/unquote-splicing ~@body :unquote-splicing QuoteNode

Thoughts:

  1. Is the qualifier name good? Should it be rewrite-clj.tag+? I think rewrite-clj.tag is fine, it is a new thing and doesn't need the +.
  2. Consider making namespaced-map a descendant of map, or are they too different?
  3. uneval for #_ is a rewrite-clj-ism, I like the name but Cloure docs call this "discard" I suppose we could make a discard parent and have uneval as a child to fashion a synonym
  4. reader-macro is a catch-all for #things we don't distinguish.
    1. We could probably distinguish a tagged literal #foo 42 and reader conditionals. If we did that, would we still need reader-macro?
    2. And technically, plenty of the above are reader macros but I'm guessing that it is not terribly interesting to categorize them as such, but it might make sense to do so for extensibility.
  5. If more categorizations are deemed generally useful we can always add them at a later date.
  6. The category of rewrtite-clj.tag/sequence is probably not great, as a map is not really a sequence. Maybe instead rewrite-clj.tag/collection?

@sogaiu
Copy link

sogaiu commented Jul 8, 2024

Among the "reader-macro" things, here's a fun construct I didn't know about for a long time [1] [2]:

#user.Fun[1 2]
#user.Fun{:a 1 :b 2}

Haven't seen it very much in the wild (^^;


[1] There are even some docs.

[2] Here is where I struggled with naming...

@lread
Copy link
Collaborator Author

lread commented Jul 8, 2024

Hi @sogaiu! Thanks so much for sharing! ❤️

I did not know about that syntax, thanks! I'll compare my list above with the elements that tree-sitter-clojure recognizes and look for anything else I might have missed.

@sogaiu
Copy link

sogaiu commented Jul 8, 2024

If you see anything missing or amiss in tree-sitter-clojure, please mention!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: High Priority (next release)
Development

No branches or pull requests

3 participants