-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
202 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
(ns bb-test-runner | ||
(:require | ||
[clojure.test :as t] | ||
[mint.core-test])) | ||
[mint.core-test] | ||
[mint.evaluator-test] | ||
[mint.evaluator.condition-test] | ||
[mint.evaluator.threading-test])) | ||
|
||
(t/run-tests 'mint.core-test) | ||
(t/run-tests | ||
'mint.core-test | ||
'mint.evaluator-test | ||
'mint.evaluator.condition-test | ||
'mint.evaluator.threading-test) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
(ns mint.constant) | ||
|
||
(def start-delm "{{") | ||
(def end-delm "}}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,12 @@ | ||
(ns mint.core | ||
(:require | ||
[clojure.string :as str])) | ||
|
||
(def ^:private start-delm "{{->") | ||
(def ^:private end-delm "}}") | ||
(def ^:private end-delm-count (count end-delm)) | ||
|
||
(defmulti eval* | ||
(fn [form _data] | ||
(when (sequential? form) | ||
(first form)))) | ||
|
||
(defmethod eval* :default | ||
[form data] | ||
(let [seq-form? (sequential? form) | ||
sym-form? (symbol? form)] | ||
(cond | ||
(and (not seq-form?) | ||
(not sym-form?)) | ||
form | ||
|
||
(not seq-form?) | ||
(get data (keyword form)) | ||
|
||
:else | ||
(when-let [f (get data (keyword (first form)))] | ||
(->> (rest form) | ||
(seq) | ||
(map #(eval* % data)) | ||
(apply f)))))) | ||
|
||
(defmethod eval* '-> | ||
[[_ v & forms] data] | ||
(when-let [x (eval* v data)] | ||
(reduce (fn [accm form] | ||
(let [[head & args] (if (sequential? form) form [form]) | ||
form' (concat (list head accm) args)] | ||
(eval* form' data))) | ||
x forms))) | ||
|
||
(defmethod eval* '->> | ||
[[_ v & forms] data] | ||
(when-let [x (eval* v data)] | ||
(reduce (fn [accm form] | ||
(let [form' (concat (if (sequential? form) form [form]) | ||
[accm])] | ||
(eval* form' data))) | ||
x forms))) | ||
|
||
(defn- parse-pre | ||
[s] | ||
(if-let [idx (str/index-of s start-delm)] | ||
[(subs s 0 idx) (subs s idx)] | ||
[s nil])) | ||
|
||
(defn- parse-post | ||
[s] | ||
(if-let [idx (str/index-of s end-delm)] | ||
[(subs s 0 (+ idx end-delm-count)) (subs s (+ idx end-delm-count))] | ||
[nil s])) | ||
|
||
(defn- parse | ||
[s] | ||
(let [[pre tmp] (parse-pre s) | ||
[body post] (when tmp (parse-post tmp))] | ||
[pre body post])) | ||
|
||
(defn- eval-body | ||
[body data] | ||
(let [form-str (-> body | ||
(str/replace start-delm "(->") | ||
(str/replace end-delm ")"))] | ||
(try | ||
(-> form-str | ||
(read-string) | ||
(eval* data) | ||
(str)) | ||
(catch #?(:clj Exception :cljs js/Error) ex | ||
(throw (ex-info "Invalid form" {:form form-str | ||
:message (ex-message ex)})))))) | ||
[mint.evaluator :as evaluator] | ||
[mint.evaluator.condition] | ||
[mint.evaluator.threading] | ||
[mint.parser :as parser])) | ||
|
||
(defn render | ||
[s data] | ||
(loop [content s | ||
res []] | ||
(let [[pre body post] (parse content)] | ||
(if (and body post) | ||
(recur post (conj res pre (eval-body body data))) | ||
(str/join "" (conj res pre)))))) | ||
[template-str data] | ||
(-> template-str | ||
(parser/parse) | ||
(evaluator/evaluate data))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
(ns mint.evaluator | ||
(:require | ||
[clojure.string :as str] | ||
[mint.constant :as const])) | ||
|
||
(defmulti eval* | ||
(fn [sexp _data] | ||
(if (sequential? sexp) | ||
(first sexp) | ||
sexp))) | ||
|
||
(defmethod eval* :default | ||
[form data] | ||
(let [[first-item :as form] (if (sequential? form) form [form]) | ||
form-count (count form)] | ||
(cond | ||
(and (= 1 form-count) | ||
(not (symbol? first-item))) | ||
first-item | ||
|
||
(= 1 form-count) | ||
(get data (keyword first-item)) | ||
|
||
:else | ||
(let [f (get data (keyword first-item))] | ||
(when (fn? f) | ||
(some->> (rest form) | ||
(seq) | ||
(map #(eval* % data)) | ||
(apply f))))))) | ||
|
||
(defn- evaluate* | ||
[expression-str data] | ||
(-> expression-str | ||
(str/replace const/start-delm "(") | ||
(str/replace const/end-delm ")") | ||
(read-string) | ||
(eval* data))) | ||
|
||
(defn evaluate | ||
[parsed data] | ||
(->> parsed | ||
(map #(if (= :expression (first %)) | ||
(evaluate* (second %) data) | ||
(second %))) | ||
(str/join ""))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
(ns mint.evaluator.condition | ||
(:require | ||
[mint.evaluator :as evaluator])) | ||
|
||
(defmethod evaluator/eval* 'when | ||
[[_ v & forms] data] | ||
(when (evaluator/eval* v data) | ||
(evaluator/eval* (last forms) data))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
(ns mint.evaluator.threading | ||
(:require | ||
[mint.evaluator :as evaluator])) | ||
|
||
(defmethod evaluator/eval* '-> | ||
[[_ v & forms] data] | ||
(reduce | ||
(fn [accm form] | ||
(let [[head & rest-form] (if (sequential? form) form [form]) | ||
form' (concat (list head accm) rest-form)] | ||
(evaluator/eval* form' data))) | ||
(evaluator/eval* v data) | ||
forms)) | ||
|
||
(defmethod evaluator/eval* '->> | ||
[[_ v & forms] data] | ||
(reduce | ||
(fn [accm form] | ||
(let [form' (concat (if (sequential? form) form [form]) | ||
[accm])] | ||
(evaluator/eval* form' data))) | ||
(evaluator/eval* v data) | ||
forms)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
(ns mint.parser | ||
(:require | ||
[clojure.string :as str] | ||
[mint.constant :as const])) | ||
|
||
(defn- parse* | ||
[template-str] | ||
(let [start-idx (str/index-of template-str const/start-delm) | ||
end-idx (and start-idx | ||
(str/index-of template-str const/end-delm (inc start-idx)))] | ||
(if (and start-idx end-idx) | ||
(let [end-idx' (+ end-idx (count const/end-delm)) | ||
pre (subs template-str 0 start-idx) | ||
body (subs template-str start-idx end-idx') | ||
post (subs template-str end-idx')] | ||
[[:string pre] | ||
[:expression body] | ||
[:string post]]) | ||
[[:string template-str]]))) | ||
|
||
(defn parse | ||
[template-str] | ||
(->> (loop [result [] | ||
template-str template-str] | ||
(let [[pre body post :as parsed] (parse* template-str)] | ||
(if (seq post) | ||
(recur (concat result [pre body]) (second post)) | ||
(concat result parsed)))) | ||
(remove #(= % [:string ""])))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
(ns mint.evaluator.condition-test | ||
(:require | ||
[clojure.test :as t] | ||
[mint.evaluator :as sut] | ||
[mint.evaluator.condition])) | ||
|
||
(t/deftest eval*-condition-test | ||
(t/testing "when" | ||
(t/is (= "hello" (sut/eval* '(when foo "hello") {:foo 10}))) | ||
(t/is (= "10hello" (sut/eval* '(when foo (str foo "hello")) {:foo 10 | ||
:str str}))) | ||
(t/is (nil? (sut/eval* '(when foo "hello") {:foo nil}))) | ||
(t/is (nil? (sut/eval* '(when foo "hello") {:foo false}))) | ||
(t/is (nil? (sut/eval* '(when foo "hello") {}))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
(ns mint.evaluator.threading-test | ||
(:require | ||
[clojure.test :as t] | ||
[mint.evaluator :as sut] | ||
[mint.evaluator.threading])) | ||
|
||
(t/deftest eval*-threading-test | ||
(t/testing "thread first" | ||
(t/is (= 10 (sut/eval* '(-> 9 inc) {:inc inc}))) | ||
(t/is (= 10 (sut/eval* '(-> foo (- 1)) {:foo 11, :- -}))) | ||
(t/is (= 10 (sut/eval* '(-> (inc foo) (- 1)) {:foo 10, :- -, :inc inc})))) | ||
|
||
(t/testing "thread last" | ||
(t/is (= 10 (sut/eval* '(->> 9 inc) {:inc inc}))) | ||
(t/is (= 10 (sut/eval* '(->> foo (- 11)) {:foo 1, :- -}))) | ||
(t/is (= 10 (sut/eval* '(->> (inc foo) (- 11)) {:foo 0, :- -, :inc inc}))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
(ns mint.evaluator-test | ||
(:require | ||
[clojure.test :as t] | ||
[mint.evaluator :as sut])) | ||
|
||
(t/deftest eval*-default-test | ||
(t/testing "replace value" | ||
(t/is (= 10 (sut/eval* 'foo {:foo 10})))) | ||
|
||
(t/testing "eval function" | ||
(t/is (= 10 (sut/eval* '(inc 9) {:inc inc}))) | ||
(t/is (= 10 (sut/eval* '(inc (inc 8)) {:inc inc})))) | ||
|
||
(t/testing "qualified keyword" | ||
(t/is (= 10 (sut/eval* '(->> foo/bar) {:foo/bar 10})))) | ||
|
||
(t/testing "unknown value" | ||
(t/is (nil? (sut/eval* 'unknown {})))) | ||
|
||
(t/testing "unknown function" | ||
(t/is (nil? (sut/eval* '(unknown 1) {}))))) | ||
|
||
(t/deftest evaluate-test | ||
(t/is (= "" (sut/evaluate [] {}))) | ||
|
||
(t/testing "string" | ||
(t/is (= "foo" | ||
(sut/evaluate [[:string "foo"]] {})))) | ||
|
||
(t/testing "expression" | ||
(t/is (= "bar" | ||
(sut/evaluate [[:expression "{{foo}}"]] | ||
{:foo "bar"}))) | ||
(t/is (= "bar" | ||
(sut/evaluate [[:expression "{{f}}"] | ||
[:expression "{{oo}}"]] | ||
{:f "b" :oo "ar"})))) | ||
|
||
(t/testing "string and expression" | ||
(t/is (= "hello world!" | ||
(sut/evaluate [[:string "hello "] | ||
[:expression "{{foo}}"] | ||
[:string "!"]] | ||
{:foo "world"}))))) |