Added some moderately-real tests.
This commit is contained in:
parent
723a23e3dc
commit
2dc5e394cc
93
README.md
93
README.md
|
@ -4,8 +4,8 @@ A rule parser for MicroWorld
|
|||
|
||||
## Usage
|
||||
|
||||
Main entry point is (parse-rule <string>), where string takes a form detailed
|
||||
in **grammar**, below. If the rule is interpretted correctly the result will
|
||||
Main entry point is (parse-rule _string_), where string takes a form detailed
|
||||
in __grammar__, below. If the rule is interpretted correctly the result will
|
||||
be a Clojure anonymous function; if the rule is not interpretted, currently nil
|
||||
is returned and there's no helpful error message.
|
||||
|
||||
|
@ -13,71 +13,76 @@ is returned and there's no helpful error message.
|
|||
|
||||
The generated function is a function of two arguments
|
||||
|
||||
* **cell** a cell in a world as defined in mw-engine.world, q.v.;
|
||||
* **world** the world of which that cell forms part.
|
||||
_ __cell__ a cell in a world as defined in mw-engine.world, q.v.;
|
||||
_ __world__ the world of which that cell forms part.
|
||||
|
||||
It returns a new cell, based on the cell passed.
|
||||
|
||||
Actions of the rule will (can only) modify properties of the cell; there are two
|
||||
properties which are special and SHOULD NOT be modified, namely the properties
|
||||
**x** and **y**. Currently there is no policing that these properties are not
|
||||
__x__ and __y__. Currently there is no policing that these properties are not
|
||||
modified.
|
||||
|
||||
### Grammar
|
||||
|
||||
A rule comprises:
|
||||
|
||||
* if *conditions* then *actions*
|
||||
+ if _conditions_ then _actions_
|
||||
|
||||
#### Conditions
|
||||
|
||||
where *conditions* is:
|
||||
where _conditions_ is:
|
||||
|
||||
* *condition*
|
||||
+ _condition_
|
||||
+ _condition_ and _conditions_
|
||||
+ _condition_ or _conditions_
|
||||
|
||||
or
|
||||
* *condition* and *conditions*
|
||||
Note that 'and' takes precedence over or, so
|
||||
|
||||
A *condition* is one of:
|
||||
conditionA and conditionB or conditionC and conditionD
|
||||
|
||||
* *property* is *value*
|
||||
* *property* is not *value*
|
||||
* *property* is in *values*
|
||||
* *property* is not in *values*
|
||||
* *property* is more than *numeric-value*
|
||||
* *property* is less than *numeric-value*
|
||||
* *number* neighbours have *property* equal to *value*
|
||||
* more than *number* neighbours have *property* equal to *value*
|
||||
* fewer than *number* neighbours have *property* equal to *value*
|
||||
is interpreted as
|
||||
|
||||
(conditionA and (conditionB or (conditionC and conditionD)))
|
||||
|
||||
A _condition_ is one of:
|
||||
|
||||
+ _property_ is _value_
|
||||
+ _property_ is not _value_
|
||||
+ _property_ is in _values_
|
||||
+ _property_ is not in _values_
|
||||
+ _property_ is more than _numeric-value_
|
||||
+ _property_ is less than _numeric-value_
|
||||
+ _number_ neighbours have _property_ equal to _value_
|
||||
+ more than _number_ neighbours have _property_ equal to _value_
|
||||
+ fewer than _number_ neighbours have _property_ equal to _value_
|
||||
|
||||
#### Actions
|
||||
|
||||
Where *actions* is:
|
||||
Where _actions_ is:
|
||||
|
||||
* *action*
|
||||
+ _action_
|
||||
+ _action_ and _actions_
|
||||
|
||||
or
|
||||
* *action* and *actions*
|
||||
and _action_ is:
|
||||
|
||||
and *action* is:
|
||||
|
||||
* *property* should be *value*
|
||||
+ _property_ should be _value_
|
||||
|
||||
#### Properties
|
||||
|
||||
In the above, *property* is the name of any property of a cell. Any alpha-numeric
|
||||
In the above, _property_ is the name of any property of a cell. Any alpha-numeric
|
||||
string of characters can form the name of a property. Actions should NOT refer
|
||||
to the reserved properties **x** and **y**.
|
||||
to the reserved properties __x__ and __y__.
|
||||
|
||||
#### Values in Conditions
|
||||
|
||||
Values in conditions and actions are considered slightly differently. In a
|
||||
condition, a value is one of
|
||||
|
||||
* *symbolic-value*
|
||||
* *numeric-value*
|
||||
+ _symbolic-value_
|
||||
+ _numeric-value_
|
||||
|
||||
The '...more than...' and '...less than...' conditions imply a *numeric-value*.
|
||||
The '...more than...' and '...less than...' conditions imply a _numeric-value_.
|
||||
Thus "if altitude is more than fertility..." is interpreted as meaning "if the value
|
||||
of the property of the current cell called 'altitude' is greater than the value
|
||||
of the property of the current cell called 'fertility'", whereas the apparently
|
||||
|
@ -85,29 +90,29 @@ similar condition 'if altitude is fertility...' is interpreted as meaning
|
|||
"if the value of the property of the current cell called 'altitude' is the symbol
|
||||
'fertility'".
|
||||
|
||||
Thus *symbolic-value* is any sequence of alphanumeric characters, whereas
|
||||
*numeric-value* is one of:
|
||||
Thus _symbolic-value_ is any sequence of alphanumeric characters, whereas
|
||||
_numeric-value_ is one of:
|
||||
|
||||
* *number*
|
||||
* *property*
|
||||
+ _number_
|
||||
+ _property_
|
||||
|
||||
and *number* is any sequence of the decimal digits 0...9, the minus character
|
||||
and _number_ is any sequence of the decimal digits 0...9, the minus character
|
||||
'-' and the period character '.', provided that the minus character can only be
|
||||
in the first position, and the period character can only appear once.
|
||||
|
||||
#### Values in Actions
|
||||
|
||||
A *value* in an action is one of
|
||||
A _value_ in an action is one of
|
||||
|
||||
* *symbolic-value*
|
||||
* *arithmetic-value*
|
||||
* *number*
|
||||
+ _symbolic-value_
|
||||
+ _arithmetic-value_
|
||||
+ _number_
|
||||
|
||||
where *arithmetic-value* is:
|
||||
where _arithmetic-value_ is:
|
||||
|
||||
* *property* *operator* *numeric-value*
|
||||
+ _property_ _operator_ _numeric-value_
|
||||
|
||||
and *operator* is one of the simple arithmetic operators '+', '-', '*' and '/'.
|
||||
and _operator_ is one of the simple arithmetic operators '+', '-', '*' and '/'.
|
||||
|
||||
### Shorthand
|
||||
|
||||
|
|
|
@ -5,16 +5,22 @@
|
|||
|
||||
|
||||
(deftest rules-tests
|
||||
(testing "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"
|
||||
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
|
||||
(is (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||
(is (let [cell (apply (eval (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||
(list {:state :forest :altitude 99} nil))]
|
||||
(and (= (:state cell) :climax) (= (:deer cell) 3))))
|
||||
(is (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||
(is (parse-rule "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"))
|
||||
(is (parse-rule "if altitude is 100 or fertility is 25 then state should be heath"))
|
||||
(is (parse-rule "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"))
|
||||
(is (parse-rule "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"))
|
||||
(is (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village"))
|
||||
(is (parse-rule "if state is forest and fertility is between 55 and 75 then state should be climax"))
|
||||
(is (parse-rule "if 6 neighbours have state equal to water then state should be village"))
|
||||
(is (parse-rule "if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"))
|
||||
|
||||
;; ideally should also test that the rule works, but I haven't worked out how to make mw-engine.utils available
|
||||
;; during eval
|
||||
;; (is (let [cell (apply (eval (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||
;; (list {:state :forest :altitude 99} nil))]
|
||||
;; (and (= (:state cell) :climax) (= (:deer cell) 3))))
|
||||
))
|
||||
|
||||
|
||||
;; * "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"
|
||||
;; * "if altitude is 100 or fertility is 25 then state should be heath"
|
||||
;; * "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"
|
||||
;; * "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"
|
||||
;;
|
Loading…
Reference in a new issue