Skip to content

Commit

Permalink
[#421] Support top-level do emitted from macro
Browse files Browse the repository at this point in the history
  • Loading branch information
borkdude authored Sep 22, 2020
1 parent 2f3760e commit fba06e9
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 43 deletions.
68 changes: 35 additions & 33 deletions src/sci/impl/analyzer.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@

;;;; End vars

(defn analyze-call [ctx expr]
(defn analyze-call [ctx expr top-level?]
(let [f (first expr)]
(if (symbol? f)
(let [;; in call position Clojure prioritizes special symbols over
Expand Down Expand Up @@ -689,8 +689,6 @@
var (analyze-var ctx expr)
set! (analyze-set! ctx expr)
import (mark-eval-call expr) ;; don't analyze children
;; macroexpand-1 (macroexpand-1 ctx expr)
;; macroexpand (macroexpand ctx expr)
;; else:
(mark-eval-call (cons f (analyze-children ctx (rest expr)))))
:else
Expand All @@ -705,9 +703,12 @@
(rest expr))
(apply f expr
(:bindings ctx) (rest expr)))
expanded (if (:sci.impl/macroexpanding ctx)
v
(analyze ctx v))]
expanded (cond (:sci.impl/macroexpanding ctx) v
(and top-level? (seq? v) (= 'do (first v)))
;; hand back control to eval-form for
;; interleaved analysis and eval
(types/->EvalForm v)
:else (analyze ctx v))]
expanded)
(mark-eval-call (cons f (analyze-children ctx (rest expr)))))
(catch #?(:clj Exception :cljs js/Error) e
Expand All @@ -720,33 +721,34 @@
ret))))

(defn analyze
[ctx expr]
;; (prn "ana" expr)
(let [ret (cond (constant? expr) expr ;; constants do not carry metadata
(symbol? expr) (let [v (resolve-symbol ctx expr false)]
(cond (constant? v) v
;; (fn? v) (utils/vary-meta* v dissoc :sci.impl/op)
(vars/var? v) (if (:const (meta v))
@v (types/->EvalVar v))
:else (merge-meta v (meta expr))))
:else
(merge-meta
(cond
(record? expr) expr ;; don't evaluate records
(map? expr)
(-> (zipmap (analyze-children ctx (keys expr))
(analyze-children ctx (vals expr)))
mark-eval)
(or (vector? expr) (set? expr))
(-> (into (empty expr) (analyze-children ctx expr))
mark-eval)
(and (seq? expr) (seq expr))
(analyze-call ctx expr)
:else expr)
(select-keys (meta expr)
[:line :column :tag])))]
;; (prn "ana" expr '-> ret 'm-> (meta ret))
ret))
([ctx expr]
(analyze ctx expr false))
([ctx expr top-level?]
;; (prn "ana" expr)
(let [ret (cond (constant? expr) expr ;; constants do not carry metadata
(symbol? expr) (let [v (resolve-symbol ctx expr false)]
(cond (constant? v) v
(vars/var? v) (if (:const (meta v))
@v (types/->EvalVar v))
:else (merge-meta v (meta expr))))
:else
(merge-meta
(cond
(record? expr) expr ;; don't evaluate records
(map? expr)
(-> (zipmap (analyze-children ctx (keys expr))
(analyze-children ctx (vals expr)))
mark-eval)
(or (vector? expr) (set? expr))
(-> (into (empty expr) (analyze-children ctx expr))
mark-eval)
(and (seq? expr) (seq expr))
(analyze-call ctx expr top-level?)
:else expr)
(select-keys (meta expr)
[:line :column :tag])))]
;; (prn "ana" expr '-> ret 'm-> (meta ret))
ret)))

;;;; Scratch

Expand Down
12 changes: 5 additions & 7 deletions src/sci/impl/interpreter.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -651,12 +651,8 @@

(vreset! utils/interpret interpret)

(defn do? [expr]
(and (list? expr)
(= 'do (first expr))))

(defn eval-form [ctx form]
(if (list? form)
(if (seq? form)
(if (= 'do (first form))
(loop [exprs (rest form)
ret nil]
Expand All @@ -668,8 +664,10 @@
(when (or (not (:uberscript ctx))
(= 'ns (first form))
(= 'require (first form)))
(let [analyzed (ana/analyze ctx form)
ret (interpret ctx analyzed)]
(let [analyzed (ana/analyze ctx form true)
ret (if (instance? sci.impl.types.EvalForm analyzed)
(eval-form ctx (t/getVal analyzed))
(interpret ctx analyzed))]
ret)))
(let [analyzed (ana/analyze ctx form)
ret (interpret ctx analyzed)]
Expand Down
5 changes: 5 additions & 0 deletions src/sci/impl/types.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@
:sci.impl.protocols/reified)
(some-> x meta :sci.impl/type)
(type x)))

;; returned from analyzer when macroexpansion needs interleaved eval
(deftype EvalForm [form]
IBox
(getVal [this] form))
7 changes: 6 additions & 1 deletion test/sci/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,12 @@
(lets))))
(foo 10)"))))
(is (= '(foo 1 2 3) (eval* "(defmacro foo [x y z] (list 'quote &form)) (foo 1 2 3)"))))
(is (= '(foo 1 2 3) (eval* "(defmacro foo [x y z] (list 'quote &form)) (foo 1 2 3)")))
(testing "top level macro that emits do form should be analyzed an eval'ed interleaved"
(is 'foo (eval* "(defmacro dude []
`(do (ns ~'foo) (def ~'x (ns-name *ns*)) (ns ~'user)))
(dude)
foo/x"))))

(deftest comment-test
(is (nil? (eval* '(comment "anything"))))
Expand Down
2 changes: 1 addition & 1 deletion test/sci/namespaces_test.cljc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns sci.namespaces-test
(:require
[clojure.set :as set]
[clojure.test :as test :refer [deftest is testing]]
[clojure.test :as test :refer [deftest is]]
[sci.test-utils :as tu]))

(defn eval*
Expand Down
8 changes: 7 additions & 1 deletion test/sci/records_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,10 @@
(let [a 6 b 2
rect (Rectangle. a b)]
(foo/area rect))"]
(is (= 12 (tu/eval* prog {})))))
(is (= 12 (tu/eval* prog {}))))
(testing "constructor can be used in protocol impls"
(is (= {:x 1}
(tu/eval*
"(defprotocol IFoo (foo [this]))
(defrecord Foo [x] IFoo (foo [this] (Foo. x)))
(foo (Foo. 1))" {})))))

0 comments on commit fba06e9

Please sign in to comment.