This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-07
Channels
- # babashka (17)
- # beginners (33)
- # calva (9)
- # cider (2)
- # clj-kondo (17)
- # cljs-dev (2)
- # cljsrn (2)
- # clojars (3)
- # clojure (60)
- # clojure-australia (2)
- # clojure-europe (8)
- # clojure-gamedev (5)
- # clojurescript (27)
- # cursive (2)
- # emacs (9)
- # fulcro (8)
- # gratitude (5)
- # lsp (41)
- # malli (3)
- # meander (18)
- # pathom (5)
- # polylith (25)
- # re-frame (17)
- # reagent (1)
- # rewrite-clj (6)
- # shadow-cljs (11)
- # uncomplicate (5)
- # vim (6)
- # web-security (5)
Hi, what is the right way to rewrite map key/value pairs of a nested structures? I think I should be using a strategy, but can't seem to make it work. For example, I thought the following would rewrite any map :viewbox
entries with :viewBox
(capital b
), but it loses the rest of the entries in the map (as if the substitution rule replaces the whole map rather than the matching entry):
(let [vb (m*/bottom-up
(m*/attempt
(m*/match
{:viewbox (m/some ?vb)} {:viewBox ?vb})))]
(vb [:svg
{:xmlns "",
:viewbox "0 0 24 24"}]))
;; => [:svg {:viewBox "0 0 24 24"}]
Thanks(let [vb (r/bottom-up
(r/attempt
(r/match
{:viewbox (m/some ?vb)
& ?rest} (into {:viewBox ?vb} ?rest))))]
(vb [:svg
{:xmlns "" ,
:viewbox "0 0 24 24"}]))
[:svg {:viewBox "0 0 24 24", :xmlns ""}]
You could also use
(r/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest})
Thanks! unfortunately I have a second rule for naively splitting the style attribute, which I do not think it can be used for a rewrite substitution (or can it?):
(let [t1 (m*/bottom-up
(m*/attempt
(m*/match
{:viewbox (m/some ?vb) & ?rest}
(assoc ?rest :viewBox ?vb)
{:style (m/some ?st) & ?rest}
(assoc ?rest :style (let [[k v] (clojure.string/split ?st #": " 2)]
{(keyword k) (str v)})))))]
(t1 [:div
{:style "width: 100px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
;; => [:div
{:style {:width "100px"}}
[:svg {:xmlns "", :viewBox "0 0 24 24"}]]
Sure, you can use m/re
for the splitting on the left side and m/keyword
on the right:
(m*/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest}
{:style (m/re #"(?s)([^:])*:\s+(.*)" [_ ?k ?v]) & ?rest}
{:style {(m/keyword ?k) ?v} & ?rest})
Thanks, pretty cool thinking only in terms of pure symbolic terms:
(let [t2 (m*/bottom-up
(m*/attempt
(m*/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest}
{:style (m/re #"(?s)([^:]+):\s+(.*)" [_ ?k ?v]) & ?rest}
{:style {(m/keyword ?k) ?v} & ?rest}
)))]
(t2 [:div
{:style "width: 100px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
;; => [:div
{:style {:width "100px"}}
[:svg {:xmlns "", :viewBox "0 0 24 24"}]]
Up to the next thing I'd need to consider, what if :style
has more than one inline styles separated by ;
that I'd like to collect in the final result, would this be something I could write with pure rewrite
terms? for example I can quickly update match
to further naively split on ;
:
(let [t1 (m*/bottom-up
(m*/attempt
(m*/match
{:viewbox (m/some ?vb) & ?rest}
(assoc ?rest :viewBox ?vb)
{:style (m/some ?st) & ?rest}
(assoc ?rest :style (into {}
(for [style (clojure.string/split ?st #"; ")]
(let [[k v] (clojure.string/split style #": " 2)]
{(keyword k) (str v)})))))))]
(t1 [:div
{:style "width: 100px; height: 200px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
;; => [:div
{:style {:width "100px", :height "200px"}}
[:svg {:xmlns "", :viewBox "0 0 24 24"}]]
(It is not important, I am just trying to see how far I could go by just using pure symbolic substitutions)
I don't have experience with strategies. I can't run the code...
;; (:require [meander.epsilon :as m]
;; [meander.strategy.epsilon :as r])
(let [t1 (r/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest}
{:style (m/re #"(?s)([^:])*:\s+(.*)" [_ ?k ?v]) & ?rest}
{:style {(m/keyword ?k) ?v} & ?rest}
)]
(t1 [:div
{:style "width: 100px; height: 200px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
gives me #meander.epsilon/fail[]
perhaps this is because r/attempt is missing from the code and fails on the first mismatch?
Oh, ok. I thought noprompt was suggesting rewrite instead of bottom-up+attempt+match. I guess it would actually be bottom-up+attempt+rewrite? Anyways, I think you can just write a function to do what you want and stick it in with app
(let [t1 (r/bottom-up
(r/attempt
(r/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest}
{:style (m/app (constantly :whatever) ?a) & ?rest}
{:style ?a & ?rest})))]
(t1 [:div
{:style "width: 100px; height: 200px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
[:div {:style :whatever} [:svg {:xmlns "", :viewBox "0 0 24 24"}]]
This looks pretty clean, IMHO. Sorry I haven’t been able to participate in the thread since I first replied, I haven’t had a moment to focus here.
Great thanks, you have both been very helpful. And the final rewrite version with the naive split:
(let [styles->map
#(into {} (for [style (clojure.string/split % #"; ")]
(let [[k v] (clojure.string/split style #": " 2)]
{(keyword k) (str v)})))
t4 (m*/bottom-up
(m*/attempt
(m*/rewrite
{:viewbox (m/some ?vb) & ?rest}
{:viewBox ?vb & ?rest}
{:style (m/app styles->map ?styles) & ?rest}
{:style ?styles & ?rest}
)))]
(t4 [:div
{:style "width: 100px; height: 200px"}
[:svg
{:xmlns "",
:viewbox "0 0 24 24"}]]))
;; => [:div
;; {:style {:width "100px", :height "200px"}}
;; [:svg {:xmlns "", :viewBox "0 0 24 24"}]]