Skip to content

Instantly share code, notes, and snippets.

@shapiy
Last active November 4, 2019 11:57
Show Gist options
  • Save shapiy/b8ccb847382d53bbd9e9f45aefb0ebba to your computer and use it in GitHub Desktop.
Save shapiy/b8ccb847382d53bbd9e9f45aefb0ebba to your computer and use it in GitHub Desktop.
Implementing XSLT-like transformation in bare Clojure: https://shapiy.github.io/blog/clojure-xslt/
(ns io.shapiy.core
(:gen-class)
(:require [clojure.data.xml :refer :all])
(:import (clojure.data.xml Element)))
(def html "<!DOCTYPE html>
<html>
<body>
<h1>Programming languages</h1>
<p>There are many, for example:</p>
<ol>
<li>Clojure (<a href=\"http://clojure.org\">official site</a>)</li>
<li>XSLT (<a href=\"http://www.w3.org/Style/XSL/\">W3C page</a>)</li>
</ol>
</body>
</html>")
(declare copy)
(declare apply-templates)
(defn element? [node] (.isInstance Element node))
(defn attrs? [node] (and (map? node) (not (element? node))))
(defn text? [node] (string? node))
(defn map-attrs [attrs]
(if (contains? attrs :href)
(let [https-url (clojure.string/replace (:href attrs) #"http://" "https://")]
(assoc attrs :href https-url))
attrs))
(defn map-identity [node] (copy node apply-templates))
(defn stylesheet [node]
(condp #(%1 %2) node
element? map-identity
attrs? map-attrs
text? map-identity))
(defn apply-template [node]
(let [template (stylesheet node)]
(template node)))
(defn apply-templates [node-or-nodes]
(if (seq? node-or-nodes)
(map apply-template node-or-nodes)
(apply-template node-or-nodes)))
(defn copy [node f]
(cond
(element? node) [(:tag node)
(f (:attrs node))
(f (:content node))]
(attrs? node) node
(text? node) node))
(defn transform []
(-> (parse-str html)
apply-templates
sexp-as-element
indent-str))
(defn -main [] (println (transform)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment