Closed frenchy64 closed 13 years ago
One possibility would be to use Clojure protocols to define a common set of functionality stevedore implementations should provide.
(defprotocol StevedoreImpl
"Protocol for Stevedore implementations in different languages"
(emit-when
"Emit a form equivilant to when"
[this when test & form])
(emit-group
[this group & exprs]))
Provide a few implementations..
(deftype BashScript
StevedoreImpl
(emit-when
[this when test & form]
(str "if "
(if (logical-test? test) (str "[ " (emit test) " ]") (emit test))
"; then"
(str \newline (string/trim (emit-do form)) \newline)
"fi"))
(emit-group
[this group & exprs]
(str "{ " (string/join "; " (map emit exprs)) "; }")))
(deftype BatchScript
StevedoreImpl
(emit-when
[this when test & form]
(str "IF "
"making this up"
"ENDIF"))
(emit-group
[this group & exprs]
(str "not supported?")))
We could reuse the base infrastructure provided by stevedore that parses the stevedore DSL, and extract out any implementation specific details in the StevedoreImpl protocol.
(defmethod emit-special 'when [type [when test & form]]
(apply emit-when *stevedore-impl* when test form))
(defmethod emit-special 'group
[type [ group & exprs]]
(apply emit-group *stevedore-impl* when test form))
To tie it all together, we could use with-stevedore-impl
.
(defmacro with-stevedore-impl
[impl & body]
`(binding [*stevedore-impl* impl]
~@body))
(with-stevedore-impl :bash
(script
(when (< 1 2)
(println "something"))))
with-stevedore-impl
would be a middle layer between with-script-context
and choosing the stevedore implementation.
(def *os-to-stevedore-impl*
{:linux pallet.script.impl.BashScript
:windows pallet.script.impl.BatchScript})
(defn get-stevedore-impl-from-template
[template]
.....)
(defmacro with-script-context
[template & body]
(let [stevedore-impl (get-stevedore-impl-from-template template)]
`(with-stevedore-impl stevedore-impl
(binding [*script-context* (filter identity ~template)]
~@body))))
Thoughts?
We might be able to remove the multimethod and just have the protocol.
At the moment there is a list of special-forms
and a predicate special-form?
. If the protocol were in a separate namespace, this could be changed to a call to resolve
to obtain the protocol function.
Currently leaning towards using a multimethod emit
instead of protocols. Much simpler, and makes implementation inheritance saner.
(defmulti emit
"Emit a shell expression as a string. Dispatched on the :type of the
expression."
(fn [ expr ] [ *current-stevedore-impl* (type expr)]))
(derive ::bash3 ::script-common)
(derive ::batch ::script-common)
(derive ::bash4 ::bash3)
;; common functions
(defmethod emit [::script-common clojure.lang.IPersistentList] [expr]
(emit-s-expr expr))
(defmethod emit [::script-common clojure.lang.Cons]
[expr]
(if (= 'list (first expr))
(emit-s-expr (rest expr))
(emit-s-expr expr)))
(defmethod emit-special [::bash3 'file-exists?] [type [file-exists? path]]
(str "-e " (emit path)))
;;; hypothetical difference btwn bash3 and 4
(defmethod emit-special [::bash4 'file-exists?] [type [file-exists? path]]
(str "-empty " (emit path)))
(defmethod emit-special [::batch 'file-exists?] [type [file-exists? path]]
(str "exists?!? " (emit path)))
Added with-stevedore-impl
and changed emit
to dispatch on current implementation.
https://github.com/pallet/stevedore/commit/4e48503000cbbfe24ade8ff974f4c2663c4b0f16
Merged into develop.
Core stevedore generates bash script. We want to generate other languages (eg. batch script).
A dispatch mechanism on OS exists with
defscript
/defimpl
in pallet.script.pallet.script/implement
can probably be reused here.defimpl
is inappropriate because it contains an implicit call toscript
. We need lower level string generating functions.Of course, we don't want to have to
unquote
to access stevedore, as indefimpl
.