Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
Emacs Lisp can unquote splice an atom at the last position of a list.…
Browse files Browse the repository at this point in the history
… Closure mess.
  • Loading branch information
Håkan Råberg committed Mar 10, 2013
1 parent 2f39368 commit 01790c5
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 18 deletions.
11 changes: 0 additions & 11 deletions src/deuce-loadup.el
Expand Up @@ -153,18 +153,7 @@

;; DEUCE: minibuffer implements completion, the actual minibuffer is (not yet) in minibuf.clj
;; Autoloads (but fails) pcase, which is a pattern matcher utterly confused by Deuce's concept of cons.
;; Also, a minor mode macro which many files use is blowing up, next thing to investigate.
;; cl-macs and autoloads seem to be the main things from actually compiling and loading the rest of the needed files.
;; PersistentList instead of Cons and syntax-quoted deuce.emacs-lisp.cons/pair are known issues.
;; (Running properly is another matter altogether.)
;; The pcase issue is not really solved, but autoloads are now delayed until actually called.
;; Pcase is pretty new in Emacs terms, seems to be more of it in 24.3.
;; This enters a never ending loop in pcase.clj, works in pcase.el, something with and:
;; Emacs Lisp:
;; (pcase '(current-time . 1) (`(,(and (pred functionp) x) . ,_) (funcall x)))
;; Same in Clojure:
;; (pcase '(current-time . 1) ((#el/sym "\\`" ((#el/sym "\\," (and (pred functionp) x)) . (#el/sym "\\," _))) (funcall x)))
;; Pcase currently fails with Don't know how to create ISeq from: clojure.lang.Symbol in an odd way.
;; The problem is this: `(match ,sym ,@upat) both sym and upat are x.
;; An atom is allowed at a the tail as unquote-splicing in an Emacs Lisp, which basically uses setcdr:
;; '(match x . x)
Expand Down
2 changes: 1 addition & 1 deletion src/deuce/emacs/frame.clj
Expand Up @@ -151,7 +151,7 @@
(defun frame-parameter (frame parameter)
"Return FRAME's value for parameter PARAMETER.
If FRAME is nil, describe the currently selected frame."
)
nil)

(defun framep (object)
"Return non-nil if OBJECT is a frame.
Expand Down
37 changes: 31 additions & 6 deletions src/deuce/emacs_lisp.clj
Expand Up @@ -79,7 +79,10 @@
(c/defmacro el-var-get [name]
(c/let [name (sym name)]
(if (c/and (symbol? name) (name &env))
`(if (var? ~name) @~name ~name)
`(if (var? ~name)
(if (bound? ~name)@~name
(throw* '~'void-variable '~name))
~name)
`(el-var-get* '~name))))

;; split these out into el-var-set-** and get rid of eval in deuce.emacs.data/set(-default)
Expand All @@ -95,7 +98,7 @@
`(c/let [value# ~value]
(if-let [^Var v# (c/or ~(c/and (symbol? name) (name &env) name)
((some-fn *dynamic-vars* el-var-buffer-local) '~name))]
(if (c/and (.hasRoot v#) (not (.getThreadBinding v#)))
(if (c/or (c/and (.hasRoot v#) (not (.getThreadBinding v#))) (not (bound? v#)))
(alter-var-root v# (constantly value#))
(var-set v# value#))
(el-var-set-default ~name value#)))))
Expand Down Expand Up @@ -159,7 +162,9 @@
(map el->clj rst))))))))
symbol? (if (namespace x)
(if (-> (resolve x) meta :macro) (resolve x) x)
(list `el-var-get x))
(if (= '. x)
x
(list `el-var-get x)))
x))

(defn ^Throwable cause [^Throwable e]
Expand All @@ -177,18 +182,29 @@
(defn syntax-quote* [form]
(.invoke clojure-syntax-quote nil (into-array [form])))

;; Emacs Lisp allows an atom to be spliced if it is in the last position, like this:
;; (let ((upat 'x) (sym 'x))
;; `(match ,sym ,@upat))
;; => (match x . x)
(defn maybe-splice-dotted-list [x]
(if ((some-fn seq? nil?) x) x `(. ~x)))

;; There's a version of this already defined as macro in backquote.el, use it / override it?
;; What's their relationship?
(defn emacs-lisp-backquote [form]
(w/postwalk
#(c/cond
(c/and (seq? %) (seq? (last %)) (= `unquote-splicing (first (last %))))
(concat (butlast %) `((unquote-splicing (maybe-splice-dotted-list ~(second (last %))))))

(c/and (seq? %) (= '#el/sym "\\`" (first %)))
(w/postwalk cons/maybe-seq (el->clj (syntax-quote* (second %))))

(= '#el/sym "\\," %) `unquote
(= '#el/sym "\\,@" %) `unquote-splicing
:else %) form))

;; Explore to either get rid of or just using the macro, not both el->clj and it
;; Explore to either get rid of or just using the macro, not both el->clj and it.
;; (c/defmacro #el/sym "\\`" [form]
;; (emacs-lisp-backquote (list '#el/sym "\\`" form)))

Expand Down Expand Up @@ -288,15 +304,20 @@
BODY should be a list of Lisp expressions."
{:arglists '([ARGS [DOCSTRING] [INTERACTIVE] BODY])}
[& cdr]
(println cdr)
(c/let [[args & body] cdr
[docstring body] (parse-doc-string body)
doc (apply str docstring)
vars (remove #(re-find #"__\d+" (name %)) (keys &env))
vars (vec (remove (c/set args) vars))]
;; This is wrong as it won't share updates between original definition and the lambda var.
;; Yet to see if this ends up being a real issue.
;; Yet to see if this ends up being a real issue. A few days later: Indeed it is!
`(c/let [closure# (zipmap '~vars
(map #(doto (Var/create (if (var? %) (deref %) %)) .setDynamic)
(map #(doto
(if (dynamic-binding?) ;; Temporary hack.
(if (var? %) % (Var/create %))
(Var/create (if (var? %) (deref %) %)))
.setDynamic)
~vars))]
(with-meta
(def-helper* fn nil lambda ~args
Expand Down Expand Up @@ -459,6 +480,10 @@
[arg]
`(quote ~arg))

;; Bindings refering to other bindings and modifying them don't work properly.
;; The vars must be created here instead of in with-local-el-vars (which might be removed).
;; Everytime you make a 'sane' assumption you're bound to find some Emacs Lisp breaking it.
;; (let* ((x 2) (y (setq x 4))) (+ x y)) => 8
(c/defmacro let-helper* [can-refer? varlist & body]
(c/let [varlist (map #(if (symbol? %) [% nil] %) varlist)
all-vars (map (comp sym first) varlist)
Expand Down

0 comments on commit 01790c5

Please sign in to comment.