mw-parser/README.md

217 lines
9.8 KiB
Markdown

# mw-parser
A rule parser for MicroWorld
## Part of the overall MicroWorld system
While this code works and is interesting on its own, you also need at least
[mw-engine](https://github.com/simon-brooke/mw-engine) and
[mw-ui](https://github.com/simon-brooke/mw-ui). There will be other
modules in due course.
You can see MicroWorld in action [here](http://www.journeyman.cc/microworld/) -
but please don't be mean to my poor little server. If you want to run big maps
or complex rule-sets, please run it on your own machines.
## Usage
Main entry point is (parse-rule _string_), where string takes a form detailed
in __[grammar](#grammar)__, below. If the rule is interpretted correctly the result will
be the source code of a Clojure anonymous function; if the rule cannot be interpretted,
an error 'I did not understand...' will be shown.
The function (compile-rule _string_) is like parse-rule, except that it returns
a compiled Clojure anonymous function.
### Generated function and evaluation environment
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.
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__.
### Execution
Each time the world is transformed, exactly the same set of rules is applied to every
cell. The rules are applied to the cell in turn, in the order in which they are
written in the rule text, until the conditions of one of them match the cell.
The actions of that rule are then used to transform the cell, and the rest of
the rules are not applied.
So, for example, if your first rule is
if x is more than -1 then state should be new
then no matter what your other rules are, your world will never change, because
all cells have x more than -1.
If you are having problems because one of your rules isn't working, look to
see whether there is another rule above it which is 'blocking' it.
### <a name="grammar"></a>Grammar
#### Comments
+ Any line which starts with the hash character (#) is ignored;
+ Any line which starts with a semi-colon (;) is ignored.
#### Rules
A rule comprises:
+ if _conditions_ then _actions_
Each rule must be on a single line. There should be nothing else on that line.
#### Conditions
In rules, _conditions_ is one of:
+ _condition_
+ _condition_ and _conditions_
+ _condition_ or _conditions_
Note that 'and' takes precedence over or, so
conditionA and conditionB or conditionC and conditionD
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_
+ _number_ neighbours have _property_ more than _numeric-value_
+ _number_ neighbours have _property_ less than _numeric-value_
+ more than _number_ neighbours have _property_ equal to _value_
+ fewer than _number_ neighbours have _property_ equal to _value_
+ some neighbours have _property_ equal to _value_
+ more than _number_ neighbours have _property_ more than _numeric-value_
+ fewer than _number_ neighbours have _property_ more than _numeric-value_
+ some neighbours have _property_ more than _numeric-value_
+ more than _number_ neighbours have _property_ less than _numeric-value_
+ fewer than _number_ neighbours have _property_ less than _numeric-value_
+ some neighbours have _property_ less than _numeric-value_
#### About neighbours
Note that everywhere above I've used 'neighbours', you can use 'neighbours
within _distance_', where _distance_ is a (small) positive integer.
A cell has eight immediate neighbours - cells which actually touch it (except
for cells on the edge of the map, which have fewer). If the cell we're
interested in is the cell marked 'X' in the table below, its immediate neighbours
are the ones marked '1'. But outside the ones marked '1', it has more distant
neighbours - those marked '2' and '3' in the table, and still more outside those.
<table style="padding-left: 20%;">
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: green;">1</td><td style="color:white; width: 1.5em; background-color: red;">X</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: green;">1</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: lime;">2</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
<tr><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td><td style="width: 1.5em; background-color: chartreuse;">3</td></tr>
</table>
If a rule just says 'neighbours', and not 'neighbours within', it means
'neighbours within 1'; so
if some neighbours are scrub then state should be scrub
has exactly the same meaning as
if some neighbours within 1 are scrub then state should be scrub
#### Actions
In these rules, _actions_ is one of:
+ _action_
+ _action_ and _actions_
and _action_ is:
+ _property_ should be _value_
+ _number_ chance in _number_ _property_ should be _value_
#### Properties
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__
try to change 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_
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
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:
+ _number_
+ _property_
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
+ _symbolic-value_
+ _arithmetic-value_
+ _number_
where _arithmetic-value_ is:
+ _property_ _operator_ _numeric-value_
and _operator_ is one of the simple arithmetic operators '+', '-', '*' and '/'.
### Shorthand
Note that '...neighbours are...' is equivalent to '...neighbours have state equal to...',
and 'some neighbours...' is equivalent to 'more than 0 neighbours...'
### Roadmap
The existing parser, *mw-parser.core*, works but is not well written. A much
better parser which does not yet completely work, *mw-parser.insta*, is also
included for the adventurous.
I intend to replace *mw-parser.core* with *mw-parser.insta* as soon as
*mw-parser.insta* correctly parses all the test rules.
## License
Copyright © 2014 [Simon Brooke](mailto:simon@journeyman.cc)
Distributed under the terms of the
[GNU General Public License v2](http://www.gnu.org/licenses/gpl-2.0.html)