Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Command-line structural data editing
(ns ded
"Structural Data EDitor for Clojure with zippers. Inspired by Interlisp: http://larry.masinter.net/interlisp-ieee.pdf"
(:require [clojure.zip :as z])
(:use [clojure.pprint :only (with-pprint-dispatch code-dispatch pprint)]
[clojure.repl :only (source-fn)]))
(defn print-hr
"Prints 30 dashes and a newline."
[c]
(println (apply str (repeat 30 c))))
(defn print-loc
"pprints form using code-dispatch, wrapping in HRs."
[root loc]
(with-pprint-dispatch code-dispatch
(print-hr \_)
(pprint root)
(print-hr \-)
(pprint loc)
(print-hr \_)
(flush)))
(defn code-zip
[root]
(z/zipper (some-fn seq? vector?)
identity
(fn [node children] (with-meta children (meta node)))
root))
(def ^{:doc "Map of command characters to functions. Command functions take the current node as their argument."}
commands {;; basic movement
\u z/up
\d z/down
\< z/left
\> z/right
\b z/leftmost
\e z/rightmost
;; nav to top level
\t #(code-zip (z/root %))
;; evaluate node at loc
\. #(do (print "= ") (pprint (eval (z/node %))) %)
;; evaluate root
\! #(do (print "= ") (pprint (eval (z/root %))) %)
;; insert to the left
\L #(z/left (z/insert-left % (read)))
;; insert to the right
\R #(z/right (z/insert-right % (read)))
;; remove
\D z/remove
;; overwrite
\O #(z/replace % (read))
;; wrap with fn call. Prompts for fn name.
\W #(z/replace % `(~(read) ~(z/node %)))
;; raise
\w #(z/replace (z/up %) (z/node %))})
(defn edit
"Opens form for editing. Commands are one character each, and are
executed in sequence. After successful command execution, prints root
followed by node at loc."
([] (edit ()))
([form]
(loop [loc (code-zip form)]
(print-loc (z/node (code-zip (z/root loc)))
(z/node loc))
(print "> ")
(flush)
(let [input (str (read))]
(condp = input
"q" (z/root loc)
"Q" (do (eval (z/root loc)) (z/root loc))
(recur (try (reduce #(%2 %1) loc (map commands input))
(catch Exception _ (do (println "Error.")
loc)))))))))
(defn edit-fn
"Attempts to read fn's source code using clojure.repl/source-fn and
present it for editing."
[fn]
(edit (read-string (source-fn fn))))
;; ded=> (edit '(+ (* 12 13) 2 (- 10 5)))
;; ------------------------------
;; (+ (* 12 13) 2 (- 10 5)) <- root
;; ------------------------------
;; (+ (* 12 13) 2 (- 10 5)) <- current location
;; ------------------------------
;; > ! <- eval root
;; 163
;; ------------------------------
;; (+ (* 12 13) 2 (- 10 5))
;; ------------------------------
;; (+ (* 12 13) 2 (- 10 5))
;; ------------------------------
;; > dedbO! <- down, end, down, beginning, overwrite, eval
;; +
;; 173
;; ------------------------------
;; (+ (* 12 13) 2 (+ 10 5))
;; ------------------------------
;; +
;; ------------------------------
;; > ub>D! <- up, beginning, right, delete, eval
;; 17
;; ------------------------------
;; (+ 2 (+ 10 5))
;; ------------------------------
;; +
;; ------------------------------
;; > >>. <- right, right, eval at loc
;; 15
;; ------------------------------
;; (+ 2 (+ 10 5))
;; ------------------------------
;; (+ 10 5)
;; ------------------------------
;; >
Owner

See also Pascal J. Bourguignon's Sexp Edit, a structural editing example in Common Lisp.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment