Skip to content

Commit eb20251

Browse files
committed
Add sexpr-able? to node and zip APIs
Closes #113
1 parent 9fcdf9e commit eb20251

File tree

11 files changed

+119
-38
lines changed

11 files changed

+119
-38
lines changed

CHANGELOG.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,17 @@ Rewrite-cljs users migrating to rewrite-clj v1 are now at, and will remain at, f
4242
*** Adds namespaced element support functions
4343
**** `reapply-context` - reapplies (or removes) map qualifier node context from keywords and symbols
4444
**** zipper creation functions now optionally accept an auto-resolve function to support sexpr on namespaced element nodes
45+
*** Other additions
46+
**** `sexpr-able?` - return true if `sexpr` is supported for current node
4547
** `rewrite-clj.node`
4648
*** Additions:
4749
**** `keyword-node?` - returns true if form is a rewrite-clj keyword node
4850
**** `map-qualifier-node` - to create a namespaced map's map qualifier node manually
4951
**** `map-context-apply` - apply map qualifier to keyword or symbol
5052
**** `map-context-clear` - remove map qualifier from keyword or symbol
5153
**** `node?` - returns true if a form is a rewrite-clj created node
52-
**** `symbol-node?` - return true if node is a rwrite-clj symbol node
54+
**** `sexpr-able?` - return true if `sexpr` is supported for node
55+
**** `symbol-node?` - return true if node is a rewrite-clj symbol node
5356
*** Updates:
5457
**** `sexpr`, `sepxrs` and `child-sexprs` - now optionally take an options argument to specify an auto-resolve function
5558
* Many updates to docs and docstrings

doc/01-user-guide.adoc

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -575,29 +575,67 @@ The whitespace that a rewrite-clj so carefully preserves is lost when converting
575575

576576
=== Not all Source is Sexpr-able
577577

578-
Some source code elements are not sexpr-able.
579-
Reader ignore/discard `#_`, comment and whitespace all throw an "unsupported operation" exception.
578+
Some source code elements are not sexpr-able:
579+
580+
* Reader ignore/discard `#_` (also known as "uneval" in rewrite-clj)
581+
* Comments
582+
* Clojure whitespace (which includes commas)
583+
584+
Both the zip and node APIs include `sexpr-able?` to check if sexpr is supported for a node.
580585

