Tactical commit before experimenting with a major change.
This commit is contained in:
parent
4b721219bd
commit
8c2e44b42a
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,3 +7,5 @@ pom.xml
|
||||||
.clj-kondo/
|
.clj-kondo/
|
||||||
.lsp/
|
.lsp/
|
||||||
.nrepl-port
|
.nrepl-port
|
||||||
|
|
||||||
|
doc/scratch.clj
|
||||||
|
|
|
@ -157,16 +157,18 @@
|
||||||
|
|
||||||
Throws an exception if parsing fails."
|
Throws an exception if parsing fails."
|
||||||
([rule-text return-tuple?]
|
([rule-text return-tuple?]
|
||||||
(let [lines (remove comment? (split-lines rule-text))]
|
(let [lines (map trim (remove comment? (split-lines rule-text)))]
|
||||||
(if (> (count lines) 1)
|
(if (> (count lines) 1)
|
||||||
(map #(compile % return-tuple?) lines)
|
(map #(compile % return-tuple?) lines)
|
||||||
(let [src (trim rule-text)
|
(let [src (first lines)
|
||||||
parse-tree (simplify (parse src))
|
parse-tree (doall (simplify (parse src)))
|
||||||
fn' (generate parse-tree)
|
fn' (doall (generate parse-tree))
|
||||||
afn (try
|
afn (try
|
||||||
(if (= 'fn (first fn'))
|
(if (#{'fn 'fn*} (first fn'))
|
||||||
(vary-meta (eval fn') merge (meta fn'))
|
(vary-meta (eval fn') merge (meta fn'))
|
||||||
(throw (Exception. (format "Parse of `%s` did not return a functionn" src))))
|
(throw (Exception.
|
||||||
|
(format "Parse of `%s` did not return a function: %s"
|
||||||
|
src fn'))))
|
||||||
(catch Exception any (throw (ex-info (.getMessage any)
|
(catch Exception any (throw (ex-info (.getMessage any)
|
||||||
{:src src
|
{:src src
|
||||||
:parse parse-tree
|
:parse parse-tree
|
||||||
|
|
|
@ -25,13 +25,20 @@
|
||||||
|
|
||||||
(declare generate generate-action)
|
(declare generate generate-action)
|
||||||
|
|
||||||
|
;;; macros used in generated rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;; production (if-then) rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn generate-rule
|
(defn generate-rule
|
||||||
"From this `tree`, assumed to be a syntactically correct rule specification,
|
"From this `tree`, assumed to be a syntactically correct rule specification,
|
||||||
generate and return the appropriate rule as a function of two arguments."
|
generate and return the appropriate rule as a function of two arguments."
|
||||||
[tree]
|
[tree]
|
||||||
(assert-type tree :RULE)
|
(assert-type tree :RULE)
|
||||||
(vary-meta
|
(vary-meta
|
||||||
(list 'fn ['cell 'world] (list 'when (generate (nth tree 2)) (generate (nth tree 3))))
|
;; do macro-expansion here, because at least in theory I know what
|
||||||
|
;; macros are in scope here.
|
||||||
|
(macroexpand
|
||||||
|
(list 'fn ['cell 'world] (list 'when (generate (nth tree 2)) (generate (nth tree 3)))))
|
||||||
merge
|
merge
|
||||||
{:rule-type
|
{:rule-type
|
||||||
:production}))
|
:production}))
|
||||||
|
@ -275,19 +282,27 @@
|
||||||
{:tree tree}
|
{:tree tree}
|
||||||
x)))))
|
x)))))
|
||||||
|
|
||||||
;;; Flow rules. A flow rule DOES NOT return a modified world; instead, it
|
;;; Flow rules. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;; returns a PLAN to modify the world, in the form of a sequence of `flows`.
|
;;; A flow rule DOES NOT return a modified cell; instead, it
|
||||||
;;; It is only when the plan is executed that the world is modified.
|
;;; returns a PLAN to modify the world, in the form of a sequence of `flow`
|
||||||
|
;;; objects. See `mw-engine.flow`
|
||||||
;;;
|
;;;
|
||||||
;;; so we're looking at something like
|
;;; It is only when the plan is executed that the world is modified.
|
||||||
;;; (fn [cell world])
|
|
||||||
;;; (if (= (:state cell) (or (:house cell) :house))
|
|
||||||
|
|
||||||
(defn flow-rule
|
(defn flow-rule
|
||||||
"Generate a flow rule for this `quantity` of this `property` from this
|
"Generate a flow rule for this `quantity` of this `property` from this
|
||||||
`source` to this `destination`."
|
`source` to this `destination`.
|
||||||
|
|
||||||
|
A flow rule **does not** return a modified cell; instead, it
|
||||||
|
returns a **plan** to modify the world, in the form of a sequence of
|
||||||
|
`flow` objects. See `mw-engine.flow`
|
||||||
|
|
||||||
|
It is only when the plan is executed that the world is modified."
|
||||||
[source property quantity-frag destinations]
|
[source property quantity-frag destinations]
|
||||||
(vary-meta
|
(vary-meta
|
||||||
|
;; do macro-expansion here, because at least in theory I know what
|
||||||
|
;; macros are in scope here.
|
||||||
|
(macroexpand
|
||||||
(list 'fn ['cell 'world]
|
(list 'fn ['cell 'world]
|
||||||
(list 'when (list 'and source (list 'pos? (list 'cell property)))
|
(list 'when (list 'and source (list 'pos? (list 'cell property)))
|
||||||
(list 'map
|
(list 'map
|
||||||
|
@ -296,7 +311,7 @@
|
||||||
:destination (list 'select-keys 'd [:x :y])
|
:destination (list 'select-keys 'd [:x :y])
|
||||||
:property property
|
:property property
|
||||||
:quantity quantity-frag})
|
:quantity quantity-frag})
|
||||||
destinations)))
|
destinations))))
|
||||||
merge
|
merge
|
||||||
{:rule-type
|
{:rule-type
|
||||||
:flow}))
|
:flow}))
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
[mw-engine.core :refer [transform-world]]
|
[mw-engine.core :refer [transform-world]]
|
||||||
[mw-engine.utils :refer [get-cell]]
|
[mw-engine.utils :refer [get-cell]]
|
||||||
[mw-engine.world :refer [make-world]]
|
[mw-engine.world :refer [make-world]]
|
||||||
[mw-parser.declarative :refer [compile parse-rule]]
|
[mw-parser.declarative :refer [compile parse parse-rule]]
|
||||||
[mw-parser.utils :refer [rule?]]))
|
[mw-parser.utils :refer [rule?]]))
|
||||||
|
|
||||||
(deftest rules-tests
|
(deftest rules-tests
|
||||||
|
@ -34,21 +34,20 @@
|
||||||
(deftest exception-tests
|
(deftest exception-tests
|
||||||
(testing "Constructions which should cause exceptions to be thrown"
|
(testing "Constructions which should cause exceptions to be thrown"
|
||||||
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||||
(compile "the quick brown fox jumped over the lazy dog"))
|
(parse "the quick brown fox jumped over the lazy dog"))
|
||||||
"Exception thrown if rule text does not match grammar")
|
"Exception thrown if rule text does not match grammar")
|
||||||
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||||
(compile "if i have a cat on my lap then everything is fine"))
|
(parse "if i have a cat on my lap then everything is fine"))
|
||||||
"Exception thrown if rule text does not match grammar")
|
"Exception thrown if rule text does not match grammar")
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(compile "if state is new then x should be 0"))
|
(parse "if state is new then x should be 0"))
|
||||||
"Exception thrown on attempt to set 'x'")
|
"Exception thrown on attempt to set 'x'")
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(compile "if state is new then y should be 0"))
|
(parse "if state is new then y should be 0"))
|
||||||
"Exception thrown on attempt to set 'y'")))
|
"Exception thrown on attempt to set 'y'")))
|
||||||
|
|
||||||
|
|
||||||
(deftest correctness-tests
|
(deftest correctness-tests
|
||||||
;; these are, in so far as possible, the same as the correctness-tests in core-tests - i.e., the two compilers
|
;; these are, in so far as possible, the same as the correctness-tests in core-tests - i.e., the two compilers
|
||||||
;; compile the same language.
|
;; compile the same language.
|
||||||
|
|
|
@ -8,30 +8,22 @@
|
||||||
(testing "flow-grammar"
|
(testing "flow-grammar"
|
||||||
(let [rule "flow 1 food from house having food more than 10 to house within 2 with least food"
|
(let [rule "flow 1 food from house having food more than 10 to house within 2 with least food"
|
||||||
expected '(:FLOW-RULE
|
expected '(:FLOW-RULE
|
||||||
(:SIMPLE-EXPRESSION (:NUMBER "1"))
|
(:FLOW "flow")
|
||||||
|
(:QUANTITY (:SIMPLE-EXPRESSION (:NUMBER "1")))
|
||||||
(:SYMBOL "food")
|
(:SYMBOL "food")
|
||||||
(:FROM "from")
|
(:FROM "from")
|
||||||
(:SOURCE
|
(:SOURCE
|
||||||
(:PROPERTY-CONDITION
|
(:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:WITH "having")
|
(:WITH "having")
|
||||||
(:PROPERTY-CONDITION
|
(:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than"))) (:NUMBER "10")))
|
||||||
(:SYMBOL "food")
|
|
||||||
(:QUALIFIER (:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than")))
|
|
||||||
(:NUMBER "10")))
|
|
||||||
(:TO-HOW (:TO "to"))
|
(:TO-HOW (:TO "to"))
|
||||||
(:DESTINATION
|
(:DESTINATION
|
||||||
(:PROPERTY-CONDITION
|
(:TARGET
|
||||||
(:SYMBOL "state")
|
(:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
(:RANGE (:WITHIN "within") (:NUMBER "2")))
|
||||||
(:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:WITHIN "within")
|
|
||||||
(:VALUE (:NUMBER "2"))
|
|
||||||
(:WITH "with")
|
(:WITH "with")
|
||||||
(:FLOW-CONDITIONS
|
(:DETERMINER-CONDITION (:DETERMINER (:LEAST "least")) (:SYMBOL "food"))))
|
||||||
(:DETERMINER-CONDITION (:LEAST "least") (:SYMBOL "food")))))
|
|
||||||
actual (simplify (parse rule))]
|
actual (simplify (parse rule))]
|
||||||
|
|
||||||
(is (= actual expected) rule))
|
(is (= actual expected) rule))
|
||||||
|
@ -41,29 +33,11 @@
|
||||||
(:QUANTITY (:PERCENTAGE (:NUMBER "10") "%"))
|
(:QUANTITY (:PERCENTAGE (:NUMBER "10") "%"))
|
||||||
(:SYMBOL "food")
|
(:SYMBOL "food")
|
||||||
(:FROM "from")
|
(:FROM "from")
|
||||||
(:SOURCE
|
(:SOURCE (:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"]))) (:WITH "having") (:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than"))) (:NUMBER "10")))
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:WITH "having")
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "food")
|
|
||||||
(:QUALIFIER
|
|
||||||
(:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than")))
|
|
||||||
(:NUMBER "10")))
|
|
||||||
(:TO-HOW (:TO-EACH (:TO "to") (:EACH "each")))
|
(:TO-HOW (:TO-EACH (:TO "to") (:EACH "each")))
|
||||||
(:DESTINATION
|
(:DESTINATION
|
||||||
(:TARGET
|
(:TARGET (:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:RANGE (:WITHIN "within") (:NUMBER "2")))
|
(:RANGE (:WITHIN "within") (:NUMBER "2")))
|
||||||
(:WITH "with")
|
(:WITH "with") (:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:LESS "less") (:THAN "than"))) (:NUMBER "4"))))
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "food")
|
|
||||||
(:QUALIFIER
|
|
||||||
(:COMPARATIVE-QUALIFIER (:LESS "less") (:THAN "than")))
|
|
||||||
(:NUMBER "4"))))
|
|
||||||
actual (simplify (parse rule))]
|
actual (simplify (parse rule))]
|
||||||
(is (= actual expected) rule))))
|
(is (= actual expected) rule))))
|
||||||
|
|
|
@ -77,10 +77,10 @@
|
||||||
(:SYMBOL "state")
|
(:SYMBOL "state")
|
||||||
(:BECOMES "should be")
|
(:BECOMES "should be")
|
||||||
(:SYMBOL "climax"))))
|
(:SYMBOL "climax"))))
|
||||||
expected '(fn [cell world]
|
expected '(fn* ([cell world]
|
||||||
(when
|
(when
|
||||||
(= (:state cell) (or (:forest cell) :forest))
|
(= (:state cell) (or (:forest cell) :forest))
|
||||||
(merge cell {:state :climax})))
|
(merge cell {:state :climax}))))
|
||||||
actual (generate rule)
|
actual (generate rule)
|
||||||
expected-meta {:rule-type :production}
|
expected-meta {:rule-type :production}
|
||||||
actual-meta (meta actual)]
|
actual-meta (meta actual)]
|
||||||
|
|
Loading…
Reference in a new issue