Skip to content

Instantly share code, notes, and snippets.

Embed
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)
;; ------------------------------
;; >
@alandipert

This comment has been minimized.

Copy link
Owner Author

commented Aug 24, 2011

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
You can’t perform that action at this time.