581586
[source, clojure]
582587
----
583-
(require '[rewrite-clj.zip :as z])
588+
(require '[rewrite-clj.node :as n]
589+
'[rewrite-clj.parser :as p]
590+
'[rewrite-clj.zip :as z])
584591
585592
#?(:clj (import clojure.lang.ExceptionInfo))
586593
594+
;;
595+
;; Most nodes are sexpr-able
596+
;;
597+
598+
;; we can check sexpr-ability through the node API
599+
(-> "hello" p/parse-string n/sexpr-able?)
600+
;; => true
601+
602+
;; or through the zip API
603+
(-> "hello" z/of-string z/sexpr-able?)
604+
;; => true
605+
606+
;;
607+
;; But some nodes are not sexpr-able
608+
;;
609+
610+
;; the discard #_ node is not sexpr-able
611+
(-> "#_42" z/of-string z/sexpr-able?)
612+
;; => false
613+
614+
;; and will throw if an attempt is made to sexpr
587615
(try
588-
(-> (z/of-string "#_42") z/sexpr)
616+
(-> "#_42" z/of-string z/sexpr)
589617
(catch ExceptionInfo e
590618
(ex-message e)))
591619
;; => "unsupported operation"
592620
621+
;; comments nodes are not sexpr-able
622+
(-> ";; can’t sexpr me!" z/of-string z/next* z/sexpr-able?) ;; <1>
623+
;; => false
624+
625+
;; and will throw
593626
(try
594-
(-> (z/of-string ";; can’t sexpr me!") z/next* z/sexpr) ;; <1>
627+
(-> ";; can’t sexpr me!" z/of-string z/next* z/sexpr) ;; <1>
595628
(catch ExceptionInfo e
596629
(ex-message e)))
597630
;; => "unsupported operation"
598631
632+
;; and finally, Clojure whitespace nodes are not sexpr-able
633+
(-> " " z/of-string z/next* z/sexpr-able?) ;; <1>
634+
;; => false
635+
636+
;; and will throw
599637
(try
600-
(-> (z/of-string " ") z/next* z/sexpr) ;; <1>
638+
(-> " " z/of-string z/next* z/sexpr) ;; <1>
601639
(catch ExceptionInfo e
602640
(ex-message e)))
603641
;; => "unsupported operation"

src/rewrite_clj/node.cljc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@
8787
([node] (rewrite-clj.node.protocols/sexpr node))
8888
([node opts] (rewrite-clj.node.protocols/sexpr node opts)))
8989

90+
;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.node.protocols
91+
(defn sexpr-able?
92+
"Return true if [[sexpr]] is supported for `node`."
93+
[node] (rewrite-clj.node.protocols/sexpr-able? node))
94+
9095
;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.node.protocols
9196
(defn sexprs
9297
"Return forms for `nodes`. Nodes that do not represent s-expression are skipped.

src/rewrite_clj/node/namespaced_map.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
node/Node
88
(tag [_node] :map-qualifier)
99
(node-type [_node] :map-qualifier)
10-
(printable-only? [_node] true)
10+
(printable-only? [_node] false)
1111
(sexpr* [_node opts]
1212
(if auto-resolved?
1313
((or (:auto-resolve opts) node/default-auto-resolve)

src/rewrite_clj/node/protocols.cljc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
(length [this] (count (string this)))
3232
(string [this] (pr-str this)))
3333

34+
(defn sexpr-able?
35+
"Return true if [[sexpr]] is supported for `node`."
36+
[node]
37+
(not (printable-only? node)))
38+
3439
(defn sexpr
3540
"Return `node` converted to form.
3641

src/rewrite_clj/zip.cljc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
[[length]]
9191
9292
**Convert**
93+
[[sexpr-able?]]
9394
[[sexpr]]
9495
[[child-sexprs]]
9596
[[reapply-context]]
@@ -233,6 +234,11 @@
233234
See docs for [sexpr nuances](/doc/01-user-guide.adoc#sexpr-nuances)."
234235
[zloc] (rewrite-clj.zip.base/sexpr zloc))
235236

237+
;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
238+
(defn sexpr-able?
239+
"Return true if current node in `zloc` can be [[sexpr]]-ed."
240+
[zloc] (rewrite-clj.zip.base/sexpr-able? zloc))
241+
236242
;; DO NOT EDIT FILE, automatically imported from: rewrite-clj.zip.base
237243
(defn length
238244
"Return length of printable string of current node in `zloc`."

src/rewrite_clj/zip/base.cljc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
[zloc]
5858
(some-> zloc zraw/node node/tag))
5959

60+
(defn sexpr-able?
61+
"Return true if current node in `zloc` can be [[sexpr]]-ed."
62+
[zloc]
63+
(some-> zloc zraw/node node/sexpr-able?))
64+
6065
(defn sexpr
6166
"Return s-expression (the Clojure form) of current node in `zloc`.
6267

template/rewrite_clj/node.cljc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
printable-only?
4040
replace-children
4141
sexpr
42+
sexpr-able?
4243
sexprs
4344
map-context-apply
4445
map-context-clear

template/rewrite_clj/zip.cljc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
[[length]]
9090
9191
**Convert**
92+
[[sexpr-able?]]
9293
[[sexpr]]
9394
[[child-sexprs]]
9495
[[reapply-context]]
@@ -172,7 +173,7 @@
172173
[rewrite-clj.zip.base
173174
child-sexprs
174175
edn* edn
175-
tag sexpr
176+
tag sexpr sexpr-able?
176177
length
177178
value
178179
of-string

test/rewrite_clj/node_test.cljc

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,53 @@
55
[rewrite-clj.node.protocols :as proto]
66
[rewrite-clj.parser :as p]))
77

8-
(deftest nodes-convert-to-strings
8+
(deftest nodes-convert-to-strings-and-sexpr-ability
99
(testing "easily parseable"
10-
(are [?in ?expected-tag ?expected-type]
11-
(let [n (p/parse-string ?in)]
10+
(are [?in ?expected-tag ?expected-type ?expected-sexpr-able?]
11+
(let [n (p/parse-string ?in)]
1212
(is (= ?in (str n)))
1313
(is (= ?expected-tag (n/tag n)))
14-
(is (= ?expected-type (proto/node-type n))))
15-
"," :comma :comma
16-
"; comment" :comment :comment
17-
"@deref" :deref :deref
18-
"#(fn %1)" :fn :fn
19-
;; forms
20-
;; int
21-
":my-kw" :token :keyword
22-
"^:meta b" :meta :meta
23-
"#:prefix {:a 1}" :namespaced-map :namespaced-map
24-
"\n" :newline :newline
25-
"'quoted" :quote :quote
26-
"#booya 32" :reader-macro :reader-macro
27-
"#'myvar" :var :reader
28-
"#\"regex\"" :regex :regex
29-
"[1 2 3]" :vector :seq
30-
"\"string\"" :token :string
31-
"symbol" :token :symbol
32-
"43" :token :token
33-
"#_ nope" :uneval :uneval
34-
" " :whitespace :whitespace))
14+
(is (= ?expected-type (proto/node-type n)))
15+
(is (= ?expected-sexpr-able? (n/sexpr-able? n))))
16+
"," :comma :comma false
17+
"; comment" :comment :comment false
18+
"@deref" :deref :deref true
19+
"#(fn %1)" :fn :fn true
20+
":my-kw" :token :keyword true
21+
"^:meta b" :meta :meta true
22+
"#:prefix {:a 1}" :namespaced-map :namespaced-map true
23+
"\n" :newline :newline false
24+
"'quoted" :quote :quote true
25+
"#booya 32" :reader-macro :reader-macro true
26+
"#'myvar" :var :reader true
27+
"#\"regex\"" :regex :regex true
28+
"[1 2 3]" :vector :seq true
29+
"\"string\"" :token :string true
30+
"symbol" :token :symbol true
31+
"43" :token :token true
32+
"#_ nope" :uneval :uneval false
33+
" " :whitespace :whitespace false))
3534
(testing "map qualifier"
36-
(is (= ":prefix" (str (n/map-qualifier-node false "prefix"))))
37-
(is (= "::" (str (n/map-qualifier-node true nil))))
38-
(is (= "::nsalias" (str (n/map-qualifier-node true "nsalias")))))
35+
(are [?auto-resolved ?prefix ?expected-str]
36+
(let [n (n/map-qualifier-node ?auto-resolved ?prefix)]
37+
(is (= ?expected-str (str n)))
38+
(is (= :map-qualifier (n/tag n)))
39+
(is (= :map-qualifier (proto/node-type n)))
40+
(is (= true (n/sexpr-able? n))))
41+
false "prefix" ":prefix"
42+
true nil "::"
43+
true "nsalias" "::nsalias"))
3944
(testing "integer"
40-
(is (= "42" (str (n/integer-node 42)))))
45+
(let [n (n/integer-node 42)]
46+
(is (= :token (n/tag n)))
47+
(is (= :int (proto/node-type n)))
48+
(is (= "42" (str n)))
49+
(is (n/sexpr-able? n))))
4150
(testing "forms node"
42-
(is (= "5 7" (str (p/parse-string-all "5 7"))))))
51+
(let [n (p/parse-string-all "5 7")]
52+
(is (= "5 7" (str n)))
53+
(is (n/sexpr-able? n)))))
54+
4355

4456
(deftest namespaced-keyword
4557
(is (= ":dill/dall"

0 commit comments

Comments
 (0)