-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tree walking functions in
basilisp.walk
- Loading branch information
1 parent
c2017ab
commit e153717
Showing
4 changed files
with
253 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
(ns basilisp.walk) | ||
|
||
(defn walk | ||
"Walk an arbitrary, possibly nested data structure, applying inner to each | ||
element of form and then applying outer to the resulting form. | ||
All built in data structures are supported. | ||
Lazy sequences will be completely consumed (and thus may not be infinite)." | ||
[inner outer form] | ||
(cond | ||
(list? form) | ||
(outer (apply list (map inner form))) | ||
|
||
(map-entry? form) | ||
(outer (map-entry (inner (key form)) (inner (val form)))) | ||
|
||
(seq? form) | ||
(outer (doall (map inner form))) | ||
|
||
(vector? form) | ||
(outer (apply vector (map inner form))) | ||
|
||
(map? form) | ||
(outer (apply hash-map (mapcat inner form))) | ||
|
||
(set? form) | ||
(outer (apply hash-set (map inner form))) | ||
|
||
(record? form) | ||
(outer (reduce (fn [rec field] | ||
(conj rec (inner field))) | ||
form | ||
form)) | ||
|
||
:else | ||
(outer form))) | ||
|
||
(defn postwalk | ||
"Walk form using depth-first, post-order traversal, applying f to each form | ||
and replacing form with its result. | ||
All built in data structures are supported. | ||
Lazy sequences will be completely consumed (and thus may not be infinite)." | ||
[f form] | ||
(walk (partial postwalk f) f form)) | ||
|
||
(defn prewalk | ||
"Walk form using depth-first, pre-order traversal, applying f to each form | ||
and replacing form with its result. | ||
All built in data structures are supported. | ||
Lazy sequences will be completely consumed (and thus may not be infinite)." | ||
[f form] | ||
(walk (partial prewalk f) identity (f form))) | ||
|
||
(defn postwalk-replace | ||
"Recursively walk through form as by postwalk, replacing elements appearing | ||
as keys in replacements with the corresponding values." | ||
[replacements form] | ||
(postwalk #(if-let [newv (get replacements %)] | ||
newv | ||
%) | ||
form)) | ||
|
||
(defn prewalk-replace | ||
"Recursively walk through form as by prewalk, replacing elements appearing | ||
as keys in replacements with the corresponding values." | ||
[replacements form] | ||
(prewalk #(if-let [newv (get replacements %)] | ||
newv | ||
%) | ||
form)) | ||
|
||
(defn postwalk-demo | ||
"Print each element as it is walked as by postwalk." | ||
[form] | ||
(postwalk #(do (println (str "Walked: " %)) %) form)) | ||
|
||
(defn prewalk-demo | ||
"Print each element as it is walked as by postwalk." | ||
[form] | ||
(prewalk #(do (println (str "Walked: " %)) %) form)) | ||
|
||
(defn keywordize-keys | ||
"Recursively walk form, transforming string keys into keywords in any maps." | ||
[form] | ||
(postwalk (fn [v] | ||
(if (map? v) | ||
(->> v | ||
(mapcat (fn [[k v]] [(cond-> k (string? k) (keyword)) v])) | ||
(apply hash-map)) | ||
v)) | ||
form)) | ||
|
||
(defn stringify-keys | ||
"Recursively walk form, transforming keyword keys into strings in any maps." | ||
[form] | ||
(postwalk (fn [v] | ||
(if (map? v) | ||
(->> v | ||
(mapcat (fn [[k v]] [(cond-> k (keyword? k) (name)) v])) | ||
(apply hash-map)) | ||
v)) | ||
form)) | ||
|
||
(defn macroexpand-all | ||
"Recursively macroexpand all eligible forms contained in form." | ||
[form] | ||
(prewalk (fn [v] | ||
(if (seq? v) | ||
(macroexpand v) | ||
v)) | ||
form)) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
(ns tests.basilisp.walk-test | ||
(:require | ||
[basilisp.test :refer [deftest are is testing]] | ||
[basilisp.walk :as walk])) | ||
|
||
(deftest postwalk-replace-test | ||
(is (= [:c :d] (walk/postwalk-replace {:a 1 :b 2} [:c :d]))) | ||
(is (= [1 2] (walk/postwalk-replace {:a 1 :b 2} [:a :b]))) | ||
(is (= [1 2 :c] (walk/postwalk-replace {:a 1 :b 2} [:a :b :c]))) | ||
(is (= [1 2 [1 2] :c] (walk/postwalk-replace {:a 1 :b 2} [:a :b [:a :b] :c]))) | ||
(is (= {:NIL 4, :a 1, :b :NIL, :c 3} | ||
(walk/postwalk-replace {nil :NIL} {:a 1, :b nil, :c 3, nil 4})))) | ||
|
||
(deftest prewalk-replace-test | ||
(is (= [:c :d] (walk/prewalk-replace {:a 1 :b 2} [:c :d]))) | ||
(is (= [1 2] (walk/prewalk-replace {:a 1 :b 2} [:a :b]))) | ||
(is (= [1 2 :c] (walk/prewalk-replace {:a 1 :b 2} [:a :b :c]))) | ||
(is (= [1 2 [1 2] :c] (walk/prewalk-replace {:a 1 :b 2} [:a :b [:a :b] :c]))) | ||
(is (= {:NIL 4, :a 1, :b :NIL, :c 3} | ||
(walk/postwalk-replace {nil :NIL} {:a 1, :b nil, :c 3, nil 4})))) | ||
|
||
(deftest keywordize-keys-test | ||
(testing "maps and nested maps" | ||
(is (= {} (walk/keywordize-keys {}))) | ||
(is (= {:a 1 :b 2} (walk/keywordize-keys {"a" 1 "b" 2}))) | ||
(is (= {:a 1 :b 2 :c 3} (walk/keywordize-keys {"a" 1 "b" 2 :c 3}))) | ||
(is (= {:a 1 :c 3 4 :d} | ||
(walk/keywordize-keys {"a" 1 :c 3 4 :d}))) | ||
(is (= {:a 1 :c 3 4 :d :nested {:e 5 'b 6}} | ||
(walk/keywordize-keys {"a" 1 :c 3 4 :d "nested" {"e" 5 'b 6}})))) | ||
|
||
(testing "maps in lists and seqs" | ||
(is (= '({:a 1 :b 2}) (walk/keywordize-keys '({"a" 1 "b" 2})))) | ||
(is (= '({:a 1 :b 2 :c 3}) (walk/keywordize-keys '({"a" 1 "b" 2 :c 3})))) | ||
(is (= '({:a 1 :c 3 4 :d}) | ||
(walk/keywordize-keys '({"a" 1 :c 3 4 :d})))) | ||
(is (= '({:a 1 :c 3 4 :d :nested {:e 5 'b 6}}) | ||
(walk/keywordize-keys '({"a" 1 :c 3 4 :d "nested" {"e" 5 'b 6}})))) | ||
(is (= '({:a 1} {:a 2} {:a 3}) | ||
(walk/keywordize-keys (map #(hash-map "a" %) (range 1 4)))))) | ||
|
||
(testing "maps in sets" | ||
(is (= #{{:a 1 :b 2}} (walk/keywordize-keys #{{"a" 1 "b" 2}}))) | ||
(is (= #{{:a 1 :b 2 :c 3}} (walk/keywordize-keys #{{"a" 1 "b" 2 :c 3}}))) | ||
(is (= #{{:a 1 :c 3 4 :d}} | ||
(walk/keywordize-keys #{{"a" 1 :c 3 4 :d}}))) | ||
(is (= #{{:a 1 :c 3 4 :d :nested {:e 5 'b 6}}} | ||
(walk/keywordize-keys #{{"a" 1 :c 3 4 :d "nested" {"e" 5 'b 6}}})))) | ||
|
||
(testing "maps in vectors" | ||
(is (= [{:a 1 :b 2}] (walk/keywordize-keys [{"a" 1 "b" 2}]))) | ||
(is (= [{:a 1 :b 2 :c 3}] (walk/keywordize-keys [{"a" 1 "b" 2 :c 3}]))) | ||
(is (= [{:a 1 :c 3 4 :d}] | ||
(walk/keywordize-keys [{"a" 1 :c 3 4 :d}]))) | ||
(is (= [{:a 1 :c 3 4 :d :nested {:e 5 'b 6}}] | ||
(walk/keywordize-keys [{"a" 1 :c 3 4 :d "nested" {"e" 5 'b 6}}]))))) | ||
|
||
(deftest stringify-keys-test | ||
(testing "maps and nested maps" | ||
(is (= {} (walk/stringify-keys {}))) | ||
(is (= {"a" 1 "b" 2} (walk/stringify-keys {:a 1 :b 2}))) | ||
(is (= {"a" 1 "b" 2 "c" 3} (walk/stringify-keys {"a" 1 "b" 2 :c 3}))) | ||
(is (= {"a" 1 "c" 3 4 :d} | ||
(walk/stringify-keys {"a" 1 :c 3 4 :d}))) | ||
(is (= {"a" 1 "c" 3 4 :d "nested" {"e" 5 'b 6}} | ||
(walk/stringify-keys {"a" 1 :c 3 4 :d "nested" {:e 5 'b 6}})))) | ||
|
||
(testing "maps in lists and seqs" | ||
(is (= '({"a" 1 "b" 2}) (walk/stringify-keys '({:a 1 :b 2})))) | ||
(is (= '({"a" 1 "b" 2 "c" 3}) (walk/stringify-keys '({"a" 1 "b" 2 :c 3})))) | ||
(is (= '({"a" 1 "c" 3 4 :d}) | ||
(walk/stringify-keys '({"a" 1 :c 3 4 :d})))) | ||
(is (= '({"a" 1 "c" 3 4 :d "nested" {"e" 5 'b 6}}) | ||
(walk/stringify-keys '({"a" 1 :c 3 4 :d "nested" {:e 5 'b 6}})))) | ||
(is (= '({"a" 1} {"a" 2} {"a" 3}) | ||
(walk/stringify-keys (map #(hash-map :a %) (range 1 4)))))) | ||
|
||
(testing "maps in sets" | ||
(is (= #{{"a" 1 "b" 2}} (walk/stringify-keys #{{:a 1 :b 2}}))) | ||
(is (= #{{"a" 1 "b" 2 "c" 3}} (walk/stringify-keys #{{"a" 1 "b" 2 :c 3}}))) | ||
(is (= #{{"a" 1 "c" 3 4 :d}} | ||
(walk/stringify-keys #{{"a" 1 :c 3 4 :d}}))) | ||
(is (= #{{"a" 1 "c" 3 4 :d "nested" {"e" 5 'b 6}}} | ||
(walk/stringify-keys #{{"a" 1 :c 3 4 :d "nested" {:e 5 'b 6}}})))) | ||
|
||
(testing "maps in vectors" | ||
(is (= [{"a" 1 "b" 2}] (walk/stringify-keys [{:a 1 :b 2}]))) | ||
(is (= [{"a" 1 "b" 2 "c" 3}] (walk/stringify-keys [{"a" 1 "b" 2 :c 3}]))) | ||
(is (= [{"a" 1 "c" 3 4 :d}] | ||
(walk/stringify-keys [{"a" 1 :c 3 4 :d}]))) | ||
(is (= [{"a" 1 "c" 3 4 :d "nested" {"e" 5 'b 6}}] | ||
(walk/stringify-keys [{"a" 1 :c 3 4 :d "nested" {:e 5 'b 6}}]))))) | ||
|
||
(defmacro plus [n1 n2] | ||
`(+ ~n1 ~n2)) | ||
|
||
(defmacro pl [p1 p2] | ||
`(plus ~p1 ~p2)) | ||
|
||
(defmacro minus [m1 m2] | ||
`(- ~m1 ~m2)) | ||
|
||
(defmacro calc [c1 c2] | ||
`(pl ~c1 (minus ~c1 ~c2))) | ||
|
||
(deftest macroexpand-all-test | ||
(is (= '(tests.basilisp.walk-test/pl 20 (tests.basilisp.walk-test/minus 20 30)) | ||
(macroexpand-1 '(calc 20 30)))) | ||
(is (= '(basilisp.core/+ 20 (tests.basilisp.walk-test/minus 20 30)) | ||
(macroexpand '(calc 20 30)))) | ||
(is (= '(basilisp.core/+ 20 (basilisp.core/- 20 30)) | ||
(walk/macroexpand-all '(calc 20 30))))) |