Compare commits
27 commits
Author | SHA1 | Date | |
---|---|---|---|
|
74fbd2efe9 | ||
|
3829bd97a9 | ||
|
a436499d98 | ||
|
b4f796aca4 | ||
|
8c2e44b42a | ||
|
4b721219bd | ||
|
4de7b0beb4 | ||
|
93a0f3ea1d | ||
|
2a5d598f28 | ||
|
256f9efd5e | ||
|
fb39f1ee9c | ||
|
ca3861b505 | ||
|
bbaca4710b | ||
|
fe92045f14 | ||
|
311ebafa5c | ||
|
22b179a675 | ||
|
11090d63ef | ||
|
5089615401 | ||
|
a68a3c9135 | ||
|
88d707a32e | ||
|
ddf967088e | ||
|
948bd7e5f2 | ||
|
ca9553fe83 | ||
|
3168c1b2fb | ||
|
d2a73ba408 | ||
|
9836cbff50 | ||
|
1c6ceb899c |
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
buildall.tmp.*
|
||||
.lein-failures
|
||||
.lein-repl-history
|
||||
target/
|
||||
pom.xml
|
||||
.calva/
|
||||
.clj-kondo/
|
||||
.lsp/
|
||||
.nrepl-port
|
||||
|
||||
doc/scratch.clj
|
||||
|
||||
src/mw_parser/scratch.clj
|
46
README.md
46
README.md
|
@ -13,15 +13,49 @@ 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 (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.
|
||||
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.
|
||||
|
||||
The function (compile-rule _string_) is like parse-rule, except that it returns
|
||||
a compiled Clojure anonymous function.
|
||||
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.
|
||||
|
||||
### Generated function and evaluation environment
|
||||
|
||||
|
|
40
docs/cloverage/coverage.css
Normal file
40
docs/cloverage/coverage.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
.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;
|
||||
}
|
149
docs/cloverage/index.html
Normal file
149
docs/cloverage/index.html
Normal file
|
@ -0,0 +1,149 @@
|
|||
<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>
|
194
docs/cloverage/mw_parser/bulk.clj.html
Normal file
194
docs/cloverage/mw_parser/bulk.clj.html
Normal file
|
@ -0,0 +1,194 @@
|
|||
<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 (ns ^{:doc "parse multiple rules from a stream, possibly a file."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.bulk
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:use mw-parser.core
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 mw-engine.utils
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 clojure.java.io
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [clojure.string :only [split trim]])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 (:import (java.io BufferedReader StringReader)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
009
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;;; mw-parser: a rule parser for MicroWorld.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;;;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;;;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;;;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;;;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;;;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ;;;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 ;;;; Copyright (C) 2014 Simon Brooke
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defn comment?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "Is this `line` a comment?"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 [line]
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
037 (or (empty? (trim line)) (member? (first line) '(nil \# \;))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (defn parse-string
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 "Parse rules from successive lines in this `string`, assumed to have multiple
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 lines delimited by the new-line character. Return a list of S-expressions."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 [string]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ;; TODO: tried to do this using with-open, but couldn't make it work.
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
044 (map #(parse-rule (trim %)) (remove comment? (split string #"\n"))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
045
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
046 (defn parse-file
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 "Parse rules from successive lines in the file loaded from this `filename`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 Return a list of S-expressions."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 [filename]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
050 (parse-string (slurp filename)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
051
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
052 (defn compile-string
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 "Compile each non-comment line of this `string` into an executable anonymous
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 function, and return the sequence of such functions."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 [string]
|
||||
</span><br/>
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
056 (map #(compile-rule % true) (remove comment? (split string #"\n"))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
057
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
058 (defn compile-file
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 "Compile each non-comment line of the file indicated by this `filename` into
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 an executable anonymous function, and return the sequence of such functions."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 [filename]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
062 (compile-string (slurp filename)))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
1361
docs/cloverage/mw_parser/core.clj.html
Normal file
1361
docs/cloverage/mw_parser/core.clj.html
Normal file
File diff suppressed because it is too large
Load diff
479
docs/cloverage/mw_parser/declarative.clj.html
Normal file
479
docs/cloverage/mw_parser/declarative.clj.html
Normal file
|
@ -0,0 +1,479 @@
|
|||
<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 (ns ^{:doc "A very simple parser which parses production rules."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.declarative
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [instaparse.core :refer [parser]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [clojure.string :refer [join trim]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [mw-parser.errors :refer [throw-parse-exception]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [mw-parser.generate :refer [generate]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [mw-parser.simplify :refer [simplify]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [mw-parser.utils :refer [rule?]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [trptr.java-wrapper.locale :refer [get-default]])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 (:import [java.util Locale]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;;; mw-parser: a rule parser for MicroWorld.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;;;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;;;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;;;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ;;;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ;;;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ;;;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 ;;;; Copyright (C) 2014 Simon Brooke
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
036 (def rule-grammar
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "Basic rule language grammar.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 in order to simplify translation into other natural languages, all
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 TOKENS within the parser should be unambiguou."
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
041 (join "\n" ["RULE := IF SPACE CONDITIONS SPACE THEN SPACE ACTIONS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 "ACTIONS := ACTION | ACTION SPACE AND SPACE ACTIONS"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 "ACTION := SIMPLE-ACTION | PROBABLE-ACTION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 "PROBABLE-ACTION := VALUE SPACE CHANCE-IN SPACE VALUE SPACE SIMPLE-ACTION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 "SIMPLE-ACTION := SYMBOL SPACE BECOMES SPACE EXPRESSION;"]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
046
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
047 (def common-grammar
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 "Grammar rules used both in the rule grammar and in the flow grammar"
|
||||
</span><br/>
|
||||
<span class="covered" title="30 out of 30 forms covered">
|
||||
049 (join "\n" ["COMPARATIVE := MORE | LESS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 "COMPARATIVE-QUALIFIER := IS SPACE COMPARATIVE SPACE THAN | COMPARATIVE SPACE THAN;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 "CONDITION := WITHIN-CONDITION | NEIGHBOURS-CONDITION | PROPERTY-CONDITION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 "CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | CONDITION ;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 "CONJUNCT-CONDITION := CONDITION SPACE AND SPACE CONDITIONS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 "DISJUNCT-CONDITION := CONDITION SPACE OR SPACE CONDITIONS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 "DISJUNCT-EXPRESSION := IN SPACE DISJUNCT-VALUE;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 "DISJUNCT-VALUE := VALUE | VALUE SPACE OR SPACE DISJUNCT-VALUE;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 "EQUIVALENCE := IS SPACE EQUAL | EQUAL | IS ;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 "EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 "NEGATED-QUALIFIER := QUALIFIER SPACE NOT | NOT SPACE QUALIFIER;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 "NEIGHBOURS-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE IS SPACE PROPERTY-CONDITION | QUALIFIER SPACE NEIGHBOURS-CONDITION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 "NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 "NUMERIC-EXPRESSION := VALUE | VALUE SPACE OPERATOR SPACE NUMERIC-EXPRESSION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 "OPERATOR := '+' | '-' | '*' | '/';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 "PROPERTY := SYMBOL;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 "PROPERTY-CONDITION := PROPERTY SPACE QUALIFIER SPACE EXPRESSION | VALUE;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 "PROPERTY-CONDITION-OR-EXPRESSION := PROPERTY-CONDITION | EXPRESSION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 "QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS SPACE QUALIFIER;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 "QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE SPACE THAN SPACE NUMBER;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 "RANGE-EXPRESSION := BETWEEN SPACE NUMERIC-EXPRESSION SPACE AND SPACE NUMERIC-EXPRESSION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 "SIMPLE-EXPRESSION := QUALIFIER SPACE EXPRESSION | VALUE;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 "SPACE := #'\\s+';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 "VALUE := SYMBOL | NUMBER;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 "VALUE := SYMBOL | NUMBER;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 "WITHIN-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE WITHIN SPACE NUMBER SPACE IS SPACE PROPERTY-CONDITION-OR-EXPRESSION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 ]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
076
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
077 (def keywords-en
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 "English language keyword literals used in rules - both in production
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 rules (this namespace) and in flow rules (see mw-parser.flow).
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 It's a long term aim that the rule language should be easy to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 internationalise; this isn't a full solution but it's a step towards
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 a solution."
|
||||
</span><br/>
|
||||
<span class="covered" title="32 out of 32 forms covered">
|
||||
084 (join "\n" ["ALL := 'all'"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 "AND := 'and';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 "BECOMES := 'should be' | 'becomes';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 "BETWEEN := 'between';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 "CHANCE-IN := 'chance in';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 "EACH := 'each' | 'every' | 'all';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "EQUAL := 'equal to';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 "FIRST := 'first';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 "FLOW := 'flow' | 'move';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 "FROM := 'from';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 "IF := 'if';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 "IN := 'in';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 "IS := 'is' | 'are' | 'have' | 'has';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 "LEAST := 'least';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 "LESS := 'less' | 'fewer';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 "MORE := 'more' | 'greater';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 "MOST := 'most';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 "NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 "NONE := 'no';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 "NOT := 'not';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 "OR := 'or';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
105 "SOME := 'some';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 ;; SYMBOL is in the per-language file so that languages that use
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 ;; (e.g.) Cyrillic characters can change the definition.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 "SYMBOL := #'[a-z]+';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 "THAN := 'than';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 "THEN := 'then';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 "TO := 'to';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 "WITH := 'with' | 'where' | 'having';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
113 "WITHIN := 'within';"]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
114
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
115 (defn keywords-for-locale
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 "For now, just return `keywords-en`; plan is to have resource files of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
117 keywords for different languages in a resource directory, but that isn't
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 done yet. It's probably not going to work easily for languages that use
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 non-latin alphabets, anyway."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 ([]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
121 (keywords-for-locale (get-default)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 ([^Locale _locale]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
123 keywords-en))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
124
|
||||
</span><br/>
|
||||
<span class="covered" title="58 out of 58 forms covered">
|
||||
125 (defmacro build-parser
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
126 "Compose this grammar fragment `g` with the common grammar fragments to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 make a complete grammar, and return a parser for that complete grammar."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
128 [g]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
129 `(parser (join "\n" [~g common-grammar (keywords-for-locale)])))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
130
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
131 (def parse-rule
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 "Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
133 (build-parser rule-grammar))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
134
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
135 (defn compile-rule
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 "Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 into Clojure source, and then compile it into an anonymous
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
138 function object, getting round the problem of binding mw-engine.utils in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 the compiling environment. If `return-tuple?` is present and true, return
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 a list comprising the anonymous function compiled, and the function from
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 which it was compiled.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
142
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
143 Throws an exception if parsing fails."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
144 ([rule-text return-tuple?]
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 14 forms covered">
|
||||
145 (assert (string? rule-text))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
146 (let [rule (trim rule-text)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
147 tree (simplify (parse-rule rule))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
148 afn (if (rule? tree) (eval (generate tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 ;; else
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
150 (throw-parse-exception tree))]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
151 (if return-tuple?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
152 (list afn rule)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 ;; else
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
154 afn)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
155 ([rule-text]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
156 (compile-rule rule-text false)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
157
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
212
docs/cloverage/mw_parser/errors.clj.html
Normal file
212
docs/cloverage/mw_parser/errors.clj.html
Normal file
|
@ -0,0 +1,212 @@
|
|||
<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 (ns ^{:doc "Display parse errors in a format which makes it easy for the user
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 to see where the error occurred."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 mw-parser.errors)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
005
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
024
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;; error thrown when an attempt is made to set a reserved property
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
027 (def reserved-properties-error
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 "The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 ;; error thrown when a rule cannot be parsed. Slots are for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ;; (1) rule text
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 ;; (2) cursor showing where in the rule text the error occurred
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 ;; (3) the reason for the error
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
033 (def bad-parse-error "I did not understand:\n '%s'\n %s\n %s")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
034
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
036 (defn- explain-parse-error-reason
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "Attempt to explain the reason for the parse error."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 [reason]
|
||||
</span><br/>
|
||||
<span class="covered" title="17 out of 17 forms covered">
|
||||
039 (str "Expecting one of (" (apply str (map #(str (:expecting %) " ") reason)) ")"))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
040
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
041
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
042 (defn- parser-error-to-map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 [parser-error]
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
044 (let [m (reduce (fn [map item](merge map {(first item)(second item)})) {} parser-error)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
045 reason (map
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
046 #(reduce (fn [map item] (merge {(first item) (second item)} map)) {} %)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
047 (:reason m))]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
048 (merge m {:reason reason})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
049
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
050
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
051 (defn throw-parse-exception
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 "Construct a helpful error message from this `parser-error`, and throw an exception with that message."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 [parser-error]
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 16 forms covered">
|
||||
054 (assert (coll? parser-error) "Expected a paser error structure?")
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
055 (let
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 [
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 ;; the error structure is a list, such that each element is a list of two items, and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 ;; the first element in each sublist is a keyword. Easier to work with it as a map
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
059 error-map (parser-error-to-map parser-error)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
060 text (:text error-map)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
061 reason (explain-parse-error-reason (:reason error-map))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 ;; rules have only one line, by definition; we're interested in the column
|
||||
</span><br/>
|
||||
<span class="partial" title="7 out of 8 forms covered">
|
||||
063 column (if (:column error-map)(:column error-map) 0)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 ;; create a cursor to point to that column
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
065 cursor (apply str (reverse (conj (repeat column " ") "^")))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
066 message (format bad-parse-error text cursor reason)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 ]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
068 (throw (Exception. message))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
209
docs/cloverage/mw_parser/flow.clj.html
Normal file
209
docs/cloverage/mw_parser/flow.clj.html
Normal file
|
@ -0,0 +1,209 @@
|
|||
<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 (ns ^{:doc "A very simple parser which parses flow rules."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.flow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [clojure.string :refer [join]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [mw-parser.declarative :refer [build-parser]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [mw-parser.simplify :refer [simplify-second-of-two]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (def flow-grammar
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "Grammar for flow rules.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 My initial conception of this would be that production rules
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 (if-then rules) and flow rules (flow-from-to rules) would be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 entirely separate, presented to the parser as separate text
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 files, and parsed and compiled by different chains of functions.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 This appears not to be necessary. Flow rules are easy to parse
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 with the same parser as production rules -- a lot of the grammar
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 is intentionally common -- and the rules are easily discriminated
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 at the compilation ('generate') stage.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 The basic rule I want to be able to compile at this stage is the 'mutual
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 aid' rule:
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 `flow 1 food from house having food > 1 to house with least food within 2`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 "
|
||||
</span><br/>
|
||||
<span class="covered" title="16 out of 16 forms covered">
|
||||
026 (join "\n" ["FLOW-RULE := FLOW SPACE QUANTITY SPACE PROPERTY SPACE FROM SPACE SOURCE SPACE TO-HOW SPACE DESTINATION;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 "PERCENTAGE := NUMBER #'%';"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 "QUANTITY := PERCENTAGE | NUMBER | EXPRESSION | SOME;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 "SOURCE := STATE | STATE SPACE WITH SPACE CONDITIONS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 "DESTINATION := STATE | STATE SPACE WITH SPACE FLOW-CONDITIONS | STATE SPACE WITHIN SPACE VALUE SPACE WITH SPACE FLOW-CONDITIONS;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 "DETERMINER := MOST | LEAST;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 "DETERMINER-CONDITION := DETERMINER SPACE PROPERTY | DETERMINER SPACE PROPERTY;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 "FLOW-CONDITIONS := DETERMINER-CONDITION | CONDITIONS"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 "STATE := SYMBOL;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "TO-HOW := TO | TO-EACH | TO-FIRST;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 "TO-EACH := TO SPACE EACH | TO SPACE ALL;"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "TO-FIRST := TO SPACE FIRST"]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (def parse-flow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 "Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
041 (build-parser flow-grammar))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
042
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
043 (defn simplify-flow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
045 (if (coll? tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
046 (case (first tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
047 :CONDITION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
048 :CONDITIONS (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
049 :DETERMINER (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 ;; :DETERMINER-CONDITION (simplify-determiner-condition tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
051 :EXPRESSION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 :FLOW nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 ;; :FLOW-CONDITIONS (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
054 :PROPERTY (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
055 :PROPERTY-CONDITION-OR-EXPRESSION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 :SPACE nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
057 :QUANTITY (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
058 :STATE (list :PROPERTY-CONDITION
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
059 (list :SYMBOL "state")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 '(:QUALIFIER
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 (:EQUIVALENCE
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 (:IS "is")))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
063 (list :EXPRESSION
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
064 (list :VALUE (second tree))))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
065 (remove nil? (map simplify-flow tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
066 tree))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
067
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
992
docs/cloverage/mw_parser/generate.clj.html
Normal file
992
docs/cloverage/mw_parser/generate.clj.html
Normal file
|
@ -0,0 +1,992 @@
|
|||
<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 (ns ^{:doc "Generate Clojure source from simplified parse trees."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.generate
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [clojure.pprint :refer [pprint]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [clojure.tools.trace :refer [deftrace]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [mw-parser.utils :refer [assert-type TODO]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [mw-parser.errors :as pe]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
028 (declare generate generate-action)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
029
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
030 (defn generate-rule
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 "From this `tree`, assumed to be a syntactically correct rule specification,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 generate and return the appropriate rule as a function of two arguments."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
034 (assert-type tree :RULE)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
035 (vary-meta
|
||||
</span><br/>
|
||||
<span class="covered" title="19 out of 19 forms covered">
|
||||
036 (list 'fn ['cell 'world] (list 'when (generate (nth tree 2)) (generate (nth tree 3))))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
037 merge
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
038 {:rule-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 :production}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
040
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
041 (defn generate-conditions
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 "From this `tree`, assumed to be a syntactically correct conditions clause,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 [tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
045 (assert-type tree :CONDITIONS)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
046 (generate (second tree)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
048 (defn generate-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "From this `tree`, assumed to be a syntactically correct condition clause,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 [tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
052 (assert-type tree :CONDITION)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
053 (generate (second tree)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
054
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
055 (defn generate-conjunct-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 "From this `tree`, assumed to be a syntactically conjunct correct condition clause,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
059 (assert-type tree :CONJUNCT-CONDITION)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
060 (cons 'and (map generate (rest tree))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
061
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
062 (defn generate-disjunct-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 "From this `tree`, assumed to be a syntactically correct disjunct condition clause,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
066 (assert-type tree :DISJUNCT-CONDITION)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
067 (cons 'or (map generate (rest tree))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
068
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
069 (defn generate-ranged-property-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 "From this `tree`, assumed to be a syntactically property condition clause for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 this `property` where the `expression` is a numeric range, generate and return
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 [tree property expression]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
074 (assert-type tree :PROPERTY-CONDITION)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
075 (assert-type (nth tree 3) :RANGE-EXPRESSION)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
076 (let [l1 (generate (nth expression 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
077 l2 (generate (nth expression 4))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
078 pv (list property 'cell)]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
079 (list 'let ['lower (list 'min l1 l2)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
080 'upper (list 'max l1 l2)]
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
081 (list 'and (list '>= pv 'lower) (list '<= pv 'upper)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
082
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
083 (defn generate-disjunct-property-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 "From this `tree`, assumed to be a syntactically property condition clause
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 where the expression is a a disjunction, generate and return
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 the appropriate clojure fragment.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 TODO: this is definitely still wrong!"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 ([tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
089 (let [property (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
090 qualifier (generate (nth tree 2))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
091 expression (generate (nth tree 3))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
092 (generate-disjunct-property-condition tree property qualifier expression)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 ([_tree property qualifier expression]
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
094 (let [e (list expression (list property 'cell))]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
095 (if (= qualifier '=) e
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
096 (list 'not e)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
097
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
098 (defn generate-property-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 "From this `tree`, assumed to be a syntactically property condition clause,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 ([tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
102 (assert-type tree :PROPERTY-CONDITION)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
103 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="15 out of 15 forms covered">
|
||||
104 (and (= (count tree) 2) (= (first (second tree)) :SYMBOL))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
105 ;; it's a shorthand for 'state equal to symbol'. This should probably have
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 ;; been handled in simplify...
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
107 (generate-property-condition
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
108 (list
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 :PROPERTY-CONDITION
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 '(:SYMBOL "state")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 '(:QUALIFIER (:EQUIVALENCE (:EQUAL "equal to")))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
112 (second tree)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
113 ;; otherwise...
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
114 (generate-property-condition tree (first (nth tree 3)))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
115 ([tree expression-type]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
116 (assert-type tree :PROPERTY-CONDITION)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
117 (let [property (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
118 qualifier (generate (nth tree 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
119 e (generate (nth tree 3))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
120 expression (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="20 out of 20 forms covered">
|
||||
121 (and (not (= qualifier '=)) (keyword? e)) (list 'or (list e 'cell) e)
|
||||
</span><br/>
|
||||
<span class="covered" title="20 out of 20 forms covered">
|
||||
122 (and (not (= qualifier 'not=)) (keyword? e)) (list 'or (list e 'cell) e)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
123 :else e)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
124 (case expression-type
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
125 :DISJUNCT-EXPRESSION (generate-disjunct-property-condition tree property qualifier expression)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
126 :RANGE-EXPRESSION (generate-ranged-property-condition tree property expression)
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
127 (list qualifier (list property 'cell) expression)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
128
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
129 (defn generate-qualifier
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 "From this `tree`, assumed to be a syntactically correct qualifier,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
131 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
133 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
134 (= (count tree) 2)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
135 (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 ;; else
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
137 (generate (nth tree 2))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
138
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
139 (defn generate-simple-action
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 "From this `tree`, assumed to be a syntactically correct simple action,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
142 ([tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
143 (assert-type tree :SIMPLE-ACTION)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
144 (generate-simple-action tree []))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 ([tree others]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
146 (assert-type tree :SIMPLE-ACTION)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
147 (let [property (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
148 expression (generate (nth tree 3))]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
149 (if (or (= property :x) (= property :y))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
150 (throw (Exception. pe/reserved-properties-error))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
151 (list 'merge
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
152 (if (empty? others) 'cell
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 ;; else
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
154 (generate others))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
155 {property expression})))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
156
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
157 (defn generate-probable-action
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 "From this `tree`, assumed to be a syntactically correct probable action,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
159 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
160 ([tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
161 (assert-type tree :PROBABLE-ACTION)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
162 (generate-probable-action tree []))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
163 ([tree others]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
164 (assert-type tree :PROBABLE-ACTION)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
165 (let
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
166 [chances (generate (nth tree 1))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
167 total (generate (nth tree 2))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
168 action (generate-action (nth tree 3) others)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
169 ;; TODO: could almost certainly be done better with macro syntax
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
170 (list 'if
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
171 (list '< (list 'rand total) chances)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
172 action))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
173
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
174 (defn generate-action
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
175 "From this `tree`, assumed to be a syntactically correct action,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
176 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
177 [tree others]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
178 (case (first tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
179 :ACTIONS (generate-action (first tree) others)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
180 :SIMPLE-ACTION (generate-simple-action tree others)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
181 :PROBABLE-ACTION (generate-probable-action tree others)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
182 (throw (Exception. (str "Not a known action type: " (first tree))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
183
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
184 (defn generate-multiple-actions
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
185 "From this `tree`, assumed to be one or more syntactically correct actions,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
186 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
187 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
188 (assert-type tree :ACTIONS)
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
189 (generate-action (first (rest tree)) (second (rest tree))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
190
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
191 (defn generate-disjunct-value
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
192 "Generate a disjunct value. Essentially what we need here is to generate a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
193 flat list of values, since the `member` has already been taken care of."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
194 [tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
195 (assert-type tree :DISJUNCT-VALUE)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
196 (if (= (count tree) 4)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
197 (cons (generate (second tree)) (generate (nth tree 3)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
198 (list (generate (second tree)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
199
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
200 (defn generate-numeric-expression
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
201 "From this `tree`, assumed to be a syntactically correct numeric expression,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
202 generate and return the appropriate clojure fragment."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
203 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
204 (assert-type tree :NUMERIC-EXPRESSION)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
205 (case (count tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
206 4 (let [[p operator expression] (rest tree)
|
||||
</span><br/>
|
||||
<span class="partial" title="8 out of 9 forms covered">
|
||||
207 property (if (number? p) p (list p 'cell))]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
208 (list (generate operator) (generate property) (generate expression)))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
209 (case (first (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
210 :SYMBOL (list (keyword (second (second tree))) 'cell)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
211 (generate (second tree)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
212
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
213 (defn generate-neighbours-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
214 "Generate code for a condition which refers to neighbours."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
215 ([tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
216 (assert-type tree :NEIGHBOURS-CONDITION)
|
||||
</span><br/>
|
||||
<span class="partial" title="7 out of 13 forms covered">
|
||||
217 (case (first (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
218 :NUMBER (read-string (second (second tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
219 :QUANTIFIER (generate-neighbours-condition tree (first (second (second tree))))
|
||||
</span><br/>
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
220 :QUALIFIER (cons (generate (second tree)) (rest (generate (nth tree 2))))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
221 ([tree quantifier-type]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
222 (let [quantifier (second tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
223 pc (generate (nth tree 4))]
|
||||
</span><br/>
|
||||
<span class="partial" title="3 out of 9 forms covered">
|
||||
224 (case quantifier-type
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
225 :NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc 1)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
226 :SOME (generate-neighbours-condition '> 0 pc 1)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
227 :MORE (let [value (generate (nth quantifier 3))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
228 (generate-neighbours-condition '> value pc 1))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
229 :LESS (let [value (generate (nth quantifier 3))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
230 (generate-neighbours-condition '< value pc 1)))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
231 ([comp1 quantity property-condition distance]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
232 (list comp1
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
233 (list 'count
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
234 (list 'remove 'false?
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
235 (list 'map (list 'fn ['cell] property-condition)
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
236 (list 'mw-engine.utils/get-neighbours 'world 'cell distance)))) quantity))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
237 ([comp1 quantity property-condition]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
238 (generate-neighbours-condition comp1 quantity property-condition 1)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
239
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
240 (defn generate-within-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
241 "Generate code for a condition which refers to neighbours within a specified distance.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
242 NOTE THAT there's clearly masses of commonality between this and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
243 `generate-neighbours-condition`, and that some refactoring is almost certainly
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
244 desirable. It may be that it's better to simplify a `NEIGHBOURS-CONDITION`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
245 into a `WITHIN-CONDITION` in the simplification stage."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
246 ([tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
247 (assert-type tree :WITHIN-CONDITION)
|
||||
</span><br/>
|
||||
<span class="partial" title="7 out of 13 forms covered">
|
||||
248 (case (first (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
249 :QUANTIFIER (generate-within-condition tree (first (second (second tree))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
250 :QUALIFIER (TODO "qualified within... help!")))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
251 ([tree quantifier-type]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
252 (let [quantifier (second tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
253 distance (generate (nth tree 4))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
254 pc (generate (nth tree 6))]
|
||||
</span><br/>
|
||||
<span class="partial" title="3 out of 9 forms covered">
|
||||
255 (case quantifier-type
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
256 :NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc distance)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
257 :SOME (generate-neighbours-condition '> 0 pc distance)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
258 :MORE (let [value (generate (nth quantifier 3))]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
259 (generate-neighbours-condition '> value pc distance))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
260 :LESS (let [value (generate (nth quantifier 3))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
261 (generate-neighbours-condition '< value pc distance))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
262
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
263 (defn- generate-disjunct-expression
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
264 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
265 (assert-type tree :DISJUNCT-EXPRESSION)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
266 (try
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
267 (set (map generate (rest tree)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
268 (catch Exception x
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
269 (throw
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
270 (ex-info
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
271 "Failed to compile :DISJUNCT-EXPRESSION"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
272 {:tree tree}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
273 x)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
274
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
275 ;;; Flow rules. A flow rule DOES NOT return a modified world; instead, it
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
276 ;;; returns a PLAN to modify the world, in the form of a sequence of `flows`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
277 ;;; It is only when the plan is executed that the world is modified.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
278 ;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
279 ;;; so we're looking at something like
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
280 ;;; (fn [cell world])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
281 ;;; (if (= (:state cell) (or (:house cell) :house))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
282
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
283 (defn generate-flow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
284 [tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
285 (assert-type tree :FLOW-RULE))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
286
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
287 ;;; Top level; only function anything outside this file (except tests) should
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
288 ;;; really call.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
289
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
290 (defn generate
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
291 "Generate code for this (fragment of a) parse tree"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
292 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
293 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
294 (coll? tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
295 (case (first tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
296 :ACTIONS (generate-multiple-actions tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
297 :COMPARATIVE (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
298 :COMPARATIVE-QUALIFIER (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
299 :CONDITION (generate-condition tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
300 :CONDITIONS (generate-conditions tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
301 :CONJUNCT-CONDITION (generate-conjunct-condition tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
302 :DISJUNCT-CONDITION (generate-disjunct-condition tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
303 :DISJUNCT-EXPRESSION (generate-disjunct-expression tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
304 :DISJUNCT-VALUE (generate-disjunct-value tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
305 :EQUIVALENCE '=
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
306 :EXPRESSION (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
307 :FLOW-RULE (generate-flow tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
308 :LESS '<
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
309 :MORE '>
|
||||
</span><br/>
|
||||
<span class="partial" title="8 out of 16 forms covered">
|
||||
310 :NEGATED-QUALIFIER (case (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
311 = 'not=
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
312 > '<
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
313 < '>)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
314 :NEIGHBOURS-CONDITION (generate-neighbours-condition tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
315 :NUMERIC-EXPRESSION (generate-numeric-expression tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
316 :NUMBER (read-string (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
317 :OPERATOR (symbol (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
318 :PROBABLE-ACTION (generate-probable-action tree)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
319 :PROPERTY (list (generate (second tree)) 'cell) ;; dubious - may not be right
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
320 :PROPERTY-CONDITION (generate-property-condition tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
321 :QUALIFIER (generate-qualifier tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
322 :RULE (generate-rule tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
323 :SIMPLE-ACTION (generate-simple-action tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
324 :SYMBOL (keyword (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
325 :VALUE (generate (second tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
326 :WITHIN-CONDITION (generate-within-condition tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
327 (map generate tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
328 tree))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
281
docs/cloverage/mw_parser/simplify.clj.html
Normal file
281
docs/cloverage/mw_parser/simplify.clj.html
Normal file
|
@ -0,0 +1,281 @@
|
|||
<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 (ns ^{:doc "Simplify a parse tree."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.simplify)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;;;; mw-parser: a rule parser for MicroWorld.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;;;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;;;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;;;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;;;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;;;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;;;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;;;; Copyright (C) 2014 Simon Brooke
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
028 (declare simplify)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
029
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
030 (defn simplify-second-of-two
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 "There are a number of possible simplifications such that if the `tree` has
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 only two elements, the second is semantically sufficient."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 [tree]
|
||||
</span><br/>
|
||||
<span class="partial" title="10 out of 11 forms covered">
|
||||
034 (if (= (count tree) 2) (simplify (nth tree 1)) tree))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
036 (defn simplify-chained-list
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "Some parse trees take the form
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 `[:X [:Y 1] :NOISE :NOISE [:X [:Y 2] :NOISE :NOISE [:X [:Y 3]]]]`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 where what's wanted is `[:X [:Y 1] [:Y 2] [:Y 2]]` -- :DISJUNCT-VALUE is a case
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 in point. This takes such a parse `tree`, where `branch-tag` is the tag of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 the enclosing form and `leaf-tag` is the tag of the form to be collected, and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 returns the desired form."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 [tree branch-tag leaf-tag]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
044 (cons
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
045 (first tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
046 (reverse
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
047 (loop [chain (rest tree) v '()]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
048 (let [car (first chain)]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
049 (cond (empty? chain) v
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
050 (coll? car) (let [caar (first car)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
051 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
052 (= branch-tag caar) (recur car v)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
053 (= leaf-tag caar) (recur
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 (rest chain)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 (cons (simplify car) v))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
056 :else (recur (rest chain) v)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
057 :else (recur (rest chain) v)))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
058
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
059 (defn simplify
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 "Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 semantically identical simpler fragments"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 [tree]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
063 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
064 (coll? tree)
|
||||
</span><br/>
|
||||
<span class="partial" title="10 out of 11 forms covered">
|
||||
065 (case (first tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
066 :ACTION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
067 :ACTIONS (cons (first tree) (simplify (rest tree)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 :AND nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 :CHANCE-IN nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
070 :COMPARATIVE (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
071 :CONDITION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
072 :CONDITIONS (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
073 :DISJUNCT-EXPRESSION (simplify-chained-list tree :DISJUNCT-VALUE :VALUE)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
074 :EXPRESSION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 :IN nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
076 :PROPERTY (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
077 :PROPERTY-CONDITION-OR-EXPRESSION (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 :OR nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 :SPACE nil
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 :THEN nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
081 :VALUE (simplify-second-of-two tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
082 (remove nil? (map simplify tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
083 tree))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
084
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
085 (defn simplify-determiner-condition
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 [tree]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
087 (apply vector
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
088 (cons :DETERMINER-CONDITION
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
089 (cons
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
090 (simplify-second-of-two (second tree))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
091 (rest (rest tree))))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
200
docs/cloverage/mw_parser/utils.clj.html
Normal file
200
docs/cloverage/mw_parser/utils.clj.html
Normal file
|
@ -0,0 +1,200 @@
|
|||
<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 (ns ^{:doc "Utilities used in more than one namespace within the parser."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 mw-parser.utils)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;;;; mw-parser: a rule parser for MicroWorld.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;;;; This program is free software; you can redistribute it and/or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;;;; modify it under the terms of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;;;; as published by the Free Software Foundation; either version 2
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;;; of the License, or (at your option) any later version.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;;;; This program is distributed in the hope that it will be useful,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;;; GNU General Public License for more details.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;;;; You should have received a copy of the GNU General Public License
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;;; along with this program; if not, write to the Free Software
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;;;; USA.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;;;; Copyright (C) 2014 Simon Brooke
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
028
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
029 (defn suitable-fragment?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 "Return `true` if `tree-fragment` appears to be a tree fragment of the expected `type`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 [tree-fragment type]
|
||||
</span><br/>
|
||||
<span class="partial" title="11 out of 12 forms covered">
|
||||
032 (and (coll? tree-fragment)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
033 (keyword? type)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
034 (= (first tree-fragment) type)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
035
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
036 (defn rule?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 "Return true if the argument appears to be a parsed rule tree, else false."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 [maybe-rule]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
039 (suitable-fragment? maybe-rule :RULE))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
040
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
041 (defn TODO
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 "Marker to indicate I'm not yet finished!"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 [message]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
044 message)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
045
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
046
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
048 (defn assert-type
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 "If `tree-fragment` is not a tree fragment of the expected `type`, throw an exception."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 [tree-fragment type]
|
||||
</span><br/>
|
||||
<span class="partial" title="12 out of 16 forms covered">
|
||||
051 (assert (suitable-fragment? tree-fragment type)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
052 (throw (Exception. (format "Expected a %s fragment" type)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
054
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
055 (defn search-tree
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 "Return the first element of this tree which has this tag in a depth-first, left-to-right search"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 [tree tag]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
058 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
059 (= (first tree) tag) tree
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
060 :else (first
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
061 (remove nil?
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
062 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
063 #(search-tree % tag)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
064 (filter coll? (rest tree)))))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
551
docs/codox/css/default.css
Normal file
551
docs/codox/css/default.css
Normal file
|
@ -0,0 +1,551 @@
|
|||
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;
|
||||
}
|
97
docs/codox/css/highlight.css
Normal file
97
docs/codox/css/highlight.css
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
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;
|
||||
}
|
11
docs/codox/index.html
Normal file
11
docs/codox/index.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!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>
|
5
docs/codox/intro.html
Normal file
5
docs/codox/intro.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<!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>
|
2
docs/codox/js/highlight.min.js
vendored
Normal file
2
docs/codox/js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
docs/codox/js/jquery.min.js
vendored
Normal file
4
docs/codox/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
112
docs/codox/js/page_effects.js
Normal file
112
docs/codox/js/page_effects.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
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)
|
||||
})
|
9
docs/codox/mw-parser.bulk.html
Normal file
9
docs/codox/mw-parser.bulk.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!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>
|
25
docs/codox/mw-parser.core.html
Normal file
25
docs/codox/mw-parser.core.html
Normal file
File diff suppressed because one or more lines are too long
14
docs/codox/mw-parser.declarative.html
Normal file
14
docs/codox/mw-parser.declarative.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!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>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.</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 isn’t done yet. It’s 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>
|
7
docs/codox/mw-parser.errors.html
Normal file
7
docs/codox/mw-parser.errors.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<!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>
|
11
docs/codox/mw-parser.flow.html
Normal file
11
docs/codox/mw-parser.flow.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!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 > 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>
|
23
docs/codox/mw-parser.generate.html
Normal file
23
docs/codox/mw-parser.generate.html
Normal file
File diff suppressed because one or more lines are too long
8
docs/codox/mw-parser.simplify.html
Normal file
8
docs/codox/mw-parser.simplify.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!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 what’s 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>
|
9
docs/codox/mw-parser.utils.html
Normal file
9
docs/codox/mw-parser.utils.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!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 I’m 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>
|
4020
docs/uberdoc.html
Normal file
4020
docs/uberdoc.html
Normal file
File diff suppressed because one or more lines are too long
33
project.clj
33
project.clj
|
@ -1,18 +1,23 @@
|
|||
(defproject mw-parser "0.1.5-SNAPSHOT"
|
||||
(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"]]
|
||||
:description "Parser for production rules for MicroWorld engine"
|
||||
:url "http://www.journeyman.cc/microworld"
|
||||
:manifest {
|
||||
"build-signature-version" "unset"
|
||||
:license {:name "GNU General Public License v2"
|
||||
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
|
||||
:manifest {"build-signature-version" "unset"
|
||||
"build-signature-user" "unset"
|
||||
"build-signature-email" "unset"
|
||||
"build-signature-timestamp" "unset"
|
||||
"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"]
|
||||
])
|
||||
"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")
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
;; 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)))
|
|
@ -1,424 +0,0 @@
|
|||
;; 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)))
|
|
@ -1,368 +1,211 @@
|
|||
(ns mw-parser.declarative
|
||||
(:use mw-engine.utils
|
||||
[clojure.string :only [split trim triml]])
|
||||
(:require [instaparse.core :as insta]))
|
||||
(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]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; 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
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; 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")
|
||||
;;; 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.
|
||||
|
||||
(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 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 rule-grammar
|
||||
"Basic rule language grammar.
|
||||
|
||||
(defn TODO
|
||||
"Marker to indicate I'm not yet finished!"
|
||||
[message]
|
||||
message)
|
||||
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 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;"]))
|
||||
|
||||
(declare generate simplify)
|
||||
(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 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 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))
|
||||
|
||||
(def ^:private raw-parser
|
||||
(parser (join "\n" [ruleset-grammar rule-grammar flow-grammar common-grammar (keywords-for-locale)])))
|
||||
|
||||
(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
|
||||
(defn parse
|
||||
"Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
|
||||
(insta/parser grammar))
|
||||
[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)))
|
||||
|
||||
(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- 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:
|
||||
|
||||
(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})))
|
||||
* `: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 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
|
||||
"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.
|
||||
|
||||
(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))))
|
||||
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))))))
|
63
src/mw_parser/errors.clj
Normal file
63
src/mw_parser/errors.clj
Normal file
|
@ -0,0 +1,63 @@
|
|||
(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))))
|
37
src/mw_parser/flow.clj
Normal file
37
src/mw_parser/flow.clj
Normal file
|
@ -0,0 +1,37 @@
|
|||
(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"]))
|
432
src/mw_parser/generate.clj
Normal file
432
src/mw_parser/generate.clj
Normal file
|
@ -0,0 +1,432 @@
|
|||
(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))
|
|
@ -1,92 +0,0 @@
|
|||
(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"))
|
90
src/mw_parser/simplify.clj
Normal file
90
src/mw_parser/simplify.clj
Normal file
|
@ -0,0 +1,90 @@
|
|||
(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))
|
57
src/mw_parser/utils.clj
Normal file
57
src/mw_parser/utils.clj
Normal file
|
@ -0,0 +1,57 @@
|
|||
(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)))))))
|
|
@ -1,24 +0,0 @@
|
|||
(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")
|
||||
))
|
|
@ -1,471 +0,0 @@
|
|||
(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."))))
|
|
@ -1,517 +1,514 @@
|
|||
(ns mw-parser.declarative-test
|
||||
(:use clojure.pprint
|
||||
mw-engine.core
|
||||
mw-engine.world
|
||||
mw-engine.utils)
|
||||
(:require [clojure.test :refer :all]
|
||||
[mw-parser.declarative :refer :all]))
|
||||
(: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))
|
||||
|
||||
(deftest rules-tests
|
||||
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
|
||||
(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")))
|
||||
))
|
||||
(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")))))
|
||||
|
||||
(deftest neighbours-rules-tests
|
||||
(testing "Rules which relate to neighbours - hard!"
|
||||
(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}))))
|
||||
))
|
||||
(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")))))
|
||||
|
||||
|
||||
(deftest exception-tests
|
||||
(testing "Constructions which should cause exceptions to be thrown"
|
||||
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||
(compile-rule "the quick brown fox jumped over the lazy dog"))
|
||||
(is (thrown-with-msg? Exception #"^Parse error at line.*"
|
||||
(parse "the quick brown fox jumped over the lazy dog"))
|
||||
"Exception thrown if rule text does not match grammar")
|
||||
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||
(compile-rule "if i have a cat on my lap then everything is fine"))
|
||||
(is (thrown-with-msg? Exception #"^Parse error at line.*"
|
||||
(parse "if i have a cat on my lap then everything is fine"))
|
||||
"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"
|
||||
(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'")
|
||||
))
|
||||
;; 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'")))))
|
||||
|
||||
(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 (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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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"
|
||||
(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'"))
|
||||
(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'"))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
;; 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 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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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' 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.")))
|
||||
(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.")))
|
||||
|
||||
(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.")))
|
||||
;; 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.")))
|
||||
|
||||
(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.")))
|
||||
(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.")))
|
||||
|
||||
(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.")))
|
||||
(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.")))
|
||||
|
||||
(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."))
|
||||
)
|
||||
(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.")))
|
||||
|
||||
(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.")))
|
||||
(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.")))
|
||||
|
||||
(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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
(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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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.")))
|
||||
;; 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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
;; 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 (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.")))
|
||||
(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.")))
|
||||
|
||||
|
||||
;; '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 (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")))
|
||||
(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")))
|
||||
|
||||
;; '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 (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")))
|
||||
(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")))
|
||||
|
||||
;; property operator numeric-value
|
||||
;; 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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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")))
|
||||
|
||||
;; simple within distance
|
||||
;; 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.")))
|
||||
(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.")))
|
||||
|
||||
;; comparator within distance
|
||||
;; 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."))
|
||||
))
|
||||
(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."))))
|
||||
|
|
43
test/mw_parser/flow_test.clj
Normal file
43
test/mw_parser/flow_test.clj
Normal file
|
@ -0,0 +1,43 @@
|
|||
(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))))
|
124
test/mw_parser/generate_test.clj
Normal file
124
test/mw_parser/generate_test.clj
Normal file
|
@ -0,0 +1,124 @@
|
|||
(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)))))
|
||||
|
98
test/mw_parser/simplify_test.clj
Normal file
98
test/mw_parser/simplify_test.clj
Normal file
|
@ -0,0 +1,98 @@
|
|||
(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))))))
|
Loading…
Reference in a new issue