Compare commits

..

No commits in common. "develop" and "master" have entirely different histories.

45 changed files with 1855 additions and 10666 deletions

13
.gitignore vendored
View file

@ -1,13 +0,0 @@
buildall.tmp.*
.lein-failures
.lein-repl-history
target/
pom.xml
.calva/
.clj-kondo/
.lsp/
.nrepl-port
doc/scratch.clj
src/mw_parser/scratch.clj

View file

@ -13,49 +13,15 @@ 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.
### Version compatibility
There are substantial changes in how rule functions are evaluated between 0.1.x
versions of MicroWorld libraries and 0.3.x versions. In particular, in 0.3.x
metadata is held on rule functions which is essential to the functioning of the
engine. Consequently, you cannot mix 0.1.x and 0.3.x libraries: it will not work.
In particular the parser in actual use has changed in 0.3.x from the
`mw-parser.core` parser to the `mw-parser.declarative` parser. The API of the
parser is also substantially revised and is not backwards compatible, so if
you have code written to use 0.1.x versions of this library it will need to be
modified. I apologise for this. On the upside, the new parser API is much
simpler.
## Usage
Main entry point is (compile _string_), where string takes a form detailed
in __[grammar](#grammar)__, below. If the rules represnted by the string are
interpretted correctly, the result will be a a list of compiled Clojure
anonymous functions; if the rule cannot be interpretted, an error 'I did not
understand...' will be thrown.
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.
Each of these functions will have metadata including:
* `:rule-type` : the type of rule the function represents;
* `:lisp` : the lisp source from which the function was compiled;
* `:parse` : the parse-tree from which that lisp source was derived;
* `:source` : the rule source from which the parse-tree was derived;
* `:line : the one-based line number of the rule source in the source file.
The values of `:rule-type` currently supported are:
* `:production` : an if-then rule which transforms the properties of a single
cell, based on the values of properties of that cell and optionally of its
neighbours;
* `:flow` : a flow rule which creates flows of values of a numeric property
from one cell to other cells.
Values which it is intended will be supported include rules to create graphs
which will enable the user to aggregate and interpret what is happening in
the world. Types which it is envisaged will be supported include
`:time-series`, `bar-graph` and perhaps others, but grammar for these has not
yet been developed.
The function (compile-rule _string_) is like parse-rule, except that it returns
a compiled Clojure anonymous function.
### Generated function and evaluation environment

View file

@ -1,40 +0,0 @@
.covered {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
background-color: #558B55;
}
.not-covered {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
background-color: red;
}
.partial {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
background-color: orange;
}
.not-tracked {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
}
.blank {
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
}
td {
padding-right: 10px;
}
td.with-bar {
width: 250px;
text-align: center;
}
td.with-number {
text-align: right;
}
td.ns-name {
min-width: 150px;
padding-right: 25px;
}

View file

@ -1,149 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="./coverage.css"/>
<title>Coverage Summary</title>
</head>
<body>
<table>
<thead><tr>
<td class="ns-name"> Namespace </td>
<td class="with-bar"> Forms </td>
<td class="with-number">Forms %</td>
<td class="with-bar"> Lines </td>
<td class="with-number">Lines %</td>
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
</tr></thead>
<tr>
<td><a href="mw_parser/bulk.clj.html">mw-parser.bulk</a></td><td class="with-bar"><div class="covered"
style="width:100.0%;
float:left;"> 60 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-bar"><div class="covered"
style="width:100.0%;
float:left;"> 11 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">62</td><td class="with-number">7</td><td class="with-number">11</td>
</tr>
<tr>
<td><a href="mw_parser/core.clj.html">mw-parser.core</a></td><td class="with-bar"><div class="covered"
style="width:96.05475040257649%;
float:left;"> 1193 </div><div class="not-covered"
style="width:3.9452495974235107%;
float:left;"> 49 </div></td>
<td class="with-number">96.05 %</td>
<td class="with-bar"><div class="covered"
style="width:84.48979591836735%;
float:left;"> 207 </div><div class="partial"
style="width:14.285714285714286%;
float:left;"> 35 </div><div class="not-covered"
style="width:1.2244897959183674%;
float:left;"> 3 </div></td>
<td class="with-number">98.78 %</td>
<td class="with-number">451</td><td class="with-number">41</td><td class="with-number">245</td>
</tr>
<tr>
<td><a href="mw_parser/declarative.clj.html">mw-parser.declarative</a></td><td class="with-bar"><div class="covered"
style="width:93.29896907216495%;
float:left;"> 181 </div><div class="not-covered"
style="width:6.701030927835052%;
float:left;"> 13 </div></td>
<td class="with-number">93.30 %</td>
<td class="with-bar"><div class="covered"
style="width:91.66666666666667%;
float:left;"> 22 </div><div class="partial"
style="width:4.166666666666667%;
float:left;"> 1 </div><div class="not-covered"
style="width:4.166666666666667%;
float:left;"> 1 </div></td>
<td class="with-number">95.83 %</td>
<td class="with-number">157</td><td class="with-number">10</td><td class="with-number">24</td>
</tr>
<tr>
<td><a href="mw_parser/errors.clj.html">mw-parser.errors</a></td><td class="with-bar"><div class="covered"
style="width:90.4%;
float:left;"> 113 </div><div class="not-covered"
style="width:9.6%;
float:left;"> 12 </div></td>
<td class="with-number">90.40 %</td>
<td class="with-bar"><div class="covered"
style="width:90.47619047619048%;
float:left;"> 19 </div><div class="partial"
style="width:9.523809523809524%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">68</td><td class="with-number">9</td><td class="with-number">21</td>
</tr>
<tr>
<td><a href="mw_parser/flow.clj.html">mw-parser.flow</a></td><td class="with-bar"><div class="covered"
style="width:89.1566265060241%;
float:left;"> 74 </div><div class="not-covered"
style="width:10.843373493975903%;
float:left;"> 9 </div></td>
<td class="with-number">89.16 %</td>
<td class="with-bar"><div class="covered"
style="width:85.71428571428571%;
float:left;"> 18 </div><div class="not-covered"
style="width:14.285714285714286%;
float:left;"> 3 </div></td>
<td class="with-number">85.71 %</td>
<td class="with-number">67</td><td class="with-number">5</td><td class="with-number">21</td>
</tr>
<tr>
<td><a href="mw_parser/generate.clj.html">mw-parser.generate</a></td><td class="with-bar"><div class="covered"
style="width:75.6989247311828%;
float:left;"> 704 </div><div class="not-covered"
style="width:24.301075268817204%;
float:left;"> 226 </div></td>
<td class="with-number">75.70 %</td>
<td class="with-bar"><div class="covered"
style="width:75.67567567567568%;
float:left;"> 140 </div><div class="partial"
style="width:3.2432432432432434%;
float:left;"> 6 </div><div class="not-covered"
style="width:21.08108108108108%;
float:left;"> 39 </div></td>
<td class="with-number">78.92 %</td>
<td class="with-number">328</td><td class="with-number">24</td><td class="with-number">185</td>
</tr>
<tr>
<td><a href="mw_parser/simplify.clj.html">mw-parser.simplify</a></td><td class="with-bar"><div class="covered"
style="width:85.81560283687944%;
float:left;"> 121 </div><div class="not-covered"
style="width:14.184397163120567%;
float:left;"> 20 </div></td>
<td class="with-number">85.82 %</td>
<td class="with-bar"><div class="covered"
style="width:82.05128205128206%;
float:left;"> 32 </div><div class="partial"
style="width:5.128205128205129%;
float:left;"> 2 </div><div class="not-covered"
style="width:12.820512820512821%;
float:left;"> 5 </div></td>
<td class="with-number">87.18 %</td>
<td class="with-number">91</td><td class="with-number">6</td><td class="with-number">39</td>
</tr>
<tr>
<td><a href="mw_parser/utils.clj.html">mw-parser.utils</a></td><td class="with-bar"><div class="covered"
style="width:93.58974358974359%;
float:left;"> 73 </div><div class="not-covered"
style="width:6.410256410256411%;
float:left;"> 5 </div></td>
<td class="with-number">93.59 %</td>
<td class="with-bar"><div class="covered"
style="width:90.0%;
float:left;"> 18 </div><div class="partial"
style="width:10.0%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">64</td><td class="with-number">10</td><td class="with-number">20</td>
</tr>
<tr><td>Totals:</td>
<td class="with-bar"></td>
<td class="with-number">88.29 %</td>
<td class="with-bar"></td>
<td class="with-number">90.99 %</td>
</tr>
</table>
</body>
</html>

View file

@ -1,194 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/bulk.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;parse&nbsp;multiple&nbsp;rules&nbsp;from&nbsp;a&nbsp;stream,&nbsp;possibly&nbsp;a&nbsp;file.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;mw-parser.bulk
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;(:use&nbsp;mw-parser.core
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mw-engine.utils
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clojure.java.io
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[clojure.string&nbsp;:only&nbsp;[split&nbsp;trim]])
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;&nbsp;&nbsp;(:import&nbsp;(java.io&nbsp;BufferedReader&nbsp;StringReader)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
009&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;;;&nbsp;mw-parser:&nbsp;a&nbsp;rule&nbsp;parser&nbsp;for&nbsp;MicroWorld.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;;;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;;;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
027&nbsp;&nbsp;;;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;;;;;&nbsp;Copyright&nbsp;(C)&nbsp;2014&nbsp;Simon&nbsp;Brooke
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
032&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
033&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
034&nbsp;&nbsp;(defn&nbsp;comment?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&quot;Is&nbsp;this&nbsp;`line`&nbsp;a&nbsp;comment?&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;[line]
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;(or&nbsp;(empty?&nbsp;(trim&nbsp;line))&nbsp;(member?&nbsp;(first&nbsp;line)&nbsp;&#x27;(nil&nbsp;\#&nbsp;\;))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
038&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
039&nbsp;&nbsp;(defn&nbsp;parse-string
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&quot;Parse&nbsp;rules&nbsp;from&nbsp;successive&nbsp;lines&nbsp;in&nbsp;this&nbsp;`string`,&nbsp;assumed&nbsp;to&nbsp;have&nbsp;multiple
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines&nbsp;delimited&nbsp;by&nbsp;the&nbsp;new-line&nbsp;character.&nbsp;Return&nbsp;a&nbsp;list&nbsp;of&nbsp;S-expressions.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;[string]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;tried&nbsp;to&nbsp;do&nbsp;this&nbsp;using&nbsp;with-open,&nbsp;but&nbsp;couldn&#x27;t&nbsp;make&nbsp;it&nbsp;work.
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;#(parse-rule&nbsp;(trim&nbsp;%))&nbsp;(remove&nbsp;comment?&nbsp;(split&nbsp;string&nbsp;#&quot;\n&quot;))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
045&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
046&nbsp;&nbsp;(defn&nbsp;parse-file
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&quot;Parse&nbsp;rules&nbsp;from&nbsp;successive&nbsp;lines&nbsp;in&nbsp;the&nbsp;file&nbsp;loaded&nbsp;from&nbsp;this&nbsp;`filename`.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Return&nbsp;a&nbsp;list&nbsp;of&nbsp;S-expressions.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;[filename]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;(parse-string&nbsp;(slurp&nbsp;filename)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
051&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
052&nbsp;&nbsp;(defn&nbsp;compile-string
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&quot;Compile&nbsp;each&nbsp;non-comment&nbsp;line&nbsp;of&nbsp;this&nbsp;`string`&nbsp;into&nbsp;an&nbsp;executable&nbsp;anonymous
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function,&nbsp;and&nbsp;return&nbsp;the&nbsp;sequence&nbsp;of&nbsp;such&nbsp;functions.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;[string]
</span><br/>
<span class="covered" title="14 out of 14 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;#(compile-rule&nbsp;%&nbsp;true)&nbsp;(remove&nbsp;comment?&nbsp;(split&nbsp;string&nbsp;#&quot;\n&quot;))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
057&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
058&nbsp;&nbsp;(defn&nbsp;compile-file
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&quot;Compile&nbsp;each&nbsp;non-comment&nbsp;line&nbsp;of&nbsp;the&nbsp;file&nbsp;indicated&nbsp;by&nbsp;this&nbsp;`filename`&nbsp;into
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;an&nbsp;executable&nbsp;anonymous&nbsp;function,&nbsp;and&nbsp;return&nbsp;the&nbsp;sequence&nbsp;of&nbsp;such&nbsp;functions.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;[filename]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;(compile-string&nbsp;(slurp&nbsp;filename)))
</span><br/>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -1,479 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/declarative.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;A&nbsp;very&nbsp;simple&nbsp;parser&nbsp;which&nbsp;parses&nbsp;production&nbsp;rules.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;mw-parser.declarative
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[instaparse.core&nbsp;:refer&nbsp;[parser]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[clojure.string&nbsp;:refer&nbsp;[join&nbsp;trim]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.errors&nbsp;:refer&nbsp;[throw-parse-exception]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.generate&nbsp;:refer&nbsp;[generate]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.simplify&nbsp;:refer&nbsp;[simplify]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.utils&nbsp;:refer&nbsp;[rule?]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[trptr.java-wrapper.locale&nbsp;:refer&nbsp;[get-default]])
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;(:import&nbsp;[java.util&nbsp;Locale]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
012&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;;;&nbsp;mw-parser:&nbsp;a&nbsp;rule&nbsp;parser&nbsp;for&nbsp;MicroWorld.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;;;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;;;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
027&nbsp;&nbsp;;;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;;;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;;;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;;;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;;;;;&nbsp;Copyright&nbsp;(C)&nbsp;2014&nbsp;Simon&nbsp;Brooke
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
034&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
035&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
036&nbsp;&nbsp;(def&nbsp;rule-grammar
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&quot;Basic&nbsp;rule&nbsp;language&nbsp;grammar.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;order&nbsp;to&nbsp;simplify&nbsp;translation&nbsp;into&nbsp;other&nbsp;natural&nbsp;languages,&nbsp;all
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;TOKENS&nbsp;within&nbsp;the&nbsp;parser&nbsp;should&nbsp;be&nbsp;unambiguou.&quot;
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;(join&nbsp;&quot;\n&quot;&nbsp;[&quot;RULE&nbsp;:=&nbsp;IF&nbsp;SPACE&nbsp;CONDITIONS&nbsp;SPACE&nbsp;THEN&nbsp;SPACE&nbsp;ACTIONS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;ACTIONS&nbsp;:=&nbsp;ACTION&nbsp;|&nbsp;ACTION&nbsp;SPACE&nbsp;AND&nbsp;SPACE&nbsp;ACTIONS&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;ACTION&nbsp;:=&nbsp;SIMPLE-ACTION&nbsp;|&nbsp;PROBABLE-ACTION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;PROBABLE-ACTION&nbsp;:=&nbsp;VALUE&nbsp;SPACE&nbsp;CHANCE-IN&nbsp;SPACE&nbsp;VALUE&nbsp;SPACE&nbsp;SIMPLE-ACTION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SIMPLE-ACTION&nbsp;:=&nbsp;SYMBOL&nbsp;SPACE&nbsp;BECOMES&nbsp;SPACE&nbsp;EXPRESSION;&quot;]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
046&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
047&nbsp;&nbsp;(def&nbsp;common-grammar
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&quot;Grammar&nbsp;rules&nbsp;used&nbsp;both&nbsp;in&nbsp;the&nbsp;rule&nbsp;grammar&nbsp;and&nbsp;in&nbsp;the&nbsp;flow&nbsp;grammar&quot;
</span><br/>
<span class="covered" title="30 out of 30 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;(join&nbsp;&quot;\n&quot;&nbsp;[&quot;COMPARATIVE&nbsp;:=&nbsp;MORE&nbsp;|&nbsp;LESS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;COMPARATIVE-QUALIFIER&nbsp;:=&nbsp;IS&nbsp;SPACE&nbsp;COMPARATIVE&nbsp;SPACE&nbsp;THAN&nbsp;|&nbsp;COMPARATIVE&nbsp;SPACE&nbsp;THAN;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;CONDITION&nbsp;:=&nbsp;WITHIN-CONDITION&nbsp;|&nbsp;NEIGHBOURS-CONDITION&nbsp;|&nbsp;PROPERTY-CONDITION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;CONDITIONS&nbsp;:=&nbsp;DISJUNCT-CONDITION&nbsp;|&nbsp;CONJUNCT-CONDITION&nbsp;|&nbsp;CONDITION&nbsp;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;CONJUNCT-CONDITION&nbsp;:=&nbsp;CONDITION&nbsp;SPACE&nbsp;AND&nbsp;SPACE&nbsp;CONDITIONS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DISJUNCT-CONDITION&nbsp;:=&nbsp;CONDITION&nbsp;SPACE&nbsp;OR&nbsp;SPACE&nbsp;CONDITIONS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DISJUNCT-EXPRESSION&nbsp;:=&nbsp;IN&nbsp;SPACE&nbsp;DISJUNCT-VALUE;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DISJUNCT-VALUE&nbsp;:=&nbsp;VALUE&nbsp;|&nbsp;VALUE&nbsp;SPACE&nbsp;OR&nbsp;SPACE&nbsp;DISJUNCT-VALUE;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;EQUIVALENCE&nbsp;:=&nbsp;IS&nbsp;SPACE&nbsp;EQUAL&nbsp;|&nbsp;EQUAL&nbsp;|&nbsp;IS&nbsp;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;EXPRESSION&nbsp;:=&nbsp;SIMPLE-EXPRESSION&nbsp;|&nbsp;RANGE-EXPRESSION&nbsp;|&nbsp;NUMERIC-EXPRESSION&nbsp;|&nbsp;DISJUNCT-EXPRESSION&nbsp;|&nbsp;VALUE;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NEGATED-QUALIFIER&nbsp;:=&nbsp;QUALIFIER&nbsp;SPACE&nbsp;NOT&nbsp;|&nbsp;NOT&nbsp;SPACE&nbsp;QUALIFIER;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NEIGHBOURS-CONDITION&nbsp;:=&nbsp;QUANTIFIER&nbsp;SPACE&nbsp;NEIGHBOURS&nbsp;SPACE&nbsp;IS&nbsp;SPACE&nbsp;PROPERTY-CONDITION&nbsp;|&nbsp;QUALIFIER&nbsp;SPACE&nbsp;NEIGHBOURS-CONDITION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NUMBER&nbsp;:=&nbsp;#&#x27;[0-9]+&#x27;&nbsp;|&nbsp;#&#x27;[0-9]+.[0-9]+&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NUMERIC-EXPRESSION&nbsp;:=&nbsp;VALUE&nbsp;|&nbsp;VALUE&nbsp;SPACE&nbsp;OPERATOR&nbsp;SPACE&nbsp;NUMERIC-EXPRESSION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;OPERATOR&nbsp;:=&nbsp;&#x27;+&#x27;&nbsp;|&nbsp;&#x27;-&#x27;&nbsp;|&nbsp;&#x27;*&#x27;&nbsp;|&nbsp;&#x27;&#x2F;&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;PROPERTY&nbsp;:=&nbsp;SYMBOL;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;PROPERTY-CONDITION&nbsp;:=&nbsp;PROPERTY&nbsp;SPACE&nbsp;QUALIFIER&nbsp;SPACE&nbsp;EXPRESSION&nbsp;|&nbsp;VALUE;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;PROPERTY-CONDITION-OR-EXPRESSION&nbsp;:=&nbsp;PROPERTY-CONDITION&nbsp;|&nbsp;EXPRESSION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;QUALIFIER&nbsp;:=&nbsp;COMPARATIVE-QUALIFIER&nbsp;|&nbsp;NEGATED-QUALIFIER&nbsp;|&nbsp;EQUIVALENCE&nbsp;|&nbsp;IS&nbsp;SPACE&nbsp;QUALIFIER;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;QUANTIFIER&nbsp;:=&nbsp;NUMBER&nbsp;|&nbsp;SOME&nbsp;|&nbsp;NONE&nbsp;|&nbsp;ALL&nbsp;|&nbsp;COMPARATIVE&nbsp;SPACE&nbsp;THAN&nbsp;SPACE&nbsp;NUMBER;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;RANGE-EXPRESSION&nbsp;:=&nbsp;BETWEEN&nbsp;SPACE&nbsp;NUMERIC-EXPRESSION&nbsp;SPACE&nbsp;AND&nbsp;SPACE&nbsp;NUMERIC-EXPRESSION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SIMPLE-EXPRESSION&nbsp;:=&nbsp;QUALIFIER&nbsp;SPACE&nbsp;EXPRESSION&nbsp;|&nbsp;VALUE;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SPACE&nbsp;:=&nbsp;#&#x27;\\s+&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;VALUE&nbsp;:=&nbsp;SYMBOL&nbsp;|&nbsp;NUMBER;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;VALUE&nbsp;:=&nbsp;SYMBOL&nbsp;|&nbsp;NUMBER;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
074&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;WITHIN-CONDITION&nbsp;:=&nbsp;QUANTIFIER&nbsp;SPACE&nbsp;NEIGHBOURS&nbsp;SPACE&nbsp;WITHIN&nbsp;SPACE&nbsp;NUMBER&nbsp;SPACE&nbsp;IS&nbsp;SPACE&nbsp;PROPERTY-CONDITION-OR-EXPRESSION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
076&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
077&nbsp;&nbsp;(def&nbsp;keywords-en
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;&quot;English&nbsp;language&nbsp;keyword&nbsp;literals&nbsp;used&nbsp;in&nbsp;rules&nbsp;-&nbsp;both&nbsp;in&nbsp;production
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rules&nbsp;(this&nbsp;namespace)&nbsp;and&nbsp;in&nbsp;flow&nbsp;rules&nbsp;(see&nbsp;mw-parser.flow).
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It&#x27;s&nbsp;a&nbsp;long&nbsp;term&nbsp;aim&nbsp;that&nbsp;the&nbsp;rule&nbsp;language&nbsp;should&nbsp;be&nbsp;easy&nbsp;to&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;internationalise;&nbsp;this&nbsp;isn&#x27;t&nbsp;a&nbsp;full&nbsp;solution&nbsp;but&nbsp;it&#x27;s&nbsp;a&nbsp;step&nbsp;towards
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;solution.&quot;
</span><br/>
<span class="covered" title="32 out of 32 forms covered">
084&nbsp;&nbsp;&nbsp;&nbsp;(join&nbsp;&quot;\n&quot;&nbsp;[&quot;ALL&nbsp;:=&nbsp;&#x27;all&#x27;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
085&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;AND&nbsp;:=&nbsp;&#x27;and&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;BECOMES&nbsp;:=&nbsp;&#x27;should&nbsp;be&#x27;&nbsp;|&nbsp;&#x27;becomes&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;BETWEEN&nbsp;:=&nbsp;&#x27;between&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;CHANCE-IN&nbsp;:=&nbsp;&#x27;chance&nbsp;in&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;EACH&nbsp;:=&nbsp;&#x27;each&#x27;&nbsp;|&nbsp;&#x27;every&#x27;&nbsp;|&nbsp;&#x27;all&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;EQUAL&nbsp;:=&nbsp;&#x27;equal&nbsp;to&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;FIRST&nbsp;:=&nbsp;&#x27;first&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
092&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;FLOW&nbsp;:=&nbsp;&#x27;flow&#x27;&nbsp;|&nbsp;&#x27;move&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
093&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;FROM&nbsp;:=&nbsp;&#x27;from&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
094&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;IF&nbsp;:=&nbsp;&#x27;if&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
095&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;IN&nbsp;:=&nbsp;&#x27;in&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;IS&nbsp;:=&nbsp;&#x27;is&#x27;&nbsp;|&nbsp;&#x27;are&#x27;&nbsp;|&nbsp;&#x27;have&#x27;&nbsp;|&nbsp;&#x27;has&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
097&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;LEAST&nbsp;:=&nbsp;&#x27;least&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
098&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;LESS&nbsp;:=&nbsp;&#x27;less&#x27;&nbsp;|&nbsp;&#x27;fewer&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
099&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;MORE&nbsp;:=&nbsp;&#x27;more&#x27;&nbsp;|&nbsp;&#x27;greater&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;MOST&nbsp;:=&nbsp;&#x27;most&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NEIGHBOURS&nbsp;:=&nbsp;&#x27;neighbour&#x27;&nbsp;|&nbsp;&#x27;neighbor&#x27;&nbsp;|&nbsp;&#x27;neighbours&#x27;&nbsp;|&nbsp;&#x27;neighbors&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NONE&nbsp;:=&nbsp;&#x27;no&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NOT&nbsp;:=&nbsp;&#x27;not&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;OR&nbsp;:=&nbsp;&#x27;or&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
105&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SOME&nbsp;:=&nbsp;&#x27;some&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
106&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;SYMBOL&nbsp;is&nbsp;in&nbsp;the&nbsp;per-language&nbsp;file&nbsp;so&nbsp;that&nbsp;languages&nbsp;that&nbsp;use
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
107&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;(e.g.)&nbsp;Cyrillic&nbsp;characters&nbsp;can&nbsp;change&nbsp;the&nbsp;definition.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
108&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SYMBOL&nbsp;:=&nbsp;#&#x27;[a-z]+&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
109&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;THAN&nbsp;:=&nbsp;&#x27;than&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
110&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;THEN&nbsp;:=&nbsp;&#x27;then&#x27;;&quot;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
111&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;TO&nbsp;:=&nbsp;&#x27;to&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;WITH&nbsp;:=&nbsp;&#x27;with&#x27;&nbsp;|&nbsp;&#x27;where&#x27;&nbsp;|&nbsp;&#x27;having&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;WITHIN&nbsp;:=&nbsp;&#x27;within&#x27;;&quot;]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
114&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
115&nbsp;&nbsp;(defn&nbsp;keywords-for-locale
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
116&nbsp;&nbsp;&nbsp;&nbsp;&quot;For&nbsp;now,&nbsp;just&nbsp;return&nbsp;`keywords-en`;&nbsp;plan&nbsp;is&nbsp;to&nbsp;have&nbsp;resource&nbsp;files&nbsp;of&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
117&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keywords&nbsp;for&nbsp;different&nbsp;languages&nbsp;in&nbsp;a&nbsp;resource&nbsp;directory,&nbsp;but&nbsp;that&nbsp;isn&#x27;t
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
118&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;done&nbsp;yet.&nbsp;It&#x27;s&nbsp;probably&nbsp;not&nbsp;going&nbsp;to&nbsp;work&nbsp;easily&nbsp;for&nbsp;languages&nbsp;that&nbsp;use
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
119&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;non-latin&nbsp;alphabets,&nbsp;anyway.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
120&nbsp;&nbsp;&nbsp;&nbsp;([]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
121&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keywords-for-locale&nbsp;(get-default)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
122&nbsp;&nbsp;&nbsp;&nbsp;([^Locale&nbsp;_locale]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
123&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keywords-en))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
124&nbsp;&nbsp;
</span><br/>
<span class="covered" title="58 out of 58 forms covered">
125&nbsp;&nbsp;(defmacro&nbsp;build-parser&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
126&nbsp;&nbsp;&nbsp;&nbsp;&quot;Compose&nbsp;this&nbsp;grammar&nbsp;fragment&nbsp;`g`&nbsp;with&nbsp;the&nbsp;common&nbsp;grammar&nbsp;fragments&nbsp;to&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
127&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;make&nbsp;a&nbsp;complete&nbsp;grammar,&nbsp;and&nbsp;return&nbsp;a&nbsp;parser&nbsp;for&nbsp;that&nbsp;complete&nbsp;grammar.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
128&nbsp;&nbsp;&nbsp;&nbsp;[g]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
129&nbsp;&nbsp;&nbsp;&nbsp;`(parser&nbsp;(join&nbsp;&quot;\n&quot;&nbsp;[~g&nbsp;common-grammar&nbsp;(keywords-for-locale)])))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
130&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
131&nbsp;&nbsp;(def&nbsp;parse-rule
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
132&nbsp;&nbsp;&nbsp;&nbsp;&quot;Parse&nbsp;the&nbsp;argument,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;string&nbsp;in&nbsp;the&nbsp;correct&nbsp;syntax,&nbsp;and&nbsp;return&nbsp;a&nbsp;parse&nbsp;tree.&quot;
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
133&nbsp;&nbsp;&nbsp;&nbsp;(build-parser&nbsp;rule-grammar))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
134&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
135&nbsp;&nbsp;(defn&nbsp;compile-rule
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
136&nbsp;&nbsp;&nbsp;&nbsp;&quot;Parse&nbsp;this&nbsp;`rule-text`,&nbsp;a&nbsp;string&nbsp;conforming&nbsp;to&nbsp;the&nbsp;grammar&nbsp;of&nbsp;MicroWorld&nbsp;rules,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
137&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;Clojure&nbsp;source,&nbsp;and&nbsp;then&nbsp;compile&nbsp;it&nbsp;into&nbsp;an&nbsp;anonymous
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
138&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;object,&nbsp;getting&nbsp;round&nbsp;the&nbsp;problem&nbsp;of&nbsp;binding&nbsp;mw-engine.utils&nbsp;in
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
139&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;compiling&nbsp;environment.&nbsp;If&nbsp;`return-tuple?`&nbsp;is&nbsp;present&nbsp;and&nbsp;true,&nbsp;return
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
140&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;list&nbsp;comprising&nbsp;the&nbsp;anonymous&nbsp;function&nbsp;compiled,&nbsp;and&nbsp;the&nbsp;function&nbsp;from
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
141&nbsp;&nbsp;&nbsp;&nbsp;which&nbsp;it&nbsp;was&nbsp;compiled.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
142&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
143&nbsp;&nbsp;&nbsp;&nbsp;Throws&nbsp;an&nbsp;exception&nbsp;if&nbsp;parsing&nbsp;fails.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
144&nbsp;&nbsp;&nbsp;&nbsp;([rule-text&nbsp;return-tuple?]
</span><br/>
<span class="partial" title="5 out of 14 forms covered">
145&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert&nbsp;(string?&nbsp;rule-text))
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
146&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[rule&nbsp;(trim&nbsp;rule-text)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
147&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tree&nbsp;(simplify&nbsp;(parse-rule&nbsp;rule))
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
148&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;afn&nbsp;(if&nbsp;(rule?&nbsp;tree)&nbsp;(eval&nbsp;(generate&nbsp;tree))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
149&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;else
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
150&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw-parse-exception&nbsp;tree))]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
151&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;return-tuple?
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
152&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;afn&nbsp;rule)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
153&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;else
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
154&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;afn)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
155&nbsp;&nbsp;&nbsp;&nbsp;([rule-text]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
156&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(compile-rule&nbsp;rule-text&nbsp;false)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
157&nbsp;&nbsp;
</span><br/>
</body>
</html>

View file

@ -1,212 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/errors.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;Display&nbsp;parse&nbsp;errors&nbsp;in&nbsp;a&nbsp;format&nbsp;which&nbsp;makes&nbsp;it&nbsp;easy&nbsp;for&nbsp;the&nbsp;user
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;see&nbsp;where&nbsp;the&nbsp;error&nbsp;occurred.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;mw-parser.errors)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
005&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
024&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
025&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;&nbsp;error&nbsp;thrown&nbsp;when&nbsp;an&nbsp;attempt&nbsp;is&nbsp;made&nbsp;to&nbsp;set&nbsp;a&nbsp;reserved&nbsp;property
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
027&nbsp;&nbsp;(def&nbsp;reserved-properties-error
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;properties&nbsp;&#x27;x&#x27;&nbsp;and&nbsp;&#x27;y&#x27;&nbsp;of&nbsp;a&nbsp;cell&nbsp;are&nbsp;reserved&nbsp;and&nbsp;should&nbsp;not&nbsp;be&nbsp;set&nbsp;in&nbsp;rule&nbsp;actions&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;;;&nbsp;error&nbsp;thrown&nbsp;when&nbsp;a&nbsp;rule&nbsp;cannot&nbsp;be&nbsp;parsed.&nbsp;Slots&nbsp;are&nbsp;for
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;;;&nbsp;(1)&nbsp;rule&nbsp;text
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;;;&nbsp;(2)&nbsp;cursor&nbsp;showing&nbsp;where&nbsp;in&nbsp;the&nbsp;rule&nbsp;text&nbsp;the&nbsp;error&nbsp;occurred
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;;;&nbsp;(3)&nbsp;the&nbsp;reason&nbsp;for&nbsp;the&nbsp;error
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
033&nbsp;&nbsp;(def&nbsp;bad-parse-error&nbsp;&quot;I&nbsp;did&nbsp;not&nbsp;understand:\n&nbsp;&nbsp;&#x27;%s&#x27;\n&nbsp;&nbsp;%s\n&nbsp;&nbsp;%s&quot;)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
034&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
035&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
036&nbsp;&nbsp;(defn-&nbsp;explain-parse-error-reason
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&quot;Attempt&nbsp;to&nbsp;explain&nbsp;the&nbsp;reason&nbsp;for&nbsp;the&nbsp;parse&nbsp;error.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;[reason]
</span><br/>
<span class="covered" title="17 out of 17 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;(str&nbsp;&quot;Expecting&nbsp;one&nbsp;of&nbsp;(&quot;&nbsp;(apply&nbsp;str&nbsp;(map&nbsp;#(str&nbsp;(:expecting&nbsp;%)&nbsp;&quot;&nbsp;&quot;)&nbsp;reason))&nbsp;&quot;)&quot;))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
040&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
041&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
042&nbsp;&nbsp;(defn-&nbsp;parser-error-to-map
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;[parser-error]
</span><br/>
<span class="covered" title="16 out of 16 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(reduce&nbsp;(fn&nbsp;[map&nbsp;item](merge&nbsp;map&nbsp;{(first&nbsp;item)(second&nbsp;item)}))&nbsp;{}&nbsp;parser-error)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reason&nbsp;(map
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(reduce&nbsp;(fn&nbsp;[map&nbsp;item]&nbsp;(merge&nbsp;{(first&nbsp;item)&nbsp;(second&nbsp;item)}&nbsp;map))&nbsp;{}&nbsp;%)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:reason&nbsp;m))]
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(merge&nbsp;m&nbsp;{:reason&nbsp;reason})))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
049&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
050&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
051&nbsp;&nbsp;(defn&nbsp;throw-parse-exception
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&quot;Construct&nbsp;a&nbsp;helpful&nbsp;error&nbsp;message&nbsp;from&nbsp;this&nbsp;`parser-error`,&nbsp;and&nbsp;throw&nbsp;an&nbsp;exception&nbsp;with&nbsp;that&nbsp;message.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;[parser-error]
</span><br/>
<span class="partial" title="5 out of 16 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;(assert&nbsp;(coll?&nbsp;parser-error)&nbsp;&quot;Expected&nbsp;a&nbsp;paser&nbsp;error&nbsp;structure?&quot;)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;(let
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;the&nbsp;error&nbsp;structure&nbsp;is&nbsp;a&nbsp;list,&nbsp;such&nbsp;that&nbsp;each&nbsp;element&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;two&nbsp;items,&nbsp;and
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;the&nbsp;first&nbsp;element&nbsp;in&nbsp;each&nbsp;sublist&nbsp;is&nbsp;a&nbsp;keyword.&nbsp;Easier&nbsp;to&nbsp;work&nbsp;with&nbsp;it&nbsp;as&nbsp;a&nbsp;map
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error-map&nbsp;(parser-error-to-map&nbsp;parser-error)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;(:text&nbsp;error-map)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reason&nbsp;(explain-parse-error-reason&nbsp;(:reason&nbsp;error-map))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;rules&nbsp;have&nbsp;only&nbsp;one&nbsp;line,&nbsp;by&nbsp;definition;&nbsp;we&#x27;re&nbsp;interested&nbsp;in&nbsp;the&nbsp;column
</span><br/>
<span class="partial" title="7 out of 8 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;column&nbsp;(if&nbsp;(:column&nbsp;error-map)(:column&nbsp;error-map)&nbsp;0)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;create&nbsp;a&nbsp;cursor&nbsp;to&nbsp;point&nbsp;to&nbsp;that&nbsp;column
</span><br/>
<span class="covered" title="12 out of 12 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor&nbsp;(apply&nbsp;str&nbsp;(reverse&nbsp;(conj&nbsp;(repeat&nbsp;column&nbsp;&quot;&nbsp;&quot;)&nbsp;&quot;^&quot;)))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message&nbsp;(format&nbsp;bad-parse-error&nbsp;text&nbsp;cursor&nbsp;reason)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;message))))
</span><br/>
</body>
</html>

View file

@ -1,209 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/flow.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;A&nbsp;very&nbsp;simple&nbsp;parser&nbsp;which&nbsp;parses&nbsp;flow&nbsp;rules.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;mw-parser.flow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[clojure.string&nbsp;:refer&nbsp;[join]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.declarative&nbsp;:refer&nbsp;[build-parser]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.simplify&nbsp;:refer&nbsp;[simplify-second-of-two]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
007&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
008&nbsp;&nbsp;(def&nbsp;flow-grammar
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;&nbsp;&nbsp;&quot;Grammar&nbsp;for&nbsp;flow&nbsp;rules.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;My&nbsp;initial&nbsp;conception&nbsp;of&nbsp;this&nbsp;would&nbsp;be&nbsp;that&nbsp;production&nbsp;rules&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if-then&nbsp;rules)&nbsp;and&nbsp;flow&nbsp;rules&nbsp;(flow-from-to&nbsp;rules)&nbsp;would&nbsp;be&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;entirely&nbsp;separate,&nbsp;presented&nbsp;to&nbsp;the&nbsp;parser&nbsp;as&nbsp;separate&nbsp;text&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files,&nbsp;and&nbsp;parsed&nbsp;and&nbsp;compiled&nbsp;by&nbsp;different&nbsp;chains&nbsp;of&nbsp;functions.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This&nbsp;appears&nbsp;not&nbsp;to&nbsp;be&nbsp;necessary.&nbsp;Flow&nbsp;rules&nbsp;are&nbsp;easy&nbsp;to&nbsp;parse
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;the&nbsp;same&nbsp;parser&nbsp;as&nbsp;production&nbsp;rules&nbsp;--&nbsp;a&nbsp;lot&nbsp;of&nbsp;the&nbsp;grammar&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is&nbsp;intentionally&nbsp;common&nbsp;--&nbsp;and&nbsp;the&nbsp;rules&nbsp;are&nbsp;easily&nbsp;discriminated
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;the&nbsp;compilation&nbsp;(&#x27;generate&#x27;)&nbsp;stage.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;basic&nbsp;rule&nbsp;I&nbsp;want&nbsp;to&nbsp;be&nbsp;able&nbsp;to&nbsp;compile&nbsp;at&nbsp;this&nbsp;stage&nbsp;is&nbsp;the&nbsp;&#x27;mutual
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aid&#x27;&nbsp;rule:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
023&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`flow&nbsp;1&nbsp;food&nbsp;from&nbsp;house&nbsp;having&nbsp;food&nbsp;&gt;&nbsp;1&nbsp;to&nbsp;house&nbsp;with&nbsp;least&nbsp;food&nbsp;within&nbsp;2`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;
</span><br/>
<span class="covered" title="16 out of 16 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;(join&nbsp;&quot;\n&quot;&nbsp;[&quot;FLOW-RULE&nbsp;:=&nbsp;FLOW&nbsp;SPACE&nbsp;QUANTITY&nbsp;SPACE&nbsp;PROPERTY&nbsp;SPACE&nbsp;FROM&nbsp;SPACE&nbsp;SOURCE&nbsp;SPACE&nbsp;TO-HOW&nbsp;SPACE&nbsp;DESTINATION;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;PERCENTAGE&nbsp;:=&nbsp;NUMBER&nbsp;#&#x27;%&#x27;;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;QUANTITY&nbsp;:=&nbsp;PERCENTAGE&nbsp;|&nbsp;NUMBER&nbsp;|&nbsp;EXPRESSION&nbsp;|&nbsp;SOME;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;SOURCE&nbsp;:=&nbsp;STATE&nbsp;|&nbsp;STATE&nbsp;SPACE&nbsp;WITH&nbsp;SPACE&nbsp;CONDITIONS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DESTINATION&nbsp;:=&nbsp;STATE&nbsp;|&nbsp;STATE&nbsp;SPACE&nbsp;WITH&nbsp;SPACE&nbsp;FLOW-CONDITIONS&nbsp;|&nbsp;STATE&nbsp;SPACE&nbsp;WITHIN&nbsp;SPACE&nbsp;VALUE&nbsp;SPACE&nbsp;WITH&nbsp;SPACE&nbsp;FLOW-CONDITIONS;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DETERMINER&nbsp;:=&nbsp;MOST&nbsp;|&nbsp;LEAST;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;DETERMINER-CONDITION&nbsp;:=&nbsp;DETERMINER&nbsp;SPACE&nbsp;PROPERTY&nbsp;|&nbsp;DETERMINER&nbsp;SPACE&nbsp;PROPERTY;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;FLOW-CONDITIONS&nbsp;:=&nbsp;DETERMINER-CONDITION&nbsp;|&nbsp;CONDITIONS&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;STATE&nbsp;:=&nbsp;SYMBOL;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;TO-HOW&nbsp;:=&nbsp;TO&nbsp;|&nbsp;TO-EACH&nbsp;|&nbsp;TO-FIRST;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;TO-EACH&nbsp;:=&nbsp;TO&nbsp;SPACE&nbsp;EACH&nbsp;|&nbsp;TO&nbsp;SPACE&nbsp;ALL;&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;TO-FIRST&nbsp;:=&nbsp;TO&nbsp;SPACE&nbsp;FIRST&quot;]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
038&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
039&nbsp;&nbsp;(def&nbsp;parse-flow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&quot;Parse&nbsp;the&nbsp;argument,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;string&nbsp;in&nbsp;the&nbsp;correct&nbsp;syntax,&nbsp;and&nbsp;return&nbsp;a&nbsp;parse&nbsp;tree.&quot;
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;(build-parser&nbsp;flow-grammar))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
042&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
043&nbsp;&nbsp;(defn&nbsp;simplify-flow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(coll?&nbsp;tree)
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITIONS&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DETERMINER&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
050&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DETERMINER-CONDITION&nbsp;(simplify-determiner-condition&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:EXPRESSION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:FLOW&nbsp;nil
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:FLOW-CONDITIONS&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY-CONDITION-OR-EXPRESSION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SPACE&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUANTITY&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:STATE&nbsp;(list&nbsp;:PROPERTY-CONDITION
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;:SYMBOL&nbsp;&quot;state&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#x27;(:QUALIFIER
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:EQUIVALENCE
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:IS&nbsp;&quot;is&quot;)))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;:EXPRESSION
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;:VALUE&nbsp;(second&nbsp;tree))))
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove&nbsp;nil?&nbsp;(map&nbsp;simplify-flow&nbsp;tree)))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tree))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
067&nbsp;&nbsp;
</span><br/>
</body>
</html>

View file

@ -1,992 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/generate.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;Generate&nbsp;Clojure&nbsp;source&nbsp;from&nbsp;simplified&nbsp;parse&nbsp;trees.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;mw-parser.generate
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[clojure.pprint&nbsp;:refer&nbsp;[pprint]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[clojure.tools.trace&nbsp;:refer&nbsp;[deftrace]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.utils&nbsp;:refer&nbsp;[assert-type&nbsp;TODO]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mw-parser.errors&nbsp;:as&nbsp;pe]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
008&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
027&nbsp;&nbsp;
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
028&nbsp;&nbsp;(declare&nbsp;generate&nbsp;generate-action)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
029&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
030&nbsp;&nbsp;(defn&nbsp;generate-rule
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;rule&nbsp;specification,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;rule&nbsp;as&nbsp;a&nbsp;function&nbsp;of&nbsp;two&nbsp;arguments.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:RULE)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;(vary-meta
</span><br/>
<span class="covered" title="19 out of 19 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;fn&nbsp;[&#x27;cell&nbsp;&#x27;world]&nbsp;(list&nbsp;&#x27;when&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;3))))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:rule-type
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:production}))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
040&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
041&nbsp;&nbsp;(defn&nbsp;generate-conditions
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;conditions&nbsp;clause,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:CONDITIONS)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;(second&nbsp;tree)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
047&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
048&nbsp;&nbsp;(defn&nbsp;generate-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;condition&nbsp;clause,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:CONDITION)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;(second&nbsp;tree)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
054&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
055&nbsp;&nbsp;(defn&nbsp;generate-conjunct-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;conjunct&nbsp;correct&nbsp;condition&nbsp;clause,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:CONJUNCT-CONDITION)
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;&#x27;and&nbsp;(map&nbsp;generate&nbsp;(rest&nbsp;tree))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
061&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
062&nbsp;&nbsp;(defn&nbsp;generate-disjunct-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;disjunct&nbsp;condition&nbsp;clause,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:DISJUNCT-CONDITION)
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;&#x27;or&nbsp;(map&nbsp;generate&nbsp;(rest&nbsp;tree))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
068&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
069&nbsp;&nbsp;(defn&nbsp;generate-ranged-property-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;property&nbsp;condition&nbsp;clause&nbsp;for
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;this&nbsp;`property`&nbsp;where&nbsp;the&nbsp;`expression`&nbsp;is&nbsp;a&nbsp;numeric&nbsp;range,&nbsp;generate&nbsp;and&nbsp;return
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;[tree&nbsp;property&nbsp;expression]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
074&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:PROPERTY-CONDITION)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;(nth&nbsp;tree&nbsp;3)&nbsp;:RANGE-EXPRESSION)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[l1&nbsp;(generate&nbsp;(nth&nbsp;expression&nbsp;2))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
077&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l2&nbsp;(generate&nbsp;(nth&nbsp;expression&nbsp;4))
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pv&nbsp;(list&nbsp;property&nbsp;&#x27;cell)]
</span><br/>
<span class="covered" title="11 out of 11 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;let&nbsp;[&#x27;lower&nbsp;(list&nbsp;&#x27;min&nbsp;l1&nbsp;l2)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#x27;upper&nbsp;(list&nbsp;&#x27;max&nbsp;l1&nbsp;l2)]
</span><br/>
<span class="covered" title="13 out of 13 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;and&nbsp;(list&nbsp;&#x27;&gt;=&nbsp;pv&nbsp;&#x27;lower)&nbsp;(list&nbsp;&#x27;&lt;=&nbsp;pv&nbsp;&#x27;upper)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
082&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
083&nbsp;&nbsp;(defn&nbsp;generate-disjunct-property-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
084&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;property&nbsp;condition&nbsp;clause
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
085&nbsp;&nbsp;&nbsp;&nbsp;where&nbsp;the&nbsp;expression&nbsp;is&nbsp;a&nbsp;a&nbsp;disjunction,&nbsp;generate&nbsp;and&nbsp;return
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;TODO:&nbsp;this&nbsp;is&nbsp;definitely&nbsp;still&nbsp;wrong!&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[property&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qualifier&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expression&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;3))]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
092&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-disjunct-property-condition&nbsp;tree&nbsp;property&nbsp;qualifier&nbsp;expression)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
093&nbsp;&nbsp;&nbsp;&nbsp;([_tree&nbsp;property&nbsp;qualifier&nbsp;expression]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
094&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[e&nbsp;(list&nbsp;expression&nbsp;(list&nbsp;property&nbsp;&#x27;cell))]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
095&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(=&nbsp;qualifier&nbsp;&#x27;=)&nbsp;e
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;not&nbsp;e)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
097&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
098&nbsp;&nbsp;(defn&nbsp;generate-property-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
099&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;property&nbsp;condition&nbsp;clause,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
100&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
101&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:PROPERTY-CONDITION)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(=&nbsp;(count&nbsp;tree)&nbsp;2)&nbsp;(=&nbsp;(first&nbsp;(second&nbsp;tree))&nbsp;:SYMBOL))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
105&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;it&#x27;s&nbsp;a&nbsp;shorthand&nbsp;for&nbsp;&#x27;state&nbsp;equal&nbsp;to&nbsp;symbol&#x27;.&nbsp;This&nbsp;should&nbsp;probably&nbsp;have
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
106&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;been&nbsp;handled&nbsp;in&nbsp;simplify...
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
107&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-property-condition
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
108&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
109&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY-CONDITION
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
110&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#x27;(:SYMBOL&nbsp;&quot;state&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
111&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#x27;(:QUALIFIER&nbsp;(:EQUIVALENCE&nbsp;(:EQUAL&nbsp;&quot;equal&nbsp;to&quot;)))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(second&nbsp;tree)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;otherwise...
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
114&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-property-condition&nbsp;tree&nbsp;(first&nbsp;(nth&nbsp;tree&nbsp;3)))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
115&nbsp;&nbsp;&nbsp;&nbsp;([tree&nbsp;expression-type]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
116&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:PROPERTY-CONDITION)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
117&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[property&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
118&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qualifier&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
119&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;3))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
120&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expression&nbsp;(cond
</span><br/>
<span class="covered" title="20 out of 20 forms covered">
121&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(not&nbsp;(=&nbsp;qualifier&nbsp;&#x27;=))&nbsp;(keyword?&nbsp;e))&nbsp;(list&nbsp;&#x27;or&nbsp;(list&nbsp;e&nbsp;&#x27;cell)&nbsp;e)
</span><br/>
<span class="covered" title="20 out of 20 forms covered">
122&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(not&nbsp;(=&nbsp;qualifier&nbsp;&#x27;not=))&nbsp;(keyword?&nbsp;e))&nbsp;(list&nbsp;&#x27;or&nbsp;(list&nbsp;e&nbsp;&#x27;cell)&nbsp;e)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
123&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else&nbsp;e)]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
124&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;expression-type
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
125&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DISJUNCT-EXPRESSION&nbsp;(generate-disjunct-property-condition&nbsp;tree&nbsp;property&nbsp;qualifier&nbsp;expression)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
126&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:RANGE-EXPRESSION&nbsp;(generate-ranged-property-condition&nbsp;tree&nbsp;property&nbsp;expression)
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
127&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;qualifier&nbsp;(list&nbsp;property&nbsp;&#x27;cell)&nbsp;expression)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
128&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
129&nbsp;&nbsp;(defn&nbsp;generate-qualifier
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
130&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;qualifier,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
131&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
132&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
133&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
134&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;(count&nbsp;tree)&nbsp;2)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
135&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
136&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;else
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
137&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
138&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
139&nbsp;&nbsp;(defn&nbsp;generate-simple-action
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
140&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;simple&nbsp;action,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
141&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
142&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
143&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:SIMPLE-ACTION)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
144&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-simple-action&nbsp;tree&nbsp;[]))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
145&nbsp;&nbsp;&nbsp;&nbsp;([tree&nbsp;others]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
146&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:SIMPLE-ACTION)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
147&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[property&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
148&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expression&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;3))]
</span><br/>
<span class="covered" title="11 out of 11 forms covered">
149&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(or&nbsp;(=&nbsp;property&nbsp;:x)&nbsp;(=&nbsp;property&nbsp;:y))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
150&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;pe&#x2F;reserved-properties-error))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
151&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;merge
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
152&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(empty?&nbsp;others)&nbsp;&#x27;cell
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
153&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;else
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
154&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;others))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
155&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{property&nbsp;expression})))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
156&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
157&nbsp;&nbsp;(defn&nbsp;generate-probable-action
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
158&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;probable&nbsp;action,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
159&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
160&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
161&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:PROBABLE-ACTION)
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
162&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-probable-action&nbsp;tree&nbsp;[]))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
163&nbsp;&nbsp;&nbsp;&nbsp;([tree&nbsp;others]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
164&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:PROBABLE-ACTION)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
165&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
166&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[chances&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;1))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
167&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
168&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;action&nbsp;(generate-action&nbsp;(nth&nbsp;tree&nbsp;3)&nbsp;others)]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
169&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;could&nbsp;almost&nbsp;certainly&nbsp;be&nbsp;done&nbsp;better&nbsp;with&nbsp;macro&nbsp;syntax
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
170&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;if
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
171&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;&lt;&nbsp;(list&nbsp;&#x27;rand&nbsp;total)&nbsp;chances)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
172&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;action))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
173&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
174&nbsp;&nbsp;(defn&nbsp;generate-action
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
175&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;action,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
176&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
177&nbsp;&nbsp;&nbsp;&nbsp;[tree&nbsp;others]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
178&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
179&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ACTIONS&nbsp;(generate-action&nbsp;(first&nbsp;tree)&nbsp;others)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
180&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SIMPLE-ACTION&nbsp;(generate-simple-action&nbsp;tree&nbsp;others)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
181&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROBABLE-ACTION&nbsp;(generate-probable-action&nbsp;tree&nbsp;others)
</span><br/>
<span class="not-covered" title="0 out of 8 forms covered">
182&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;(str&nbsp;&quot;Not&nbsp;a&nbsp;known&nbsp;action&nbsp;type:&nbsp;&quot;&nbsp;(first&nbsp;tree))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
183&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
184&nbsp;&nbsp;(defn&nbsp;generate-multiple-actions
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
185&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;one&nbsp;or&nbsp;more&nbsp;syntactically&nbsp;correct&nbsp;actions,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
186&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
187&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
188&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:ACTIONS)
</span><br/>
<span class="covered" title="12 out of 12 forms covered">
189&nbsp;&nbsp;&nbsp;&nbsp;(generate-action&nbsp;(first&nbsp;(rest&nbsp;tree))&nbsp;(second&nbsp;(rest&nbsp;tree))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
190&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
191&nbsp;&nbsp;(defn&nbsp;generate-disjunct-value
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
192&nbsp;&nbsp;&nbsp;&nbsp;&quot;Generate&nbsp;a&nbsp;disjunct&nbsp;value.&nbsp;Essentially&nbsp;what&nbsp;we&nbsp;need&nbsp;here&nbsp;is&nbsp;to&nbsp;generate&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
193&nbsp;&nbsp;&nbsp;&nbsp;flat&nbsp;list&nbsp;of&nbsp;values,&nbsp;since&nbsp;the&nbsp;`member`&nbsp;has&nbsp;already&nbsp;been&nbsp;taken&nbsp;care&nbsp;of.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
194&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
195&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:DISJUNCT-VALUE)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
196&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(=&nbsp;(count&nbsp;tree)&nbsp;4)
</span><br/>
<span class="not-covered" title="0 out of 12 forms covered">
197&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;(generate&nbsp;(second&nbsp;tree))&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;3)))
</span><br/>
<span class="not-covered" title="0 out of 7 forms covered">
198&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;(generate&nbsp;(second&nbsp;tree)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
199&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
200&nbsp;&nbsp;(defn&nbsp;generate-numeric-expression
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
201&nbsp;&nbsp;&nbsp;&nbsp;&quot;From&nbsp;this&nbsp;`tree`,&nbsp;assumed&nbsp;to&nbsp;be&nbsp;a&nbsp;syntactically&nbsp;correct&nbsp;numeric&nbsp;expression,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
202&nbsp;&nbsp;&nbsp;&nbsp;generate&nbsp;and&nbsp;return&nbsp;the&nbsp;appropriate&nbsp;clojure&nbsp;fragment.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
203&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
204&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:NUMERIC-EXPRESSION)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
205&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(count&nbsp;tree)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
206&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;(let&nbsp;[[p&nbsp;operator&nbsp;expression]&nbsp;(rest&nbsp;tree)
</span><br/>
<span class="partial" title="8 out of 9 forms covered">
207&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property&nbsp;(if&nbsp;(number?&nbsp;p)&nbsp;p&nbsp;(list&nbsp;p&nbsp;&#x27;cell))]
</span><br/>
<span class="covered" title="11 out of 11 forms covered">
208&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;(generate&nbsp;operator)&nbsp;(generate&nbsp;property)&nbsp;(generate&nbsp;expression)))
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
209&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
210&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SYMBOL&nbsp;(list&nbsp;(keyword&nbsp;(second&nbsp;(second&nbsp;tree)))&nbsp;&#x27;cell)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
211&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate&nbsp;(second&nbsp;tree)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
212&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
213&nbsp;&nbsp;(defn&nbsp;generate-neighbours-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
214&nbsp;&nbsp;&nbsp;&nbsp;&quot;Generate&nbsp;code&nbsp;for&nbsp;a&nbsp;condition&nbsp;which&nbsp;refers&nbsp;to&nbsp;neighbours.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
215&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
216&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:NEIGHBOURS-CONDITION)
</span><br/>
<span class="partial" title="7 out of 13 forms covered">
217&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 7 forms covered">
218&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NUMBER&nbsp;(read-string&nbsp;(second&nbsp;(second&nbsp;tree)))
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
219&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUANTIFIER&nbsp;(generate-neighbours-condition&nbsp;tree&nbsp;(first&nbsp;(second&nbsp;(second&nbsp;tree))))
</span><br/>
<span class="covered" title="14 out of 14 forms covered">
220&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUALIFIER&nbsp;(cons&nbsp;(generate&nbsp;(second&nbsp;tree))&nbsp;(rest&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;2))))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
221&nbsp;&nbsp;&nbsp;&nbsp;([tree&nbsp;quantifier-type]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
222&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[quantifier&nbsp;(second&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
223&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pc&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;4))]
</span><br/>
<span class="partial" title="3 out of 9 forms covered">
224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;quantifier-type
</span><br/>
<span class="covered" title="12 out of 12 forms covered">
225&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NUMBER&nbsp;(generate-neighbours-condition&nbsp;&#x27;=&nbsp;(read-string&nbsp;(second&nbsp;(second&nbsp;quantifier)))&nbsp;pc&nbsp;1)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
226&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SOME&nbsp;(generate-neighbours-condition&nbsp;&#x27;&gt;&nbsp;0&nbsp;pc&nbsp;1)
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
227&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:MORE&nbsp;(let&nbsp;[value&nbsp;(generate&nbsp;(nth&nbsp;quantifier&nbsp;3))]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
228&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-neighbours-condition&nbsp;&#x27;&gt;&nbsp;value&nbsp;pc&nbsp;1))
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
229&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:LESS&nbsp;(let&nbsp;[value&nbsp;(generate&nbsp;(nth&nbsp;quantifier&nbsp;3))]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
230&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-neighbours-condition&nbsp;&#x27;&lt;&nbsp;value&nbsp;pc&nbsp;1)))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
231&nbsp;&nbsp;&nbsp;&nbsp;([comp1&nbsp;quantity&nbsp;property-condition&nbsp;distance]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;comp1
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
233&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;count
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
234&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;remove&nbsp;&#x27;false?
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
235&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;map&nbsp;(list&nbsp;&#x27;fn&nbsp;[&#x27;cell]&nbsp;property-condition)
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
236&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(list&nbsp;&#x27;mw-engine.utils&#x2F;get-neighbours&nbsp;&#x27;world&nbsp;&#x27;cell&nbsp;distance))))&nbsp;quantity))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
237&nbsp;&nbsp;&nbsp;&nbsp;([comp1&nbsp;quantity&nbsp;property-condition]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
238&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-neighbours-condition&nbsp;comp1&nbsp;quantity&nbsp;property-condition&nbsp;1)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
239&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
240&nbsp;&nbsp;(defn&nbsp;generate-within-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
241&nbsp;&nbsp;&nbsp;&nbsp;&quot;Generate&nbsp;code&nbsp;for&nbsp;a&nbsp;condition&nbsp;which&nbsp;refers&nbsp;to&nbsp;neighbours&nbsp;within&nbsp;a&nbsp;specified&nbsp;distance.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
242&nbsp;&nbsp;&nbsp;&nbsp;NOTE&nbsp;THAT&nbsp;there&#x27;s&nbsp;clearly&nbsp;masses&nbsp;of&nbsp;commonality&nbsp;between&nbsp;this&nbsp;and
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
243&nbsp;&nbsp;&nbsp;&nbsp;`generate-neighbours-condition`,&nbsp;and&nbsp;that&nbsp;some&nbsp;refactoring&nbsp;is&nbsp;almost&nbsp;certainly
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
244&nbsp;&nbsp;&nbsp;&nbsp;desirable.&nbsp;It&nbsp;may&nbsp;be&nbsp;that&nbsp;it&#x27;s&nbsp;better&nbsp;to&nbsp;simplify&nbsp;a&nbsp;`NEIGHBOURS-CONDITION`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
245&nbsp;&nbsp;&nbsp;&nbsp;into&nbsp;a&nbsp;`WITHIN-CONDITION`&nbsp;in&nbsp;the&nbsp;simplification&nbsp;stage.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
246&nbsp;&nbsp;&nbsp;&nbsp;([tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
247&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:WITHIN-CONDITION)
</span><br/>
<span class="partial" title="7 out of 13 forms covered">
248&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
249&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUANTIFIER&nbsp;(generate-within-condition&nbsp;tree&nbsp;(first&nbsp;(second&nbsp;(second&nbsp;tree))))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUALIFIER&nbsp;(TODO&nbsp;&quot;qualified&nbsp;within...&nbsp;help!&quot;)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
251&nbsp;&nbsp;&nbsp;&nbsp;([tree&nbsp;quantifier-type]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
252&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[quantifier&nbsp;(second&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
253&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;distance&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;4))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
254&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pc&nbsp;(generate&nbsp;(nth&nbsp;tree&nbsp;6))]
</span><br/>
<span class="partial" title="3 out of 9 forms covered">
255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;quantifier-type
</span><br/>
<span class="covered" title="12 out of 12 forms covered">
256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NUMBER&nbsp;(generate-neighbours-condition&nbsp;&#x27;=&nbsp;(read-string&nbsp;(second&nbsp;(second&nbsp;quantifier)))&nbsp;pc&nbsp;distance)
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
257&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SOME&nbsp;(generate-neighbours-condition&nbsp;&#x27;&gt;&nbsp;0&nbsp;pc&nbsp;distance)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
258&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:MORE&nbsp;(let&nbsp;[value&nbsp;(generate&nbsp;(nth&nbsp;quantifier&nbsp;3))]
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
259&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-neighbours-condition&nbsp;&#x27;&gt;&nbsp;value&nbsp;pc&nbsp;distance))
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
260&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:LESS&nbsp;(let&nbsp;[value&nbsp;(generate&nbsp;(nth&nbsp;quantifier&nbsp;3))]
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
261&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(generate-neighbours-condition&nbsp;&#x27;&lt;&nbsp;value&nbsp;pc&nbsp;distance))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
262&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
263&nbsp;&nbsp;(defn-&nbsp;generate-disjunct-expression
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
264&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
265&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:DISJUNCT-EXPRESSION)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
266&nbsp;&nbsp;&nbsp;&nbsp;(try
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
267&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(set&nbsp;(map&nbsp;generate&nbsp;(rest&nbsp;tree)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
268&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(catch&nbsp;Exception&nbsp;x
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
269&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
270&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ex-info
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
271&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Failed&nbsp;to&nbsp;compile&nbsp;:DISJUNCT-EXPRESSION&quot;
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
272&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:tree&nbsp;tree}
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
273&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
274&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
275&nbsp;&nbsp;;;;&nbsp;Flow&nbsp;rules.&nbsp;A&nbsp;flow&nbsp;rule&nbsp;DOES&nbsp;NOT&nbsp;return&nbsp;a&nbsp;modified&nbsp;world;&nbsp;instead,&nbsp;it&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
276&nbsp;&nbsp;;;;&nbsp;returns&nbsp;a&nbsp;PLAN&nbsp;to&nbsp;modify&nbsp;the&nbsp;world,&nbsp;in&nbsp;the&nbsp;form&nbsp;of&nbsp;a&nbsp;sequence&nbsp;of&nbsp;`flows`.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
277&nbsp;&nbsp;;;;&nbsp;It&nbsp;is&nbsp;only&nbsp;when&nbsp;the&nbsp;plan&nbsp;is&nbsp;executed&nbsp;that&nbsp;the&nbsp;world&nbsp;is&nbsp;modified.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
278&nbsp;&nbsp;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
279&nbsp;&nbsp;;;;&nbsp;so&nbsp;we&#x27;re&nbsp;looking&nbsp;at&nbsp;something&nbsp;like
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
280&nbsp;&nbsp;;;;&nbsp;(fn&nbsp;[cell&nbsp;world])
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
281&nbsp;&nbsp;;;;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(=&nbsp;(:state&nbsp;cell)&nbsp;(or&nbsp;(:house&nbsp;cell)&nbsp;:house))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
282&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
283&nbsp;&nbsp;(defn&nbsp;generate-flow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
284&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
285&nbsp;&nbsp;&nbsp;&nbsp;(assert-type&nbsp;tree&nbsp;:FLOW-RULE))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
286&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
287&nbsp;&nbsp;;;;&nbsp;Top&nbsp;level;&nbsp;only&nbsp;function&nbsp;anything&nbsp;outside&nbsp;this&nbsp;file&nbsp;(except&nbsp;tests)&nbsp;should&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
288&nbsp;&nbsp;;;;&nbsp;really&nbsp;call.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
289&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
290&nbsp;&nbsp;(defn&nbsp;generate
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
291&nbsp;&nbsp;&nbsp;&nbsp;&quot;Generate&nbsp;code&nbsp;for&nbsp;this&nbsp;(fragment&nbsp;of&nbsp;a)&nbsp;parse&nbsp;tree&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
292&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
293&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
294&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(coll?&nbsp;tree)
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
295&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
296&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ACTIONS&nbsp;(generate-multiple-actions&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
297&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:COMPARATIVE&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
298&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:COMPARATIVE-QUALIFIER&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
299&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITION&nbsp;(generate-condition&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITIONS&nbsp;(generate-conditions&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
301&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONJUNCT-CONDITION&nbsp;(generate-conjunct-condition&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
302&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DISJUNCT-CONDITION&nbsp;(generate-disjunct-condition&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
303&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DISJUNCT-EXPRESSION&nbsp;(generate-disjunct-expression&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
304&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DISJUNCT-VALUE&nbsp;(generate-disjunct-value&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
305&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:EQUIVALENCE&nbsp;&#x27;=
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
306&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:EXPRESSION&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
307&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:FLOW-RULE&nbsp;(generate-flow&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
308&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:LESS&nbsp;&#x27;&lt;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
309&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:MORE&nbsp;&#x27;&gt;
</span><br/>
<span class="partial" title="8 out of 16 forms covered">
310&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NEGATED-QUALIFIER&nbsp;(case&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
311&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;&#x27;not=
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
312&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&gt;&nbsp;&#x27;&lt;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
313&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;&nbsp;&#x27;&gt;)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
314&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NEIGHBOURS-CONDITION&nbsp;(generate-neighbours-condition&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
315&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NUMERIC-EXPRESSION&nbsp;(generate-numeric-expression&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
316&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:NUMBER&nbsp;(read-string&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
317&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:OPERATOR&nbsp;(symbol&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
318&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROBABLE-ACTION&nbsp;(generate-probable-action&nbsp;tree)
</span><br/>
<span class="not-covered" title="0 out of 8 forms covered">
319&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY&nbsp;(list&nbsp;(generate&nbsp;(second&nbsp;tree))&nbsp;&#x27;cell)&nbsp;;;&nbsp;dubious&nbsp;-&nbsp;may&nbsp;not&nbsp;be&nbsp;right
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
320&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY-CONDITION&nbsp;(generate-property-condition&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
321&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:QUALIFIER&nbsp;(generate-qualifier&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
322&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:RULE&nbsp;(generate-rule&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
323&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SIMPLE-ACTION&nbsp;(generate-simple-action&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
324&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SYMBOL&nbsp;(keyword&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
325&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:VALUE&nbsp;(generate&nbsp;(second&nbsp;tree))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
326&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:WITHIN-CONDITION&nbsp;(generate-within-condition&nbsp;tree)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
327&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;generate&nbsp;tree))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
328&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tree))
</span><br/>
</body>
</html>

View file

@ -1,281 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/simplify.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;Simplify&nbsp;a&nbsp;parse&nbsp;tree.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;mw-parser.simplify)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
004&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;;;&nbsp;mw-parser:&nbsp;a&nbsp;rule&nbsp;parser&nbsp;for&nbsp;MicroWorld.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;;;;;&nbsp;Copyright&nbsp;(C)&nbsp;2014&nbsp;Simon&nbsp;Brooke
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
027&nbsp;&nbsp;
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
028&nbsp;&nbsp;(declare&nbsp;simplify)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
029&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
030&nbsp;&nbsp;(defn&nbsp;simplify-second-of-two
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&quot;There&nbsp;are&nbsp;a&nbsp;number&nbsp;of&nbsp;possible&nbsp;simplifications&nbsp;such&nbsp;that&nbsp;if&nbsp;the&nbsp;`tree`&nbsp;has
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;only&nbsp;two&nbsp;elements,&nbsp;the&nbsp;second&nbsp;is&nbsp;semantically&nbsp;sufficient.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="partial" title="10 out of 11 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(=&nbsp;(count&nbsp;tree)&nbsp;2)&nbsp;(simplify&nbsp;(nth&nbsp;tree&nbsp;1))&nbsp;tree))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
035&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
036&nbsp;&nbsp;(defn&nbsp;simplify-chained-list
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&quot;Some&nbsp;parse&nbsp;trees&nbsp;take&nbsp;the&nbsp;form&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`[:X&nbsp;[:Y&nbsp;1]&nbsp;:NOISE&nbsp;:NOISE&nbsp;[:X&nbsp;[:Y&nbsp;2]&nbsp;:NOISE&nbsp;:NOISE&nbsp;[:X&nbsp;[:Y&nbsp;3]]]]`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;where&nbsp;what&#x27;s&nbsp;wanted&nbsp;is&nbsp;`[:X&nbsp;[:Y&nbsp;1]&nbsp;[:Y&nbsp;2]&nbsp;[:Y&nbsp;2]]`&nbsp;--&nbsp;:DISJUNCT-VALUE&nbsp;is&nbsp;a&nbsp;case
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in&nbsp;point.&nbsp;This&nbsp;takes&nbsp;such&nbsp;a&nbsp;parse&nbsp;`tree`,&nbsp;where&nbsp;`branch-tag`&nbsp;is&nbsp;the&nbsp;tag&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;enclosing&nbsp;form&nbsp;and&nbsp;`leaf-tag`&nbsp;is&nbsp;the&nbsp;tag&nbsp;of&nbsp;the&nbsp;form&nbsp;to&nbsp;be&nbsp;collected,&nbsp;and&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;returns&nbsp;the&nbsp;desired&nbsp;form.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;[tree&nbsp;branch-tag&nbsp;leaf-tag]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;(cons
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(first&nbsp;tree)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reverse
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(loop&nbsp;[chain&nbsp;(rest&nbsp;tree)&nbsp;v&nbsp;&#x27;()]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[car&nbsp;(first&nbsp;chain)]
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cond&nbsp;(empty?&nbsp;chain)&nbsp;v
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(coll?&nbsp;car)&nbsp;(let&nbsp;[caar&nbsp;(first&nbsp;car)]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;branch-tag&nbsp;caar)&nbsp;(recur&nbsp;car&nbsp;v)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;leaf-tag&nbsp;caar)&nbsp;(recur
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(rest&nbsp;chain)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;(simplify&nbsp;car)&nbsp;v))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else&nbsp;(recur&nbsp;(rest&nbsp;chain)&nbsp;v)))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else&nbsp;(recur&nbsp;(rest&nbsp;chain)&nbsp;v)))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
058&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
059&nbsp;&nbsp;(defn&nbsp;simplify
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&quot;Simplify&#x2F;canonicalise&nbsp;this&nbsp;`tree`.&nbsp;Opportunistically&nbsp;replace&nbsp;complex&nbsp;fragments&nbsp;with
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;semantically&nbsp;identical&nbsp;simpler&nbsp;fragments&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(coll?&nbsp;tree)
</span><br/>
<span class="partial" title="10 out of 11 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(case&nbsp;(first&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ACTION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ACTIONS&nbsp;(cons&nbsp;(first&nbsp;tree)&nbsp;(simplify&nbsp;(rest&nbsp;tree)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:AND&nbsp;nil
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CHANCE-IN&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:COMPARATIVE&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:CONDITIONS&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:DISJUNCT-EXPRESSION&nbsp;(simplify-chained-list&nbsp;tree&nbsp;:DISJUNCT-VALUE&nbsp;:VALUE)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
074&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:EXPRESSION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:IN&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
077&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:PROPERTY-CONDITION-OR-EXPRESSION&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:OR&nbsp;nil
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:SPACE&nbsp;nil
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:THEN&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:VALUE&nbsp;(simplify-second-of-two&nbsp;tree)
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove&nbsp;nil?&nbsp;(map&nbsp;simplify&nbsp;tree)))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tree))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
084&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
085&nbsp;&nbsp;(defn&nbsp;simplify-determiner-condition
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;[tree]
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;(apply&nbsp;vector
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;:DETERMINER-CONDITION
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(simplify-second-of-two&nbsp;(second&nbsp;tree))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(rest&nbsp;(rest&nbsp;tree))))))
</span><br/>
</body>
</html>

View file

@ -1,200 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/utils.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;^{:doc&nbsp;&quot;Utilities&nbsp;used&nbsp;in&nbsp;more&nbsp;than&nbsp;one&nbsp;namespace&nbsp;within&nbsp;the&nbsp;parser.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:author&nbsp;&quot;Simon&nbsp;Brooke&quot;}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;mw-parser.utils)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
004&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;;;&nbsp;mw-parser:&nbsp;a&nbsp;rule&nbsp;parser&nbsp;for&nbsp;MicroWorld.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software;&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and&#x2F;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;;;&nbsp;modify&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;;;&nbsp;as&nbsp;published&nbsp;by&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation;&nbsp;either&nbsp;version&nbsp;2
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;;;&nbsp;of&nbsp;the&nbsp;License,&nbsp;or&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;;;&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;;;&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;;;&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;;;&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;;;;;&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;;;;;&nbsp;along&nbsp;with&nbsp;this&nbsp;program;&nbsp;if&nbsp;not,&nbsp;write&nbsp;to&nbsp;the&nbsp;Free&nbsp;Software
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;;;;;&nbsp;Foundation,&nbsp;Inc.,&nbsp;51&nbsp;Franklin&nbsp;Street,&nbsp;Fifth&nbsp;Floor,&nbsp;Boston,&nbsp;MA&nbsp;&nbsp;02110-1301,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;;;;;&nbsp;USA.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;;;;;&nbsp;Copyright&nbsp;(C)&nbsp;2014&nbsp;Simon&nbsp;Brooke
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;;;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
027&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
028&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
029&nbsp;&nbsp;(defn&nbsp;suitable-fragment?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;`true`&nbsp;if&nbsp;`tree-fragment`&nbsp;appears&nbsp;to&nbsp;be&nbsp;a&nbsp;tree&nbsp;fragment&nbsp;of&nbsp;the&nbsp;expected&nbsp;`type`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;[tree-fragment&nbsp;type]
</span><br/>
<span class="partial" title="11 out of 12 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(coll?&nbsp;tree-fragment)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;type)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;(first&nbsp;tree-fragment)&nbsp;type)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
035&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
036&nbsp;&nbsp;(defn&nbsp;rule?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;true&nbsp;if&nbsp;the&nbsp;argument&nbsp;appears&nbsp;to&nbsp;be&nbsp;a&nbsp;parsed&nbsp;rule&nbsp;tree,&nbsp;else&nbsp;false.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;[maybe-rule]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;(suitable-fragment?&nbsp;maybe-rule&nbsp;:RULE))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
040&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
041&nbsp;&nbsp;(defn&nbsp;TODO
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;&quot;Marker&nbsp;to&nbsp;indicate&nbsp;I&#x27;m&nbsp;not&nbsp;yet&nbsp;finished!&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;[message]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;message)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
045&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
046&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
047&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
048&nbsp;&nbsp;(defn&nbsp;assert-type
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&quot;If&nbsp;`tree-fragment`&nbsp;is&nbsp;not&nbsp;a&nbsp;tree&nbsp;fragment&nbsp;of&nbsp;the&nbsp;expected&nbsp;`type`,&nbsp;throw&nbsp;an&nbsp;exception.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;[tree-fragment&nbsp;type]
</span><br/>
<span class="partial" title="12 out of 16 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;(assert&nbsp;(suitable-fragment?&nbsp;tree-fragment&nbsp;type)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;(format&nbsp;&quot;Expected&nbsp;a&nbsp;%s&nbsp;fragment&quot;&nbsp;type)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
053&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
054&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
055&nbsp;&nbsp;(defn&nbsp;search-tree
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;the&nbsp;first&nbsp;element&nbsp;of&nbsp;this&nbsp;tree&nbsp;which&nbsp;has&nbsp;this&nbsp;tag&nbsp;in&nbsp;a&nbsp;depth-first,&nbsp;left-to-right&nbsp;search&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;[tree&nbsp;tag]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;(cond&nbsp;
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;(first&nbsp;tree)&nbsp;tag)&nbsp;tree
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else&nbsp;(first
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove&nbsp;nil?
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(search-tree&nbsp;%&nbsp;tag)
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter&nbsp;coll?&nbsp;(rest&nbsp;tree)))))))
</span><br/>
</body>
</html>

View file

@ -1,551 +0,0 @@
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 15px;
}
pre, code {
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
font-size: 9pt;
margin: 15px 0;
}
h1 {
font-weight: normal;
font-size: 29px;
margin: 10px 0 2px 0;
padding: 0;
}
h2 {
font-weight: normal;
font-size: 25px;
}
h5.license {
margin: 9px 0 22px 0;
color: #555;
font-weight: normal;
font-size: 12px;
font-style: italic;
}
.document h1, .namespace-index h1 {
font-size: 32px;
margin-top: 12px;
}
#header, #content, .sidebar {
position: fixed;
}
#header {
top: 0;
left: 0;
right: 0;
height: 22px;
color: #f5f5f5;
padding: 5px 7px;
}
#content {
top: 32px;
right: 0;
bottom: 0;
overflow: auto;
background: #fff;
color: #333;
padding: 0 18px;
}
.sidebar {
position: fixed;
top: 32px;
bottom: 0;
overflow: auto;
}
.sidebar.primary {
background: #e2e2e2;
border-right: solid 1px #cccccc;
left: 0;
width: 250px;
}
.sidebar.secondary {
background: #f2f2f2;
border-right: solid 1px #d7d7d7;
left: 251px;
width: 200px;
}
#content.namespace-index, #content.document {
left: 251px;
}
#content.namespace-docs {
left: 452px;
}
#content.document {
padding-bottom: 10%;
}
#header {
background: #3f3f3f;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
z-index: 100;
}
#header h1 {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: lighter;
text-shadow: -1px -1px 0px #333;
}
#header h1 .project-version {
font-weight: normal;
}
.project-version {
padding-left: 0.15em;
}
#header a, .sidebar a {
display: block;
text-decoration: none;
}
#header a {
color: #f5f5f5;
}
.sidebar a {
color: #333;
}
#header h2 {
float: right;
font-size: 9pt;
font-weight: normal;
margin: 4px 3px;
padding: 0;
color: #bbb;
}
#header h2 a {
display: inline;
}
.sidebar h3 {
margin: 0;
padding: 10px 13px 0 13px;
font-size: 19px;
font-weight: lighter;
}
.sidebar h3 a {
color: #444;
}
.sidebar h3.no-link {
color: #636363;
}
.sidebar ul {
padding: 7px 0 6px 0;
margin: 0;
}
.sidebar ul.index-link {
padding-bottom: 4px;
}
.sidebar li {
display: block;
vertical-align: middle;
}
.sidebar li a, .sidebar li .no-link {
border-left: 3px solid transparent;
padding: 0 10px;
white-space: nowrap;
}
.sidebar li .no-link {
display: block;
color: #777;
font-style: italic;
}
.sidebar li .inner {
display: inline-block;
padding-top: 7px;
height: 24px;
}
.sidebar li a, .sidebar li .tree {
height: 31px;
}
.depth-1 .inner { padding-left: 2px; }
.depth-2 .inner { padding-left: 6px; }
.depth-3 .inner { padding-left: 20px; }
.depth-4 .inner { padding-left: 34px; }
.depth-5 .inner { padding-left: 48px; }
.depth-6 .inner { padding-left: 62px; }
.sidebar li .tree {
display: block;
float: left;
position: relative;
top: -10px;
margin: 0 4px 0 0;
padding: 0;
}
.sidebar li.depth-1 .tree {
display: none;
}
.sidebar li .tree .top, .sidebar li .tree .bottom {
display: block;
margin: 0;
padding: 0;
width: 7px;
}
.sidebar li .tree .top {
border-left: 1px solid #aaa;
border-bottom: 1px solid #aaa;
height: 19px;
}
.sidebar li .tree .bottom {
height: 22px;
}
.sidebar li.branch .tree .bottom {
border-left: 1px solid #aaa;
}
.sidebar.primary li.current a {
border-left: 3px solid #a33;
color: #a33;
}
.sidebar.secondary li.current a {
border-left: 3px solid #33a;
color: #33a;
}
.namespace-index h2 {
margin: 30px 0 0 0;
}
.namespace-index h3 {
font-size: 16px;
font-weight: bold;
margin-bottom: 0;
}
.namespace-index .topics {
padding-left: 30px;
margin: 11px 0 0 0;
}
.namespace-index .topics li {
padding: 5px 0;
}
.namespace-docs h3 {
font-size: 18px;
font-weight: bold;
}
.public h3 {
margin: 0;
float: left;
}
.usage {
clear: both;
}
.public {
margin: 0;
border-top: 1px solid #e0e0e0;
padding-top: 14px;
padding-bottom: 6px;
}
.public:last-child {
margin-bottom: 20%;
}
.members .public:last-child {
margin-bottom: 0;
}
.members {
margin: 15px 0;
}
.members h4 {
color: #555;
font-weight: normal;
font-variant: small-caps;
margin: 0 0 5px 0;
}
.members .inner {
padding-top: 5px;
padding-left: 12px;
margin-top: 2px;
margin-left: 7px;
border-left: 1px solid #bbb;
}
#content .members .inner h3 {
font-size: 12pt;
}
.members .public {
border-top: none;
margin-top: 0;
padding-top: 6px;
padding-bottom: 0;
}
.members .public:first-child {
padding-top: 0;
}
h4.type,
h4.dynamic,
h4.added,
h4.deprecated {
float: left;
margin: 3px 10px 15px 0;
font-size: 15px;
font-weight: bold;
font-variant: small-caps;
}
.public h4.type,
.public h4.dynamic,
.public h4.added,
.public h4.deprecated {
font-size: 13px;
font-weight: bold;
margin: 3px 0 0 10px;
}
.members h4.type,
.members h4.added,
.members h4.deprecated {
margin-top: 1px;
}
h4.type {
color: #717171;
}
h4.dynamic {
color: #9933aa;
}
h4.added {
color: #508820;
}
h4.deprecated {
color: #880000;
}
.namespace {
margin-bottom: 30px;
}
.namespace:last-child {
margin-bottom: 10%;
}
.index {
padding: 0;
font-size: 80%;
margin: 15px 0;
line-height: 16px;
}
.index * {
display: inline;
}
.index p {
padding-right: 3px;
}
.index li {
padding-right: 5px;
}
.index ul {
padding-left: 0;
}
.type-sig {
clear: both;
color: #088;
}
.type-sig pre {
padding-top: 10px;
margin: 0;
}
.usage code {
display: block;
color: #008;
margin: 2px 0;
}
.usage code:first-child {
padding-top: 10px;
}
p {
margin: 15px 0;
}
.public p:first-child, .public pre.plaintext {
margin-top: 12px;
}
.doc {
margin: 0 0 26px 0;
clear: both;
}
.public .doc {
margin: 0;
}
.namespace-index .doc {
margin-bottom: 20px;
}
.namespace-index .namespace .doc {
margin-bottom: 10px;
}
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
line-height: 22px;
}
.markdown li {
padding: 2px 0;
}
.markdown h2 {
font-weight: normal;
font-size: 25px;
margin: 30px 0 10px 0;
}
.markdown h3 {
font-weight: normal;
font-size: 20px;
margin: 30px 0 0 0;
}
.markdown h4 {
font-size: 15px;
margin: 22px 0 -4px 0;
}
.doc, .public, .namespace .index {
max-width: 680px;
overflow-x: visible;
}
.markdown pre > code {
display: block;
padding: 10px;
}
.markdown pre > code, .src-link a {
border: 1px solid #e4e4e4;
border-radius: 2px;
}
.markdown code:not(.hljs), .src-link a {
background: #f6f6f6;
}
pre.deps {
display: inline-block;
margin: 0 10px;
border: 1px solid #e4e4e4;
border-radius: 2px;
padding: 10px;
background-color: #f6f6f6;
}
.markdown hr {
border-style: solid;
border-top: none;
color: #ccc;
}
.doc ul, .doc ol {
padding-left: 30px;
}
.doc table {
border-collapse: collapse;
margin: 0 10px;
}
.doc table td, .doc table th {
border: 1px solid #dddddd;
padding: 4px 6px;
}
.doc table th {
background: #f2f2f2;
}
.doc dl {
margin: 0 10px 20px 10px;
}
.doc dl dt {
font-weight: bold;
margin: 0;
padding: 3px 0;
border-bottom: 1px solid #ddd;
}
.doc dl dd {
padding: 5px 0;
margin: 0 0 5px 10px;
}
.doc abbr {
border-bottom: 1px dotted #333;
font-variant: none;
cursor: help;
}
.src-link {
margin-bottom: 15px;
}
.src-link a {
font-size: 70%;
padding: 1px 4px;
text-decoration: none;
color: #5555bb;
}

View file

@ -1,97 +0,0 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}
.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}
.hljs-string,
.hljs-doctag {
color: #d14;
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}
.hljs-subst {
font-weight: normal;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}
.hljs-regexp,
.hljs-link {
color: #009926;
}
.hljs-symbol,
.hljs-bullet {
color: #990073;
}
.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}
.hljs-meta {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

View file

@ -1,11 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>Mw-parser 0.2.0-SNAPSHOT</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 current"><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="namespace-index" id="content"><h1><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></h1><h5 class="license">Released under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License v2</a></h5><div class="doc"><p>Parser for production rules for MicroWorld engine.</p></div><h2>Installation</h2><p>To install, add the following dependency to your project or build file:</p><pre class="deps">[mw-parser "0.2.0-SNAPSHOT"]</pre><h2>Topics</h2><ul class="topics"><li><a href="intro.html">Introduction to mw-parser</a></li></ul><h2>Namespaces</h2><div class="namespace"><h3><a href="mw-parser.bulk.html">mw-parser.bulk</a></h3><div class="doc"><div class="markdown"><p>parse multiple rules from a stream, possibly a file.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.bulk.html#var-comment.3F">comment?</a> </li><li> <a href="mw-parser.bulk.html#var-compile-file">compile-file</a> </li><li> <a href="mw-parser.bulk.html#var-compile-string">compile-string</a> </li><li> <a href="mw-parser.bulk.html#var-parse-file">parse-file</a> </li><li> <a href="mw-parser.bulk.html#var-parse-string">parse-string</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.core.html">mw-parser.core</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.core.html#var-bad-parse-error">bad-parse-error</a> </li><li> <a href="mw-parser.core.html#var-compile-rule">compile-rule</a> </li><li> <a href="mw-parser.core.html#var-gen-token-value">gen-token-value</a> </li><li> <a href="mw-parser.core.html#var-parse-comparator-neighbours-condition">parse-comparator-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-disjunct-value">parse-disjunct-value</a> </li><li> <a href="mw-parser.core.html#var-parse-member-condition">parse-member-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-neighbours-condition">parse-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-numeric-value">parse-numeric-value</a> </li><li> <a href="mw-parser.core.html#var-parse-property-int">parse-property-int</a> </li><li> <a href="mw-parser.core.html#var-parse-property-value">parse-property-value</a> </li><li> <a href="mw-parser.core.html#var-parse-rule">parse-rule</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-condition">parse-simple-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-neighbours-condition">parse-simple-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-value">parse-simple-value</a> </li><li> <a href="mw-parser.core.html#var-parse-some-neighbours-condition">parse-some-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-token-value">parse-token-value</a> </li><li> <a href="mw-parser.core.html#var-parse-value">parse-value</a> </li><li> <a href="mw-parser.core.html#var-re-number">re-number</a> </li><li> <a href="mw-parser.core.html#var-reserved-properties-error">reserved-properties-error</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.declarative.html">mw-parser.declarative</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.declarative.html#var-build-parser">build-parser</a> </li><li> <a href="mw-parser.declarative.html#var-common-grammar">common-grammar</a> </li><li> <a href="mw-parser.declarative.html#var-compile-rule">compile-rule</a> </li><li> <a href="mw-parser.declarative.html#var-keywords-en">keywords-en</a> </li><li> <a href="mw-parser.declarative.html#var-keywords-for-locale">keywords-for-locale</a> </li><li> <a href="mw-parser.declarative.html#var-parse-rule">parse-rule</a> </li><li> <a href="mw-parser.declarative.html#var-rule-grammar">rule-grammar</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.errors.html">mw-parser.errors</a></h3><div class="doc"><div class="markdown"><p>Display parse errors in a format which makes it easy for the user to see where the error occurred.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.errors.html#var-bad-parse-error">bad-parse-error</a> </li><li> <a href="mw-parser.errors.html#var-reserved-properties-error">reserved-properties-error</a> </li><li> <a href="mw-parser.errors.html#var-throw-parse-exception">throw-parse-exception</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.flow.html">mw-parser.flow</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses flow rules.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.flow.html#var-flow-grammar">flow-grammar</a> </li><li> <a href="mw-parser.flow.html#var-parse-flow">parse-flow</a> </li><li> <a href="mw-parser.flow.html#var-simplify-flow">simplify-flow</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.generate.html">mw-parser.generate</a></h3><div class="doc"><div class="markdown"><p>Generate Clojure source from simplified parse trees.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.generate.html#var-generate">generate</a> </li><li> <a href="mw-parser.generate.html#var-generate-action">generate-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-condition">generate-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-conditions">generate-conditions</a> </li><li> <a href="mw-parser.generate.html#var-generate-conjunct-condition">generate-conjunct-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-condition">generate-disjunct-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-property-condition">generate-disjunct-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-value">generate-disjunct-value</a> </li><li> <a href="mw-parser.generate.html#var-generate-flow">generate-flow</a> </li><li> <a href="mw-parser.generate.html#var-generate-multiple-actions">generate-multiple-actions</a> </li><li> <a href="mw-parser.generate.html#var-generate-neighbours-condition">generate-neighbours-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-numeric-expression">generate-numeric-expression</a> </li><li> <a href="mw-parser.generate.html#var-generate-probable-action">generate-probable-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-property-condition">generate-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-qualifier">generate-qualifier</a> </li><li> <a href="mw-parser.generate.html#var-generate-ranged-property-condition">generate-ranged-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-rule">generate-rule</a> </li><li> <a href="mw-parser.generate.html#var-generate-simple-action">generate-simple-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-within-condition">generate-within-condition</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.simplify.html">mw-parser.simplify</a></h3><div class="doc"><div class="markdown"><p>Simplify a parse tree.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.simplify.html#var-simplify">simplify</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-chained-list">simplify-chained-list</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-determiner-condition">simplify-determiner-condition</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-second-of-two">simplify-second-of-two</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.utils.html">mw-parser.utils</a></h3><div class="doc"><div class="markdown"><p>Utilities used in more than one namespace within the parser.</p>
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.utils.html#var-assert-type">assert-type</a> </li><li> <a href="mw-parser.utils.html#var-rule.3F">rule?</a> </li><li> <a href="mw-parser.utils.html#var-search-tree">search-tree</a> </li><li> <a href="mw-parser.utils.html#var-suitable-fragment.3F">suitable-fragment?</a> </li><li> <a href="mw-parser.utils.html#var-TODO">TODO</a> </li></ul></div></div></div></body></html>

View file

@ -1,5 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>Introduction to mw-parser</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-mw-parser" id="introduction-to-mw-parser"></a>Introduction to mw-parser</h1>
<p>TODO: write <a href="http://jacobian.org/writing/great-documentation/what-to-write/">great documentation</a></p>
</div></div></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,112 +0,0 @@
function visibleInParent(element) {
var position = $(element).position().top
return position > -50 && position < ($(element).offsetParent().height() - 50)
}
function hasFragment(link, fragment) {
return $(link).attr("href").indexOf("#" + fragment) != -1
}
function findLinkByFragment(elements, fragment) {
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
}
function scrollToCurrentVarLink(elements) {
var elements = $(elements);
var parent = elements.offsetParent();
if (elements.length == 0) return;
var top = elements.first().position().top;
var bottom = elements.last().position().top + elements.last().height();
if (top >= 0 && bottom <= parent.height()) return;
if (top < 0) {
parent.scrollTop(parent.scrollTop() + top);
}
else if (bottom > parent.height()) {
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
}
}
function setCurrentVarLink() {
$('.secondary a').parent().removeClass('current')
$('.anchor').
filter(function(index) { return visibleInParent(this) }).
each(function(index, element) {
findLinkByFragment(".secondary a", element.id).
parent().
addClass('current')
});
scrollToCurrentVarLink('.secondary .current');
}
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
function scrollPositionId(element) {
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
return 'scroll::' + $(element).attr('id') + '::' + directory
}
function storeScrollPosition(element) {
if (!hasStorage) return;
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
}
function recallScrollPosition(element) {
if (!hasStorage) return;
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
}
function persistScrollPosition(element) {
recallScrollPosition(element)
$(element).scroll(function() { storeScrollPosition(element) })
}
function sidebarContentWidth(element) {
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
return Math.max.apply(Math, widths)
}
function calculateSize(width, snap, margin, minimum) {
if (width == 0) {
return 0
}
else {
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
}
}
function resizeSidebars() {
var primaryWidth = sidebarContentWidth('.primary')
var secondaryWidth = 0
if ($('.secondary').length != 0) {
secondaryWidth = sidebarContentWidth('.secondary')
}
// snap to grid
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
$('.primary').css('width', primaryWidth)
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
if (secondaryWidth > 0) {
$('#content').css('left', primaryWidth + secondaryWidth + 2)
}
else {
$('#content').css('left', primaryWidth + 1)
}
}
$(window).ready(resizeSidebars)
$(window).ready(setCurrentVarLink)
$(window).ready(function() { persistScrollPosition('.primary')})
$(window).ready(function() {
$('#content').scroll(setCurrentVarLink)
$(window).resize(setCurrentVarLink)
})

View file

@ -1,9 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.bulk documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch current"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.bulk.html#var-comment.3F"><div class="inner"><span>comment?</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-compile-file"><div class="inner"><span>compile-file</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-compile-string"><div class="inner"><span>compile-string</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-parse-file"><div class="inner"><span>parse-file</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-parse-string"><div class="inner"><span>parse-string</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.bulk</h1><div class="doc"><div class="markdown"><p>parse multiple rules from a stream, possibly a file.</p>
</div></div><div class="public anchor" id="var-comment.3F"><h3>comment?</h3><div class="usage"><code>(comment? line)</code></div><div class="doc"><div class="markdown"><p>Is this <code>line</code> a comment?</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L34">view source</a></div></div><div class="public anchor" id="var-compile-file"><h3>compile-file</h3><div class="usage"><code>(compile-file filename)</code></div><div class="doc"><div class="markdown"><p>Compile each non-comment line of the file indicated by this <code>filename</code> into an executable anonymous function, and return the sequence of such functions.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L58">view source</a></div></div><div class="public anchor" id="var-compile-string"><h3>compile-string</h3><div class="usage"><code>(compile-string string)</code></div><div class="doc"><div class="markdown"><p>Compile each non-comment line of this <code>string</code> into an executable anonymous function, and return the sequence of such functions.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L52">view source</a></div></div><div class="public anchor" id="var-parse-file"><h3>parse-file</h3><div class="usage"><code>(parse-file filename)</code></div><div class="doc"><div class="markdown"><p>Parse rules from successive lines in the file loaded from this <code>filename</code>. Return a list of S-expressions.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L46">view source</a></div></div><div class="public anchor" id="var-parse-string"><h3>parse-string</h3><div class="usage"><code>(parse-string string)</code></div><div class="doc"><div class="markdown"><p>Parse rules from successive lines in this <code>string</code>, assumed to have multiple lines delimited by the new-line character. Return a list of S-expressions.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L39">view source</a></div></div></div></body></html>

File diff suppressed because one or more lines are too long

View file

@ -1,14 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.declarative documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.declarative.html#var-build-parser"><div class="inner"><span>build-parser</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-common-grammar"><div class="inner"><span>common-grammar</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-compile-rule"><div class="inner"><span>compile-rule</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-keywords-en"><div class="inner"><span>keywords-en</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-keywords-for-locale"><div class="inner"><span>keywords-for-locale</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-parse-rule"><div class="inner"><span>parse-rule</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-rule-grammar"><div class="inner"><span>rule-grammar</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.declarative</h1><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
</div></div><div class="public anchor" id="var-build-parser"><h3>build-parser</h3><h4 class="type">macro</h4><div class="usage"><code>(build-parser g)</code></div><div class="doc"><div class="markdown"><p>Compose this grammar fragment <code>g</code> with the common grammar fragments to make a complete grammar, and return a parser for that complete grammar.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L125">view source</a></div></div><div class="public anchor" id="var-common-grammar"><h3>common-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Grammar rules used both in the rule grammar and in the flow grammar</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L47">view source</a></div></div><div class="public anchor" id="var-compile-rule"><h3>compile-rule</h3><div class="usage"><code>(compile-rule rule-text return-tuple?)</code><code>(compile-rule rule-text)</code></div><div class="doc"><div class="markdown"><p>Parse this <code>rule-text</code>, a string conforming to the grammar of MicroWorld rules, into Clojure source, and then compile it into an anonymous function object, getting round the problem of binding mw-engine.utils in the compiling environment. If <code>return-tuple?</code> is present and true, return a list comprising the anonymous function compiled, and the function from which it was compiled.</p>
<p>Throws an exception if parsing fails.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L135">view source</a></div></div><div class="public anchor" id="var-keywords-en"><h3>keywords-en</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>English language keyword literals used in rules - both in production rules (this namespace) and in flow rules (see mw-parser.flow).</p>
<p>Its a long term aim that the rule language should be easy to internationalise; this isnt a full solution but its a step towards a solution.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L77">view source</a></div></div><div class="public anchor" id="var-keywords-for-locale"><h3>keywords-for-locale</h3><div class="usage"><code>(keywords-for-locale)</code><code>(keywords-for-locale _locale)</code></div><div class="doc"><div class="markdown"><p>For now, just return <code>keywords-en</code>; plan is to have resource files of keywords for different languages in a resource directory, but that isnt done yet. Its probably not going to work easily for languages that use non-latin alphabets, anyway.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L115">view source</a></div></div><div class="public anchor" id="var-parse-rule"><h3>parse-rule</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Parse the argument, assumed to be a string in the correct syntax, and return a parse tree.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L131">view source</a></div></div><div class="public anchor" id="var-rule-grammar"><h3>rule-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Basic rule language grammar.</p>
<p>in order to simplify translation into other natural languages, all TOKENS within the parser should be unambiguou.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L36">view source</a></div></div></div></body></html>

View file

@ -1,7 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.errors documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.errors.html#var-bad-parse-error"><div class="inner"><span>bad-parse-error</span></div></a></li><li class="depth-1"><a href="mw-parser.errors.html#var-reserved-properties-error"><div class="inner"><span>reserved-properties-error</span></div></a></li><li class="depth-1"><a href="mw-parser.errors.html#var-throw-parse-exception"><div class="inner"><span>throw-parse-exception</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.errors</h1><div class="doc"><div class="markdown"><p>Display parse errors in a format which makes it easy for the user to see where the error occurred.</p>
</div></div><div class="public anchor" id="var-bad-parse-error"><h3>bad-parse-error</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L33">view source</a></div></div><div class="public anchor" id="var-reserved-properties-error"><h3>reserved-properties-error</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L27">view source</a></div></div><div class="public anchor" id="var-throw-parse-exception"><h3>throw-parse-exception</h3><div class="usage"><code>(throw-parse-exception parser-error)</code></div><div class="doc"><div class="markdown"><p>Construct a helpful error message from this <code>parser-error</code>, and throw an exception with that message.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L51">view source</a></div></div></div></body></html>

View file

@ -1,11 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.flow documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.flow.html#var-flow-grammar"><div class="inner"><span>flow-grammar</span></div></a></li><li class="depth-1"><a href="mw-parser.flow.html#var-parse-flow"><div class="inner"><span>parse-flow</span></div></a></li><li class="depth-1"><a href="mw-parser.flow.html#var-simplify-flow"><div class="inner"><span>simplify-flow</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.flow</h1><div class="doc"><div class="markdown"><p>A very simple parser which parses flow rules.</p>
</div></div><div class="public anchor" id="var-flow-grammar"><h3>flow-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Grammar for flow rules.</p>
<p>My initial conception of this would be that production rules (if-then rules) and flow rules (flow-from-to rules) would be entirely separate, presented to the parser as separate text files, and parsed and compiled by different chains of functions.</p>
<p>This appears not to be necessary. Flow rules are easy to parse with the same parser as production rules a lot of the grammar is intentionally common and the rules are easily discriminated at the compilation (generate) stage.</p>
<p>The basic rule I want to be able to compile at this stage is the mutual aid rule:</p>
<p><code>flow 1 food from house having food &gt; 1 to house with least food within 2</code></p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L8">view source</a></div></div><div class="public anchor" id="var-parse-flow"><h3>parse-flow</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Parse the argument, assumed to be a string in the correct syntax, and return a parse tree.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L39">view source</a></div></div><div class="public anchor" id="var-simplify-flow"><h3>simplify-flow</h3><div class="usage"><code>(simplify-flow tree)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L43">view source</a></div></div></div></body></html>

File diff suppressed because one or more lines are too long

View file

@ -1,8 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.simplify documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify"><div class="inner"><span>simplify</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-chained-list"><div class="inner"><span>simplify-chained-list</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-determiner-condition"><div class="inner"><span>simplify-determiner-condition</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-second-of-two"><div class="inner"><span>simplify-second-of-two</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.simplify</h1><div class="doc"><div class="markdown"><p>Simplify a parse tree.</p>
</div></div><div class="public anchor" id="var-simplify"><h3>simplify</h3><div class="usage"><code>(simplify tree)</code></div><div class="doc"><div class="markdown"><p>Simplify/canonicalise this <code>tree</code>. Opportunistically replace complex fragments with semantically identical simpler fragments</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/simplify.clj#L59">view source</a></div></div><div class="public anchor" id="var-simplify-chained-list"><h3>simplify-chained-list</h3><div class="usage"><code>(simplify-chained-list tree branch-tag leaf-tag)</code></div><div class="doc"><div class="markdown"><p>Some parse trees take the form <code>[:X [:Y 1] :NOISE :NOISE [:X [:Y 2] :NOISE :NOISE [:X [:Y 3]]]]</code> where whats wanted is <code>[:X [:Y 1] [:Y 2] [:Y 2]]</code> :DISJUNCT-VALUE is a case in point. This takes such a parse <code>tree</code>, where <code>branch-tag</code> is the tag of the enclosing form and <code>leaf-tag</code> is the tag of the form to be collected, and returns the desired form.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/simplify.clj#L36">view source</a></div></div><div class="public anchor" id="var-simplify-determiner-condition"><h3>simplify-determiner-condition</h3><div class="usage"><code>(simplify-determiner-condition tree)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/simplify.clj#L85">view source</a></div></div><div class="public anchor" id="var-simplify-second-of-two"><h3>simplify-second-of-two</h3><div class="usage"><code>(simplify-second-of-two tree)</code></div><div class="doc"><div class="markdown"><p>There are a number of possible simplifications such that if the <code>tree</code> has only two elements, the second is semantically sufficient.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/simplify.clj#L30">view source</a></div></div></div></body></html>

View file

@ -1,9 +0,0 @@
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>mw-parser.utils documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2 current"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.utils.html#var-assert-type"><div class="inner"><span>assert-type</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-rule.3F"><div class="inner"><span>rule?</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-search-tree"><div class="inner"><span>search-tree</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-suitable-fragment.3F"><div class="inner"><span>suitable-fragment?</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-TODO"><div class="inner"><span>TODO</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.utils</h1><div class="doc"><div class="markdown"><p>Utilities used in more than one namespace within the parser.</p>
</div></div><div class="public anchor" id="var-assert-type"><h3>assert-type</h3><div class="usage"><code>(assert-type tree-fragment type)</code></div><div class="doc"><div class="markdown"><p>If <code>tree-fragment</code> is not a tree fragment of the expected <code>type</code>, throw an exception.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L48">view source</a></div></div><div class="public anchor" id="var-rule.3F"><h3>rule?</h3><div class="usage"><code>(rule? maybe-rule)</code></div><div class="doc"><div class="markdown"><p>Return true if the argument appears to be a parsed rule tree, else false.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L36">view source</a></div></div><div class="public anchor" id="var-search-tree"><h3>search-tree</h3><div class="usage"><code>(search-tree tree tag)</code></div><div class="doc"><div class="markdown"><p>Return the first element of this tree which has this tag in a depth-first, left-to-right search</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L55">view source</a></div></div><div class="public anchor" id="var-suitable-fragment.3F"><h3>suitable-fragment?</h3><div class="usage"><code>(suitable-fragment? tree-fragment type)</code></div><div class="doc"><div class="markdown"><p>Return <code>true</code> if <code>tree-fragment</code> appears to be a tree fragment of the expected <code>type</code>.</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L29">view source</a></div></div><div class="public anchor" id="var-TODO"><h3>TODO</h3><div class="usage"><code>(TODO message)</code></div><div class="doc"><div class="markdown"><p>Marker to indicate Im not yet finished!</p>
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L41">view source</a></div></div></div></body></html>

File diff suppressed because one or more lines are too long

View file

@ -1,23 +1,18 @@
(defproject mw-parser "0.3.0-SNAPSHOT"
:cloverage {:output "docs/cloverage"}
:codox {:metadata {:doc "**TODO**: write docs"
:doc/format :markdown}
:output-path "docs/codox"
:source-uri "https://github.com/simon-brooke/mw-parser/blob/master/{filepath}#L{line}"}
:dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/tools.trace "0.7.11"]
[instaparse "1.4.12"]
[mw-engine "0.3.0-SNAPSHOT"]
[trptr/java-wrapper "0.2.3"]]
(defproject mw-parser "0.1.5-SNAPSHOT"
:description "Parser for production rules for MicroWorld engine"
:license {:name "GNU General Public License v2"
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
:manifest {"build-signature-version" "unset"
:url "http://www.journeyman.cc/microworld"
:manifest {
"build-signature-version" "unset"
"build-signature-user" "unset"
"build-signature-email" "unset"
"build-signature-timestamp" "unset"
"Implementation-Version" "unset"}
:plugins [[lein-marginalia "0.7.1"]
[lein-cloverage "1.2.2"]
[lein-codox "0.10.8"]]
:url "http://www.journeyman.cc/microworld")
"Implementation-Version" "unset"
}
:license {:name "GNU General Public License v2"
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
:plugins [[lein-marginalia "0.7.1"]]
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/tools.trace "0.7.9"]
[instaparse "1.4.1"]
[mw-engine "0.1.5-SNAPSHOT"]
])

View file

@ -6,19 +6,19 @@
## Vegetation rules
;; rules which populate the world with plants
;; Occasionally, passing birds plant tree seeds into grassland
;; Occasionally, passing birds plant tree seeds into grassland
if state is grassland then 1 chance in 10 state should be heath
;; heath below the treeline grows gradually into forest
if state is heath and altitude is less than 120 then state should be scrub
if state is heath and altitude is less than 120 then state should be scrub
if state is scrub then 1 chance in 5 state should be forest
;; Forest on fertile land grows to climax
if state is forest and fertility is more than 5 and altitude is less than 70 then state should be climax
if state is forest and fertility is more than 5 and altitude is less than 70 then state should be climax
;; Climax forest occasionally catches fire (e.g. lightning strikes)
if state is climax then 1 chance in 500 state should be fire
@ -40,7 +40,7 @@ if state is waste then state should be grassland
## Potential blockers
;; Forest increases soil fertility.
;; Forest increases soil fertility.
if state is in forest or climax then fertility should be fertility + 1

39
src/mw_parser/bulk.clj Normal file
View file

@ -0,0 +1,39 @@
;; parse multiple rules from a stream, possibly a file - although the real
;; objective is to parse rules out of a block of text from a textarea
(ns mw-parser.bulk
(:use mw-parser.core
mw-engine.utils
clojure.java.io
[clojure.string :only [split trim]])
(:import (java.io BufferedReader StringReader)))
(defn comment?
"Is this `line` a comment?"
[line]
(or (empty? (trim line)) (member? (first line) '(nil \# \;))))
(defn parse-string
"Parse rules from successive lines in this `string`, assumed to have multiple
lines delimited by the new-line character. Return a list of S-expressions."
[string]
;; TODO: tried to do this using with-open, but couldn't make it work.
(map parse-rule (remove comment? (split string #"\n"))))
(defn parse-file
"Parse rules from successive lines in the file loaded from this `filename`.
Return a list of S-expressions."
[filename]
(parse-string (slurp filename)))
(defn compile-string
"Compile each non-comment line of this `string` into an executable anonymous
function, and return the sequence of such functions."
[string]
(map #(compile-rule % true) (remove comment? (split string #"\n"))))
(defn compile-file
"Compile each non-comment line of the file indicated by this `filename` into
an executable anonymous function, and return the sequence of such functions."
[filename]
(compile-string (slurp filename)))

424
src/mw_parser/core.clj Normal file
View file

@ -0,0 +1,424 @@
;; A very simple parser which parses production rules of the following forms:
;;
;; * "if altitude is less than 100 and state is forest then state should be climax and deer should be 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"
;; * "if state is grassland and 4 neighbours have state equal to water then state should be village"
;; * "if state is forest and fertility is between 55 and 75 then state should be climax"
;; * "if 6 neighbours have state equal to water then state should be village"
;; * "if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"
;; * "if state is forest or state is climax and some neighbours have state equal to fire then 3 in 5 chance that state should be fire"
;; * "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"
;; *
;;
;; it generates rules in the form expected by `mw-engine.core`, q.v.
;;
;; It is, as I say, very simple; it generates a complete rule, or it fails completely, returning nil.
;; Very occasionally it generates a wrong rule - one which is not a correct translation of the rule
;; semantics - but that is buggy behaviour, which I'll try to fix over the next few weeks, not a
;; design fault.
;;
;; More significantly it does not generate useful error messages on failure.
;;
;; This is the parser that is actually used currently; but see also insta.clj,
;; which is potentially a much better parser but does not quite work yet.
(ns mw-parser.core
(:use mw-engine.utils
[clojure.string :only [split trim triml]])
(:gen-class)
)
(declare parse-conditions)
(declare parse-not-condition)
(declare parse-simple-condition)
;; a regular expression which matches string representation of numbers
(def re-number #"^[0-9.]*$")
;; error thrown when an attempt is made to set a reserved property
(def reserved-properties-error
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
;; error thrown when a rule cannot be parsed
(def bad-parse-error "I did not understand '%s'")
(defn- keyword-or-numeric
"If this token appears to represent an explicit number, return that number;
otherwise, make a keyword of it and return that."
[token]
(cond
(re-matches re-number token) (read-string token)
(keyword? token) token
true (keyword token)))
;; Generally all functions in this file with names beginning 'parse-' take a
;; sequence of tokens (and in some cases other optional arguments) and return a
;; vector comprising
;;
;; 1. A code fragment parsed from the front of the sequence of tokens, and
;; 2. the remaining tokens which were not consumed in constructing that fragment.
;;
;; In every case if the function cannot parse the desired construct from the
;; front of the sequence of tokens it returns nil.
(defn parse-numeric-value
"Parse a number."
[[value & remainder]]
(if (and value (re-matches re-number value)) [(read-string value) remainder]))
(defn parse-property-int
"Parse a token assumed to be the name of a property of the current cell,
whose value is assumed to be an integer."
[[value & remainder]]
(if value [(list 'get-int 'cell (keyword value)) remainder]))
(defn parse-property-value
"Parse a token assumed to be the name of a property of the current cell."
[[value & remainder]]
(if value [(list (keyword value) 'cell) remainder]))
(defn parse-token-value
"Parse a token assumed to be a simple token value."
[[value & remainder]]
(if value [(keyword value) remainder]))
(defn parse-simple-value
"Parse a value from the first of these `tokens`. If `expect-int` is true, return
an integer or something which will evaluate to an integer."
([tokens expect-int]
(or
(parse-numeric-value tokens)
(cond expect-int
(parse-property-int tokens)
true (parse-token-value tokens))))
([tokens]
(parse-simple-value tokens false)))
(defn gen-token-value
"Parse a single value from this single token and return just the generated
code, not a pair."
[token expect-int]
(first (parse-simple-value (list token) expect-int)))
(defn parse-disjunct-value
"Parse a list of values from among these `tokens`. If `expect-int` is true, return
integers or things which will evaluate to integers."
[[OR token & tokens] expect-int]
(cond (member? OR '("or" "in"))
(let [value (first (parse-simple-value (list token) expect-int))
seek-others (= (first tokens) "or")]
(cond seek-others
(let [[others remainder] (parse-disjunct-value tokens expect-int)]
[(cons value others) remainder])
true
[(list value) tokens]))))
(defn parse-value
"Parse a value from among these `tokens`. If `expect-int` is true, return
an integer or something which will evaluate to an integer."
([tokens expect-int]
(or
(parse-disjunct-value tokens expect-int)
(parse-simple-value tokens expect-int)))
([tokens]
(parse-value tokens false)))
(defn parse-member-condition
"Parses a condition of the form '[property] in [value] or [value]...'"
[[property IS IN & rest]]
(if (and (member? IS '("is" "are")) (= IN "in"))
(let [[l remainder] (parse-disjunct-value (cons "in" rest) false)]
[(list 'member? (list (keyword property) 'cell) (list 'quote l)) remainder])))
(defn- parse-less-condition
"Parse '[property] less than [value]'."
[[property IS LESS THAN & rest]]
(cond (and (member? IS '("is" "are")) (member? LESS '("less" "fewer")) (= THAN "than"))
(let [[value remainder] (parse-value rest true)]
[(list '< (list 'get-int 'cell (keyword property)) value) remainder])))
(defn- parse-more-condition
"Parse '[property] more than [value]'."
[[property IS MORE THAN & rest]]
(cond (and (member? IS '("is" "are")) (member? MORE '("more" "greater")) (= THAN "than"))
(let [[value remainder] (parse-value rest true)]
[(list '> (list 'get-int 'cell (keyword property)) value) remainder])))
(defn- parse-between-condition
[[p IS BETWEEN v1 AND v2 & rest]]
(cond (and (member? IS '("is" "are")) (= BETWEEN "between") (= AND "and") (not (nil? v2)))
(let [property (first (parse-simple-value (list p) true))
value1 (first (parse-simple-value (list v1) true))
value2 (first (parse-simple-value (list v2) true))]
[(list 'or
(list '< value1 property value2)
(list '> value1 property value2)) rest])))
(defn- parse-is-condition
"Parse clauses of the form 'x is y', 'x is in y or z...',
'x is between y and z', 'x is more than y' or 'x is less than y'.
It is necessary to disambiguate whether value is a numeric or keyword."
[[property IS value & rest]]
(cond
(member? IS '("is" "are"))
(let [tokens (cons property (cons value rest))]
(cond
(re-matches re-number value) [(list '= (list 'get-int 'cell (keyword property)) (read-string value)) rest]
value [(list '= (list (keyword property) 'cell) (keyword value)) rest]))))
(defn- parse-not-condition
"Parse the negation of a simple condition."
[[property IS NOT & rest]]
(cond (and (member? IS '("is" "are")) (= NOT "not"))
(let [partial (parse-simple-condition (cons property (cons "is" rest)))]
(cond partial
(let [[condition remainder] partial]
[(list 'not condition) remainder])))))
(defn- gen-neighbours-condition
([comp1 quantity property value remainder comp2 distance]
[(list comp1
(list 'count
(list 'get-neighbours-with-property-value 'world
'(cell :x) '(cell :y) distance
(keyword property) (keyword-or-numeric value) comp2))
quantity)
remainder])
([comp1 quantity property value remainder comp2]
(gen-neighbours-condition comp1 quantity property value remainder comp2 1)))
(defn parse-comparator-neighbours-condition
"Parse conditions of the form '...more than 6 neighbours are [condition]'"
[[MORE THAN n NEIGHBOURS WITHIN distance have-or-are & rest]]
(let [quantity (first (parse-numeric-value (list n)))
comparator (cond (= MORE "more") '>
(member? MORE '("fewer" "less")) '<)]
(cond
(not= WITHIN "within")
(parse-comparator-neighbours-condition
(flatten
;; two tokens were mis-parsed as 'within distance' that weren't
;; actually 'within' and a distance. Splice in 'within 1' and try
;; again.
(list MORE THAN n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
(and quantity
comparator
(= THAN "than")
(= NEIGHBOURS "neighbours"))
(cond
(= have-or-are "are")
(let [[value & remainder] rest
dist (gen-token-value distance true)]
(gen-neighbours-condition comparator quantity :state value remainder = dist))
(= have-or-are "have")
(let [[property comp1 comp2 value & remainder] rest
dist (gen-token-value distance true)]
(cond (and (= comp1 "equal") (= comp2 "to"))
(gen-neighbours-condition comparator quantity property
value remainder = dist)
(and (= comp1 "more") (= comp2 "than"))
(gen-neighbours-condition comparator quantity property
value remainder > dist)
(and (= comp1 "less") (= comp2 "than"))
(gen-neighbours-condition comparator quantity property
value remainder < dist)
))))))
(defn parse-some-neighbours-condition
[[SOME NEIGHBOURS & rest]]
(cond
(and (= SOME "some") (= NEIGHBOURS "neighbours"))
(parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest))))
(defn parse-simple-neighbours-condition
"Parse conditions of the form '...6 neighbours are [condition]'"
[[n NEIGHBOURS WITHIN distance have-or-are & rest]]
(let [quantity (first (parse-numeric-value (list n)))]
(cond
(and quantity (= NEIGHBOURS "neighbours"))
(cond
(not= WITHIN "within")
(parse-simple-neighbours-condition
(flatten
;; two tokens were mis-parsed as 'within distance' that weren't
;; actually 'within' and a distance. Splice in 'within 1' and try
;; again.
(list n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
(= have-or-are "are")
(let [[value & remainder] rest
dist (gen-token-value distance true)]
(gen-neighbours-condition '= quantity :state value remainder = dist))
(= have-or-are "have")
(let [[property comp1 comp2 value & remainder] rest
dist (gen-token-value distance true)]
(cond (and (= comp1 "equal") (= comp2 "to"))
(gen-neighbours-condition '= quantity property value remainder =
dist)
(and (= comp1 "more") (= comp2 "than"))
(gen-neighbours-condition '= quantity property value remainder >
dist)
(and (= comp1 "less") (= comp2 "than"))
(gen-neighbours-condition '= quantity property value remainder <
dist)
))))))
(defn parse-neighbours-condition
"Parse conditions referring to neighbours"
[tokens]
(or
(parse-simple-neighbours-condition tokens)
(parse-comparator-neighbours-condition tokens)
(parse-some-neighbours-condition tokens)
))
(defn parse-simple-condition
"Parse conditions of the form '[property] [comparison] [value]'."
[tokens]
(or
(parse-neighbours-condition tokens)
(parse-member-condition tokens)
(parse-not-condition tokens)
(parse-less-condition tokens)
(parse-more-condition tokens)
(parse-between-condition tokens)
(parse-is-condition tokens)))
(defn- parse-disjunction-condition
"Parse '... or [condition]' from `tokens`, where `left` is the already parsed first disjunct."
[left tokens]
(let [partial (parse-conditions tokens)]
(if partial
(let [[right remainder] partial]
[(list 'or left right) remainder]))))
(defn- parse-conjunction-condition
"Parse '... and [condition]' from `tokens`, where `left` is the already parsed first conjunct."
[left tokens]
(let [partial (parse-conditions tokens)]
(if partial
(let [[right remainder] partial]
[(list 'and left right) remainder]))))
(defn- parse-conditions
"Parse conditions from `tokens`, where conditions may be linked by either 'and' or 'or'."
[tokens]
(let [partial (parse-simple-condition tokens)]
(if partial
(let [[left [next & remainder]] partial]
(cond
(= next "and") (parse-conjunction-condition left remainder)
(= next "or") (parse-disjunction-condition left remainder)
true partial)))))
(defn- parse-left-hand-side
"Parse the left hand side ('if...') of a production rule."
[[IF & tokens]]
(if
(= IF "if")
(parse-conditions tokens)))
(defn- parse-arithmetic-action
"Parse actions of the form '[property] should be [property] [arithmetic-operator] [value]',
e.g. 'fertility should be fertility + 1', or 'deer should be deer - wolves'."
[previous [prop1 SHOULD BE prop2 operator value & rest]]
(cond
(member? prop1 '("x" "y"))
(throw
(Exception. reserved-properties-error))
(and (= SHOULD "should")
(= BE "be")
(member? operator '("+" "-" "*" "/")))
[(list 'merge (or previous 'cell)
{(keyword prop1) (list 'int
(list (symbol operator) (list 'get-int 'cell (keyword prop2))
(cond
(re-matches re-number value) (read-string value)
true (list 'get-int 'cell (keyword value)))))}) rest]))
(defn- parse-set-action
"Parse actions of the form '[property] should be [value].'"
[previous [property SHOULD BE value & rest]]
(cond
(member? property '("x" "y"))
(throw
(Exception. reserved-properties-error))
(and (= SHOULD "should") (= BE "be"))
[(list 'merge (or previous 'cell)
{(keyword property) (cond (re-matches re-number value) (read-string value) true (keyword value))}) rest]))
(defn- parse-simple-action [previous tokens]
(or (parse-arithmetic-action previous tokens)
(parse-set-action previous tokens)))
(defn- parse-actions
"Parse actions from tokens."
[previous tokens]
(let [[left remainder] (parse-simple-action previous tokens)]
(cond left
(cond (= (first remainder) "and")
(parse-actions left (rest remainder))
true (list left)))))
(defn- parse-probability
"Parse a probability of an action from this collection of tokens"
[previous [n CHANCE IN m & tokens]]
(cond
(and (= CHANCE "chance")(= IN "in"))
(let [[action remainder] (parse-actions previous tokens)]
(cond action
[(list 'cond
(list '<
(list 'rand
(first (parse-simple-value (list m) true)))
(first (parse-simple-value (list n) true)))
action) remainder]))))
(defn- parse-right-hand-side
"Parse the right hand side ('then...') of a production rule."
[[THEN & tokens]]
(if (= THEN "then")
(or
(parse-probability nil tokens)
(parse-actions nil tokens))))
(defn parse-rule
"Parse a complete rule from this `line`, expected to be either a string or a
sequence of string tokens. Return the rule in the form of an S-expression.
Throws an exception if parsing fails."
[line]
(cond
(string? line)
(let [rule (parse-rule (split (triml line) #"\s+"))]
(cond rule rule
true (throw (Exception. (format bad-parse-error line)))))
true
(let [[left remainder] (parse-left-hand-side line)
[right junk] (parse-right-hand-side remainder)]
(cond
;; there should be a valide left hand side and a valid right hand side
;; there shouldn't be anything left over (junk should be empty)
(and left right (empty? junk))
(list 'fn ['cell 'world] (list 'if left right))))))
(defn compile-rule
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
into Clojure source, and then compile it into an anonymous
function object, getting round the problem of binding mw-engine.utils in
the compiling environment. If `return-tuple?` is present and true, return
a list comprising the anonymous function compiled, and the function from
which it was compiled.
Throws an exception if parsing fails."
([rule-text return-tuple?]
(do
(use 'mw-engine.utils)
(let [afn (eval (parse-rule rule-text))]
(cond
(and afn return-tuple?)(list afn (trim rule-text))
true afn))))
([rule-text]
(compile-rule rule-text false)))

View file

@ -1,211 +1,368 @@
(ns ^{:doc "A very simple parser which parses production rules."
:author "Simon Brooke"}
mw-parser.declarative
(:require [clojure.string :refer [join split-lines]]
[instaparse.core :refer [failure? get-failure parser]]
[instaparse.failure :refer [pprint-failure]]
[mw-parser.flow :refer [flow-grammar]]
[mw-parser.generate :refer [generate]]
[mw-parser.simplify :refer [simplify]]
[taoensso.timbre :as l]
[trptr.java-wrapper.locale :refer [get-default]])
(:import [java.util Locale]))
(ns mw-parser.declarative
(:use mw-engine.utils
[clojure.string :only [split trim triml]])
(:require [instaparse.core :as insta]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; mw-parser: a rule parser for MicroWorld.
;;;;
;;;; This program is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License
;;;; as published by the Free Software Foundation; either version 2
;;;; of the License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;;;; USA.
;;;;
;;;; Copyright (C) 2014 Simon Brooke
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; TODO: Either, when I first wrote this parser, I didn't adequately read the
;;; Instaparse documentation, or Instaparse has advanced considerably since
;;; then. Reading the documentation now, I could probably rewrite this to
;;; eliminate the simplify step altogether, and that would be well worth doing.
;; error thrown when an attempt is made to set a reserved property
(def reserved-properties-error
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
;; error thrown when a rule cannot be parsed. Slots are for
;; (1) rule text
;; (2) cursor showing where in the rule text the error occurred
;; (3) the reason for the error
(def bad-parse-error "I did not understand:\n'%s'\n%s\n%s")
(def ruleset-grammar
"Experimental: parse a whole file in one go."
;; TODO: bug here. We're double-counting (some) blank lines
(join "\n" ["LINES := (LINE)+;"
"LINE := RULE <CR> | FLOW-RULE <CR> | COMMENT <CR> | <CR> ;"
"CR := #'[ \\t]*[\\r\\n][- \\t]*';"
"COMMENT := #'[;\\#]+[^\\r\\n]*' | #'/\\*.*\\*/'"]))
(def rule-grammar
"Basic rule language grammar.
in order to simplify translation into other natural languages, all
TOKENS within the parser should be unambiguous."
(join "\n" ["RULE := IF <SPACE> CONDITIONS <SPACE> <THEN> <SPACE> ACTIONS;"
"ACTIONS := ACTION | (ACTION <SPACE> <AND> <SPACE> ACTION)+"
"ACTION := SIMPLE-ACTION | PROBABLE-ACTION;"
"PROBABLE-ACTION := VALUE <SPACE> <CHANCE-IN> <SPACE> VALUE <SPACE> SIMPLE-ACTION;"
"SIMPLE-ACTION := SYMBOL <SPACE> BECOMES <SPACE> EXPRESSION;"]))
(def grammar
;; in order to simplify translation into other natural languages, all
;; TOKENS within the parser should be unambiguous
"RULE := IF SPACE CONDITIONS SPACE THEN SPACE ACTIONS;
CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | PROPERTY-CONDITION | NEIGHBOURS-CONDITION ;
DISJUNCT-CONDITION := CONDITION SPACE OR SPACE CONDITIONS;
CONJUNCT-CONDITION := CONDITION SPACE AND SPACE CONDITIONS;
CONDITION := NEIGHBOURS-CONDITION | PROPERTY-CONDITION;
WITHIN-CONDITION := NEIGHBOURS-CONDITION SPACE WITHIN SPACE NUMERIC-EXPRESSION;
NEIGHBOURS-CONDITION := WITHIN-CONDITION | QUANTIFIER SPACE NEIGHBOURS SPACE IS SPACE PROPERTY-CONDITION | QUANTIFIER SPACE NEIGHBOURS IS EXPRESSION | QUALIFIER SPACE NEIGHBOURS-CONDITION;
PROPERTY-CONDITION := PROPERTY SPACE QUALIFIER SPACE EXPRESSION | VALUE;
EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE;
SIMPLE-EXPRESSION := QUALIFIER SPACE EXPRESSION | VALUE;
DISJUNCT-EXPRESSION := IN SPACE DISJUNCT-VALUE;
RANGE-EXPRESSION := BETWEEN SPACE NUMERIC-EXPRESSION SPACE AND SPACE NUMERIC-EXPRESSION;
NUMERIC-EXPRESSION := VALUE | VALUE SPACE OPERATOR SPACE NUMERIC-EXPRESSION;
NEGATED-QUALIFIER := QUALIFIER SPACE NOT | NOT SPACE QUALIFIER;
COMPARATIVE-QUALIFIER := IS SPACE COMPARATIVE SPACE THAN;
QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS SPACE QUALIFIER;
QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE SPACE THAN SPACE NUMBER;
EQUIVALENCE := IS SPACE EQUAL | EQUAL | IS ;
COMPARATIVE := MORE | LESS;
DISJUNCT-VALUE := VALUE | VALUE SPACE OR SPACE DISJUNCT-VALUE;
IF := 'if';
THEN := 'then';
THAN := 'than';
OR := 'or';
NOT := 'not';
AND := 'and';
SOME := 'some';
NONE := 'no';
ALL := 'all'
BETWEEN := 'between';
WITHIN := 'within';
IN := 'in';
MORE := 'more' | 'greater';
LESS := 'less' | 'fewer';
OPERATOR := '+' | '-' | '*' | '/';
NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';
PROPERTY := SYMBOL;
VALUE := SYMBOL | NUMBER;
EQUAL := 'equal to';
IS := 'is' | 'are' | 'have' | 'has';
NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';
SYMBOL := #'[a-z]+';
ACTIONS := ACTION | ACTION SPACE 'and' SPACE ACTIONS
ACTION := SIMPLE-ACTION | PROBABLE-ACTION;
PROBABLE-ACTION := VALUE SPACE 'chance in' SPACE VALUE SPACE SIMPLE-ACTION;
SIMPLE-ACTION := SYMBOL SPACE BECOMES SPACE EXPRESSION
BECOMES := 'should be'
SPACE := #' *'"
)
(def common-grammar
"Grammar rules used both in the rule grammar and in the flow grammar"
(join "\n" ["COMPARATIVE := MORE | LESS;"
"COMPARATIVE-QUALIFIER := IS <SPACE> COMPARATIVE <SPACE> THAN | COMPARATIVE <SPACE> THAN;"
"CONDITION := WITHIN-CONDITION | NEIGHBOURS-CONDITION | PROPERTY-CONDITION;"
"CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | CONDITION ;"
"CONJUNCT-CONDITION := CONDITION <SPACE> <AND> <SPACE> CONDITIONS;"
"DISJUNCT-CONDITION := CONDITION <SPACE> <OR> <SPACE> CONDITIONS;"
"DISJUNCT-EXPRESSION := <IN> <SPACE> DISJUNCT-VALUE;"
"DISJUNCT-VALUE := (VALUE <SPACE> <OR> <SPACE>)* VALUE;"
"EQUIVALENCE := IS <SPACE> EQUAL | EQUAL | IS ;"
"EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE;"
"NEGATED-QUALIFIER := QUALIFIER <SPACE> NOT | NOT <SPACE> QUALIFIER;"
"NEIGHBOURS-CONDITION := QUANTIFIER <SPACE> NEIGHBOURS <SPACE> IS <SPACE> PROPERTY-CONDITION | QUALIFIER <SPACE> NEIGHBOURS-CONDITION;"
"NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';"
"NUMERIC-EXPRESSION := VALUE | VALUE <SPACE> OPERATOR <SPACE> NUMERIC-EXPRESSION;"
"OPERATOR := '+' | '-' | '*' | '/';"
"PROPERTY := SYMBOL;"
"PROPERTY-CONDITION := PROPERTY <SPACE> QUALIFIER <SPACE> EXPRESSION | VALUE;"
"PROPERTY-CONDITION-OR-EXPRESSION := PROPERTY-CONDITION | EXPRESSION;"
"QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS <SPACE> QUALIFIER;"
"QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE <SPACE> THAN <SPACE> NUMBER;"
"RANGE-EXPRESSION := BETWEEN <SPACE> NUMERIC-EXPRESSION <SPACE> AND <SPACE> NUMERIC-EXPRESSION;"
"SIMPLE-EXPRESSION := QUALIFIER <SPACE> EXPRESSION | VALUE;"
"SPACE := #'[ \\t]+';"
"VALUE := SYMBOL | NUMBER;"
"VALUE := SYMBOL | NUMBER;"
"WITHIN-CONDITION := QUANTIFIER <SPACE> NEIGHBOURS <SPACE> WITHIN <SPACE> NUMBER <SPACE> IS <SPACE> PROPERTY-CONDITION-OR-EXPRESSION;"]))
(defn TODO
"Marker to indicate I'm not yet finished!"
[message]
message)
(def keywords-en
"English language keyword literals used in rules - both in production
rules (this namespace) and in flow rules (see mw-parser.flow).
It's a long term aim that the rule language should be easy to
internationalise; this isn't a full solution but it's a step towards
a solution."
(join "\n" ["ALL := 'all'"
"AND := 'and';"
"BECOMES := 'should be' | 'becomes';"
"BETWEEN := 'between';"
"CHANCE-IN := 'chance in';"
"EACH := 'each' | 'every' | 'all';"
"EQUAL := 'equal to';"
"FIRST := 'first';"
"FLOW := 'flow' | 'move';"
"FROM := 'from';"
"IF := 'if';"
"IN := 'in';"
"IS := 'is' | 'are' | 'have' | 'has';"
"LEAST := 'least';"
"LESS := 'less' | 'fewer';"
"MORE := 'more' | 'greater';"
"MOST := 'most';"
"NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';"
"NONE := 'no';"
"NOT := 'not';"
"OR := 'or';"
"SOME := 'some';"
;; SYMBOL is in the per-language file so that languages that use
;; (e.g.) Cyrillic characters can change the definition.
"SYMBOL := #'[a-z]+';"
"THAN := 'than';"
"THEN := 'then';"
"TO := 'to';"
"WITH := 'with' | 'where' | 'having';"
"WITHIN := 'within';"]))
(defn keywords-for-locale
"For now, just return `keywords-en`; plan is to have resource files of
keywords for different languages in a resource directory, but that isn't
done yet. It's probably not going to work easily for languages that use
non-latin alphabets, anyway."
([]
(keywords-for-locale (get-default)))
([^Locale _locale]
keywords-en))
(declare generate simplify)
(def ^:private raw-parser
(parser (join "\n" [ruleset-grammar rule-grammar flow-grammar common-grammar (keywords-for-locale)])))
(defn parse
(defn suitable-fragment?
"Return `true` if `tree-fragment` appears to be a tree fragment of the expected `type`."
[tree-fragment type]
(and (coll? tree-fragment)
(= (first tree-fragment) type)))
(defn assert-type
"If `tree-fragment` is not a tree fragment of the expected `type`, throw an exception."
[tree-fragment type]
(assert (suitable-fragment? tree-fragment type)
(throw (Exception. (format "Expected a %s fragment" type)))))
(defn generate-rule
"From this `tree`, assumed to be a syntactically correct rule specification,
generate and return the appropriate rule as a function of two arguments."
[tree]
(assert-type tree :RULE)
(list 'fn ['cell 'world] (list 'if (generate (nth tree 2)) (generate (nth tree 3)))))
(defn generate-conditions
"From this `tree`, assumed to be a syntactically correct conditions clause,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :CONDITIONS)
(generate (nth tree 1)))
(defn generate-condition
[tree]
(assert-type tree :CONDITION)
(generate (nth tree 1)))
(defn generate-conjunct-condition
[tree]
(assert-type tree :CONJUNCT-CONDITION)
(list 'and (generate (nth tree 1))(generate (nth tree 3))))
(defn generate-disjunct-condition
[tree]
(assert-type tree :DISJUNCT-CONDITION)
(list 'or (generate (nth tree 1))(generate (nth tree 3))))
(defn generate-ranged-property-condition
"Generate a property condition where the expression is a numeric range"
[tree property expression]
(assert-type tree :PROPERTY-CONDITION)
(assert-type (nth tree 3) :RANGE-EXPRESSION)
(let [l1 (generate (nth expression 2))
l2 (generate (nth expression 4))
pv (list property 'cell)]
(list 'let ['lower (list 'min l1 l2)
'upper (list 'max l1 l2)]
(list 'and (list '>= pv 'lower)(list '<= pv 'upper)))))
(defn generate-disjunct-property-condition
"Generate a property condition where the expression is a disjunct expression.
TODO: this is definitely still wrong!"
([tree]
(let [property (generate (nth tree 1))
qualifier (generate (nth tree 2))
expression (generate (nth tree 3))]
(generate-disjunct-property-condition tree property qualifier expression)))
([tree property qualifier expression]
(let [e (list 'some (list 'fn ['i] '(= i value)) (list 'quote expression))]
(list 'let ['value (list property 'cell)]
(if (= qualifier '=) e
(list 'not e))))))
(defn generate-property-condition
([tree]
(assert-type tree :PROPERTY-CONDITION)
(if
(and (= (count tree) 2) (= (first (second tree)) :SYMBOL))
;; it's a shorthand for 'state equal to symbol'. This should probably have
;; been handled in simplify...
(generate-property-condition
(list
:PROPERTY-CONDITION
'(:SYMBOL "state")
'(:QUALIFIER (:EQUIVALENCE (:EQUAL "equal to")))
(second tree)))
;; otherwise...
(generate-property-condition tree (first (nth tree 3)))))
([tree expression-type]
(assert-type tree :PROPERTY-CONDITION)
(let [property (generate (nth tree 1))
qualifier (generate (nth tree 2))
expression (generate (nth tree 3))]
(case expression-type
:DISJUNCT-EXPRESSION (generate-disjunct-property-condition tree property qualifier expression)
:RANGE-EXPRESSION (generate-ranged-property-condition tree property expression)
(list qualifier (list property 'cell) expression)))))
(defn generate-simple-action
[tree]
(assert-type tree :SIMPLE-ACTION)
(let [property (generate (nth tree 1))
expression (generate (nth tree 3))]
(if (or (= property :x) (= property :y))
(throw (Exception. reserved-properties-error))
(list 'merge 'cell {property expression}))))
(defn generate-multiple-actions
[tree]
(assert (and (coll? tree)(= (first tree) :ACTIONS)) "Expected an ACTIONS fragment")
(conj 'do (map generate-simple-action (rest tree))))
(defn generate-disjunct-value
"Generate a disjunct value. Essentially what we need here is to generate a
flat list of values, since the `member` has already been taken care of."
[tree]
(assert-type tree :DISJUNCT-VALUE)
(if (= (count tree) 4)
(cons (generate (second tree)) (generate (nth tree 3)))
(list (generate (second tree)))))
(defn generate-numeric-expression
[tree]
(assert-type tree :NUMERIC-EXPRESSION)
(case (first (second tree))
:SYMBOL (list (keyword (second (second tree))) 'cell)
(generate (second tree))))
(defn generate-neighbours-condition
"Generate code for a condition which refers to neighbours."
([tree]
(assert-type tree :NEIGHBOURS-CONDITION)
(generate-neighbours-condition tree (first (second (second tree)))))
([tree quantifier-type]
(let [quantifier (second tree)
pc (generate (nth tree 4))]
(case quantifier-type
:NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc 1)
:SOME (generate-neighbours-condition '> 0 pc 1)
:MORE (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '> value pc 1))
:LESS (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '< value pc 1)))))
([comp1 quantity property-condition distance]
(list comp1
(list 'count
(list 'remove 'false?
(list 'map (list 'fn ['cell] property-condition)
(list 'mw-engine.utils/get-neighbours 'world 'cell distance)))) quantity))
([comp1 quantity property-condition]
(generate-neighbours-condition comp1 quantity property-condition 1)))
(defn generate
"Generate code for this (fragment of a) parse tree"
[tree]
(if
(coll? tree)
(case (first tree)
:ACTIONS (generate-multiple-actions tree)
:COMPARATIVE (generate (second tree))
:COMPARATIVE-QUALIFIER (generate (nth tree 2))
:CONDITION (generate-condition tree)
:CONDITIONS (generate-conditions tree)
:CONJUNCT-CONDITION (generate-conjunct-condition tree)
:DISJUNCT-CONDITION (generate-disjunct-condition tree)
:DISJUNCT-EXPRESSION (generate (nth tree 2))
:DISJUNCT-VALUE (generate-disjunct-value tree)
:EQUIVALENCE '=
:EXPRESSION (generate (second tree))
:LESS '<
:MORE '>
:NEGATED-QUALIFIER (case (generate (second tree))
= 'not=
> '<
< '>)
:NEIGHBOURS-CONDITION (generate-neighbours-condition tree)
:NUMERIC-EXPRESSION (generate-numeric-expression tree)
:NUMBER (read-string (second tree))
:PROPERTY (list (generate (second tree)) 'cell) ;; dubious - may not be right
:PROPERTY-CONDITION (generate-property-condition tree)
:QUALIFIER (generate (second tree))
:RULE (generate-rule tree)
:SIMPLE-ACTION (generate-simple-action tree)
:SYMBOL (keyword (second tree))
:VALUE (generate (second tree))
(map generate tree))
tree))
(defn simplify-qualifier
"Given that this `tree` fragment represents a qualifier, what
qualifier is that?"
[tree]
(cond
(empty? tree) nil
(and (coll? tree)
(member? (first tree) '(:EQUIVALENCE :COMPARATIVE))) tree
(coll? (first tree)) (or (simplify-qualifier (first tree))
(simplify-qualifier (rest tree)))
(coll? tree) (simplify-qualifier (rest tree))
true tree))
(defn simplify-second-of-two
"There are a number of possible simplifications such that if the `tree` has
only two elements, the second is semantically sufficient."
[tree]
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
(defn rule?
"Return true if the argument appears to be a parsed rule tree, else false."
[maybe-rule]
(and (coll? maybe-rule) (= (first maybe-rule) :RULE)))
(defn simplify
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
semantically identical simpler fragments"
[tree]
(if
(coll? tree)
(case (first tree)
:ACTION (simplify-second-of-two tree)
:ACTIONS (simplify-second-of-two tree)
:COMPARATIVE (simplify-second-of-two tree)
:CONDITION (simplify-second-of-two tree)
:CONDITIONS (simplify-second-of-two tree)
:EXPRESSION (simplify-second-of-two tree)
:NOT nil ;; TODO is this right?!? It looks wrong
:PROPERTY (simplify-second-of-two tree)
:SPACE nil
:THEN nil
:VALUE (simplify-second-of-two tree)
(remove nil? (map simplify tree)))
tree))
(def parse-rule
"Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
[arg]
(let [parse-tree-or-error (raw-parser arg :total true)]
(if (failure? parse-tree-or-error)
(throw (ex-info (format "Some rules were not understood:\n%s"
(pprint-failure (get-failure parse-tree-or-error)))
{:source arg
:failure (get-failure parse-tree-or-error)}))
parse-tree-or-error)))
(insta/parser grammar))
(defn- compile-rule
"Compile a rule function from this `parse-tree` derived from this `source`
at the zero-based line number `n` in the source file; return a compiled
function, whose metadata has the keys:
* `:rule-type` : the type of rule the function represents;
* `:parse` : this `parse-tree`;
* `:source` : the rule source from which the parse tree was derived;
* `:lisp` : the lisp source generated from this `parse-tree`;
* `:line : the one-based line number of the definition in the source file,
i.e. `(inc n)`."
[parse-tree source n]
(if (#{:COMMENT :LINE} (first parse-tree))
(do
(l/info (format "Skipping line %d, `%s`, parse-tree %s."
(inc n) source parse-tree))
nil)
(let [lisp (generate parse-tree)
line-no (inc n)]
(l/info (format "Compiling rule at line %d, `%s`." line-no source))
(try
(if (#{'fn 'fn*} (first lisp))
(vary-meta
(eval lisp)
merge (meta lisp) {:source source :lisp lisp :line line-no})
(throw
(Exception.
(format "Parse of `%s` did not return a function: %s" source lisp))))
(catch Exception any (throw (ex-info (.getMessage any)
{:source source
:parse parse-tree
:lisp lisp
:line line-no})))))))
(defn explain-parse-error-reason
"Attempt to explain the reason for the parse error."
[reason]
(str "Expecting one of (" (apply str (map #(str (:expecting %) " ") reason)) ")"))
(defn parser-error-to-map
[parser-error]
(let [m (reduce (fn [map item](merge map {(first item)(second item)})) {} parser-error)
reason (map
#(reduce (fn [map item] (merge {(first item) (second item)} map)) {} %)
(:reason m))]
(merge m {:reason reason})))
(defn throw-parse-exception
"Construct a helpful error message from this `parser-error`, and throw an exception with that message."
[parser-error]
(assert (coll? parser-error) "Expected a paser error structure?")
(let
[
;; the error structure is a list, such that each element is a list of two items, and
;; the first element in each sublist is a keyword. Easier to work with it as a map
error-map (parser-error-to-map parser-error)
text (:text error-map)
reason (explain-parse-error-reason (:reason error-map))
;; rules have only one line, by definition; we're interested in the column
column (if (:column error-map)(:column error-map) 0)
;; create a cursor to point to that column
cursor (apply str (reverse (conj (repeat column " ") "^")))
message (format bad-parse-error text cursor reason)
]
(throw (Exception. message))))
(defn compile-rule
"Compile this `rule`, assumed to be a string with appropriate syntax, into a function of two arguments,
a `cell` and a `world`, having the same semantics."
[rule]
(assert (string? rule))
(let [tree (simplify (parse-rule rule))]
(if (rule? tree) (eval (generate tree))
(throw-parse-exception tree))))
(defn compile
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
into Clojure source, and then compile it into an anonymous
function object, getting round the problem of binding mw-engine.utils in
the compiling environment.
Returns a list of anonymous functions each of two arguments, `[cell world]`,
as expected for a MicroWorld rule function. Each function is decorated with
metadata having the keys:
* `:rule-type` : the type of rule the function represents;
* `:lisp` : the lisp source from which the function was compiled;
* `:parse` : the parse-tree from which that lisp source was derived;
* `:source` : the rule source from which the parse-tree was derived;
* `:line : the one-based line number of the rule source in the source file.
Throws an exception if parsing fails."
[rule-text]
(let [lines (split-lines rule-text)]
(remove
nil?
(map
compile-rule
(simplify (parse rule-text))
lines
(range (count lines))))))

View file

@ -1,63 +0,0 @@
(ns ^{:doc "Display parse errors in a format which makes it easy for the user
to see where the error occurred."
:author "Simon Brooke"}
mw-parser.errors)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;; USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; error thrown when an attempt is made to set a reserved property
;; error thrown when a rule cannot be parsed. Slots are for
;; (1) rule text
;; (2) cursor showing where in the rule text the error occurred
;; (3) the reason for the error
(def bad-parse-error "I did not understand:\n '%s'\n %s\n %s")
(defn- explain-parse-error-reason
"Attempt to explain the reason for the parse error."
[reason]
(str "Expecting one of (" (apply str (map #(str (:expecting %) " ") reason)) ")"))
(defn- parser-error-to-map
[parser-error]
(let [m (reduce (fn [map item](merge map {(first item)(second item)})) {} parser-error)
reason (map
#(reduce (fn [map item] (merge {(first item) (second item)} map)) {} %)
(:reason m))]
(merge m {:reason reason})))
(defn throw-parse-exception
"Construct a helpful error message from this `parser-error`, and throw an exception with that message."
[parser-error]
(assert (coll? parser-error) "Expected a paser error structure?")
(let
[
;; the error structure is a list, such that each element is a list of two items, and
;; the first element in each sublist is a keyword. Easier to work with it as a map
error-map (parser-error-to-map parser-error)
text (:text error-map)
reason (explain-parse-error-reason (:reason error-map))
;; rules have only one line, by definition; we're interested in the column
column (if (:column error-map)(:column error-map) 0)
;; create a cursor to point to that column
cursor (apply str (reverse (conj (repeat column " ") "^")))
message (format bad-parse-error text cursor reason)
]
(throw (Exception. message))))

View file

@ -1,37 +0,0 @@
(ns ^{:doc "A very simple parser which parses flow rules."
:author "Simon Brooke"}
mw-parser.flow
(:require [clojure.string :refer [join]]))
(def flow-grammar
"Grammar for flow rules.
My initial conception of this would be that production rules
(if-then rules) and flow rules (flow-from-to rules) would be
entirely separate, presented to the parser as separate text
files, and parsed and compiled by different chains of functions.
This appears not to be necessary. Flow rules are easy to parse
with the same parser as production rules -- a lot of the grammar
is intentionally common -- and the rules are easily discriminated
at the compilation ('generate') stage.
The basic rule I want to be able to compile at this stage is the 'mutual
aid' rule:
`flow 1 food from house to house within 2 with least food`
"
(join "\n" ["FLOW-RULE := FLOW SPACE QUANTITY SPACE PROPERTY SPACE FROM SPACE SOURCE SPACE TO-HOW SPACE DESTINATION;"
"PERCENTAGE := NUMBER #'%';"
"QUANTITY := PERCENTAGE | NUMBER | EXPRESSION | SOME;"
"SOURCE := STATE | STATE SPACE WITH SPACE CONDITIONS;"
"DESTINATION := TARGET | TARGET SPACE WITH SPACE FLOW-CONDITIONS;"
"DETERMINER := MOST | LEAST;"
"DETERMINER-CONDITION := DETERMINER SPACE PROPERTY;"
"FLOW-CONDITIONS := DETERMINER-CONDITION | CONDITIONS"
"RANGE := WITHIN SPACE VALUE;"
"STATE := SYMBOL;"
"TARGET := STATE | STATE SPACE RANGE;"
"TO-HOW := TO | TO-EACH | TO-FIRST;" ;; SHARED BETWEEN
"TO-EACH := TO SPACE EACH | TO SPACE ALL;"
"TO-FIRST := TO SPACE FIRST"]))

View file

@ -1,432 +0,0 @@
(ns ^{:doc "Generate Clojure source from simplified parse trees."
:author "Simon Brooke"}
mw-parser.generate
(:require
[mw-engine.utils :refer :all] ;; may need these when macro-expanding rules.
[mw-parser.utils :refer [assert-type search-tree TODO]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;; USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare generate generate-action)
(def reserved-properties-error
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
;;; macros used in generated rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; production (if-then) rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn generate-rule
"From this `tree`, assumed to be a syntactically correct rule specification,
generate and return the appropriate rule as a function of two arguments."
[tree]
(assert-type tree :RULE)
(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 'when (generate (nth tree 2)) (generate (nth tree 3)))))
merge
{:rule-type
:production}))
(defn generate-conditions
"From this `tree`, assumed to be a syntactically correct conditions clause,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :CONDITIONS)
(generate (second tree)))
(defn generate-condition
"From this `tree`, assumed to be a syntactically correct condition clause,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :CONDITION)
(generate (second tree)))
(defn generate-conjunct-condition
"From this `tree`, assumed to be a syntactically conjunct correct condition clause,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :CONJUNCT-CONDITION)
(cons 'and (map generate (rest tree))))
(defn generate-disjunct-condition
"From this `tree`, assumed to be a syntactically correct disjunct condition clause,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :DISJUNCT-CONDITION)
(cons 'or (map generate (rest tree))))
(defn generate-ranged-property-condition
"From this `tree`, assumed to be a syntactically property condition clause for
this `property` where the `expression` is a numeric range, generate and return
the appropriate clojure fragment."
[tree property expression]
(assert-type tree :PROPERTY-CONDITION)
(assert-type (nth tree 3) :RANGE-EXPRESSION)
(let [l1 (generate (nth expression 2))
l2 (generate (nth expression 4))
pv (list property 'cell)]
(list 'let ['lower (list 'min l1 l2)
'upper (list 'max l1 l2)]
(list 'and (list '>= pv 'lower) (list '<= pv 'upper)))))
(defn generate-disjunct-property-condition
"From this `tree`, assumed to be a syntactically property condition clause
where the expression is a a disjunction, generate and return
the appropriate clojure fragment.
TODO: this is definitely still wrong!"
([tree]
(let [property (generate (second tree))
qualifier (generate (nth tree 2))
expression (generate (nth tree 3))]
(generate-disjunct-property-condition tree property qualifier expression)))
([_tree property qualifier expression]
(let [e (list expression (list property 'cell))]
(if (= qualifier '=) e
(list 'not e)))))
(defn generate-property-condition
"From this `tree`, assumed to be a syntactically property condition clause,
generate and return the appropriate clojure fragment."
([tree]
(assert-type tree :PROPERTY-CONDITION)
(if
(and (= (count tree) 2) (= (first (second tree)) :SYMBOL))
;; it's a shorthand for 'state equal to symbol'. This should probably have
;; been handled in simplify...
(generate-property-condition
(list
:PROPERTY-CONDITION
'(:SYMBOL "state")
'(:QUALIFIER (:EQUIVALENCE (:EQUAL "equal to")))
(second tree)))
;; otherwise...
(generate-property-condition tree (first (nth tree 3)))))
([tree expression-type]
(assert-type tree :PROPERTY-CONDITION)
(let [property (generate (second tree))
qualifier (generate (nth tree 2))
e (generate (nth tree 3))
expression (cond
(and (not (= qualifier '=)) (keyword? e)) (list 'or (list e 'cell) e)
(and (not (= qualifier 'not=)) (keyword? e)) (list 'or (list e 'cell) e)
:else e)]
(case expression-type
:DISJUNCT-EXPRESSION (generate-disjunct-property-condition tree property qualifier expression)
:RANGE-EXPRESSION (generate-ranged-property-condition tree property expression)
(list qualifier (if (number? expression)
(list 'mw-engine.utils/get-num 'cell property)
(list property 'cell)) expression)))))
(defn generate-qualifier
"From this `tree`, assumed to be a syntactically correct qualifier,
generate and return the appropriate clojure fragment."
[tree]
(if
(= (count tree) 2)
(generate (second tree))
;; else
(generate (nth tree 2))))
(defn generate-simple-action
"From this `tree`, assumed to be a syntactically correct simple action,
generate and return the appropriate clojure fragment."
([tree]
(assert-type tree :SIMPLE-ACTION)
(generate-simple-action tree []))
([tree others]
(assert-type tree :SIMPLE-ACTION)
(let [property (generate (second tree))
expression (generate (nth tree 3))]
(if (or (= property :x) (= property :y))
(throw (Exception. reserved-properties-error))
(list 'merge
(if (empty? others) 'cell
;; else
(generate others))
{property expression})))))
(defn trap-errors-in-dice-throw
"We're getting a wierd -- many would say 'impossible' -- intermittent bug
which appears to happen here. "
[sides chances action]
;; (list 'try
(list 'if (list '< (list 'rand sides) chances) action)
;; (list 'catch 'Exception 'any
;; (list 'println (list 'format "Dice throw bug %d/%d" chances sides))
;; (list 'throw (list 'ex-info "Error in dice throw"
;; {:total sides
;; :chances chances
;; :action action}
;; 'any))))
)
(defn generate-probable-action
"From this `tree`, assumed to be a syntactically correct probable action,
generate and return the appropriate clojure fragment."
([tree]
(assert-type tree :PROBABLE-ACTION)
(generate-probable-action tree []))
([tree others]
(assert-type tree :PROBABLE-ACTION)
(let
[chances (generate (nth tree 1))
total (generate (nth tree 2))
action (generate-action (nth tree 3) others)]
;; TODO: could almost certainly be done better with macro syntax
(trap-errors-in-dice-throw total chances action))))
(defn generate-action
"From this `tree`, assumed to be a syntactically correct action,
generate and return the appropriate clojure fragment."
[tree others]
(case (first tree)
:ACTIONS (generate-action (first tree) others)
:SIMPLE-ACTION (generate-simple-action tree others)
:PROBABLE-ACTION (generate-probable-action tree others)
(throw (Exception. (str "Not a known action type: " (first tree))))))
(defn generate-multiple-actions
"From this `tree`, assumed to be one or more syntactically correct actions,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :ACTIONS)
(generate-action (first (rest tree)) (second (rest tree))))
(defn generate-numeric-expression
"From this `tree`, assumed to be a syntactically correct numeric expression,
generate and return the appropriate clojure fragment."
[tree]
(assert-type tree :NUMERIC-EXPRESSION)
(case (count tree)
4 (let [[p operator expression] (rest tree)
property (if (number? p) p (list 'mw-engine.utils/get-num 'cell p))]
(list (generate operator) (generate property) (generate expression)))
(case (first (second tree))
:SYMBOL (list 'mw-engine.utils/get-num 'cell (generate (second tree)))
(generate (second tree)))))
(defn generate-neighbours-condition
"Generate code for a condition which refers to neighbours."
([tree]
(assert-type tree :NEIGHBOURS-CONDITION)
(case (first (second tree))
:NUMBER (read-string (second (second tree)))
:QUANTIFIER (generate-neighbours-condition tree (first (second (second tree))))
:QUALIFIER (cons (generate (second tree)) (rest (generate (nth tree 2))))))
([tree quantifier-type]
(let [quantifier (second tree)
pc (generate (nth tree 4))]
(case quantifier-type
:NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc 1)
:SOME (generate-neighbours-condition '> 0 pc 1)
:MORE (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '> value pc 1))
:LESS (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '< value pc 1)))))
([comp1 quantity property-condition distance]
(list comp1
(list 'count
(list 'remove 'false?
(list 'map (list 'fn ['cell] property-condition)
(list 'mw-engine.utils/get-neighbours 'world 'cell distance))))
quantity))
([comp1 quantity property-condition]
(generate-neighbours-condition comp1 quantity property-condition 1)))
(defn generate-within-condition
"Generate code for a condition which refers to neighbours within a specified distance.
NOTE THAT there's clearly masses of commonality between this and
`generate-neighbours-condition`, and that some refactoring is almost certainly
desirable. It may be that it's better to simplify a `NEIGHBOURS-CONDITION`
into a `WITHIN-CONDITION` in the simplification stage."
([tree]
(assert-type tree :WITHIN-CONDITION)
(case (first (second tree))
:QUANTIFIER (generate-within-condition tree (first (second (second tree))))
:QUALIFIER (TODO "qualified within... help!")))
([tree quantifier-type]
(let [quantifier (second tree)
distance (generate (nth tree 4))
pc (generate (nth tree 6))]
(case quantifier-type
:NUMBER (generate-neighbours-condition
'=
(read-string (second (second quantifier)))
pc
distance)
:SOME (generate-neighbours-condition '> 0 pc distance)
:MORE (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '> value pc distance))
:LESS (let [value (generate (nth quantifier 3))]
(generate-neighbours-condition '< value pc distance))))))
;;; Flow rules. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 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.
(defn flow-rule
"Generate a flow rule for this `quantity` of this `property` from this
`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]
(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 'when
(list 'and
source
(list 'pos?
(list 'mw-engine.utils/get-num 'cell property)))
(list 'map
(list 'fn ['d]
{:source (list 'select-keys 'cell [:x :y])
:destination (list 'select-keys 'd [:x :y])
:property property
:quantity quantity-frag})
destinations))))
merge
{:rule-type
:flow}))
(defn generate-quantity-accessor
"Generate a code fragment which will generate the appropriate quantity of
the `property` specified in a rule, from this `q-clause`."
[q-clause property]
(case (first q-clause)
;; TODO :EXPRESSION still needed
:NUMBER (generate q-clause)
:PERCENTAGE (let [multiplier (/ (generate (second q-clause)) 100)]
(list '* multiplier
(list 'mw-engine.utils/get-num 'cell property)))
:SIMPLE-EXPRESSION (if (= (count q-clause) 2)
(generate-quantity-accessor (second q-clause)
property)
(throw (ex-info
(format
"Cannot yet handle q-clause of form: `%s`"
q-clause)
{:clause q-clause
:property property})))
:SOME (list 'rand (list 'mw-engine.utils/get-num 'cell property))
(throw (ex-info
(format "Unexpected QUANTITY type: `%s`" (first q-clause))
{:clause q-clause
:property property}))))
(defn generate-target-state-filter
[clause targets-frag]
(assert-type clause :DESTINATION)
(list 'filter
(list 'fn ['cell]
(generate-property-condition
(search-tree (search-tree clause :TARGET)
:PROPERTY-CONDITION)))
targets-frag))
(defn generate-dest-accessor
[clause]
(let [dc (search-tree clause :DETERMINER-CONDITION)
range (search-tree clause :RANGE)
distance (if range (generate (nth range 2)) 1)]
(list 'let ['candidates
(generate-target-state-filter
clause
(list 'mw-engine.utils/get-neighbours
'world 'cell distance))]
(if dc
(list 'list
(let [determiner (first
(second
(search-tree dc :DETERMINER)))
prop (generate (nth dc 2))]
(case determiner
:LEAST (list 'mw-engine.utils/get-least-cell
'candidates prop)
:MOST (list 'mw-engine.utils/get-most-cell
'candidates prop))))
'candidates))))
(defn generate-flow
[tree]
(assert-type tree :FLOW-RULE)
(let [clauses (reduce #(assoc %1 (first %2) %2) {} (rest tree))
source-accessor (generate (:SOURCE clauses))
property (generate (:SYMBOL clauses))
quantity (generate-quantity-accessor (second (:QUANTITY clauses)) property)
dest-accessor (generate-dest-accessor (:DESTINATION clauses))]
(flow-rule source-accessor property quantity dest-accessor)))
;;; Top level; only function anything outside this file (except tests) should
;;; really call.
(defn generate
"Generate code for this (fragment of a) parse tree"
[tree]
(if
(coll? tree)
(case (first tree)
:ACTIONS (generate-multiple-actions tree)
:COMPARATIVE (generate (second tree))
:COMPARATIVE-QUALIFIER (generate (second tree))
:CONDITION (generate-condition tree)
:CONDITIONS (generate-conditions tree)
:CONJUNCT-CONDITION (generate-conjunct-condition tree)
:DISJUNCT-CONDITION (generate-disjunct-condition tree)
:DISJUNCT-EXPRESSION (set (generate (second tree)))
:DISJUNCT-VALUE (map generate (rest tree))
:EQUIVALENCE '=
:EXPRESSION (generate (second tree))
:FLOW-RULE (generate-flow tree)
:LESS '<
:MORE '>
:NEGATED-QUALIFIER (case (generate (second tree))
= 'not=
> '<
< '>)
:NEIGHBOURS-CONDITION (generate-neighbours-condition tree)
:NUMERIC-EXPRESSION (generate-numeric-expression tree)
:NUMBER (read-string (second tree))
:OPERATOR (symbol (second tree))
:PROBABLE-ACTION (generate-probable-action tree)
:PROPERTY (list (generate (second tree)) 'cell) ;; dubious - may not be right
:PROPERTY-CONDITION (generate-property-condition tree)
:QUALIFIER (generate-qualifier tree)
:QUANTITY (generate (second tree))
:RULE (generate-rule tree)
:SIMPLE-ACTION (generate-simple-action tree)
:SOURCE (generate (second tree))
:SYMBOL (keyword (second tree))
:VALUE (generate (second tree))
:WITHIN-CONDITION (generate-within-condition tree)
(map generate tree))
tree))

View file

@ -0,0 +1,92 @@
(ns mw-parser.simplifier
(:use mw-engine.utils
mw-parser.parser))
(declare simplify)
(defn simplify-qualifier
"Given that this `tree` fragment represents a qualifier, what
qualifier is that?"
[tree]
(cond
(empty? tree) nil
(and (coll? tree)
(member? (first tree) '(:EQUIVALENCE :COMPARATIVE))) tree
(coll? (first tree)) (or (simplify-qualifier (first tree))
(simplify-qualifier (rest tree)))
(coll? tree) (simplify-qualifier (rest tree))
true tree))
(defn simplify-second-of-two
"There are a number of possible simplifications such that if the `tree` has
only two elements, the second is semantically sufficient."
[tree]
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
(defn simplify-some
"'some' is the same as 'more than zero'"
[tree]
[:COMPARATIVE '> 0])
(defn simplify-none
"'none' is the same as 'zero'"
[tree]
[:COMPARATIVE '= 0])
(defn simplify-all
"'all' isn't actually the same as 'eight', because cells at the edges of the world have
fewer than eight neighbours; but it's a simplifying (ha!) assumption for now."
[tree]
[:COMPARATIVE '= 8])
(defn simplify-quantifier
"If this quantifier is a number, 'simplifiy' it into a comparative whose operator is '='
and whose quantity is that number. This is actually more complicated but makes generation easier."
[tree]
(if (number? (second tree)) [:COMPARATIVE '= (second tree)] (simplify (second tree))))
(defn simplify
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
semantically identical simpler fragments"
[tree]
(if
(coll? tree)
(case (first tree)
:SPACE nil
:QUALIFIER (simplify-qualifier tree)
:CONDITIONS (simplify-second-of-two tree)
:CONDITION (simplify-second-of-two tree)
:EXPRESSION (simplify-second-of-two tree)
:COMPARATIVE (simplify-second-of-two tree)
:QUANTIFIER (simplify-quantifier tree)
:VALUE (simplify-second-of-two tree)
:PROPERTY (simplify-second-of-two tree)
:ACTIONS (simplify-second-of-two tree)
:ACTION (simplify-second-of-two tree)
:ALL (simplify-all tree)
:SOME (simplify-some tree)
:NONE (simplify-none tree)
(remove nil? (map simplify tree)))
tree))
(simplify (parse-rule "if state is climax and 4 neighbours have state equal to fire then 3 chance in 5 state should be fire"))
(simplify (parse-rule "if state is climax and no neighbours have state equal to fire then 3 chance in 5 state should be fire"))
(simplify (parse-rule "if state is in grassland or pasture or heath and more than 4 neighbours have state equal to water then state should be village"))
(simplify (parse-rule "if 6 neighbours have state equal to water then state should be village"))
(simplify (parse-rule "if fertility is between 55 and 75 then state should be climax"))
(simplify (parse-rule "if state is forest then state should be climax"))
(simplify (parse-rule "if state is in grassland or pasture or heath and more than 4 neighbours have state equal to water then state should be village"))
(simplify (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
(simplify (parse-rule "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"))
(simplify (parse-rule "if altitude is 100 or fertility is 25 then state should be heath"))
(simplify (parse-rule "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"))
(simplify (parse-rule "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"))
(simplify (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village"))

View file

@ -1,90 +0,0 @@
(ns ^{:doc "Simplify a parse tree."
:author "Simon Brooke"}
mw-parser.simplify
(:require [mw-parser.utils :refer [search-tree]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; mw-parser: a rule parser for MicroWorld.
;;;;
;;;; This program is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License
;;;; as published by the Free Software Foundation; either version 2
;;;; of the License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;;;; USA.
;;;;
;;;; Copyright (C) 2014 Simon Brooke
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare simplify)
(defn simplify-second-of-two
"There are a number of possible simplifications such that if the `tree` has
only two elements, the second is semantically sufficient."
[tree]
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
(defn simplify-chained-list
"Some parse trees take the form
`[:X [:Y 1] :NOISE :NOISE [:X [:Y 2] :NOISE :NOISE [:X [:Y 3]]]]`
where what's wanted is `[:X [:Y 1] [:Y 2] [:Y 2]]` -- :DISJUNCT-VALUE is a case
in point. This takes such a parse `tree`, where `branch-tag` is the tag of
the enclosing form and `leaf-tag` is the tag of the form to be collected, and
returns the desired form."
[tree branch-tag leaf-tag]
(cons
(first tree)
(reverse
(loop [chain (rest tree) v '()]
(let [car (first chain)]
(cond (empty? chain) v
(coll? car) (let [caar (first car)]
(cond
(= branch-tag caar) (recur car v)
(= leaf-tag caar) (recur
(rest chain)
(cons (simplify car) v))
:else (recur (rest chain) v)))
:else (recur (rest chain) v)))))))
(defn simplify
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
semantically identical simpler fragments"
[tree]
(if
(coll? tree)
(case (first tree)
:ACTION (simplify-second-of-two tree)
:COMPARATIVE (simplify-second-of-two tree)
:CONDITION (simplify-second-of-two tree)
:CONDITIONS (simplify-second-of-two tree)
;; :DISJUNCT-EXPRESSION (simplify-chained-list tree :DISJUNCT-VALUE :VALUE)
:EXPRESSION (simplify-second-of-two tree)
:FLOW-CONDITIONS (simplify-second-of-two tree)
;; this is like simplify-second-of-two except if there isn't
;; a second element it returns nil
:LINE (if (= (count tree) 2) (simplify (nth tree 1)) tree)
:LINES (map simplify (rest tree))
:PROPERTY (simplify-second-of-two tree)
:PROPERTY-CONDITION-OR-EXPRESSION (simplify-second-of-two tree)
:STATE (list :PROPERTY-CONDITION
(list :SYMBOL "state")
'(:QUALIFIER
(:EQUIVALENCE
(:IS "is")))
(list :EXPRESSION
(list :VALUE (second tree))))
:VALUE (simplify-second-of-two tree)
;; default
(remove nil? (map simplify tree)))
tree))

View file

@ -1,57 +0,0 @@
(ns ^{:doc "Utilities used in more than one namespace within the parser."
:author "Simon Brooke"}
mw-parser.utils)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; mw-parser: a rule parser for MicroWorld.
;;;;
;;;; This program is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License
;;;; as published by the Free Software Foundation; either version 2
;;;; of the License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;;;; USA.
;;;;
;;;; Copyright (C) 2014 Simon Brooke
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn suitable-fragment?
"Return `true` if `tree-fragment` appears to be a tree fragment of the expected `type`."
[tree-fragment type]
(and (coll? tree-fragment)
(keyword? type)
(= (first tree-fragment) type)))
(defn TODO
"Marker to indicate I'm not yet finished!"
[message]
message)
(defn assert-type
"If `tree-fragment` is not a tree fragment of the expected `type`, throw an exception."
[tree-fragment type]
(assert (suitable-fragment? tree-fragment type)
(throw (ex-info (format "Expected a %s fragment" type)
{:actual tree-fragment
:expected type}))))
(defn search-tree
"Return the first element of this tree which has this tag in a depth-first, left-to-right search"
[tree tag]
(cond
(= (first tree) tag) tree
:else (first
(remove nil?
(map
#(search-tree % tag)
(filter coll? (rest tree)))))))

View file

@ -0,0 +1,24 @@
(ns mw-parser.bulk-test
(:use clojure.java.io)
(:require [clojure.test :refer :all]
[mw-parser.bulk :refer :all]))
(deftest bulk-parsing-test
(testing "Bulk (file) parsing and compilation"
(is (= (count (parse-file (as-file "resources/rules.txt"))) 15)
"Should parse all rules and throw no exceptions")
(is (empty?
(remove #(= % 'fn)
(map first
(parse-file
(as-file "resources/rules.txt")))))
"all parsed rules should be lambda sexprs")
(is (= (count (compile-file (as-file "resources/rules.txt"))) 15)
"Should compile all rules and throw no exceptions")
(is (empty?
(remove ifn?
(map first
(compile-file
(as-file "resources/rules.txt")))))
"all compiled rules should be ifns")
))

View file

@ -0,0 +1,471 @@
(ns mw-parser.core-test
(:use clojure.pprint
mw-engine.core
mw-engine.world)
(:require [clojure.test :refer :all]
[mw-parser.core :refer :all]))
(deftest primitives-tests
(testing "Simple functions supporting the parser"
(is (= (parse-simple-value '()) nil)
"if there's nothing to parse, return nil")
(is (= (first (parse-simple-value '("1234" "and" "that"))) 1234)
"a simple value is expected to be just a number.")
(is (= (first (parse-simple-value '("this" "and" "that"))) :this)
"or else just a keyword")
(is (= (first (parse-simple-value '("this" "and" "that") true))
'(get-int cell :this))
"...unless an integer is explicitly sought, in which case it should be something which gets an integer from the current cell")
(is (= (parse-value '()) nil)
"if there's nothing to parse, return nil")
(is (= (first (parse-value '("1234" "and" "that"))) 1234)
"a simple value is expected to be just a number.")
(is (= (first (parse-value '("this" "and" "that"))) :this)
"or else just a keyword")
(is (= (first (parse-value '("this" "and" "that") true))
'(get-int cell :this))
"...unless an integer is explicitly sought, in which case it should be something which gets an integer from the current cell")
(is (= (parse-property-value '()) nil)
"if there's nothing to parse, return nil")
(is (= (first (parse-property-value '("this" "and" "that"))) '(:this cell))
"Parsing a property value returns a code function to pull its value off the current cell")
))
(deftest rules-tests
(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 (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"))
(is (parse-rule "if state is climax and some neighbours have state equal to fire then 3 chance in 5 state should be fire"))
(is (parse-rule "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"))
))
(deftest exception-tests
(testing "Constructions which should cause exceptions to be thrown"
(is (thrown-with-msg? Exception #"^I did not understand.*"
(parse-rule "the quick brown fox jumped over the lazy dog"))
"Exception thrown if rule text does not match grammar")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(parse-rule "if state is new then x should be 0"))
"Exception thrown on attempt to set 'x'")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(parse-rule "if state is new then y should be 0"))
"Exception thrown on attempt to set 'y'")
(is (thrown? Exception (compile-rule "if state is new then x should be 0"))
"Can't set x property to number, as this would break the world")
(is (thrown? Exception (compile-rule "if state is new then y should be 0"))
"Can't set y property to number, as this would break the world")
(is (thrown? Exception (compile-rule "if state is new then x should be heath"))
"Can't set x property to symbol, as this would break the world")
(is (thrown? Exception (compile-rule "if state is new then y should be heath"))
"Can't set y property to symbol, as this would break the world")
))
(deftest correctness-tests
(testing "Simplest possible rule"
(let [afn (compile-rule "if state is new then state should be grassland")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :forest} nil))))
"Rule doesn't fire when condition isn't met"))
(testing "Condition conjunction rule"
(let [afn (compile-rule "if state is new and altitude is 0 then state should be water")]
(is (= (apply afn (list {:state :new :altitude 0} nil))
{:state :water :altitude 0})
"Rule fires when conditions are met")
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
"Rule does not fire: second condition not met")
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
"Rule does not fire: first condition not met")))
(testing "Condition disjunction rule"
(let [afn (compile-rule "if state is new or state is waste then state should be grassland")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires: first condition met")
(is (= (apply afn (list {:state :waste} nil))
{:state :grassland})
"Rule fires: second condition met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire: neither condition met")))
(testing "Simple negation rule"
(let [afn (compile-rule "if state is not new then state should be grassland")]
(is (nil? (apply afn (list {:state :new} nil)))
"Rule doesn't fire when condition isn't met")
(is (= (apply afn (list {:state :forest} nil))
{:state :grassland})
"Rule fires when condition is met")))
(testing "Can't set x or y properties")
(testing "Simple list membership rule"
(let [afn (compile-rule "if state is in heath or scrub or forest then state should be climax")]
(is (= (apply afn (list {:state :heath} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :scrub} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :forest} nil))
{:state :climax})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :grassland} nil)))
"Rule does not fire when condition is not met")))
(testing "Negated list membership rule"
(let [afn (compile-rule "if state is not in heath or scrub or forest then state should be climax")]
(is (nil? (apply afn (list {:state :heath} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :scrub} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire when condition is not met")
(is (= (apply afn (list {:state :grassland} nil))
{:state :climax})
"Rule fires when condition is met")))
(testing "Property is more than numeric-value"
(let [afn (compile-rule "if altitude is more than 200 then state should be snow")]
(is (= (apply afn (list {:altitude 201} nil))
{:state :snow :altitude 201})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 200} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is more than property"
(let [afn (compile-rule "if wolves are more than deer then deer should be 0")]
(is (= (apply afn (list {:deer 2 :wolves 3} nil))
{:deer 0 :wolves 3})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is less than numeric-value"
(let [afn (compile-rule "if altitude is less than 10 then state should be water")]
(is (= (apply afn (list {:altitude 9} nil))
{:state :water :altitude 9})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 10} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is less than property"
(let [afn (compile-rule "if wolves are less than deer then deer should be deer - wolves")]
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
{:deer 1 :wolves 2})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
"Rule does not fire when condition is not met")))
(testing "Number neighbours have property equal to value"
(let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water")
world (make-world 3 3)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire."))
(let [afn (compile-rule "if 3 neighbours are new then state should be water")
world (make-world 3 3)]
;; 'are new' should be the same as 'have state equal to new'
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire.")))
(testing "Number neighbours have property more than numeric-value"
(let [afn (compile-rule "if 3 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(testing "Number neighbours have property less than numeric-value"
(let [afn (compile-rule "if 5 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has two high neighbours, so rule should not fire.")))
(testing "More than number neighbours have property equal to numeric-value"
(let [afn (compile-rule "if more than 2 neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(testing "More than number neighbours have property equal to symbolic-value"
(let [afn (compile-rule "if more than 2 neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
(let [afn (compile-rule "if more than 2 neighbours are grassland then state should be beach")
;; 'are grassland' should mean the same as 'have state equal to grassland'.
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
)
(testing "Fewer than number neighbours have property equal to numeric-value"
(let [afn (compile-rule "if fewer than 3 neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
(testing "Fewer than number neighbours have property equal to symbolic-value"
(let [afn (compile-rule "if fewer than 3 neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
;; some neighbours have property equal to value
(testing "Some neighbours have property equal to numeric-value"
(let [afn (compile-rule "if some neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
(testing "Some neighbours have property equal to symbolic-value"
(let [afn (compile-rule "if some neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
;; more than number neighbours have property more than numeric-value
(testing "More than number neighbours have property more than symbolic-value"
(let [afn (compile-rule "if more than 2 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
;; fewer than number neighbours have property more than numeric-value
(testing "Fewer than number neighbours have property more than numeric-value"
(let [afn (compile-rule "if fewer than 3 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
;; some neighbours have property more than numeric-value
(testing "Some neighbours have property more than numeric-value"
(let [afn (compile-rule "if some neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
;; more than number neighbours have property less than numeric-value
(testing "More than number neighbours have property less than numeric-value"
(let [afn (compile-rule "if more than 4 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only three low neighbours, so rule should not fire.")))
;; fewer than number neighbours have property less than numeric-value
(testing "Fewer than number neighbours have property less than numeric-value"
(let [afn (compile-rule "if fewer than 4 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Centre cell has five low neighbours, so rule should not fire")
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Middle cell of the strip has only three low neighbours, so rule should fire.")))
;; some neighbours have property less than numeric-value
(testing "Some number neighbours have property less than numeric-value"
(let [afn (compile-rule "if some neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is less than 2 then altitude should be 11")
(compile-rule "if x is 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left of world is all high, so rule should not fire.")))
;; 'single action' already tested in 'condition' tests above
;; action and actions
(testing "Conjunction of actions"
(let [afn (compile-rule "if state is new then state should be grassland and fertility should be 0")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland :fertility 0})
"Both actions are executed")))
;; 'property should be symbolic-value' and 'property should be numeric-value'
;; already tested in tests above
;; number chance in number property should be value
(testing "Syntax of probability rule - action of real probability very hard to test"
(let [afn (compile-rule "if state is forest then 5 chance in 5 state should be climax")]
(is (= (:state (apply afn (list {:state :forest} nil))) :climax)
"five chance in five should fire every time"))
(let [afn (compile-rule "if state is forest then 0 chance in 5 state should be climax")]
(is (nil? (apply afn (list {:state :forest} nil)))
"zero chance in five should never fire")))
;; property operator numeric-value
(testing "Arithmetic action: addition of number"
(let [afn (compile-rule "if state is climax then fertility should be fertility + 1")]
(is (= (:fertility
(apply afn (list {:state :climax :fertility 0} nil)))
1)
"Addition is executed")))
(testing "Arithmetic action: addition of property value"
(let [afn (compile-rule "if state is climax then fertility should be fertility + leaf-fall")]
(is (= (:fertility
(apply afn
(list {:state :climax
:fertility 0
:leaf-fall 1} nil)))
1)
"Addition is executed")))
(testing "Arithmetic action: subtraction of number"
(let [afn (compile-rule "if state is crop then fertility should be fertility - 1")]
(is (= (:fertility
(apply afn (list {:state :crop :fertility 2} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: subtraction of property value"
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer - wolves")]
(is (= (:deer
(apply afn
(list {:deer 3
:wolves 2} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: multiplication by number"
(let [afn (compile-rule "if deer are more than 1 then deer should be deer * 2")]
(is (= (:deer
(apply afn (list {:deer 2} nil)))
4)
"Action is executed")))
(testing "Arithmetic action: multiplication by property value"
(let [afn (compile-rule "if state is crop then deer should be deer * deer")]
(is (= (:deer
(apply afn
(list {:state :crop :deer 2} nil)))
4)
"Action is executed")))
(testing "Arithmetic action: division by number"
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / 2")]
(is (= (:deer
(apply afn (list {:deer 2 :wolves 1} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: division by property value"
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / wolves")]
(is (= (:deer
(apply afn
(list {:deer 2 :wolves 2} nil)))
1)
"Action is executed")))
;; simple within distance
(testing "Number neighbours within distance have property equal to value"
(let [afn (compile-rule "if 8 neighbours within 2 have state equal to new then state should be water")
world (make-world 5 5)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has eight neighbours within two)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has twenty-four neighbours within two, so rule does not fire.")))
;; comparator within distance
(testing "More than number neighbours within distance have property equal to symbolic-value"
(let [afn (compile-rule "if more than 7 neighbours within 2 have state equal to grassland and more than 7 neighbours within 2 have state equal to water then state should be beach")
;; 5x5 world, strip of high ground two cells wide down left hand side
;; xxooo
;; xxooo
;; xxooo
;; xxooo
;; xxooo
world (transform-world
(make-world 5 5)
(list (compile-rule "if x is less than 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is more than 1 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))))

View file

@ -1,514 +1,517 @@
(ns mw-parser.declarative-test
(:require [clojure.string :refer [join]]
[clojure.test :refer [deftest is testing]]
[mw-engine.core :refer [transform-world]]
[mw-engine.utils :refer [get-cell]]
[mw-engine.world :refer [make-world]]
[mw-parser.declarative :refer [compile parse]]
[mw-parser.generate :refer [generate]]
[mw-parser.simplify :refer [simplify]]
[mw-parser.utils :refer [suitable-fragment?]]))
(defn rule?
"Return true if the argument appears to be a parsed rule tree, else false."
[maybe-rule]
(suitable-fragment? maybe-rule :RULE))
(:use clojure.pprint
mw-engine.core
mw-engine.world
mw-engine.utils)
(:require [clojure.test :refer :all]
[mw-parser.declarative :refer :all]))
(deftest rules-tests
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
(is (rule? (parse "if state is forest then state should be climax")))
(is (rule? (parse "if state is in grassland or pasture or heath then state should be village")))
(is (rule? (parse "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))
(is (rule? (parse "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3")))
(is (rule? (parse "if altitude is 100 or fertility is 25 then state should be heath")))
(is (rule? (parse "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2")))
(is (rule? (parse "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves")))
(is (rule? (parse "if state is forest and fertility is between 55 and 75 then state should be climax")))
(is (rule? (parse "if fertility is between 55 and 75 then state should be climax")))
(is (rule? (parse "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))))
(is (rule? (parse-rule "if state is forest then state should be climax")))
(is (rule? (parse-rule "if state is in grassland or pasture or heath then state should be village")))
(is (rule? (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))
(is (rule? (parse-rule "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3")))
(is (rule? (parse-rule "if altitude is 100 or fertility is 25 then state should be heath")))
(is (rule? (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 (rule? (parse-rule "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves")))
(is (rule? (parse-rule "if state is forest and fertility is between 55 and 75 then state should be climax")))
(is (rule? (parse-rule "if fertility is between 55 and 75 then state should be climax")))
(is (rule? (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))
))
(deftest neighbours-rules-tests
(testing "Rules which relate to neighbours - hard!"
(is (rule? (parse "if state is climax and some neighbours have state equal to fire then 3 chance in 5 state should be fire")))
(is (rule? (parse "if state is in grassland or pasture or heath and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse "if 6 neighbours have state equal to water then state should be village")))
(is (rule? (parse "if state is grassland and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub")))
(is (rule? (parse "if state is in grassland or pasture or heath and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse "if state is grassland and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse "if 6 neighbours have state equal to water then state should be village")))))
(is (rule? (parse-rule "if state is climax and some neighbours have state equal to fire then 3 chance in 5 state should be fire")))
(is (rule? (parse-rule "if state is in grassland or pasture or heath and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse-rule "if 6 neighbours have state equal to water then state should be village")))
(is (rule? (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse-rule "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub")))
(is (rule? (parse-rule "if state is in grassland or pasture or heath and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village")))
(is (rule? (parse-rule "if 6 neighbours have state equal to water then state should be village")))
))
(deftest expressions-tests
(testing "Generating primitive expressions."
(is (generate '(:NUMERIC-EXPRESSION (:NUMBER "50"))) 50)
(is (generate '(:NUMERIC-EXPRESSION (:SYMBOL "sealevel")))
'(:sealevel cell))
))
(deftest lhs-generators-tests
(testing "Generating left-hand-side fragments of rule functions from appropriate fragments of parse trees"
(is (generate
'(:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")))
'(= (:state cell) :forest))
(is (generate
'(:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10")))
'(= (:fertility cell) 10))
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:LESS "less"]] (:NUMBER "10")))
'(< (:fertility cell) 10))
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:MORE "more"]] (:NUMBER "10")))
'(> (:fertility cell) 10))
(is (generate '(:CONJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:AND "and") (:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10"))))
'(and (= (:state cell) :forest) (= (:fertility cell) 10)))
(is (generate '(:DISJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:OR "or") (:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10"))))
'(or (= (:state cell) :forest) (= (:fertility cell) 10)))
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:DISJUNCT-EXPRESSION (:IN "in") (:DISJUNCT-VALUE (:SYMBOL "grassland") (:OR "or") (:DISJUNCT-VALUE (:SYMBOL "pasture") (:OR "or") (:DISJUNCT-VALUE (:SYMBOL "heath")))))))
'(let [value (:state cell)] (some (fn [i] (= i value)) (quote (:grassland :pasture :heath)))))
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "altitude") [:EQUIVALENCE [:IS "is"]] (:RANGE-EXPRESSION (:BETWEEN "between") (:NUMERIC-EXPRESSION (:NUMBER "50")) (:AND "and") (:NUMERIC-EXPRESSION (:NUMBER "100")))))
'(let [lower (min 50 100) upper (max 50 100)] (and (>= (:altitude cell) lower) (<= (:altitude cell) upper))))
))
(deftest rhs-generators-tests
(testing "Generating right-hand-side fragments of rule functions from appropriate fragments of parse trees"
(is (generate
'(:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax")))
'(merge cell {:state :climax}))
(is (generate
'(:SIMPLE-ACTION (:SYMBOL "fertility") (:BECOMES "should be") (:NUMBER "10")))
'(merge cell {:fertility 10}))
))
(deftest full-generation-tests
(testing "Full rule generation from pre-parsed tree"
(is (generate '(:RULE (:IF "if") (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax"))))
'(fn [cell world] (if (= (:state cell) :forest) (merge cell {:state :climax}))))
))
(deftest exception-tests
(testing "Constructions which should cause exceptions to be thrown"
(is (thrown-with-msg? Exception #"^Parse error at line.*"
(parse "the quick brown fox jumped over the lazy dog"))
(is (thrown-with-msg? Exception #"^I did not understand.*"
(compile-rule "the quick brown fox jumped over the lazy dog"))
"Exception thrown if rule text does not match grammar")
(is (thrown-with-msg? Exception #"^Parse error at line.*"
(parse "if i have a cat on my lap then everything is fine"))
(is (thrown-with-msg? Exception #"^I did not understand.*"
(compile-rule "if i have a cat on my lap then everything is fine"))
"Exception thrown if rule text does not match grammar")
;; TODO: these two should be moved to generate-test; the exception should be
;; being thrown (but isn't) in the generate phase.
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(generate (simplify (parse "if state is new then x should be 0")))
"Exception thrown on attempt to set 'x'")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(generate (simplify (parse "if state is new then y should be 0")))
"Exception thrown on attempt to set 'y'")))))
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(compile-rule "if state is new then x should be 0"))
"Exception thrown on attempt to set 'x'")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(compile-rule "if state is new then y should be 0"))
"Exception thrown on attempt to set 'y'")
))
(deftest correctness-tests
;; 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.
(testing "Simplest possible rule"
(let [afn (first (compile "if state is new then state should be grassland"))]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule doesn't fire when condition isn't met")))
(let [afn (compile-rule "if state is new then state should be grassland")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule doesn't fire when condition isn't met")))
(testing "Condition conjunction rule"
(let [afn (first (compile "if state is new and altitude is 0 then state should be water"))]
(is (= (apply afn (list {:state :new :altitude 0} nil))
{:state :water :altitude 0})
"Rule fires when conditions are met")
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
"Rule does not fire: second condition not met")
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
"Rule does not fire: first condition not met")))
(let [afn (compile-rule "if state is new and altitude is 0 then state should be water")]
(is (= (apply afn (list {:state :new :altitude 0} nil))
{:state :water :altitude 0})
"Rule fires when conditions are met")
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
"Rule does not fire: second condition not met")
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
"Rule does not fire: first condition not met")))
(testing "Condition disjunction rule"
(let [afn (first (compile "if state is new or state is waste then state should be grassland"))]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires: first condition met")
(is (= (apply afn (list {:state :waste} nil))
{:state :grassland})
"Rule fires: second condition met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire: neither condition met")))
(let [afn (compile-rule "if state is new or state is waste then state should be grassland")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland})
"Rule fires: first condition met")
(is (= (apply afn (list {:state :waste} nil))
{:state :grassland})
"Rule fires: second condition met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire: neither condition met")))
(testing "Simple negation rule"
(let [afn (first (compile "if state is not new then state should be grassland"))]
(is (nil? (apply afn (list {:state :new} nil)))
"Rule doesn't fire when condition isn't met")
(is (= (apply afn (list {:state :forest} nil))
{:state :grassland})
"Rule fires when condition is met")))
(let [afn (compile-rule "if state is not new then state should be grassland")]
(is (nil? (apply afn (list {:state :new} nil)))
"Rule doesn't fire when condition isn't met")
(is (= (apply afn (list {:state :forest} nil))
{:state :grassland})
"Rule fires when condition is met")))
(testing "Can't set x or y properties"
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(first (compile "if state is new then x should be 0")))
"Exception thrown on attempt to set 'x'")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(first (compile "if state is new then y should be 0")))
"Exception thrown on attempt to set 'y'"))
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(compile-rule "if state is new then x should be 0"))
"Exception thrown on attempt to set 'x'")
(is (thrown-with-msg?
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
(compile-rule "if state is new then y should be 0"))
"Exception thrown on attempt to set 'y'"))
(testing "Simple list membership rule"
(let [afn (first (compile "if state is in heath or scrub or forest then state should be climax"))]
(is (= (apply afn (list {:state :heath} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :scrub} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :forest} nil))
{:state :climax})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :grassland} nil)))
"Rule does not fire when condition is not met")))
(let [afn (compile-rule "if state is in heath or scrub or forest then state should be climax")]
(is (= (apply afn (list {:state :heath} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :scrub} nil))
{:state :climax})
"Rule fires when condition is met")
(is (= (apply afn (list {:state :forest} nil))
{:state :climax})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:state :grassland} nil)))
"Rule does not fire when condition is not met")))
(testing "Negated list membership rule"
(let [afn (first (compile "if state is not in heath or scrub or forest then state should be climax"))]
(is (nil? (apply afn (list {:state :heath} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :scrub} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire when condition is not met")
(is (= (apply afn (list {:state :grassland} nil))
{:state :climax})
"Rule fires when condition is met")))
(let [afn (compile-rule "if state is not in heath or scrub or forest then state should be climax")]
(is (nil? (apply afn (list {:state :heath} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :scrub} nil)))
"Rule does not fire when condition is not met")
(is (nil? (apply afn (list {:state :forest} nil)))
"Rule does not fire when condition is not met")
(is (= (apply afn (list {:state :grassland} nil))
{:state :climax})
"Rule fires when condition is met")))
(testing "Property is more than numeric-value"
(let [afn (first (compile "if altitude is more than 200 then state should be snow"))]
(is (= (apply afn (list {:altitude 201} nil))
{:state :snow :altitude 201})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 200} nil)))
"Rule does not fire when condition is not met")))
(let [afn (compile-rule "if altitude is more than 200 then state should be snow")]
(is (= (apply afn (list {:altitude 201} nil))
{:state :snow :altitude 201})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 200} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is more than property"
(let [afn (first (compile "if wolves are more than deer then deer should be 0"))]
(is (= (apply afn (list {:deer 2 :wolves 3} nil))
{:deer 0 :wolves 3})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
"Rule does not fire when condition is not met")))
;; TODO: this one is very tricky and will require a rethink of the way conditions are parsed.
;; (testing "Property is more than property"
;; (let [afn (compile-rule "if wolves are more than deer then deer should be 0")]
;; (is (= (apply afn (list {:deer 2 :wolves 3} nil))
;; {:deer 0 :wolves 3})
;; "Rule fires when condition is met")
;; (is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
;; "Rule does not fire when condition is not met")))
(testing "Property is less than numeric-value"
(let [afn (first (compile "if altitude is less than 10 then state should be water"))]
(is (= (apply afn (list {:altitude 9} nil))
{:state :water :altitude 9})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 10} nil)))
"Rule does not fire when condition is not met")))
(let [afn (compile-rule "if altitude is less than 10 then state should be water")]
(is (= (apply afn (list {:altitude 9} nil))
{:state :water :altitude 9})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 10} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is less than property"
(let [afn (first (compile "if wolves are less than deer then deer should be deer - wolves"))]
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
{:deer 1 :wolves 2})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
"Rule does not fire when condition is not met")))
(let [afn (compile-rule "if wolves are less than deer then deer should be deer - wolves")]
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
{:deer 1 :wolves 2})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
"Rule does not fire when condition is not met")))
(testing "Number neighbours have property equal to value"
(let [afn (first (compile "if 3 neighbours have state equal to new then state should be water"))
world (make-world 3 3)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire."))
(let [afn (first (compile "if 3 neighbours are new then state should be water"))
world (make-world 3 3)]
;; 'are new' and 'is new' should be the same as 'have state equal to new'
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire."))
(let [afn (first (compile "if 3 neighbours is new then state should be water"))
world (make-world 3 3)]
;; 'are new' and 'is new' should be the same as 'have state equal to new'
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire.")))
(let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water")
world (make-world 3 3)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire."))
(let [afn (compile-rule "if 3 neighbours are new then state should be water")
world (make-world 3 3)]
;; 'are new' and 'is new' should be the same as 'have state equal to new'
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire."))
(let [afn (compile-rule "if 3 neighbours is new then state should be water")
world (make-world 3 3)]
;; 'are new' and 'is new' should be the same as 'have state equal to new'
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has eight neighbours, so rule does not fire.")))
(testing "Number neighbours have property more than numeric-value"
;; if 3 neighbours have altitude more than 10 then state should be beach
(let [afn (first (compile "if 3 neighbours have altitude more than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile (join "\n" ["if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"])))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if 3 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(testing "Number neighbours have property less than numeric-value"
(let [afn (first (compile "if 5 neighbours have altitude less than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile (join "\n" ["if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"])))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has two high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if 5 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has two high neighbours, so rule should not fire.")))
(testing "More than number neighbours have property equal to numeric-value"
(let [afn (first (compile "if more than 2 neighbours have altitude equal to 11 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile (join "\n" ["if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"])))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if more than 2 neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(testing "More than number neighbours have property equal to symbolic-value"
(let [afn (first (compile "if more than 2 neighbours have state equal to grassland then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11 and state should be grassland"
"if x is less than 2 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
(let [afn (first (compile "if more than 2 neighbours are grassland then state should be beach"))
;; 'are grassland' should mean the same as 'have state equal to grassland'.
world (transform-world
(make-world 3 3)
(compile (join "\n" (list "if x is 2 then altitude should be 11 and state should be grassland"
"if x is less than 2 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if more than 2 neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
(let [afn (compile-rule "if more than 2 neighbours are grassland then state should be beach")
;; 'are grassland' should mean the same as 'have state equal to grassland'.
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
)
(testing "Fewer than number neighbours have property equal to numeric-value"
(let [afn (first (compile "if fewer than 3 neighbours have altitude equal to 11 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile (join "\n" (list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if fewer than 3 neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
(testing "Fewer than number neighbours have property equal to symbolic-value"
(let [afn (first (compile "if fewer than 3 neighbours have state equal to grassland then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11 and state should be grassland"
"if x is less than 2 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if fewer than 3 neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
;; some neighbours have property equal to value
;; some neighbours have property equal to value
(testing "Some neighbours have property equal to numeric-value"
(let [afn (first (compile "if some neighbours have altitude equal to 11 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if some neighbours have altitude equal to 11 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
(testing "Some neighbours have property equal to symbolic-value"
(let [afn (first (compile "if some neighbours have state equal to grassland then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11 and state should be grassland"
"if x is less than 2 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if some neighbours have state equal to grassland then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
;; more than number neighbours have property more than numeric-value
(testing "More than number neighbours have property more than number"
(let [afn (first (compile "if more than 2 neighbours have altitude more than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11 and state should be grassland"
"if x is less than 2 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
;; more than number neighbours have property more than numeric-value
(testing "More than number neighbours have property more than symbolic-value"
(let [afn (compile-rule "if more than 2 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
;; fewer than number neighbours have property more than numeric-value
;; fewer than number neighbours have property more than numeric-value
(testing "Fewer than number neighbours have property more than numeric-value"
(let [afn (first (compile "if fewer than 3 neighbours have altitude more than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if fewer than 3 neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell of world has three high neighbours, so rule should not fire.")))
;; some neighbours have property more than numeric-value
;; some neighbours have property more than numeric-value
(testing "Some neighbours have property more than numeric-value"
(let [afn (first (compile "if some neighbours have altitude more than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
(let [afn (compile-rule "if some neighbours have altitude more than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left hand side of world has no high neighbours, so rule should not fire.")))
;; more than number neighbours have property less than numeric-value
;; more than number neighbours have property less than numeric-value
(testing "More than number neighbours have property less than numeric-value"
(let [afn (first (compile "if more than 4 neighbours have altitude less than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only three low neighbours, so rule should not fire.")))
(let [afn (compile-rule "if more than 4 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 2 :y 1} world)))
"Middle cell of the strip has only three low neighbours, so rule should not fire.")))
;; fewer than number neighbours have property less than numeric-value
;; fewer than number neighbours have property less than numeric-value
(testing "Fewer than number neighbours have property less than numeric-value"
(let [afn (first (compile "if fewer than 4 neighbours have altitude less than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then altitude should be 0"))))]
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Centre cell has five low neighbours, so rule should not fire")
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Middle cell of the strip has only three low neighbours, so rule should fire.")))
(let [afn (compile-rule "if fewer than 4 neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is 2 then altitude should be 11")
(compile-rule "if x is less than 2 then altitude should be 0")))]
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Centre cell has five low neighbours, so rule should not fire")
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
"Middle cell of the strip has only three low neighbours, so rule should fire.")))
;; some neighbours have property less than numeric-value
;; some neighbours have property less than numeric-value
(testing "Some number neighbours have property less than numeric-value"
(let [afn (first (compile "if some neighbours have altitude less than 10 then state should be beach"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is less than 2 then altitude should be 11"
"if x is 2 then altitude should be 0"))))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left of world is all high, so rule should not fire.")))
(let [afn (compile-rule "if some neighbours have altitude less than 10 then state should be beach")
world (transform-world
(make-world 3 3)
(list (compile-rule "if x is less than 2 then altitude should be 11")
(compile-rule "if x is 2 then altitude should be 0")))]
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Left of world is all high, so rule should not fire.")))
;; 'single action' already tested in 'condition' tests above
;; action and actions
;; 'single action' already tested in 'condition' tests above
;; action and actions
(testing "Conjunction of actions"
(let [afn (first (compile "if state is new then state should be grassland and fertility should be 0"))]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland :fertility 0})
"Both actions are executed")))
(let [afn (compile-rule "if state is new then state should be grassland and fertility should be 0")]
(is (= (apply afn (list {:state :new} nil))
{:state :grassland :fertility 0})
"Both actions are executed")))
;; 'property should be symbolic-value' and 'property should be numeric-value'
;; already tested in tests above
;; 'property should be symbolic-value' and 'property should be numeric-value'
;; already tested in tests above
;; number chance in number property should be value
;; number chance in number property should be value
(testing "Syntax of probability rule - action of real probability very hard to test"
(let [afn (first (compile "if state is forest then 5 chance in 5 state should be climax"))]
(is (= (:state (apply afn (list {:state :forest} nil))) :climax)
"five chance in five should fire every time"))
(let [afn (first (compile "if state is forest then 0 chance in 5 state should be climax"))]
(is (nil? (apply afn (list {:state :forest} nil)))
"zero chance in five should never fire")))
(let [afn (compile-rule "if state is forest then 5 chance in 5 state should be climax")]
(is (= (:state (apply afn (list {:state :forest} nil))) :climax)
"five chance in five should fire every time"))
(let [afn (compile-rule "if state is forest then 0 chance in 5 state should be climax")]
(is (nil? (apply afn (list {:state :forest} nil)))
"zero chance in five should never fire")))
;; property operator numeric-value
;; property operator numeric-value
(testing "Arithmetic action: addition of number"
(let [afn (first (compile "if state is climax then fertility should be fertility + 1"))]
(is (= (:fertility
(apply afn (list {:state :climax :fertility 0} nil)))
1)
"Addition is executed")))
(let [afn (compile-rule "if state is climax then fertility should be fertility + 1")]
(is (= (:fertility
(apply afn (list {:state :climax :fertility 0} nil)))
1)
"Addition is executed")))
(testing "Arithmetic action: addition of property value"
(let [afn (first (compile "if state is climax then fertility should be fertility + leaffall"))]
(is (= (:fertility
(apply afn
(list {:state :climax
:fertility 0
:leaffall 1} nil)))
1)
"Addition is executed")))
(let [afn (compile-rule "if state is climax then fertility should be fertility + leaf-fall")]
(is (= (:fertility
(apply afn
(list {:state :climax
:fertility 0
:leaf-fall 1} nil)))
1)
"Addition is executed")))
(testing "Arithmetic action: subtraction of number"
(let [afn (first (compile "if state is crop then fertility should be fertility - 1"))]
(is (= (:fertility
(apply afn (list {:state :crop :fertility 2} nil)))
1)
"Action is executed")))
(let [afn (compile-rule "if state is crop then fertility should be fertility - 1")]
(is (= (:fertility
(apply afn (list {:state :crop :fertility 2} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: subtraction of property value"
(let [afn (first (compile "if wolves are more than 0 then deer should be deer - wolves"))]
(is (= (:deer
(apply afn
(list {:deer 3
:wolves 2} nil)))
1)
"Action is executed")))
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer - wolves")]
(is (= (:deer
(apply afn
(list {:deer 3
:wolves 2} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: multiplication by number"
(let [afn (first (compile "if deer are more than 1 then deer should be deer * 2"))]
(is (= (:deer
(apply afn (list {:deer 2} nil)))
4)
"Action is executed")))
(let [afn (compile-rule "if deer are more than 1 then deer should be deer * 2")]
(is (= (:deer
(apply afn (list {:deer 2} nil)))
4)
"Action is executed")))
(testing "Arithmetic action: multiplication by property value"
(let [afn (first (compile "if state is crop then deer should be deer * deer"))]
(is (= (:deer
(apply afn
(list {:state :crop :deer 2} nil)))
4)
"Action is executed")))
(let [afn (compile-rule "if state is crop then deer should be deer * deer")]
(is (= (:deer
(apply afn
(list {:state :crop :deer 2} nil)))
4)
"Action is executed")))
(testing "Arithmetic action: division by number"
(let [afn (first (compile "if wolves are more than 0 then deer should be deer / 2"))]
(is (= (:deer
(apply afn (list {:deer 2 :wolves 1} nil)))
1)
"Action is executed")))
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / 2")]
(is (= (:deer
(apply afn (list {:deer 2 :wolves 1} nil)))
1)
"Action is executed")))
(testing "Arithmetic action: division by property value"
(let [afn (first (compile "if wolves are more than 0 then deer should be deer / wolves"))]
(is (= (:deer
(apply afn
(list {:deer 2 :wolves 2} nil)))
1)
"Action is executed")))
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / wolves")]
(is (= (:deer
(apply afn
(list {:deer 2 :wolves 2} nil)))
1)
"Action is executed")))
;; simple within distance
;; simple within distance
(testing "Number neighbours within distance have property equal to value"
(let [afn (first (compile "if 8 neighbours within 2 have state equal to new then state should be water"))
world (make-world 5 5)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has eight neighbours within two)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has twenty-four neighbours within two, so rule does not fire.")))
(let [afn (compile-rule "if 8 neighbours within 2 have state equal to new then state should be water")
world (make-world 5 5)]
(is (= (apply afn (list {:x 0 :y 0} world))
{:state :water :x 0 :y 0})
"Rule fires when condition is met (in a new world all cells are new, corner cell has eight neighbours within two)")
(is (nil? (apply afn (list {:x 1 :y 1} world)))
"Middle cell has twenty-four neighbours within two, so rule does not fire.")))
;; comparator within distance
;; comparator within distance
(testing "More than number neighbours within distance have property equal to symbolic-value"
(let [afn (first (compile "if more than 7 neighbours within 2 have state equal to grassland and more than 7 neighbours within 2 have state equal to water then state should be beach"))
;; 5x5 world, strip of high ground two cells wide down left hand side
;; xxooo
;; xxooo
;; xxooo
;; xxooo
;; xxooo
world (transform-world
(make-world 5 5)
(compile
(join "\n"
(list "if state is new and x is less than 2 then altitude should be 11 and state should be grassland"
"if state is new and x is more than 1 then altitude should be 0 and state should be water"))))]
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))))
(deftest regression-tests
(testing "Rule in default set which failed on switchover to declarative rules"
(let [afn (first (compile "if state is scrub then 1 chance in 1 state should be forest"))
world (transform-world
(make-world 3 3)
(compile
(join "\n"
(list "if x is 2 then altitude should be 11"
"if x is less than 2 then state should be scrub"))))]
(is (= (:state (apply afn (list (get-cell world 1 1) world))) :forest)
"Centre cell is scrub, so rule should fire")
(is (= (apply afn (list (get-cell world 2 1) world)) nil)
"Middle cell of the strip is not scrub, so rule should not fire."))))
(let [afn (compile-rule "if more than 7 neighbours within 2 have state equal to grassland and more than 7 neighbours within 2 have state equal to water then state should be beach")
;; 5x5 world, strip of high ground two cells wide down left hand side
;; xxooo
;; xxooo
;; xxooo
;; xxooo
;; xxooo
world (transform-world
(make-world 5 5)
(list (compile-rule "if x is less than 2 then altitude should be 11 and state should be grassland")
(compile-rule "if x is more than 1 then altitude should be 0 and state should be water")))]
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
))

View file

@ -1,43 +0,0 @@
(ns mw-parser.flow-test
(:require ;; [clojure.pprint :as pprint]
[clojure.test :refer [deftest is testing]] ;; [mw-engine.core :refer [transform-world]]
[mw-parser.declarative :refer [parse]]
[mw-parser.simplify :refer [simplify]]))
(deftest parse-tests
(testing "flow-grammar"
(let [rule "flow 1 food from house having food more than 10 to house within 2 with least food"
expected '(:FLOW-RULE
(:FLOW "flow")
(:QUANTITY (:SIMPLE-EXPRESSION (:NUMBER "1")))
(:SYMBOL "food")
(:FROM "from")
(: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")))
(:TO-HOW (:TO "to"))
(:DESTINATION
(:TARGET
(:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
(:RANGE (:WITHIN "within") (:NUMBER "2")))
(:WITH "with")
(:DETERMINER-CONDITION (:DETERMINER (:LEAST "least")) (:SYMBOL "food"))))
actual (simplify (parse rule))]
(is (= actual expected) rule))
(let [rule "flow 10% food from house having food more than 10 to each house within 2 with food less than 4"
expected '(:FLOW-RULE
(:FLOW "flow")
(:QUANTITY (:PERCENTAGE (:NUMBER "10") "%"))
(:SYMBOL "food")
(:FROM "from")
(: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")))
(:TO-HOW (:TO-EACH (:TO "to") (:EACH "each")))
(:DESTINATION
(:TARGET (:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
(:RANGE (:WITHIN "within") (:NUMBER "2")))
(:WITH "with") (:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:LESS "less") (:THAN "than"))) (:NUMBER "4"))))
actual (simplify (parse rule))]
(is (= actual expected) rule))))

View file

@ -1,124 +0,0 @@
(ns mw-parser.generate-test
(:require [clojure.pprint :as pprint]
[clojure.test :refer [deftest is testing]]
[mw-engine.core :refer [apply-rule]]
[mw-engine.utils :refer [get-cell]]
[mw-parser.declarative :refer [compile parse]]
[mw-parser.generate :refer [generate]]
[mw-parser.simplify :refer [simplify]]))
(deftest expressions-tests
(testing "Generating primitive expressions."
(let [actual (generate '(:NUMERIC-EXPRESSION (:NUMBER "50")))
expected 50]
(is (= actual expected)))
(let [actual (generate '(:NUMERIC-EXPRESSION (:SYMBOL "sealevel")))
expected '(:sealevel cell)]
(is (= actual expected)))))
(deftest lhs-generators-tests
(testing "Generating left-hand-side fragments of rule functions from appropriate fragments of parse trees"
(let [expected '(= (:state cell) (or (:forest cell) :forest))
actual (generate
'(:PROPERTY-CONDITION
(:SYMBOL "state")
[:EQUIVALENCE [:IS "is"]]
(:SYMBOL "forest")))]
(is (= actual expected)))
(is (= (generate
'(:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10")))
'(= (:fertility cell) 10)))
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:LESS "less"]] (:NUMBER "10")))
'(< (:fertility cell) 10)))
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:MORE "more"]] (:NUMBER "10")))
'(> (:fertility cell) 10)))
(is (= (generate '(:CONJUNCT-CONDITION
(:PROPERTY-CONDITION
(:SYMBOL "state")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:SYMBOL "forest"))
(:PROPERTY-CONDITION
(:SYMBOL "fertility")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:NUMBER "10"))))
'(and (= (:state cell) (or (:forest cell) :forest)) (= (:fertility cell) 10))))
(is (= (generate '(:DISJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") (:EQUIVALENCE (:IS "is")) (:SYMBOL "forest")) (:PROPERTY-CONDITION (:SYMBOL "fertility") (:EQUIVALENCE (:IS "is")) (:NUMBER "10"))))
'(or (= (:state cell) (or (:forest cell) :forest)) (= (:fertility cell) 10))))
(is (= (generate '(:PROPERTY-CONDITION
(:SYMBOL "state")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:DISJUNCT-EXPRESSION
(:SYMBOL "heath")
(:SYMBOL "scrub")
(:SYMBOL "forest"))))
'(#{:scrub :forest :heath} (:state cell))))
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "altitude") [:EQUIVALENCE [:IS "is"]] (:RANGE-EXPRESSION (:BETWEEN "between") (:NUMERIC-EXPRESSION (:NUMBER "50")) (:AND "and") (:NUMERIC-EXPRESSION (:NUMBER "100")))))
'(let [lower (min 50 100) upper (max 50 100)] (and (>= (:altitude cell) lower) (<= (:altitude cell) upper)))))))
(deftest rhs-generators-tests
(testing "Generating right-hand-side fragments of rule functions from appropriate fragments of parse trees"
(is (= (generate
'(:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax")))
'(merge cell {:state :climax})))
(is (= (generate
'(:SIMPLE-ACTION (:SYMBOL "fertility") (:BECOMES "should be") (:NUMBER "10")))
'(merge cell {:fertility 10})))))
(deftest full-generation-tests
(testing "Full rule generation from pre-parsed tree"
(let [rule '(:RULE
(:IF "if")
(:PROPERTY-CONDITION
(:SYMBOL "state")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:SYMBOL "forest"))
(:ACTIONS
(:SIMPLE-ACTION
(:SYMBOL "state")
(:BECOMES "should be")
(:SYMBOL "climax"))))
expected '(fn* ([cell world]
(when
(= (:state cell) (or (:forest cell) :forest))
(merge cell {:state :climax}))))
actual (generate rule)
expected-meta {:rule-type :production}
actual-meta (meta actual)]
(is (= actual expected))
(is (= actual-meta expected-meta)))))
(deftest metadata-tests
(testing "Rules have correct metadata"
(let [expected :production
actual (:rule-type
(meta
(generate
(simplify
(parse "if state is house then state should be waste")))))]
(is (= actual expected)))
(let [expected :flow
actual (:rule-type
(meta
(generate
(simplify
(parse "flow 10% food from house to house within 2 with least food")))))]
(is (= actual expected)))))
(deftest chance-bug-test
(testing "exception thrown when evaluating``"
(let [cell {:y 1, :generation 10,
:state :scrub, :gradient 85,
:x 1, :altitude 92}
world [[{:y 0, :state :new, :x 0} {:y 0, :state :new, :x 1} {:y 0, :state :new, :x 2}]
[{:y 1, :state :new, :x 0} cell {:y 1, :state :new, :x 2}]
[{:y 2, :state :new, :x 0} {:y 2, :state :new, :x 1} {:y 2, :state :new, :x 2}]]
rule (first (compile "if state is scrub then 1 chance in 5 state should be forest"))
expected #{:scrub :forest}
cell' (reduce
(fn [c i] (merge (or (apply-rule world c rule) c) {:i i}))
cell
(range 20))
actual (:state cell')]
(pprint/pprint cell')
(is (expected actual)))))

View file

@ -1,98 +0,0 @@
(ns mw-parser.simplify-test
(:require [clojure.test :refer [deftest is testing]]
[mw-parser.declarative :refer [parse]]
[mw-parser.simplify :refer [simplify]]
[mw-parser.utils :refer [search-tree]]))
((deftest disjunct-condition-test
(testing "Generation of disjunct conditions has been producing wrong
output -- in a way which didn't actually alter the
correctness of the rule -- since the beginning, and because
of inadequate and badly written tests, I didn't know it."
(let [expected '(:DISJUNCT-CONDITION
(:PROPERTY-CONDITION
(:SYMBOL "state")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:SYMBOL "forest"))
(:PROPERTY-CONDITION
(:SYMBOL "fertility")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:NUMBER "10")))
actual (simplify [:DISJUNCT-CONDITION
[:CONDITION
[:PROPERTY-CONDITION
[:PROPERTY [:SYMBOL "state"]]
[:SPACE " "]
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
[:SPACE " "]
[:EXPRESSION [:VALUE [:SYMBOL "forest"]]]]]
[:SPACE " "]
[:OR "or"]
[:SPACE " "]
[:CONDITIONS
[:CONDITION
[:PROPERTY-CONDITION
[:PROPERTY [:SYMBOL "fertility"]]
[:SPACE " "]
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
[:SPACE " "]
[:EXPRESSION [:VALUE [:NUMBER "10"]]]]]]])]
(is (= actual expected))))))
(deftest conjunct-condition-test
(testing "Conjunct conditions were failing in more or less the same way"
(let [expected '(:CONJUNCT-CONDITION
(:PROPERTY-CONDITION
(:SYMBOL "state")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:SYMBOL "forest"))
(:PROPERTY-CONDITION
(:SYMBOL "fertility")
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
(:NUMBER "10")))
actual (simplify [:CONJUNCT-CONDITION
[:CONDITION
[:PROPERTY-CONDITION
[:PROPERTY [:SYMBOL "state"]]
[:SPACE " "]
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
[:SPACE " "]
[:EXPRESSION [:VALUE [:SYMBOL "forest"]]]]]
[:SPACE " "]
[:AND "and"]
[:SPACE " "]
[:CONDITIONS
[:CONDITION
[:PROPERTY-CONDITION
[:PROPERTY [:SYMBOL "fertility"]]
[:SPACE " "]
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
[:SPACE " "]
[:EXPRESSION [:VALUE [:NUMBER "10"]]]]]]])]
(is (= actual expected)))))
((deftest unchained-disjuncts-test
(testing "Disjunct values should not be chained"
(let [wrong '(:DISJUNCT-EXPRESSION
(:IN "in")
(:DISJUNCT-VALUE
(:SYMBOL "heath")
(:DISJUNCT-VALUE
(:SYMBOL "scrub")
(:DISJUNCT-VALUE (:SYMBOL "forest")))))
parse-tree (search-tree
(parse
"if state is not in heath or scrub or forest then state should be climax")
:DISJUNCT-EXPRESSION)
actual (simplify parse-tree)]
(is (not (= wrong actual))))
(let [expected '(:DISJUNCT-EXPRESSION
(:SYMBOL "heath")
(:SYMBOL "scrub")
(:SYMBOL "forest"))
parse-tree (search-tree
(parse
"if state is not in heath or scrub or forest then state should be climax")
:DISJUNCT-EXPRESSION)
actual (simplify parse-tree)]
(is (= expected actual))))))