Compare commits
No commits in common. "develop" and "master" have entirely different histories.
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -1,13 +0,0 @@
|
||||||
buildall.tmp.*
|
|
||||||
.lein-failures
|
|
||||||
.lein-repl-history
|
|
||||||
target/
|
|
||||||
pom.xml
|
|
||||||
.calva/
|
|
||||||
.clj-kondo/
|
|
||||||
.lsp/
|
|
||||||
.nrepl-port
|
|
||||||
|
|
||||||
doc/scratch.clj
|
|
||||||
|
|
||||||
src/mw_parser/scratch.clj
|
|
46
README.md
46
README.md
|
@ -13,49 +13,15 @@ You can see MicroWorld in action [here](http://www.journeyman.cc/microworld/) -
|
||||||
but please don't be mean to my poor little server. If you want to run big maps
|
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.
|
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
|
## Usage
|
||||||
|
|
||||||
Main entry point is (compile _string_), where string takes a form detailed
|
Main entry point is (parse-rule _string_), where string takes a form detailed
|
||||||
in __[grammar](#grammar)__, below. If the rules represnted by the string are
|
in __[grammar](#grammar)__, below. If the rule is interpretted correctly the result will
|
||||||
interpretted correctly, the result will be a a list of compiled Clojure
|
be the source code of a Clojure anonymous function; if the rule cannot be interpretted,
|
||||||
anonymous functions; if the rule cannot be interpretted, an error 'I did not
|
an error 'I did not understand...' will be shown.
|
||||||
understand...' will be thrown.
|
|
||||||
|
|
||||||
Each of these functions will have metadata including:
|
The function (compile-rule _string_) is like parse-rule, except that it returns
|
||||||
|
a compiled Clojure anonymous function.
|
||||||
* `: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
|
### Generated function and evaluation environment
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
.covered {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: #558B55;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-covered {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.partial {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-tracked {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blank {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.with-bar {
|
|
||||||
width: 250px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.with-number {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.ns-name {
|
|
||||||
min-width: 150px;
|
|
||||||
padding-right: 25px;
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="./coverage.css"/>
|
|
||||||
<title>Coverage Summary</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table>
|
|
||||||
<thead><tr>
|
|
||||||
<td class="ns-name"> Namespace </td>
|
|
||||||
<td class="with-bar"> Forms </td>
|
|
||||||
<td class="with-number">Forms %</td>
|
|
||||||
<td class="with-bar"> Lines </td>
|
|
||||||
<td class="with-number">Lines %</td>
|
|
||||||
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
|
|
||||||
</tr></thead>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/bulk.clj.html">mw-parser.bulk</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:100.0%;
|
|
||||||
float:left;"> 60 </div></td>
|
|
||||||
<td class="with-number">100.00 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:100.0%;
|
|
||||||
float:left;"> 11 </div></td>
|
|
||||||
<td class="with-number">100.00 %</td>
|
|
||||||
<td class="with-number">62</td><td class="with-number">7</td><td class="with-number">11</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/core.clj.html">mw-parser.core</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:96.05475040257649%;
|
|
||||||
float:left;"> 1193 </div><div class="not-covered"
|
|
||||||
style="width:3.9452495974235107%;
|
|
||||||
float:left;"> 49 </div></td>
|
|
||||||
<td class="with-number">96.05 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:84.48979591836735%;
|
|
||||||
float:left;"> 207 </div><div class="partial"
|
|
||||||
style="width:14.285714285714286%;
|
|
||||||
float:left;"> 35 </div><div class="not-covered"
|
|
||||||
style="width:1.2244897959183674%;
|
|
||||||
float:left;"> 3 </div></td>
|
|
||||||
<td class="with-number">98.78 %</td>
|
|
||||||
<td class="with-number">451</td><td class="with-number">41</td><td class="with-number">245</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/declarative.clj.html">mw-parser.declarative</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:93.29896907216495%;
|
|
||||||
float:left;"> 181 </div><div class="not-covered"
|
|
||||||
style="width:6.701030927835052%;
|
|
||||||
float:left;"> 13 </div></td>
|
|
||||||
<td class="with-number">93.30 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:91.66666666666667%;
|
|
||||||
float:left;"> 22 </div><div class="partial"
|
|
||||||
style="width:4.166666666666667%;
|
|
||||||
float:left;"> 1 </div><div class="not-covered"
|
|
||||||
style="width:4.166666666666667%;
|
|
||||||
float:left;"> 1 </div></td>
|
|
||||||
<td class="with-number">95.83 %</td>
|
|
||||||
<td class="with-number">157</td><td class="with-number">10</td><td class="with-number">24</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/errors.clj.html">mw-parser.errors</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:90.4%;
|
|
||||||
float:left;"> 113 </div><div class="not-covered"
|
|
||||||
style="width:9.6%;
|
|
||||||
float:left;"> 12 </div></td>
|
|
||||||
<td class="with-number">90.40 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:90.47619047619048%;
|
|
||||||
float:left;"> 19 </div><div class="partial"
|
|
||||||
style="width:9.523809523809524%;
|
|
||||||
float:left;"> 2 </div></td>
|
|
||||||
<td class="with-number">100.00 %</td>
|
|
||||||
<td class="with-number">68</td><td class="with-number">9</td><td class="with-number">21</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/flow.clj.html">mw-parser.flow</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:89.1566265060241%;
|
|
||||||
float:left;"> 74 </div><div class="not-covered"
|
|
||||||
style="width:10.843373493975903%;
|
|
||||||
float:left;"> 9 </div></td>
|
|
||||||
<td class="with-number">89.16 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:85.71428571428571%;
|
|
||||||
float:left;"> 18 </div><div class="not-covered"
|
|
||||||
style="width:14.285714285714286%;
|
|
||||||
float:left;"> 3 </div></td>
|
|
||||||
<td class="with-number">85.71 %</td>
|
|
||||||
<td class="with-number">67</td><td class="with-number">5</td><td class="with-number">21</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/generate.clj.html">mw-parser.generate</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:75.6989247311828%;
|
|
||||||
float:left;"> 704 </div><div class="not-covered"
|
|
||||||
style="width:24.301075268817204%;
|
|
||||||
float:left;"> 226 </div></td>
|
|
||||||
<td class="with-number">75.70 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:75.67567567567568%;
|
|
||||||
float:left;"> 140 </div><div class="partial"
|
|
||||||
style="width:3.2432432432432434%;
|
|
||||||
float:left;"> 6 </div><div class="not-covered"
|
|
||||||
style="width:21.08108108108108%;
|
|
||||||
float:left;"> 39 </div></td>
|
|
||||||
<td class="with-number">78.92 %</td>
|
|
||||||
<td class="with-number">328</td><td class="with-number">24</td><td class="with-number">185</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/simplify.clj.html">mw-parser.simplify</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:85.81560283687944%;
|
|
||||||
float:left;"> 121 </div><div class="not-covered"
|
|
||||||
style="width:14.184397163120567%;
|
|
||||||
float:left;"> 20 </div></td>
|
|
||||||
<td class="with-number">85.82 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:82.05128205128206%;
|
|
||||||
float:left;"> 32 </div><div class="partial"
|
|
||||||
style="width:5.128205128205129%;
|
|
||||||
float:left;"> 2 </div><div class="not-covered"
|
|
||||||
style="width:12.820512820512821%;
|
|
||||||
float:left;"> 5 </div></td>
|
|
||||||
<td class="with-number">87.18 %</td>
|
|
||||||
<td class="with-number">91</td><td class="with-number">6</td><td class="with-number">39</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="mw_parser/utils.clj.html">mw-parser.utils</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:93.58974358974359%;
|
|
||||||
float:left;"> 73 </div><div class="not-covered"
|
|
||||||
style="width:6.410256410256411%;
|
|
||||||
float:left;"> 5 </div></td>
|
|
||||||
<td class="with-number">93.59 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:90.0%;
|
|
||||||
float:left;"> 18 </div><div class="partial"
|
|
||||||
style="width:10.0%;
|
|
||||||
float:left;"> 2 </div></td>
|
|
||||||
<td class="with-number">100.00 %</td>
|
|
||||||
<td class="with-number">64</td><td class="with-number">10</td><td class="with-number">20</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td>Totals:</td>
|
|
||||||
<td class="with-bar"></td>
|
|
||||||
<td class="with-number">88.29 %</td>
|
|
||||||
<td class="with-bar"></td>
|
|
||||||
<td class="with-number">90.99 %</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,194 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/bulk.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,479 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/declarative.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,212 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/errors.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,209 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/flow.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,992 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/generate.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,281 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/simplify.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,200 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> mw_parser/utils.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (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>
|
|
|
@ -1,551 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, code {
|
|
||||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
|
||||||
font-size: 9pt;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 29px;
|
|
||||||
margin: 10px 0 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5.license {
|
|
||||||
margin: 9px 0 22px 0;
|
|
||||||
color: #555;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document h1, .namespace-index h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header, #content, .sidebar {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 22px;
|
|
||||||
color: #f5f5f5;
|
|
||||||
padding: 5px 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
top: 32px;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
overflow: auto;
|
|
||||||
background: #fff;
|
|
||||||
color: #333;
|
|
||||||
padding: 0 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
position: fixed;
|
|
||||||
top: 32px;
|
|
||||||
bottom: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.primary {
|
|
||||||
background: #e2e2e2;
|
|
||||||
border-right: solid 1px #cccccc;
|
|
||||||
left: 0;
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.secondary {
|
|
||||||
background: #f2f2f2;
|
|
||||||
border-right: solid 1px #d7d7d7;
|
|
||||||
left: 251px;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.namespace-index, #content.document {
|
|
||||||
left: 251px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.namespace-docs {
|
|
||||||
left: 452px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.document {
|
|
||||||
padding-bottom: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
background: #3f3f3f;
|
|
||||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: lighter;
|
|
||||||
text-shadow: -1px -1px 0px #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h1 .project-version {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-version {
|
|
||||||
padding-left: 0.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a, .sidebar a {
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a {
|
|
||||||
color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar a {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h2 {
|
|
||||||
float: right;
|
|
||||||
font-size: 9pt;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 4px 3px;
|
|
||||||
padding: 0;
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h2 a {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 13px 0 13px;
|
|
||||||
font-size: 19px;
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3 a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3.no-link {
|
|
||||||
color: #636363;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar ul {
|
|
||||||
padding: 7px 0 6px 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar ul.index-link {
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li {
|
|
||||||
display: block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li a, .sidebar li .no-link {
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
padding: 0 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .no-link {
|
|
||||||
display: block;
|
|
||||||
color: #777;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .inner {
|
|
||||||
display: inline-block;
|
|
||||||
padding-top: 7px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li a, .sidebar li .tree {
|
|
||||||
height: 31px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.depth-1 .inner { padding-left: 2px; }
|
|
||||||
.depth-2 .inner { padding-left: 6px; }
|
|
||||||
.depth-3 .inner { padding-left: 20px; }
|
|
||||||
.depth-4 .inner { padding-left: 34px; }
|
|
||||||
.depth-5 .inner { padding-left: 48px; }
|
|
||||||
.depth-6 .inner { padding-left: 62px; }
|
|
||||||
|
|
||||||
.sidebar li .tree {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
top: -10px;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li.depth-1 .tree {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .top, .sidebar li .tree .bottom {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .top {
|
|
||||||
border-left: 1px solid #aaa;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
height: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .bottom {
|
|
||||||
height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li.branch .tree .bottom {
|
|
||||||
border-left: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.primary li.current a {
|
|
||||||
border-left: 3px solid #a33;
|
|
||||||
color: #a33;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.secondary li.current a {
|
|
||||||
border-left: 3px solid #33a;
|
|
||||||
color: #33a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index h2 {
|
|
||||||
margin: 30px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index h3 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .topics {
|
|
||||||
padding-left: 30px;
|
|
||||||
margin: 11px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .topics li {
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-docs h3 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public h3 {
|
|
||||||
margin: 0;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public {
|
|
||||||
margin: 0;
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
padding-top: 14px;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public:last-child {
|
|
||||||
margin-bottom: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members {
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members h4 {
|
|
||||||
color: #555;
|
|
||||||
font-weight: normal;
|
|
||||||
font-variant: small-caps;
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .inner {
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-left: 12px;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-left: 7px;
|
|
||||||
border-left: 1px solid #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content .members .inner h3 {
|
|
||||||
font-size: 12pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public {
|
|
||||||
border-top: none;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: 6px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.type,
|
|
||||||
h4.dynamic,
|
|
||||||
h4.added,
|
|
||||||
h4.deprecated {
|
|
||||||
float: left;
|
|
||||||
margin: 3px 10px 15px 0;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-variant: small-caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public h4.type,
|
|
||||||
.public h4.dynamic,
|
|
||||||
.public h4.added,
|
|
||||||
.public h4.deprecated {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 3px 0 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members h4.type,
|
|
||||||
.members h4.added,
|
|
||||||
.members h4.deprecated {
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.type {
|
|
||||||
color: #717171;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.dynamic {
|
|
||||||
color: #9933aa;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.added {
|
|
||||||
color: #508820;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.deprecated {
|
|
||||||
color: #880000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace:last-child {
|
|
||||||
margin-bottom: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index {
|
|
||||||
padding: 0;
|
|
||||||
font-size: 80%;
|
|
||||||
margin: 15px 0;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index * {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index p {
|
|
||||||
padding-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index li {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index ul {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-sig {
|
|
||||||
clear: both;
|
|
||||||
color: #088;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-sig pre {
|
|
||||||
padding-top: 10px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage code {
|
|
||||||
display: block;
|
|
||||||
color: #008;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage code:first-child {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public p:first-child, .public pre.plaintext {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc {
|
|
||||||
margin: 0 0 26px 0;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public .doc {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .doc {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .namespace .doc {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown li {
|
|
||||||
padding: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h2 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 25px;
|
|
||||||
margin: 30px 0 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h3 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 30px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h4 {
|
|
||||||
font-size: 15px;
|
|
||||||
margin: 22px 0 -4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc, .public, .namespace .index {
|
|
||||||
max-width: 680px;
|
|
||||||
overflow-x: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown pre > code {
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown pre > code, .src-link a {
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown code:not(.hljs), .src-link a {
|
|
||||||
background: #f6f6f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.deps {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown hr {
|
|
||||||
border-style: solid;
|
|
||||||
border-top: none;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc ul, .doc ol {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table td, .doc table th {
|
|
||||||
border: 1px solid #dddddd;
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table th {
|
|
||||||
background: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl {
|
|
||||||
margin: 0 10px 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl dt {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 3px 0;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl dd {
|
|
||||||
padding: 5px 0;
|
|
||||||
margin: 0 0 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc abbr {
|
|
||||||
border-bottom: 1px dotted #333;
|
|
||||||
font-variant: none;
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.src-link {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.src-link a {
|
|
||||||
font-size: 70%;
|
|
||||||
padding: 1px 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #5555bb;
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
|
||||||
*/
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 0.5em;
|
|
||||||
color: #333;
|
|
||||||
background: #f8f8f8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #998;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-subst {
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-number,
|
|
||||||
.hljs-literal,
|
|
||||||
.hljs-variable,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-tag .hljs-attr {
|
|
||||||
color: #008080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-string,
|
|
||||||
.hljs-doctag {
|
|
||||||
color: #d14;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-title,
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-selector-id {
|
|
||||||
color: #900;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-subst {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-class .hljs-title {
|
|
||||||
color: #458;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-tag,
|
|
||||||
.hljs-name,
|
|
||||||
.hljs-attribute {
|
|
||||||
color: #000080;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-link {
|
|
||||||
color: #009926;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-bullet {
|
|
||||||
color: #990073;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-builtin-name {
|
|
||||||
color: #0086b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-meta {
|
|
||||||
color: #999;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion {
|
|
||||||
background: #fdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition {
|
|
||||||
background: #dfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>Mw-parser 0.2.0-SNAPSHOT</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 current"><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="namespace-index" id="content"><h1><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></h1><h5 class="license">Released under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License v2</a></h5><div class="doc"><p>Parser for production rules for MicroWorld engine.</p></div><h2>Installation</h2><p>To install, add the following dependency to your project or build file:</p><pre class="deps">[mw-parser "0.2.0-SNAPSHOT"]</pre><h2>Topics</h2><ul class="topics"><li><a href="intro.html">Introduction to mw-parser</a></li></ul><h2>Namespaces</h2><div class="namespace"><h3><a href="mw-parser.bulk.html">mw-parser.bulk</a></h3><div class="doc"><div class="markdown"><p>parse multiple rules from a stream, possibly a file.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.bulk.html#var-comment.3F">comment?</a> </li><li> <a href="mw-parser.bulk.html#var-compile-file">compile-file</a> </li><li> <a href="mw-parser.bulk.html#var-compile-string">compile-string</a> </li><li> <a href="mw-parser.bulk.html#var-parse-file">parse-file</a> </li><li> <a href="mw-parser.bulk.html#var-parse-string">parse-string</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.core.html">mw-parser.core</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.core.html#var-bad-parse-error">bad-parse-error</a> </li><li> <a href="mw-parser.core.html#var-compile-rule">compile-rule</a> </li><li> <a href="mw-parser.core.html#var-gen-token-value">gen-token-value</a> </li><li> <a href="mw-parser.core.html#var-parse-comparator-neighbours-condition">parse-comparator-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-disjunct-value">parse-disjunct-value</a> </li><li> <a href="mw-parser.core.html#var-parse-member-condition">parse-member-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-neighbours-condition">parse-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-numeric-value">parse-numeric-value</a> </li><li> <a href="mw-parser.core.html#var-parse-property-int">parse-property-int</a> </li><li> <a href="mw-parser.core.html#var-parse-property-value">parse-property-value</a> </li><li> <a href="mw-parser.core.html#var-parse-rule">parse-rule</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-condition">parse-simple-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-neighbours-condition">parse-simple-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-simple-value">parse-simple-value</a> </li><li> <a href="mw-parser.core.html#var-parse-some-neighbours-condition">parse-some-neighbours-condition</a> </li><li> <a href="mw-parser.core.html#var-parse-token-value">parse-token-value</a> </li><li> <a href="mw-parser.core.html#var-parse-value">parse-value</a> </li><li> <a href="mw-parser.core.html#var-re-number">re-number</a> </li><li> <a href="mw-parser.core.html#var-reserved-properties-error">reserved-properties-error</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.declarative.html">mw-parser.declarative</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.declarative.html#var-build-parser">build-parser</a> </li><li> <a href="mw-parser.declarative.html#var-common-grammar">common-grammar</a> </li><li> <a href="mw-parser.declarative.html#var-compile-rule">compile-rule</a> </li><li> <a href="mw-parser.declarative.html#var-keywords-en">keywords-en</a> </li><li> <a href="mw-parser.declarative.html#var-keywords-for-locale">keywords-for-locale</a> </li><li> <a href="mw-parser.declarative.html#var-parse-rule">parse-rule</a> </li><li> <a href="mw-parser.declarative.html#var-rule-grammar">rule-grammar</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.errors.html">mw-parser.errors</a></h3><div class="doc"><div class="markdown"><p>Display parse errors in a format which makes it easy for the user to see where the error occurred.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.errors.html#var-bad-parse-error">bad-parse-error</a> </li><li> <a href="mw-parser.errors.html#var-reserved-properties-error">reserved-properties-error</a> </li><li> <a href="mw-parser.errors.html#var-throw-parse-exception">throw-parse-exception</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.flow.html">mw-parser.flow</a></h3><div class="doc"><div class="markdown"><p>A very simple parser which parses flow rules.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.flow.html#var-flow-grammar">flow-grammar</a> </li><li> <a href="mw-parser.flow.html#var-parse-flow">parse-flow</a> </li><li> <a href="mw-parser.flow.html#var-simplify-flow">simplify-flow</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.generate.html">mw-parser.generate</a></h3><div class="doc"><div class="markdown"><p>Generate Clojure source from simplified parse trees.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.generate.html#var-generate">generate</a> </li><li> <a href="mw-parser.generate.html#var-generate-action">generate-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-condition">generate-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-conditions">generate-conditions</a> </li><li> <a href="mw-parser.generate.html#var-generate-conjunct-condition">generate-conjunct-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-condition">generate-disjunct-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-property-condition">generate-disjunct-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-disjunct-value">generate-disjunct-value</a> </li><li> <a href="mw-parser.generate.html#var-generate-flow">generate-flow</a> </li><li> <a href="mw-parser.generate.html#var-generate-multiple-actions">generate-multiple-actions</a> </li><li> <a href="mw-parser.generate.html#var-generate-neighbours-condition">generate-neighbours-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-numeric-expression">generate-numeric-expression</a> </li><li> <a href="mw-parser.generate.html#var-generate-probable-action">generate-probable-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-property-condition">generate-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-qualifier">generate-qualifier</a> </li><li> <a href="mw-parser.generate.html#var-generate-ranged-property-condition">generate-ranged-property-condition</a> </li><li> <a href="mw-parser.generate.html#var-generate-rule">generate-rule</a> </li><li> <a href="mw-parser.generate.html#var-generate-simple-action">generate-simple-action</a> </li><li> <a href="mw-parser.generate.html#var-generate-within-condition">generate-within-condition</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.simplify.html">mw-parser.simplify</a></h3><div class="doc"><div class="markdown"><p>Simplify a parse tree.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.simplify.html#var-simplify">simplify</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-chained-list">simplify-chained-list</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-determiner-condition">simplify-determiner-condition</a> </li><li> <a href="mw-parser.simplify.html#var-simplify-second-of-two">simplify-second-of-two</a> </li></ul></div></div><div class="namespace"><h3><a href="mw-parser.utils.html">mw-parser.utils</a></h3><div class="doc"><div class="markdown"><p>Utilities used in more than one namespace within the parser.</p>
|
|
||||||
</div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="mw-parser.utils.html#var-assert-type">assert-type</a> </li><li> <a href="mw-parser.utils.html#var-rule.3F">rule?</a> </li><li> <a href="mw-parser.utils.html#var-search-tree">search-tree</a> </li><li> <a href="mw-parser.utils.html#var-suitable-fragment.3F">suitable-fragment?</a> </li><li> <a href="mw-parser.utils.html#var-TODO">TODO</a> </li></ul></div></div></div></body></html>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>Introduction to mw-parser</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-mw-parser" id="introduction-to-mw-parser"></a>Introduction to mw-parser</h1>
|
|
||||||
<p>TODO: write <a href="http://jacobian.org/writing/great-documentation/what-to-write/">great documentation</a></p>
|
|
||||||
</div></div></div></body></html>
|
|
2
docs/codox/js/highlight.min.js
vendored
2
docs/codox/js/highlight.min.js
vendored
File diff suppressed because one or more lines are too long
4
docs/codox/js/jquery.min.js
vendored
4
docs/codox/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,112 +0,0 @@
|
||||||
function visibleInParent(element) {
|
|
||||||
var position = $(element).position().top
|
|
||||||
return position > -50 && position < ($(element).offsetParent().height() - 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasFragment(link, fragment) {
|
|
||||||
return $(link).attr("href").indexOf("#" + fragment) != -1
|
|
||||||
}
|
|
||||||
|
|
||||||
function findLinkByFragment(elements, fragment) {
|
|
||||||
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToCurrentVarLink(elements) {
|
|
||||||
var elements = $(elements);
|
|
||||||
var parent = elements.offsetParent();
|
|
||||||
|
|
||||||
if (elements.length == 0) return;
|
|
||||||
|
|
||||||
var top = elements.first().position().top;
|
|
||||||
var bottom = elements.last().position().top + elements.last().height();
|
|
||||||
|
|
||||||
if (top >= 0 && bottom <= parent.height()) return;
|
|
||||||
|
|
||||||
if (top < 0) {
|
|
||||||
parent.scrollTop(parent.scrollTop() + top);
|
|
||||||
}
|
|
||||||
else if (bottom > parent.height()) {
|
|
||||||
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCurrentVarLink() {
|
|
||||||
$('.secondary a').parent().removeClass('current')
|
|
||||||
$('.anchor').
|
|
||||||
filter(function(index) { return visibleInParent(this) }).
|
|
||||||
each(function(index, element) {
|
|
||||||
findLinkByFragment(".secondary a", element.id).
|
|
||||||
parent().
|
|
||||||
addClass('current')
|
|
||||||
});
|
|
||||||
scrollToCurrentVarLink('.secondary .current');
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
|
|
||||||
|
|
||||||
function scrollPositionId(element) {
|
|
||||||
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
|
|
||||||
return 'scroll::' + $(element).attr('id') + '::' + directory
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeScrollPosition(element) {
|
|
||||||
if (!hasStorage) return;
|
|
||||||
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
|
|
||||||
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
|
|
||||||
}
|
|
||||||
|
|
||||||
function recallScrollPosition(element) {
|
|
||||||
if (!hasStorage) return;
|
|
||||||
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
|
|
||||||
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
|
|
||||||
}
|
|
||||||
|
|
||||||
function persistScrollPosition(element) {
|
|
||||||
recallScrollPosition(element)
|
|
||||||
$(element).scroll(function() { storeScrollPosition(element) })
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebarContentWidth(element) {
|
|
||||||
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
|
|
||||||
return Math.max.apply(Math, widths)
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateSize(width, snap, margin, minimum) {
|
|
||||||
if (width == 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeSidebars() {
|
|
||||||
var primaryWidth = sidebarContentWidth('.primary')
|
|
||||||
var secondaryWidth = 0
|
|
||||||
|
|
||||||
if ($('.secondary').length != 0) {
|
|
||||||
secondaryWidth = sidebarContentWidth('.secondary')
|
|
||||||
}
|
|
||||||
|
|
||||||
// snap to grid
|
|
||||||
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
|
|
||||||
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
|
|
||||||
|
|
||||||
$('.primary').css('width', primaryWidth)
|
|
||||||
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
|
|
||||||
|
|
||||||
if (secondaryWidth > 0) {
|
|
||||||
$('#content').css('left', primaryWidth + secondaryWidth + 2)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#content').css('left', primaryWidth + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).ready(resizeSidebars)
|
|
||||||
$(window).ready(setCurrentVarLink)
|
|
||||||
$(window).ready(function() { persistScrollPosition('.primary')})
|
|
||||||
$(window).ready(function() {
|
|
||||||
$('#content').scroll(setCurrentVarLink)
|
|
||||||
$(window).resize(setCurrentVarLink)
|
|
||||||
})
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.bulk documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch current"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.bulk.html#var-comment.3F"><div class="inner"><span>comment?</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-compile-file"><div class="inner"><span>compile-file</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-compile-string"><div class="inner"><span>compile-string</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-parse-file"><div class="inner"><span>parse-file</span></div></a></li><li class="depth-1"><a href="mw-parser.bulk.html#var-parse-string"><div class="inner"><span>parse-string</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.bulk</h1><div class="doc"><div class="markdown"><p>parse multiple rules from a stream, possibly a file.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-comment.3F"><h3>comment?</h3><div class="usage"><code>(comment? line)</code></div><div class="doc"><div class="markdown"><p>Is this <code>line</code> a comment?</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L34">view source</a></div></div><div class="public anchor" id="var-compile-file"><h3>compile-file</h3><div class="usage"><code>(compile-file filename)</code></div><div class="doc"><div class="markdown"><p>Compile each non-comment line of the file indicated by this <code>filename</code> into an executable anonymous function, and return the sequence of such functions.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L58">view source</a></div></div><div class="public anchor" id="var-compile-string"><h3>compile-string</h3><div class="usage"><code>(compile-string string)</code></div><div class="doc"><div class="markdown"><p>Compile each non-comment line of this <code>string</code> into an executable anonymous function, and return the sequence of such functions.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L52">view source</a></div></div><div class="public anchor" id="var-parse-file"><h3>parse-file</h3><div class="usage"><code>(parse-file filename)</code></div><div class="doc"><div class="markdown"><p>Parse rules from successive lines in the file loaded from this <code>filename</code>. Return a list of S-expressions.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L46">view source</a></div></div><div class="public anchor" id="var-parse-string"><h3>parse-string</h3><div class="usage"><code>(parse-string string)</code></div><div class="doc"><div class="markdown"><p>Parse rules from successive lines in this <code>string</code>, assumed to have multiple lines delimited by the new-line character. Return a list of S-expressions.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/bulk.clj#L39">view source</a></div></div></div></body></html>
|
|
File diff suppressed because one or more lines are too long
|
@ -1,14 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.declarative documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.declarative.html#var-build-parser"><div class="inner"><span>build-parser</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-common-grammar"><div class="inner"><span>common-grammar</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-compile-rule"><div class="inner"><span>compile-rule</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-keywords-en"><div class="inner"><span>keywords-en</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-keywords-for-locale"><div class="inner"><span>keywords-for-locale</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-parse-rule"><div class="inner"><span>parse-rule</span></div></a></li><li class="depth-1"><a href="mw-parser.declarative.html#var-rule-grammar"><div class="inner"><span>rule-grammar</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.declarative</h1><div class="doc"><div class="markdown"><p>A very simple parser which parses production rules.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-build-parser"><h3>build-parser</h3><h4 class="type">macro</h4><div class="usage"><code>(build-parser g)</code></div><div class="doc"><div class="markdown"><p>Compose this grammar fragment <code>g</code> with the common grammar fragments to make a complete grammar, and return a parser for that complete grammar.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L125">view source</a></div></div><div class="public anchor" id="var-common-grammar"><h3>common-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Grammar rules used both in the rule grammar and in the flow grammar</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L47">view source</a></div></div><div class="public anchor" id="var-compile-rule"><h3>compile-rule</h3><div class="usage"><code>(compile-rule rule-text return-tuple?)</code><code>(compile-rule rule-text)</code></div><div class="doc"><div class="markdown"><p>Parse this <code>rule-text</code>, a string conforming to the grammar of MicroWorld rules, into Clojure source, and then compile it into an anonymous function object, getting round the problem of binding mw-engine.utils in the compiling environment. If <code>return-tuple?</code> is present and true, return a list comprising the anonymous function compiled, and the function from which it was compiled.</p>
|
|
||||||
<p>Throws an exception if parsing fails.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/declarative.clj#L135">view source</a></div></div><div class="public anchor" id="var-keywords-en"><h3>keywords-en</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>English language keyword literals used in rules - both in production rules (this namespace) and in flow rules (see mw-parser.flow).</p>
|
|
||||||
<p>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>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.errors documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.errors.html#var-bad-parse-error"><div class="inner"><span>bad-parse-error</span></div></a></li><li class="depth-1"><a href="mw-parser.errors.html#var-reserved-properties-error"><div class="inner"><span>reserved-properties-error</span></div></a></li><li class="depth-1"><a href="mw-parser.errors.html#var-throw-parse-exception"><div class="inner"><span>throw-parse-exception</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.errors</h1><div class="doc"><div class="markdown"><p>Display parse errors in a format which makes it easy for the user to see where the error occurred.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-bad-parse-error"><h3>bad-parse-error</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L33">view source</a></div></div><div class="public anchor" id="var-reserved-properties-error"><h3>reserved-properties-error</h3><div class="usage"></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L27">view source</a></div></div><div class="public anchor" id="var-throw-parse-exception"><h3>throw-parse-exception</h3><div class="usage"><code>(throw-parse-exception parser-error)</code></div><div class="doc"><div class="markdown"><p>Construct a helpful error message from this <code>parser-error</code>, and throw an exception with that message.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/errors.clj#L51">view source</a></div></div></div></body></html>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.flow documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.flow.html#var-flow-grammar"><div class="inner"><span>flow-grammar</span></div></a></li><li class="depth-1"><a href="mw-parser.flow.html#var-parse-flow"><div class="inner"><span>parse-flow</span></div></a></li><li class="depth-1"><a href="mw-parser.flow.html#var-simplify-flow"><div class="inner"><span>simplify-flow</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.flow</h1><div class="doc"><div class="markdown"><p>A very simple parser which parses flow rules.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-flow-grammar"><h3>flow-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Grammar for flow rules.</p>
|
|
||||||
<p>My initial conception of this would be that production rules (if-then rules) and flow rules (flow-from-to rules) would be entirely separate, presented to the parser as separate text files, and parsed and compiled by different chains of functions.</p>
|
|
||||||
<p>This appears not to be necessary. Flow rules are easy to parse with the same parser as production rules – a lot of the grammar is intentionally common – and the rules are easily discriminated at the compilation (‘generate’) stage.</p>
|
|
||||||
<p>The basic rule I want to be able to compile at this stage is the ‘mutual aid’ rule:</p>
|
|
||||||
<p><code>flow 1 food from house having food > 1 to house with least food within 2</code></p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L8">view source</a></div></div><div class="public anchor" id="var-parse-flow"><h3>parse-flow</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Parse the argument, assumed to be a string in the correct syntax, and return a parse tree.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L39">view source</a></div></div><div class="public anchor" id="var-simplify-flow"><h3>simplify-flow</h3><div class="usage"><code>(simplify-flow tree)</code></div><div class="doc"><div class="markdown"><p><strong>TODO</strong>: write docs</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/flow.clj#L43">view source</a></div></div></div></body></html>
|
|
File diff suppressed because one or more lines are too long
|
@ -1,8 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.simplify documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch current"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify"><div class="inner"><span>simplify</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-chained-list"><div class="inner"><span>simplify-chained-list</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-determiner-condition"><div class="inner"><span>simplify-determiner-condition</span></div></a></li><li class="depth-1"><a href="mw-parser.simplify.html#var-simplify-second-of-two"><div class="inner"><span>simplify-second-of-two</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.simplify</h1><div class="doc"><div class="markdown"><p>Simplify a parse tree.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-simplify"><h3>simplify</h3><div class="usage"><code>(simplify tree)</code></div><div class="doc"><div class="markdown"><p>Simplify/canonicalise this <code>tree</code>. Opportunistically replace complex fragments with semantically identical simpler fragments</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/simplify.clj#L59">view source</a></div></div><div class="public anchor" id="var-simplify-chained-list"><h3>simplify-chained-list</h3><div class="usage"><code>(simplify-chained-list tree branch-tag leaf-tag)</code></div><div class="doc"><div class="markdown"><p>Some parse trees take the form <code>[:X [:Y 1] :NOISE :NOISE [:X [:Y 2] :NOISE :NOISE [:X [:Y 3]]]]</code> where 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>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>mw-parser.utils documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Mw-parser</span> <span class="project-version">0.2.0-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to mw-parser</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mw-parser</span></div></div></li><li class="depth-2 branch"><a href="mw-parser.bulk.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>bulk</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.declarative.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>declarative</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.errors.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>errors</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.flow.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>flow</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.generate.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>generate</span></div></a></li><li class="depth-2 branch"><a href="mw-parser.simplify.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>simplify</span></div></a></li><li class="depth-2 current"><a href="mw-parser.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="mw-parser.utils.html#var-assert-type"><div class="inner"><span>assert-type</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-rule.3F"><div class="inner"><span>rule?</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-search-tree"><div class="inner"><span>search-tree</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-suitable-fragment.3F"><div class="inner"><span>suitable-fragment?</span></div></a></li><li class="depth-1"><a href="mw-parser.utils.html#var-TODO"><div class="inner"><span>TODO</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">mw-parser.utils</h1><div class="doc"><div class="markdown"><p>Utilities used in more than one namespace within the parser.</p>
|
|
||||||
</div></div><div class="public anchor" id="var-assert-type"><h3>assert-type</h3><div class="usage"><code>(assert-type tree-fragment type)</code></div><div class="doc"><div class="markdown"><p>If <code>tree-fragment</code> is not a tree fragment of the expected <code>type</code>, throw an exception.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L48">view source</a></div></div><div class="public anchor" id="var-rule.3F"><h3>rule?</h3><div class="usage"><code>(rule? maybe-rule)</code></div><div class="doc"><div class="markdown"><p>Return true if the argument appears to be a parsed rule tree, else false.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L36">view source</a></div></div><div class="public anchor" id="var-search-tree"><h3>search-tree</h3><div class="usage"><code>(search-tree tree tag)</code></div><div class="doc"><div class="markdown"><p>Return the first element of this tree which has this tag in a depth-first, left-to-right search</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L55">view source</a></div></div><div class="public anchor" id="var-suitable-fragment.3F"><h3>suitable-fragment?</h3><div class="usage"><code>(suitable-fragment? tree-fragment type)</code></div><div class="doc"><div class="markdown"><p>Return <code>true</code> if <code>tree-fragment</code> appears to be a tree fragment of the expected <code>type</code>.</p>
|
|
||||||
</div></div><div class="src-link"><a href="https://github.com/simon-brooke/mw-parser/blob/master/src/mw_parser/utils.clj#L29">view source</a></div></div><div class="public anchor" id="var-TODO"><h3>TODO</h3><div class="usage"><code>(TODO message)</code></div><div class="doc"><div class="markdown"><p>Marker to indicate 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
4020
docs/uberdoc.html
File diff suppressed because one or more lines are too long
33
project.clj
33
project.clj
|
@ -1,23 +1,18 @@
|
||||||
(defproject mw-parser "0.3.0-SNAPSHOT"
|
(defproject mw-parser "0.1.5-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"
|
:description "Parser for production rules for MicroWorld engine"
|
||||||
:license {:name "GNU General Public License v2"
|
:url "http://www.journeyman.cc/microworld"
|
||||||
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
|
:manifest {
|
||||||
:manifest {"build-signature-version" "unset"
|
"build-signature-version" "unset"
|
||||||
"build-signature-user" "unset"
|
"build-signature-user" "unset"
|
||||||
"build-signature-email" "unset"
|
"build-signature-email" "unset"
|
||||||
"build-signature-timestamp" "unset"
|
"build-signature-timestamp" "unset"
|
||||||
"Implementation-Version" "unset"}
|
"Implementation-Version" "unset"
|
||||||
:plugins [[lein-marginalia "0.7.1"]
|
}
|
||||||
[lein-cloverage "1.2.2"]
|
:license {:name "GNU General Public License v2"
|
||||||
[lein-codox "0.10.8"]]
|
:url "http://www.gnu.org/licenses/gpl-2.0.html"}
|
||||||
:url "http://www.journeyman.cc/microworld")
|
: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"]
|
||||||
|
])
|
||||||
|
|
|
@ -6,19 +6,19 @@
|
||||||
## Vegetation rules
|
## Vegetation rules
|
||||||
;; rules which populate the world with plants
|
;; rules which populate the world with plants
|
||||||
|
|
||||||
;; Occasionally, passing birds plant tree seeds into grassland
|
;; Occasionally, passing birds plant tree seeds into grassland
|
||||||
|
|
||||||
if state is grassland then 1 chance in 10 state should be heath
|
if state is grassland then 1 chance in 10 state should be heath
|
||||||
|
|
||||||
;; heath below the treeline grows gradually into forest
|
;; heath below the treeline grows gradually into forest
|
||||||
|
|
||||||
if state is heath and altitude is less than 120 then state should be scrub
|
if state is heath and altitude is less than 120 then state should be scrub
|
||||||
if state is scrub then 1 chance in 5 state should be forest
|
if state is scrub then 1 chance in 5 state should be forest
|
||||||
|
|
||||||
;; Forest on fertile land grows to climax
|
;; Forest on fertile land grows to climax
|
||||||
|
|
||||||
if state is forest and fertility is more than 5 and altitude is less than 70 then state should be climax
|
if state is forest and fertility is more than 5 and altitude is less than 70 then state should be climax
|
||||||
|
|
||||||
;; Climax forest occasionally catches fire (e.g. lightning strikes)
|
;; Climax forest occasionally catches fire (e.g. lightning strikes)
|
||||||
|
|
||||||
if state is climax then 1 chance in 500 state should be fire
|
if state is climax then 1 chance in 500 state should be fire
|
||||||
|
@ -40,7 +40,7 @@ if state is waste then state should be grassland
|
||||||
|
|
||||||
## Potential blockers
|
## Potential blockers
|
||||||
|
|
||||||
;; Forest increases soil fertility.
|
;; Forest increases soil fertility.
|
||||||
if state is in forest or climax then fertility should be fertility + 1
|
if state is in forest or climax then fertility should be fertility + 1
|
||||||
|
|
||||||
|
|
||||||
|
|
39
src/mw_parser/bulk.clj
Normal file
39
src/mw_parser/bulk.clj
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
;; parse multiple rules from a stream, possibly a file - although the real
|
||||||
|
;; objective is to parse rules out of a block of text from a textarea
|
||||||
|
|
||||||
|
(ns mw-parser.bulk
|
||||||
|
(:use mw-parser.core
|
||||||
|
mw-engine.utils
|
||||||
|
clojure.java.io
|
||||||
|
[clojure.string :only [split trim]])
|
||||||
|
(:import (java.io BufferedReader StringReader)))
|
||||||
|
|
||||||
|
(defn comment?
|
||||||
|
"Is this `line` a comment?"
|
||||||
|
[line]
|
||||||
|
(or (empty? (trim line)) (member? (first line) '(nil \# \;))))
|
||||||
|
|
||||||
|
(defn parse-string
|
||||||
|
"Parse rules from successive lines in this `string`, assumed to have multiple
|
||||||
|
lines delimited by the new-line character. Return a list of S-expressions."
|
||||||
|
[string]
|
||||||
|
;; TODO: tried to do this using with-open, but couldn't make it work.
|
||||||
|
(map parse-rule (remove comment? (split string #"\n"))))
|
||||||
|
|
||||||
|
(defn parse-file
|
||||||
|
"Parse rules from successive lines in the file loaded from this `filename`.
|
||||||
|
Return a list of S-expressions."
|
||||||
|
[filename]
|
||||||
|
(parse-string (slurp filename)))
|
||||||
|
|
||||||
|
(defn compile-string
|
||||||
|
"Compile each non-comment line of this `string` into an executable anonymous
|
||||||
|
function, and return the sequence of such functions."
|
||||||
|
[string]
|
||||||
|
(map #(compile-rule % true) (remove comment? (split string #"\n"))))
|
||||||
|
|
||||||
|
(defn compile-file
|
||||||
|
"Compile each non-comment line of the file indicated by this `filename` into
|
||||||
|
an executable anonymous function, and return the sequence of such functions."
|
||||||
|
[filename]
|
||||||
|
(compile-string (slurp filename)))
|
424
src/mw_parser/core.clj
Normal file
424
src/mw_parser/core.clj
Normal file
|
@ -0,0 +1,424 @@
|
||||||
|
;; A very simple parser which parses production rules of the following forms:
|
||||||
|
;;
|
||||||
|
;; * "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"
|
||||||
|
;; * "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"
|
||||||
|
;; * "if altitude is 100 or fertility is 25 then state should be heath"
|
||||||
|
;; * "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"
|
||||||
|
;; * "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"
|
||||||
|
;; * "if state is grassland and 4 neighbours have state equal to water then state should be village"
|
||||||
|
;; * "if state is forest and fertility is between 55 and 75 then state should be climax"
|
||||||
|
;; * "if 6 neighbours have state equal to water then state should be village"
|
||||||
|
;; * "if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"
|
||||||
|
;; * "if state is forest or state is climax and some neighbours have state equal to fire then 3 in 5 chance that state should be fire"
|
||||||
|
;; * "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"
|
||||||
|
;; *
|
||||||
|
;;
|
||||||
|
;; it generates rules in the form expected by `mw-engine.core`, q.v.
|
||||||
|
;;
|
||||||
|
;; It is, as I say, very simple; it generates a complete rule, or it fails completely, returning nil.
|
||||||
|
;; Very occasionally it generates a wrong rule - one which is not a correct translation of the rule
|
||||||
|
;; semantics - but that is buggy behaviour, which I'll try to fix over the next few weeks, not a
|
||||||
|
;; design fault.
|
||||||
|
;;
|
||||||
|
;; More significantly it does not generate useful error messages on failure.
|
||||||
|
;;
|
||||||
|
;; This is the parser that is actually used currently; but see also insta.clj,
|
||||||
|
;; which is potentially a much better parser but does not quite work yet.
|
||||||
|
|
||||||
|
(ns mw-parser.core
|
||||||
|
(:use mw-engine.utils
|
||||||
|
[clojure.string :only [split trim triml]])
|
||||||
|
(:gen-class)
|
||||||
|
)
|
||||||
|
|
||||||
|
(declare parse-conditions)
|
||||||
|
(declare parse-not-condition)
|
||||||
|
(declare parse-simple-condition)
|
||||||
|
|
||||||
|
;; a regular expression which matches string representation of numbers
|
||||||
|
(def re-number #"^[0-9.]*$")
|
||||||
|
|
||||||
|
;; error thrown when an attempt is made to set a reserved property
|
||||||
|
(def reserved-properties-error
|
||||||
|
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
|
||||||
|
;; error thrown when a rule cannot be parsed
|
||||||
|
(def bad-parse-error "I did not understand '%s'")
|
||||||
|
|
||||||
|
(defn- keyword-or-numeric
|
||||||
|
"If this token appears to represent an explicit number, return that number;
|
||||||
|
otherwise, make a keyword of it and return that."
|
||||||
|
[token]
|
||||||
|
(cond
|
||||||
|
(re-matches re-number token) (read-string token)
|
||||||
|
(keyword? token) token
|
||||||
|
true (keyword token)))
|
||||||
|
|
||||||
|
;; Generally all functions in this file with names beginning 'parse-' take a
|
||||||
|
;; sequence of tokens (and in some cases other optional arguments) and return a
|
||||||
|
;; vector comprising
|
||||||
|
;;
|
||||||
|
;; 1. A code fragment parsed from the front of the sequence of tokens, and
|
||||||
|
;; 2. the remaining tokens which were not consumed in constructing that fragment.
|
||||||
|
;;
|
||||||
|
;; In every case if the function cannot parse the desired construct from the
|
||||||
|
;; front of the sequence of tokens it returns nil.
|
||||||
|
|
||||||
|
|
||||||
|
(defn parse-numeric-value
|
||||||
|
"Parse a number."
|
||||||
|
[[value & remainder]]
|
||||||
|
(if (and value (re-matches re-number value)) [(read-string value) remainder]))
|
||||||
|
|
||||||
|
(defn parse-property-int
|
||||||
|
"Parse a token assumed to be the name of a property of the current cell,
|
||||||
|
whose value is assumed to be an integer."
|
||||||
|
[[value & remainder]]
|
||||||
|
(if value [(list 'get-int 'cell (keyword value)) remainder]))
|
||||||
|
|
||||||
|
(defn parse-property-value
|
||||||
|
"Parse a token assumed to be the name of a property of the current cell."
|
||||||
|
[[value & remainder]]
|
||||||
|
(if value [(list (keyword value) 'cell) remainder]))
|
||||||
|
|
||||||
|
(defn parse-token-value
|
||||||
|
"Parse a token assumed to be a simple token value."
|
||||||
|
[[value & remainder]]
|
||||||
|
(if value [(keyword value) remainder]))
|
||||||
|
|
||||||
|
(defn parse-simple-value
|
||||||
|
"Parse a value from the first of these `tokens`. If `expect-int` is true, return
|
||||||
|
an integer or something which will evaluate to an integer."
|
||||||
|
([tokens expect-int]
|
||||||
|
(or
|
||||||
|
(parse-numeric-value tokens)
|
||||||
|
(cond expect-int
|
||||||
|
(parse-property-int tokens)
|
||||||
|
true (parse-token-value tokens))))
|
||||||
|
([tokens]
|
||||||
|
(parse-simple-value tokens false)))
|
||||||
|
|
||||||
|
(defn gen-token-value
|
||||||
|
"Parse a single value from this single token and return just the generated
|
||||||
|
code, not a pair."
|
||||||
|
[token expect-int]
|
||||||
|
(first (parse-simple-value (list token) expect-int)))
|
||||||
|
|
||||||
|
(defn parse-disjunct-value
|
||||||
|
"Parse a list of values from among these `tokens`. If `expect-int` is true, return
|
||||||
|
integers or things which will evaluate to integers."
|
||||||
|
[[OR token & tokens] expect-int]
|
||||||
|
(cond (member? OR '("or" "in"))
|
||||||
|
(let [value (first (parse-simple-value (list token) expect-int))
|
||||||
|
seek-others (= (first tokens) "or")]
|
||||||
|
(cond seek-others
|
||||||
|
(let [[others remainder] (parse-disjunct-value tokens expect-int)]
|
||||||
|
[(cons value others) remainder])
|
||||||
|
true
|
||||||
|
[(list value) tokens]))))
|
||||||
|
|
||||||
|
(defn parse-value
|
||||||
|
"Parse a value from among these `tokens`. If `expect-int` is true, return
|
||||||
|
an integer or something which will evaluate to an integer."
|
||||||
|
([tokens expect-int]
|
||||||
|
(or
|
||||||
|
(parse-disjunct-value tokens expect-int)
|
||||||
|
(parse-simple-value tokens expect-int)))
|
||||||
|
([tokens]
|
||||||
|
(parse-value tokens false)))
|
||||||
|
|
||||||
|
(defn parse-member-condition
|
||||||
|
"Parses a condition of the form '[property] in [value] or [value]...'"
|
||||||
|
[[property IS IN & rest]]
|
||||||
|
(if (and (member? IS '("is" "are")) (= IN "in"))
|
||||||
|
(let [[l remainder] (parse-disjunct-value (cons "in" rest) false)]
|
||||||
|
[(list 'member? (list (keyword property) 'cell) (list 'quote l)) remainder])))
|
||||||
|
|
||||||
|
(defn- parse-less-condition
|
||||||
|
"Parse '[property] less than [value]'."
|
||||||
|
[[property IS LESS THAN & rest]]
|
||||||
|
(cond (and (member? IS '("is" "are")) (member? LESS '("less" "fewer")) (= THAN "than"))
|
||||||
|
(let [[value remainder] (parse-value rest true)]
|
||||||
|
[(list '< (list 'get-int 'cell (keyword property)) value) remainder])))
|
||||||
|
|
||||||
|
(defn- parse-more-condition
|
||||||
|
"Parse '[property] more than [value]'."
|
||||||
|
[[property IS MORE THAN & rest]]
|
||||||
|
(cond (and (member? IS '("is" "are")) (member? MORE '("more" "greater")) (= THAN "than"))
|
||||||
|
(let [[value remainder] (parse-value rest true)]
|
||||||
|
[(list '> (list 'get-int 'cell (keyword property)) value) remainder])))
|
||||||
|
|
||||||
|
(defn- parse-between-condition
|
||||||
|
[[p IS BETWEEN v1 AND v2 & rest]]
|
||||||
|
(cond (and (member? IS '("is" "are")) (= BETWEEN "between") (= AND "and") (not (nil? v2)))
|
||||||
|
(let [property (first (parse-simple-value (list p) true))
|
||||||
|
value1 (first (parse-simple-value (list v1) true))
|
||||||
|
value2 (first (parse-simple-value (list v2) true))]
|
||||||
|
[(list 'or
|
||||||
|
(list '< value1 property value2)
|
||||||
|
(list '> value1 property value2)) rest])))
|
||||||
|
|
||||||
|
(defn- parse-is-condition
|
||||||
|
"Parse clauses of the form 'x is y', 'x is in y or z...',
|
||||||
|
'x is between y and z', 'x is more than y' or 'x is less than y'.
|
||||||
|
It is necessary to disambiguate whether value is a numeric or keyword."
|
||||||
|
[[property IS value & rest]]
|
||||||
|
(cond
|
||||||
|
(member? IS '("is" "are"))
|
||||||
|
(let [tokens (cons property (cons value rest))]
|
||||||
|
(cond
|
||||||
|
(re-matches re-number value) [(list '= (list 'get-int 'cell (keyword property)) (read-string value)) rest]
|
||||||
|
value [(list '= (list (keyword property) 'cell) (keyword value)) rest]))))
|
||||||
|
|
||||||
|
(defn- parse-not-condition
|
||||||
|
"Parse the negation of a simple condition."
|
||||||
|
[[property IS NOT & rest]]
|
||||||
|
(cond (and (member? IS '("is" "are")) (= NOT "not"))
|
||||||
|
(let [partial (parse-simple-condition (cons property (cons "is" rest)))]
|
||||||
|
(cond partial
|
||||||
|
(let [[condition remainder] partial]
|
||||||
|
[(list 'not condition) remainder])))))
|
||||||
|
|
||||||
|
(defn- gen-neighbours-condition
|
||||||
|
([comp1 quantity property value remainder comp2 distance]
|
||||||
|
[(list comp1
|
||||||
|
(list 'count
|
||||||
|
(list 'get-neighbours-with-property-value 'world
|
||||||
|
'(cell :x) '(cell :y) distance
|
||||||
|
(keyword property) (keyword-or-numeric value) comp2))
|
||||||
|
quantity)
|
||||||
|
remainder])
|
||||||
|
([comp1 quantity property value remainder comp2]
|
||||||
|
(gen-neighbours-condition comp1 quantity property value remainder comp2 1)))
|
||||||
|
|
||||||
|
(defn parse-comparator-neighbours-condition
|
||||||
|
"Parse conditions of the form '...more than 6 neighbours are [condition]'"
|
||||||
|
[[MORE THAN n NEIGHBOURS WITHIN distance have-or-are & rest]]
|
||||||
|
(let [quantity (first (parse-numeric-value (list n)))
|
||||||
|
comparator (cond (= MORE "more") '>
|
||||||
|
(member? MORE '("fewer" "less")) '<)]
|
||||||
|
(cond
|
||||||
|
(not= WITHIN "within")
|
||||||
|
(parse-comparator-neighbours-condition
|
||||||
|
(flatten
|
||||||
|
;; two tokens were mis-parsed as 'within distance' that weren't
|
||||||
|
;; actually 'within' and a distance. Splice in 'within 1' and try
|
||||||
|
;; again.
|
||||||
|
(list MORE THAN n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
|
||||||
|
(and quantity
|
||||||
|
comparator
|
||||||
|
(= THAN "than")
|
||||||
|
(= NEIGHBOURS "neighbours"))
|
||||||
|
(cond
|
||||||
|
(= have-or-are "are")
|
||||||
|
(let [[value & remainder] rest
|
||||||
|
dist (gen-token-value distance true)]
|
||||||
|
(gen-neighbours-condition comparator quantity :state value remainder = dist))
|
||||||
|
(= have-or-are "have")
|
||||||
|
(let [[property comp1 comp2 value & remainder] rest
|
||||||
|
dist (gen-token-value distance true)]
|
||||||
|
(cond (and (= comp1 "equal") (= comp2 "to"))
|
||||||
|
(gen-neighbours-condition comparator quantity property
|
||||||
|
value remainder = dist)
|
||||||
|
(and (= comp1 "more") (= comp2 "than"))
|
||||||
|
(gen-neighbours-condition comparator quantity property
|
||||||
|
value remainder > dist)
|
||||||
|
(and (= comp1 "less") (= comp2 "than"))
|
||||||
|
(gen-neighbours-condition comparator quantity property
|
||||||
|
value remainder < dist)
|
||||||
|
))))))
|
||||||
|
|
||||||
|
(defn parse-some-neighbours-condition
|
||||||
|
[[SOME NEIGHBOURS & rest]]
|
||||||
|
(cond
|
||||||
|
(and (= SOME "some") (= NEIGHBOURS "neighbours"))
|
||||||
|
(parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest))))
|
||||||
|
|
||||||
|
(defn parse-simple-neighbours-condition
|
||||||
|
"Parse conditions of the form '...6 neighbours are [condition]'"
|
||||||
|
[[n NEIGHBOURS WITHIN distance have-or-are & rest]]
|
||||||
|
(let [quantity (first (parse-numeric-value (list n)))]
|
||||||
|
(cond
|
||||||
|
(and quantity (= NEIGHBOURS "neighbours"))
|
||||||
|
(cond
|
||||||
|
(not= WITHIN "within")
|
||||||
|
(parse-simple-neighbours-condition
|
||||||
|
(flatten
|
||||||
|
;; two tokens were mis-parsed as 'within distance' that weren't
|
||||||
|
;; actually 'within' and a distance. Splice in 'within 1' and try
|
||||||
|
;; again.
|
||||||
|
(list n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
|
||||||
|
(= have-or-are "are")
|
||||||
|
(let [[value & remainder] rest
|
||||||
|
dist (gen-token-value distance true)]
|
||||||
|
(gen-neighbours-condition '= quantity :state value remainder = dist))
|
||||||
|
(= have-or-are "have")
|
||||||
|
(let [[property comp1 comp2 value & remainder] rest
|
||||||
|
dist (gen-token-value distance true)]
|
||||||
|
(cond (and (= comp1 "equal") (= comp2 "to"))
|
||||||
|
(gen-neighbours-condition '= quantity property value remainder =
|
||||||
|
dist)
|
||||||
|
(and (= comp1 "more") (= comp2 "than"))
|
||||||
|
(gen-neighbours-condition '= quantity property value remainder >
|
||||||
|
dist)
|
||||||
|
(and (= comp1 "less") (= comp2 "than"))
|
||||||
|
(gen-neighbours-condition '= quantity property value remainder <
|
||||||
|
dist)
|
||||||
|
))))))
|
||||||
|
|
||||||
|
(defn parse-neighbours-condition
|
||||||
|
"Parse conditions referring to neighbours"
|
||||||
|
[tokens]
|
||||||
|
(or
|
||||||
|
(parse-simple-neighbours-condition tokens)
|
||||||
|
(parse-comparator-neighbours-condition tokens)
|
||||||
|
(parse-some-neighbours-condition tokens)
|
||||||
|
))
|
||||||
|
|
||||||
|
(defn parse-simple-condition
|
||||||
|
"Parse conditions of the form '[property] [comparison] [value]'."
|
||||||
|
[tokens]
|
||||||
|
(or
|
||||||
|
(parse-neighbours-condition tokens)
|
||||||
|
(parse-member-condition tokens)
|
||||||
|
(parse-not-condition tokens)
|
||||||
|
(parse-less-condition tokens)
|
||||||
|
(parse-more-condition tokens)
|
||||||
|
(parse-between-condition tokens)
|
||||||
|
(parse-is-condition tokens)))
|
||||||
|
|
||||||
|
(defn- parse-disjunction-condition
|
||||||
|
"Parse '... or [condition]' from `tokens`, where `left` is the already parsed first disjunct."
|
||||||
|
[left tokens]
|
||||||
|
(let [partial (parse-conditions tokens)]
|
||||||
|
(if partial
|
||||||
|
(let [[right remainder] partial]
|
||||||
|
[(list 'or left right) remainder]))))
|
||||||
|
|
||||||
|
(defn- parse-conjunction-condition
|
||||||
|
"Parse '... and [condition]' from `tokens`, where `left` is the already parsed first conjunct."
|
||||||
|
[left tokens]
|
||||||
|
(let [partial (parse-conditions tokens)]
|
||||||
|
(if partial
|
||||||
|
(let [[right remainder] partial]
|
||||||
|
[(list 'and left right) remainder]))))
|
||||||
|
|
||||||
|
(defn- parse-conditions
|
||||||
|
"Parse conditions from `tokens`, where conditions may be linked by either 'and' or 'or'."
|
||||||
|
[tokens]
|
||||||
|
(let [partial (parse-simple-condition tokens)]
|
||||||
|
(if partial
|
||||||
|
(let [[left [next & remainder]] partial]
|
||||||
|
(cond
|
||||||
|
(= next "and") (parse-conjunction-condition left remainder)
|
||||||
|
(= next "or") (parse-disjunction-condition left remainder)
|
||||||
|
true partial)))))
|
||||||
|
|
||||||
|
(defn- parse-left-hand-side
|
||||||
|
"Parse the left hand side ('if...') of a production rule."
|
||||||
|
[[IF & tokens]]
|
||||||
|
(if
|
||||||
|
(= IF "if")
|
||||||
|
(parse-conditions tokens)))
|
||||||
|
|
||||||
|
(defn- parse-arithmetic-action
|
||||||
|
"Parse actions of the form '[property] should be [property] [arithmetic-operator] [value]',
|
||||||
|
e.g. 'fertility should be fertility + 1', or 'deer should be deer - wolves'."
|
||||||
|
[previous [prop1 SHOULD BE prop2 operator value & rest]]
|
||||||
|
(cond
|
||||||
|
(member? prop1 '("x" "y"))
|
||||||
|
(throw
|
||||||
|
(Exception. reserved-properties-error))
|
||||||
|
(and (= SHOULD "should")
|
||||||
|
(= BE "be")
|
||||||
|
(member? operator '("+" "-" "*" "/")))
|
||||||
|
[(list 'merge (or previous 'cell)
|
||||||
|
{(keyword prop1) (list 'int
|
||||||
|
(list (symbol operator) (list 'get-int 'cell (keyword prop2))
|
||||||
|
(cond
|
||||||
|
(re-matches re-number value) (read-string value)
|
||||||
|
true (list 'get-int 'cell (keyword value)))))}) rest]))
|
||||||
|
|
||||||
|
(defn- parse-set-action
|
||||||
|
"Parse actions of the form '[property] should be [value].'"
|
||||||
|
[previous [property SHOULD BE value & rest]]
|
||||||
|
(cond
|
||||||
|
(member? property '("x" "y"))
|
||||||
|
(throw
|
||||||
|
(Exception. reserved-properties-error))
|
||||||
|
(and (= SHOULD "should") (= BE "be"))
|
||||||
|
[(list 'merge (or previous 'cell)
|
||||||
|
{(keyword property) (cond (re-matches re-number value) (read-string value) true (keyword value))}) rest]))
|
||||||
|
|
||||||
|
(defn- parse-simple-action [previous tokens]
|
||||||
|
(or (parse-arithmetic-action previous tokens)
|
||||||
|
(parse-set-action previous tokens)))
|
||||||
|
|
||||||
|
(defn- parse-actions
|
||||||
|
"Parse actions from tokens."
|
||||||
|
[previous tokens]
|
||||||
|
(let [[left remainder] (parse-simple-action previous tokens)]
|
||||||
|
(cond left
|
||||||
|
(cond (= (first remainder) "and")
|
||||||
|
(parse-actions left (rest remainder))
|
||||||
|
true (list left)))))
|
||||||
|
|
||||||
|
(defn- parse-probability
|
||||||
|
"Parse a probability of an action from this collection of tokens"
|
||||||
|
[previous [n CHANCE IN m & tokens]]
|
||||||
|
(cond
|
||||||
|
(and (= CHANCE "chance")(= IN "in"))
|
||||||
|
(let [[action remainder] (parse-actions previous tokens)]
|
||||||
|
(cond action
|
||||||
|
[(list 'cond
|
||||||
|
(list '<
|
||||||
|
(list 'rand
|
||||||
|
(first (parse-simple-value (list m) true)))
|
||||||
|
(first (parse-simple-value (list n) true)))
|
||||||
|
action) remainder]))))
|
||||||
|
|
||||||
|
(defn- parse-right-hand-side
|
||||||
|
"Parse the right hand side ('then...') of a production rule."
|
||||||
|
[[THEN & tokens]]
|
||||||
|
(if (= THEN "then")
|
||||||
|
(or
|
||||||
|
(parse-probability nil tokens)
|
||||||
|
(parse-actions nil tokens))))
|
||||||
|
|
||||||
|
(defn parse-rule
|
||||||
|
"Parse a complete rule from this `line`, expected to be either a string or a
|
||||||
|
sequence of string tokens. Return the rule in the form of an S-expression.
|
||||||
|
|
||||||
|
Throws an exception if parsing fails."
|
||||||
|
[line]
|
||||||
|
(cond
|
||||||
|
(string? line)
|
||||||
|
(let [rule (parse-rule (split (triml line) #"\s+"))]
|
||||||
|
(cond rule rule
|
||||||
|
true (throw (Exception. (format bad-parse-error line)))))
|
||||||
|
true
|
||||||
|
(let [[left remainder] (parse-left-hand-side line)
|
||||||
|
[right junk] (parse-right-hand-side remainder)]
|
||||||
|
(cond
|
||||||
|
;; there should be a valide left hand side and a valid right hand side
|
||||||
|
;; there shouldn't be anything left over (junk should be empty)
|
||||||
|
(and left right (empty? junk))
|
||||||
|
(list 'fn ['cell 'world] (list 'if left right))))))
|
||||||
|
|
||||||
|
(defn compile-rule
|
||||||
|
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
|
||||||
|
into Clojure source, and then compile it into an anonymous
|
||||||
|
function object, getting round the problem of binding mw-engine.utils in
|
||||||
|
the compiling environment. If `return-tuple?` is present and true, return
|
||||||
|
a list comprising the anonymous function compiled, and the function from
|
||||||
|
which it was compiled.
|
||||||
|
|
||||||
|
Throws an exception if parsing fails."
|
||||||
|
([rule-text return-tuple?]
|
||||||
|
(do
|
||||||
|
(use 'mw-engine.utils)
|
||||||
|
(let [afn (eval (parse-rule rule-text))]
|
||||||
|
(cond
|
||||||
|
(and afn return-tuple?)(list afn (trim rule-text))
|
||||||
|
true afn))))
|
||||||
|
([rule-text]
|
||||||
|
(compile-rule rule-text false)))
|
|
@ -1,211 +1,368 @@
|
||||||
(ns ^{:doc "A very simple parser which parses production rules."
|
(ns mw-parser.declarative
|
||||||
:author "Simon Brooke"}
|
(:use mw-engine.utils
|
||||||
mw-parser.declarative
|
[clojure.string :only [split trim triml]])
|
||||||
(:require [clojure.string :refer [join split-lines]]
|
(:require [instaparse.core :as insta]))
|
||||||
[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
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;;; TODO: Either, when I first wrote this parser, I didn't adequately read the
|
;; error thrown when an attempt is made to set a reserved property
|
||||||
;;; Instaparse documentation, or Instaparse has advanced considerably since
|
(def reserved-properties-error
|
||||||
;;; then. Reading the documentation now, I could probably rewrite this to
|
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
|
||||||
;;; eliminate the simplify step altogether, and that would be well worth doing.
|
;; error thrown when a rule cannot be parsed. Slots are for
|
||||||
|
;; (1) rule text
|
||||||
|
;; (2) cursor showing where in the rule text the error occurred
|
||||||
|
;; (3) the reason for the error
|
||||||
|
(def bad-parse-error "I did not understand:\n'%s'\n%s\n%s")
|
||||||
|
|
||||||
(def ruleset-grammar
|
|
||||||
"Experimental: parse a whole file in one go."
|
|
||||||
;; TODO: bug here. We're double-counting (some) blank lines
|
|
||||||
(join "\n" ["LINES := (LINE)+;"
|
|
||||||
"LINE := RULE <CR> | FLOW-RULE <CR> | COMMENT <CR> | <CR> ;"
|
|
||||||
"CR := #'[ \\t]*[\\r\\n][- \\t]*';"
|
|
||||||
"COMMENT := #'[;\\#]+[^\\r\\n]*' | #'/\\*.*\\*/'"]))
|
|
||||||
|
|
||||||
(def rule-grammar
|
(def grammar
|
||||||
"Basic rule language grammar.
|
;; in order to simplify translation into other natural languages, all
|
||||||
|
;; TOKENS within the parser should be unambiguous
|
||||||
in order to simplify translation into other natural languages, all
|
"RULE := IF SPACE CONDITIONS SPACE THEN SPACE ACTIONS;
|
||||||
TOKENS within the parser should be unambiguous."
|
CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | PROPERTY-CONDITION | NEIGHBOURS-CONDITION ;
|
||||||
(join "\n" ["RULE := IF <SPACE> CONDITIONS <SPACE> <THEN> <SPACE> ACTIONS;"
|
DISJUNCT-CONDITION := CONDITION SPACE OR SPACE CONDITIONS;
|
||||||
"ACTIONS := ACTION | (ACTION <SPACE> <AND> <SPACE> ACTION)+"
|
CONJUNCT-CONDITION := CONDITION SPACE AND SPACE CONDITIONS;
|
||||||
"ACTION := SIMPLE-ACTION | PROBABLE-ACTION;"
|
CONDITION := NEIGHBOURS-CONDITION | PROPERTY-CONDITION;
|
||||||
"PROBABLE-ACTION := VALUE <SPACE> <CHANCE-IN> <SPACE> VALUE <SPACE> SIMPLE-ACTION;"
|
WITHIN-CONDITION := NEIGHBOURS-CONDITION SPACE WITHIN SPACE NUMERIC-EXPRESSION;
|
||||||
"SIMPLE-ACTION := SYMBOL <SPACE> BECOMES <SPACE> EXPRESSION;"]))
|
NEIGHBOURS-CONDITION := WITHIN-CONDITION | QUANTIFIER SPACE NEIGHBOURS SPACE IS SPACE PROPERTY-CONDITION | QUANTIFIER SPACE NEIGHBOURS IS EXPRESSION | QUALIFIER SPACE NEIGHBOURS-CONDITION;
|
||||||
|
PROPERTY-CONDITION := PROPERTY SPACE QUALIFIER SPACE EXPRESSION | VALUE;
|
||||||
|
EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE;
|
||||||
|
SIMPLE-EXPRESSION := QUALIFIER SPACE EXPRESSION | VALUE;
|
||||||
|
DISJUNCT-EXPRESSION := IN SPACE DISJUNCT-VALUE;
|
||||||
|
RANGE-EXPRESSION := BETWEEN SPACE NUMERIC-EXPRESSION SPACE AND SPACE NUMERIC-EXPRESSION;
|
||||||
|
NUMERIC-EXPRESSION := VALUE | VALUE SPACE OPERATOR SPACE NUMERIC-EXPRESSION;
|
||||||
|
NEGATED-QUALIFIER := QUALIFIER SPACE NOT | NOT SPACE QUALIFIER;
|
||||||
|
COMPARATIVE-QUALIFIER := IS SPACE COMPARATIVE SPACE THAN;
|
||||||
|
QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS SPACE QUALIFIER;
|
||||||
|
QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE SPACE THAN SPACE NUMBER;
|
||||||
|
EQUIVALENCE := IS SPACE EQUAL | EQUAL | IS ;
|
||||||
|
COMPARATIVE := MORE | LESS;
|
||||||
|
DISJUNCT-VALUE := VALUE | VALUE SPACE OR SPACE DISJUNCT-VALUE;
|
||||||
|
IF := 'if';
|
||||||
|
THEN := 'then';
|
||||||
|
THAN := 'than';
|
||||||
|
OR := 'or';
|
||||||
|
NOT := 'not';
|
||||||
|
AND := 'and';
|
||||||
|
SOME := 'some';
|
||||||
|
NONE := 'no';
|
||||||
|
ALL := 'all'
|
||||||
|
BETWEEN := 'between';
|
||||||
|
WITHIN := 'within';
|
||||||
|
IN := 'in';
|
||||||
|
MORE := 'more' | 'greater';
|
||||||
|
LESS := 'less' | 'fewer';
|
||||||
|
OPERATOR := '+' | '-' | '*' | '/';
|
||||||
|
NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';
|
||||||
|
PROPERTY := SYMBOL;
|
||||||
|
VALUE := SYMBOL | NUMBER;
|
||||||
|
EQUAL := 'equal to';
|
||||||
|
IS := 'is' | 'are' | 'have' | 'has';
|
||||||
|
NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';
|
||||||
|
SYMBOL := #'[a-z]+';
|
||||||
|
ACTIONS := ACTION | ACTION SPACE 'and' SPACE ACTIONS
|
||||||
|
ACTION := SIMPLE-ACTION | PROBABLE-ACTION;
|
||||||
|
PROBABLE-ACTION := VALUE SPACE 'chance in' SPACE VALUE SPACE SIMPLE-ACTION;
|
||||||
|
SIMPLE-ACTION := SYMBOL SPACE BECOMES SPACE EXPRESSION
|
||||||
|
BECOMES := 'should be'
|
||||||
|
SPACE := #' *'"
|
||||||
|
)
|
||||||
|
|
||||||
(def common-grammar
|
(defn TODO
|
||||||
"Grammar rules used both in the rule grammar and in the flow grammar"
|
"Marker to indicate I'm not yet finished!"
|
||||||
(join "\n" ["COMPARATIVE := MORE | LESS;"
|
[message]
|
||||||
"COMPARATIVE-QUALIFIER := IS <SPACE> COMPARATIVE <SPACE> THAN | COMPARATIVE <SPACE> THAN;"
|
message)
|
||||||
"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;"]))
|
|
||||||
|
|
||||||
(def keywords-en
|
|
||||||
"English language keyword literals used in rules - both in production
|
|
||||||
rules (this namespace) and in flow rules (see mw-parser.flow).
|
|
||||||
|
|
||||||
It's a long term aim that the rule language should be easy to
|
|
||||||
internationalise; this isn't a full solution but it's a step towards
|
|
||||||
a solution."
|
|
||||||
(join "\n" ["ALL := 'all'"
|
|
||||||
"AND := 'and';"
|
|
||||||
"BECOMES := 'should be' | 'becomes';"
|
|
||||||
"BETWEEN := 'between';"
|
|
||||||
"CHANCE-IN := 'chance in';"
|
|
||||||
"EACH := 'each' | 'every' | 'all';"
|
|
||||||
"EQUAL := 'equal to';"
|
|
||||||
"FIRST := 'first';"
|
|
||||||
"FLOW := 'flow' | 'move';"
|
|
||||||
"FROM := 'from';"
|
|
||||||
"IF := 'if';"
|
|
||||||
"IN := 'in';"
|
|
||||||
"IS := 'is' | 'are' | 'have' | 'has';"
|
|
||||||
"LEAST := 'least';"
|
|
||||||
"LESS := 'less' | 'fewer';"
|
|
||||||
"MORE := 'more' | 'greater';"
|
|
||||||
"MOST := 'most';"
|
|
||||||
"NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';"
|
|
||||||
"NONE := 'no';"
|
|
||||||
"NOT := 'not';"
|
|
||||||
"OR := 'or';"
|
|
||||||
"SOME := 'some';"
|
|
||||||
;; SYMBOL is in the per-language file so that languages that use
|
|
||||||
;; (e.g.) Cyrillic characters can change the definition.
|
|
||||||
"SYMBOL := #'[a-z]+';"
|
|
||||||
"THAN := 'than';"
|
|
||||||
"THEN := 'then';"
|
|
||||||
"TO := 'to';"
|
|
||||||
"WITH := 'with' | 'where' | 'having';"
|
|
||||||
"WITHIN := 'within';"]))
|
|
||||||
|
|
||||||
(defn keywords-for-locale
|
(declare generate simplify)
|
||||||
"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 parse
|
(defn suitable-fragment?
|
||||||
|
"Return `true` if `tree-fragment` appears to be a tree fragment of the expected `type`."
|
||||||
|
[tree-fragment type]
|
||||||
|
(and (coll? tree-fragment)
|
||||||
|
(= (first tree-fragment) type)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn assert-type
|
||||||
|
"If `tree-fragment` is not a tree fragment of the expected `type`, throw an exception."
|
||||||
|
[tree-fragment type]
|
||||||
|
(assert (suitable-fragment? tree-fragment type)
|
||||||
|
(throw (Exception. (format "Expected a %s fragment" type)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-rule
|
||||||
|
"From this `tree`, assumed to be a syntactically correct rule specification,
|
||||||
|
generate and return the appropriate rule as a function of two arguments."
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :RULE)
|
||||||
|
(list 'fn ['cell 'world] (list 'if (generate (nth tree 2)) (generate (nth tree 3)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-conditions
|
||||||
|
"From this `tree`, assumed to be a syntactically correct conditions clause,
|
||||||
|
generate and return the appropriate clojure fragment."
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :CONDITIONS)
|
||||||
|
(generate (nth tree 1)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-condition
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :CONDITION)
|
||||||
|
(generate (nth tree 1)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-conjunct-condition
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :CONJUNCT-CONDITION)
|
||||||
|
(list 'and (generate (nth tree 1))(generate (nth tree 3))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-disjunct-condition
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :DISJUNCT-CONDITION)
|
||||||
|
(list 'or (generate (nth tree 1))(generate (nth tree 3))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-ranged-property-condition
|
||||||
|
"Generate a property condition where the expression is a numeric range"
|
||||||
|
[tree property expression]
|
||||||
|
(assert-type tree :PROPERTY-CONDITION)
|
||||||
|
(assert-type (nth tree 3) :RANGE-EXPRESSION)
|
||||||
|
(let [l1 (generate (nth expression 2))
|
||||||
|
l2 (generate (nth expression 4))
|
||||||
|
pv (list property 'cell)]
|
||||||
|
(list 'let ['lower (list 'min l1 l2)
|
||||||
|
'upper (list 'max l1 l2)]
|
||||||
|
(list 'and (list '>= pv 'lower)(list '<= pv 'upper)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-disjunct-property-condition
|
||||||
|
"Generate a property condition where the expression is a disjunct expression.
|
||||||
|
TODO: this is definitely still wrong!"
|
||||||
|
([tree]
|
||||||
|
(let [property (generate (nth tree 1))
|
||||||
|
qualifier (generate (nth tree 2))
|
||||||
|
expression (generate (nth tree 3))]
|
||||||
|
(generate-disjunct-property-condition tree property qualifier expression)))
|
||||||
|
([tree property qualifier expression]
|
||||||
|
(let [e (list 'some (list 'fn ['i] '(= i value)) (list 'quote expression))]
|
||||||
|
(list 'let ['value (list property 'cell)]
|
||||||
|
(if (= qualifier '=) e
|
||||||
|
(list 'not e))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-property-condition
|
||||||
|
([tree]
|
||||||
|
(assert-type tree :PROPERTY-CONDITION)
|
||||||
|
(if
|
||||||
|
(and (= (count tree) 2) (= (first (second tree)) :SYMBOL))
|
||||||
|
;; it's a shorthand for 'state equal to symbol'. This should probably have
|
||||||
|
;; been handled in simplify...
|
||||||
|
(generate-property-condition
|
||||||
|
(list
|
||||||
|
:PROPERTY-CONDITION
|
||||||
|
'(:SYMBOL "state")
|
||||||
|
'(:QUALIFIER (:EQUIVALENCE (:EQUAL "equal to")))
|
||||||
|
(second tree)))
|
||||||
|
;; otherwise...
|
||||||
|
(generate-property-condition tree (first (nth tree 3)))))
|
||||||
|
([tree expression-type]
|
||||||
|
(assert-type tree :PROPERTY-CONDITION)
|
||||||
|
(let [property (generate (nth tree 1))
|
||||||
|
qualifier (generate (nth tree 2))
|
||||||
|
expression (generate (nth tree 3))]
|
||||||
|
(case expression-type
|
||||||
|
:DISJUNCT-EXPRESSION (generate-disjunct-property-condition tree property qualifier expression)
|
||||||
|
:RANGE-EXPRESSION (generate-ranged-property-condition tree property expression)
|
||||||
|
(list qualifier (list property 'cell) expression)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-simple-action
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :SIMPLE-ACTION)
|
||||||
|
(let [property (generate (nth tree 1))
|
||||||
|
expression (generate (nth tree 3))]
|
||||||
|
(if (or (= property :x) (= property :y))
|
||||||
|
(throw (Exception. reserved-properties-error))
|
||||||
|
(list 'merge 'cell {property expression}))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-multiple-actions
|
||||||
|
[tree]
|
||||||
|
(assert (and (coll? tree)(= (first tree) :ACTIONS)) "Expected an ACTIONS fragment")
|
||||||
|
(conj 'do (map generate-simple-action (rest tree))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-disjunct-value
|
||||||
|
"Generate a disjunct value. Essentially what we need here is to generate a
|
||||||
|
flat list of values, since the `member` has already been taken care of."
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :DISJUNCT-VALUE)
|
||||||
|
(if (= (count tree) 4)
|
||||||
|
(cons (generate (second tree)) (generate (nth tree 3)))
|
||||||
|
(list (generate (second tree)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-numeric-expression
|
||||||
|
[tree]
|
||||||
|
(assert-type tree :NUMERIC-EXPRESSION)
|
||||||
|
(case (first (second tree))
|
||||||
|
:SYMBOL (list (keyword (second (second tree))) 'cell)
|
||||||
|
(generate (second tree))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate-neighbours-condition
|
||||||
|
"Generate code for a condition which refers to neighbours."
|
||||||
|
([tree]
|
||||||
|
(assert-type tree :NEIGHBOURS-CONDITION)
|
||||||
|
(generate-neighbours-condition tree (first (second (second tree)))))
|
||||||
|
([tree quantifier-type]
|
||||||
|
(let [quantifier (second tree)
|
||||||
|
pc (generate (nth tree 4))]
|
||||||
|
(case quantifier-type
|
||||||
|
:NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc 1)
|
||||||
|
:SOME (generate-neighbours-condition '> 0 pc 1)
|
||||||
|
:MORE (let [value (generate (nth quantifier 3))]
|
||||||
|
(generate-neighbours-condition '> value pc 1))
|
||||||
|
:LESS (let [value (generate (nth quantifier 3))]
|
||||||
|
(generate-neighbours-condition '< value pc 1)))))
|
||||||
|
([comp1 quantity property-condition distance]
|
||||||
|
(list comp1
|
||||||
|
(list 'count
|
||||||
|
(list 'remove 'false?
|
||||||
|
(list 'map (list 'fn ['cell] property-condition)
|
||||||
|
(list 'mw-engine.utils/get-neighbours 'world 'cell distance)))) quantity))
|
||||||
|
([comp1 quantity property-condition]
|
||||||
|
(generate-neighbours-condition comp1 quantity property-condition 1)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn generate
|
||||||
|
"Generate code for this (fragment of a) parse tree"
|
||||||
|
[tree]
|
||||||
|
(if
|
||||||
|
(coll? tree)
|
||||||
|
(case (first tree)
|
||||||
|
:ACTIONS (generate-multiple-actions tree)
|
||||||
|
:COMPARATIVE (generate (second tree))
|
||||||
|
:COMPARATIVE-QUALIFIER (generate (nth tree 2))
|
||||||
|
:CONDITION (generate-condition tree)
|
||||||
|
:CONDITIONS (generate-conditions tree)
|
||||||
|
:CONJUNCT-CONDITION (generate-conjunct-condition tree)
|
||||||
|
:DISJUNCT-CONDITION (generate-disjunct-condition tree)
|
||||||
|
:DISJUNCT-EXPRESSION (generate (nth tree 2))
|
||||||
|
:DISJUNCT-VALUE (generate-disjunct-value tree)
|
||||||
|
:EQUIVALENCE '=
|
||||||
|
:EXPRESSION (generate (second tree))
|
||||||
|
:LESS '<
|
||||||
|
:MORE '>
|
||||||
|
:NEGATED-QUALIFIER (case (generate (second tree))
|
||||||
|
= 'not=
|
||||||
|
> '<
|
||||||
|
< '>)
|
||||||
|
:NEIGHBOURS-CONDITION (generate-neighbours-condition tree)
|
||||||
|
:NUMERIC-EXPRESSION (generate-numeric-expression tree)
|
||||||
|
:NUMBER (read-string (second tree))
|
||||||
|
:PROPERTY (list (generate (second tree)) 'cell) ;; dubious - may not be right
|
||||||
|
:PROPERTY-CONDITION (generate-property-condition tree)
|
||||||
|
:QUALIFIER (generate (second tree))
|
||||||
|
:RULE (generate-rule tree)
|
||||||
|
:SIMPLE-ACTION (generate-simple-action tree)
|
||||||
|
:SYMBOL (keyword (second tree))
|
||||||
|
:VALUE (generate (second tree))
|
||||||
|
(map generate tree))
|
||||||
|
tree))
|
||||||
|
|
||||||
|
|
||||||
|
(defn simplify-qualifier
|
||||||
|
"Given that this `tree` fragment represents a qualifier, what
|
||||||
|
qualifier is that?"
|
||||||
|
[tree]
|
||||||
|
(cond
|
||||||
|
(empty? tree) nil
|
||||||
|
(and (coll? tree)
|
||||||
|
(member? (first tree) '(:EQUIVALENCE :COMPARATIVE))) tree
|
||||||
|
(coll? (first tree)) (or (simplify-qualifier (first tree))
|
||||||
|
(simplify-qualifier (rest tree)))
|
||||||
|
(coll? tree) (simplify-qualifier (rest tree))
|
||||||
|
true tree))
|
||||||
|
|
||||||
|
(defn simplify-second-of-two
|
||||||
|
"There are a number of possible simplifications such that if the `tree` has
|
||||||
|
only two elements, the second is semantically sufficient."
|
||||||
|
[tree]
|
||||||
|
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
|
||||||
|
|
||||||
|
|
||||||
|
(defn rule?
|
||||||
|
"Return true if the argument appears to be a parsed rule tree, else false."
|
||||||
|
[maybe-rule]
|
||||||
|
(and (coll? maybe-rule) (= (first maybe-rule) :RULE)))
|
||||||
|
|
||||||
|
(defn simplify
|
||||||
|
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
|
||||||
|
semantically identical simpler fragments"
|
||||||
|
[tree]
|
||||||
|
(if
|
||||||
|
(coll? tree)
|
||||||
|
(case (first tree)
|
||||||
|
:ACTION (simplify-second-of-two tree)
|
||||||
|
:ACTIONS (simplify-second-of-two tree)
|
||||||
|
:COMPARATIVE (simplify-second-of-two tree)
|
||||||
|
:CONDITION (simplify-second-of-two tree)
|
||||||
|
:CONDITIONS (simplify-second-of-two tree)
|
||||||
|
:EXPRESSION (simplify-second-of-two tree)
|
||||||
|
:NOT nil ;; TODO is this right?!? It looks wrong
|
||||||
|
:PROPERTY (simplify-second-of-two tree)
|
||||||
|
:SPACE nil
|
||||||
|
:THEN nil
|
||||||
|
:VALUE (simplify-second-of-two tree)
|
||||||
|
(remove nil? (map simplify tree)))
|
||||||
|
tree))
|
||||||
|
|
||||||
|
(def parse-rule
|
||||||
"Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
|
"Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
|
||||||
[arg]
|
(insta/parser grammar))
|
||||||
(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- compile-rule
|
(defn explain-parse-error-reason
|
||||||
"Compile a rule function from this `parse-tree` derived from this `source`
|
"Attempt to explain the reason for the parse error."
|
||||||
at the zero-based line number `n` in the source file; return a compiled
|
[reason]
|
||||||
function, whose metadata has the keys:
|
(str "Expecting one of (" (apply str (map #(str (:expecting %) " ") reason)) ")"))
|
||||||
|
|
||||||
* `:rule-type` : the type of rule the function represents;
|
(defn parser-error-to-map
|
||||||
* `:parse` : this `parse-tree`;
|
[parser-error]
|
||||||
* `:source` : the rule source from which the parse tree was derived;
|
(let [m (reduce (fn [map item](merge map {(first item)(second item)})) {} parser-error)
|
||||||
* `:lisp` : the lisp source generated from this `parse-tree`;
|
reason (map
|
||||||
* `:line : the one-based line number of the definition in the source file,
|
#(reduce (fn [map item] (merge {(first item) (second item)} map)) {} %)
|
||||||
i.e. `(inc n)`."
|
(:reason m))]
|
||||||
[parse-tree source n]
|
(merge m {:reason reason})))
|
||||||
(if (#{:COMMENT :LINE} (first parse-tree))
|
|
||||||
(do
|
(defn throw-parse-exception
|
||||||
(l/info (format "Skipping line %d, `%s`, parse-tree %s."
|
"Construct a helpful error message from this `parser-error`, and throw an exception with that message."
|
||||||
(inc n) source parse-tree))
|
[parser-error]
|
||||||
nil)
|
(assert (coll? parser-error) "Expected a paser error structure?")
|
||||||
(let [lisp (generate parse-tree)
|
(let
|
||||||
line-no (inc n)]
|
[
|
||||||
(l/info (format "Compiling rule at line %d, `%s`." line-no source))
|
;; the error structure is a list, such that each element is a list of two items, and
|
||||||
(try
|
;; the first element in each sublist is a keyword. Easier to work with it as a map
|
||||||
(if (#{'fn 'fn*} (first lisp))
|
error-map (parser-error-to-map parser-error)
|
||||||
(vary-meta
|
text (:text error-map)
|
||||||
(eval lisp)
|
reason (explain-parse-error-reason (:reason error-map))
|
||||||
merge (meta lisp) {:source source :lisp lisp :line line-no})
|
;; rules have only one line, by definition; we're interested in the column
|
||||||
(throw
|
column (if (:column error-map)(:column error-map) 0)
|
||||||
(Exception.
|
;; create a cursor to point to that column
|
||||||
(format "Parse of `%s` did not return a function: %s" source lisp))))
|
cursor (apply str (reverse (conj (repeat column " ") "^")))
|
||||||
(catch Exception any (throw (ex-info (.getMessage any)
|
message (format bad-parse-error text cursor reason)
|
||||||
{:source source
|
]
|
||||||
:parse parse-tree
|
(throw (Exception. message))))
|
||||||
:lisp lisp
|
|
||||||
:line line-no})))))))
|
(defn compile-rule
|
||||||
|
"Compile this `rule`, assumed to be a string with appropriate syntax, into a function of two arguments,
|
||||||
|
a `cell` and a `world`, having the same semantics."
|
||||||
|
[rule]
|
||||||
|
(assert (string? rule))
|
||||||
|
(let [tree (simplify (parse-rule rule))]
|
||||||
|
(if (rule? tree) (eval (generate tree))
|
||||||
|
(throw-parse-exception tree))))
|
||||||
|
|
||||||
(defn compile
|
|
||||||
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
|
|
||||||
into Clojure source, and then compile it into an anonymous
|
|
||||||
function object, getting round the problem of binding mw-engine.utils in
|
|
||||||
the compiling environment.
|
|
||||||
|
|
||||||
Returns a list of anonymous functions each of two arguments, `[cell world]`,
|
|
||||||
as expected for a MicroWorld rule function. Each function is decorated with
|
|
||||||
metadata having the keys:
|
|
||||||
|
|
||||||
* `:rule-type` : the type of rule the function represents;
|
|
||||||
* `:lisp` : the lisp source from which the function was compiled;
|
|
||||||
* `:parse` : the parse-tree from which that lisp source was derived;
|
|
||||||
* `:source` : the rule source from which the parse-tree was derived;
|
|
||||||
* `:line : the one-based line number of the rule source in the source file.
|
|
||||||
|
|
||||||
Throws an exception if parsing fails."
|
|
||||||
[rule-text]
|
|
||||||
(let [lines (split-lines rule-text)]
|
|
||||||
(remove
|
|
||||||
nil?
|
|
||||||
(map
|
|
||||||
compile-rule
|
|
||||||
(simplify (parse rule-text))
|
|
||||||
lines
|
|
||||||
(range (count lines))))))
|
|
|
@ -1,63 +0,0 @@
|
||||||
(ns ^{:doc "Display parse errors in a format which makes it easy for the user
|
|
||||||
to see where the error occurred."
|
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-parser.errors)
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;
|
|
||||||
;; This program is free software; you can redistribute it and/or
|
|
||||||
;; modify it under the terms of the GNU General Public License
|
|
||||||
;; as published by the Free Software Foundation; either version 2
|
|
||||||
;; of the License, or (at your option) any later version.
|
|
||||||
;;
|
|
||||||
;; This program is distributed in the hope that it will be useful,
|
|
||||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
;; GNU General Public License for more details.
|
|
||||||
;;
|
|
||||||
;; You should have received a copy of the GNU General Public License
|
|
||||||
;; along with this program; if not, write to the Free Software
|
|
||||||
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
;; USA.
|
|
||||||
;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
|
|
||||||
;; error thrown when an attempt is made to set a reserved property
|
|
||||||
;; error thrown when a rule cannot be parsed. Slots are for
|
|
||||||
;; (1) rule text
|
|
||||||
;; (2) cursor showing where in the rule text the error occurred
|
|
||||||
;; (3) the reason for the error
|
|
||||||
(def bad-parse-error "I did not understand:\n '%s'\n %s\n %s")
|
|
||||||
|
|
||||||
(defn- explain-parse-error-reason
|
|
||||||
"Attempt to explain the reason for the parse error."
|
|
||||||
[reason]
|
|
||||||
(str "Expecting one of (" (apply str (map #(str (:expecting %) " ") reason)) ")"))
|
|
||||||
|
|
||||||
(defn- parser-error-to-map
|
|
||||||
[parser-error]
|
|
||||||
(let [m (reduce (fn [map item](merge map {(first item)(second item)})) {} parser-error)
|
|
||||||
reason (map
|
|
||||||
#(reduce (fn [map item] (merge {(first item) (second item)} map)) {} %)
|
|
||||||
(:reason m))]
|
|
||||||
(merge m {:reason reason})))
|
|
||||||
|
|
||||||
(defn throw-parse-exception
|
|
||||||
"Construct a helpful error message from this `parser-error`, and throw an exception with that message."
|
|
||||||
[parser-error]
|
|
||||||
(assert (coll? parser-error) "Expected a paser error structure?")
|
|
||||||
(let
|
|
||||||
[
|
|
||||||
;; the error structure is a list, such that each element is a list of two items, and
|
|
||||||
;; the first element in each sublist is a keyword. Easier to work with it as a map
|
|
||||||
error-map (parser-error-to-map parser-error)
|
|
||||||
text (:text error-map)
|
|
||||||
reason (explain-parse-error-reason (:reason error-map))
|
|
||||||
;; rules have only one line, by definition; we're interested in the column
|
|
||||||
column (if (:column error-map)(:column error-map) 0)
|
|
||||||
;; create a cursor to point to that column
|
|
||||||
cursor (apply str (reverse (conj (repeat column " ") "^")))
|
|
||||||
message (format bad-parse-error text cursor reason)
|
|
||||||
]
|
|
||||||
(throw (Exception. message))))
|
|
|
@ -1,37 +0,0 @@
|
||||||
(ns ^{:doc "A very simple parser which parses flow rules."
|
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-parser.flow
|
|
||||||
(:require [clojure.string :refer [join]]))
|
|
||||||
|
|
||||||
(def flow-grammar
|
|
||||||
"Grammar for flow rules.
|
|
||||||
|
|
||||||
My initial conception of this would be that production rules
|
|
||||||
(if-then rules) and flow rules (flow-from-to rules) would be
|
|
||||||
entirely separate, presented to the parser as separate text
|
|
||||||
files, and parsed and compiled by different chains of functions.
|
|
||||||
|
|
||||||
This appears not to be necessary. Flow rules are easy to parse
|
|
||||||
with the same parser as production rules -- a lot of the grammar
|
|
||||||
is intentionally common -- and the rules are easily discriminated
|
|
||||||
at the compilation ('generate') stage.
|
|
||||||
|
|
||||||
The basic rule I want to be able to compile at this stage is the 'mutual
|
|
||||||
aid' rule:
|
|
||||||
|
|
||||||
`flow 1 food from house to house within 2 with least food`
|
|
||||||
"
|
|
||||||
(join "\n" ["FLOW-RULE := FLOW SPACE QUANTITY SPACE PROPERTY SPACE FROM SPACE SOURCE SPACE TO-HOW SPACE DESTINATION;"
|
|
||||||
"PERCENTAGE := NUMBER #'%';"
|
|
||||||
"QUANTITY := PERCENTAGE | NUMBER | EXPRESSION | SOME;"
|
|
||||||
"SOURCE := STATE | STATE SPACE WITH SPACE CONDITIONS;"
|
|
||||||
"DESTINATION := TARGET | TARGET SPACE WITH SPACE FLOW-CONDITIONS;"
|
|
||||||
"DETERMINER := MOST | LEAST;"
|
|
||||||
"DETERMINER-CONDITION := DETERMINER SPACE PROPERTY;"
|
|
||||||
"FLOW-CONDITIONS := DETERMINER-CONDITION | CONDITIONS"
|
|
||||||
"RANGE := WITHIN SPACE VALUE;"
|
|
||||||
"STATE := SYMBOL;"
|
|
||||||
"TARGET := STATE | STATE SPACE RANGE;"
|
|
||||||
"TO-HOW := TO | TO-EACH | TO-FIRST;" ;; SHARED BETWEEN
|
|
||||||
"TO-EACH := TO SPACE EACH | TO SPACE ALL;"
|
|
||||||
"TO-FIRST := TO SPACE FIRST"]))
|
|
|
@ -1,432 +0,0 @@
|
||||||
(ns ^{:doc "Generate Clojure source from simplified parse trees."
|
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-parser.generate
|
|
||||||
(:require
|
|
||||||
[mw-engine.utils :refer :all] ;; may need these when macro-expanding rules.
|
|
||||||
[mw-parser.utils :refer [assert-type search-tree TODO]]))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;
|
|
||||||
;; This program is free software; you can redistribute it and/or
|
|
||||||
;; modify it under the terms of the GNU General Public License
|
|
||||||
;; as published by the Free Software Foundation; either version 2
|
|
||||||
;; of the License, or (at your option) any later version.
|
|
||||||
;;
|
|
||||||
;; This program is distributed in the hope that it will be useful,
|
|
||||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
;; GNU General Public License for more details.
|
|
||||||
;;
|
|
||||||
;; You should have received a copy of the GNU General Public License
|
|
||||||
;; along with this program; if not, write to the Free Software
|
|
||||||
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
;; USA.
|
|
||||||
;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(declare generate generate-action)
|
|
||||||
|
|
||||||
(def reserved-properties-error
|
|
||||||
"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")
|
|
||||||
|
|
||||||
;;; macros used in generated rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;;; production (if-then) rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn generate-rule
|
|
||||||
"From this `tree`, assumed to be a syntactically correct rule specification,
|
|
||||||
generate and return the appropriate rule as a function of two arguments."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :RULE)
|
|
||||||
(vary-meta
|
|
||||||
;; do macro-expansion here, because at least in theory I know what
|
|
||||||
;; macros are in scope here.
|
|
||||||
(macroexpand
|
|
||||||
(list 'fn ['cell 'world] (list 'when (generate (nth tree 2)) (generate (nth tree 3)))))
|
|
||||||
merge
|
|
||||||
{:rule-type
|
|
||||||
:production}))
|
|
||||||
|
|
||||||
(defn generate-conditions
|
|
||||||
"From this `tree`, assumed to be a syntactically correct conditions clause,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :CONDITIONS)
|
|
||||||
(generate (second tree)))
|
|
||||||
|
|
||||||
(defn generate-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically correct condition clause,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :CONDITION)
|
|
||||||
(generate (second tree)))
|
|
||||||
|
|
||||||
(defn generate-conjunct-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically conjunct correct condition clause,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :CONJUNCT-CONDITION)
|
|
||||||
(cons 'and (map generate (rest tree))))
|
|
||||||
|
|
||||||
(defn generate-disjunct-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically correct disjunct condition clause,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :DISJUNCT-CONDITION)
|
|
||||||
(cons 'or (map generate (rest tree))))
|
|
||||||
|
|
||||||
(defn generate-ranged-property-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically property condition clause for
|
|
||||||
this `property` where the `expression` is a numeric range, generate and return
|
|
||||||
the appropriate clojure fragment."
|
|
||||||
[tree property expression]
|
|
||||||
(assert-type tree :PROPERTY-CONDITION)
|
|
||||||
(assert-type (nth tree 3) :RANGE-EXPRESSION)
|
|
||||||
(let [l1 (generate (nth expression 2))
|
|
||||||
l2 (generate (nth expression 4))
|
|
||||||
pv (list property 'cell)]
|
|
||||||
(list 'let ['lower (list 'min l1 l2)
|
|
||||||
'upper (list 'max l1 l2)]
|
|
||||||
(list 'and (list '>= pv 'lower) (list '<= pv 'upper)))))
|
|
||||||
|
|
||||||
(defn generate-disjunct-property-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically property condition clause
|
|
||||||
where the expression is a a disjunction, generate and return
|
|
||||||
the appropriate clojure fragment.
|
|
||||||
TODO: this is definitely still wrong!"
|
|
||||||
([tree]
|
|
||||||
(let [property (generate (second tree))
|
|
||||||
qualifier (generate (nth tree 2))
|
|
||||||
expression (generate (nth tree 3))]
|
|
||||||
(generate-disjunct-property-condition tree property qualifier expression)))
|
|
||||||
([_tree property qualifier expression]
|
|
||||||
(let [e (list expression (list property 'cell))]
|
|
||||||
(if (= qualifier '=) e
|
|
||||||
(list 'not e)))))
|
|
||||||
|
|
||||||
(defn generate-property-condition
|
|
||||||
"From this `tree`, assumed to be a syntactically property condition clause,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
([tree]
|
|
||||||
(assert-type tree :PROPERTY-CONDITION)
|
|
||||||
(if
|
|
||||||
(and (= (count tree) 2) (= (first (second tree)) :SYMBOL))
|
|
||||||
;; it's a shorthand for 'state equal to symbol'. This should probably have
|
|
||||||
;; been handled in simplify...
|
|
||||||
(generate-property-condition
|
|
||||||
(list
|
|
||||||
:PROPERTY-CONDITION
|
|
||||||
'(:SYMBOL "state")
|
|
||||||
'(:QUALIFIER (:EQUIVALENCE (:EQUAL "equal to")))
|
|
||||||
(second tree)))
|
|
||||||
;; otherwise...
|
|
||||||
(generate-property-condition tree (first (nth tree 3)))))
|
|
||||||
([tree expression-type]
|
|
||||||
(assert-type tree :PROPERTY-CONDITION)
|
|
||||||
(let [property (generate (second tree))
|
|
||||||
qualifier (generate (nth tree 2))
|
|
||||||
e (generate (nth tree 3))
|
|
||||||
expression (cond
|
|
||||||
(and (not (= qualifier '=)) (keyword? e)) (list 'or (list e 'cell) e)
|
|
||||||
(and (not (= qualifier 'not=)) (keyword? e)) (list 'or (list e 'cell) e)
|
|
||||||
:else e)]
|
|
||||||
(case expression-type
|
|
||||||
:DISJUNCT-EXPRESSION (generate-disjunct-property-condition tree property qualifier expression)
|
|
||||||
:RANGE-EXPRESSION (generate-ranged-property-condition tree property expression)
|
|
||||||
(list qualifier (if (number? expression)
|
|
||||||
(list 'mw-engine.utils/get-num 'cell property)
|
|
||||||
(list property 'cell)) expression)))))
|
|
||||||
|
|
||||||
(defn generate-qualifier
|
|
||||||
"From this `tree`, assumed to be a syntactically correct qualifier,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(if
|
|
||||||
(= (count tree) 2)
|
|
||||||
(generate (second tree))
|
|
||||||
;; else
|
|
||||||
(generate (nth tree 2))))
|
|
||||||
|
|
||||||
(defn generate-simple-action
|
|
||||||
"From this `tree`, assumed to be a syntactically correct simple action,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
([tree]
|
|
||||||
(assert-type tree :SIMPLE-ACTION)
|
|
||||||
(generate-simple-action tree []))
|
|
||||||
([tree others]
|
|
||||||
(assert-type tree :SIMPLE-ACTION)
|
|
||||||
(let [property (generate (second tree))
|
|
||||||
expression (generate (nth tree 3))]
|
|
||||||
(if (or (= property :x) (= property :y))
|
|
||||||
(throw (Exception. reserved-properties-error))
|
|
||||||
(list 'merge
|
|
||||||
(if (empty? others) 'cell
|
|
||||||
;; else
|
|
||||||
(generate others))
|
|
||||||
{property expression})))))
|
|
||||||
|
|
||||||
(defn trap-errors-in-dice-throw
|
|
||||||
"We're getting a wierd -- many would say 'impossible' -- intermittent bug
|
|
||||||
which appears to happen here. "
|
|
||||||
[sides chances action]
|
|
||||||
;; (list 'try
|
|
||||||
(list 'if (list '< (list 'rand sides) chances) action)
|
|
||||||
;; (list 'catch 'Exception 'any
|
|
||||||
;; (list 'println (list 'format "Dice throw bug %d/%d" chances sides))
|
|
||||||
;; (list 'throw (list 'ex-info "Error in dice throw"
|
|
||||||
;; {:total sides
|
|
||||||
;; :chances chances
|
|
||||||
;; :action action}
|
|
||||||
;; 'any))))
|
|
||||||
)
|
|
||||||
|
|
||||||
(defn generate-probable-action
|
|
||||||
"From this `tree`, assumed to be a syntactically correct probable action,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
([tree]
|
|
||||||
(assert-type tree :PROBABLE-ACTION)
|
|
||||||
(generate-probable-action tree []))
|
|
||||||
([tree others]
|
|
||||||
(assert-type tree :PROBABLE-ACTION)
|
|
||||||
(let
|
|
||||||
[chances (generate (nth tree 1))
|
|
||||||
total (generate (nth tree 2))
|
|
||||||
action (generate-action (nth tree 3) others)]
|
|
||||||
;; TODO: could almost certainly be done better with macro syntax
|
|
||||||
(trap-errors-in-dice-throw total chances action))))
|
|
||||||
|
|
||||||
(defn generate-action
|
|
||||||
"From this `tree`, assumed to be a syntactically correct action,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree others]
|
|
||||||
(case (first tree)
|
|
||||||
:ACTIONS (generate-action (first tree) others)
|
|
||||||
:SIMPLE-ACTION (generate-simple-action tree others)
|
|
||||||
:PROBABLE-ACTION (generate-probable-action tree others)
|
|
||||||
(throw (Exception. (str "Not a known action type: " (first tree))))))
|
|
||||||
|
|
||||||
(defn generate-multiple-actions
|
|
||||||
"From this `tree`, assumed to be one or more syntactically correct actions,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :ACTIONS)
|
|
||||||
(generate-action (first (rest tree)) (second (rest tree))))
|
|
||||||
|
|
||||||
(defn generate-numeric-expression
|
|
||||||
"From this `tree`, assumed to be a syntactically correct numeric expression,
|
|
||||||
generate and return the appropriate clojure fragment."
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :NUMERIC-EXPRESSION)
|
|
||||||
(case (count tree)
|
|
||||||
4 (let [[p operator expression] (rest tree)
|
|
||||||
property (if (number? p) p (list 'mw-engine.utils/get-num 'cell p))]
|
|
||||||
(list (generate operator) (generate property) (generate expression)))
|
|
||||||
(case (first (second tree))
|
|
||||||
:SYMBOL (list 'mw-engine.utils/get-num 'cell (generate (second tree)))
|
|
||||||
(generate (second tree)))))
|
|
||||||
|
|
||||||
(defn generate-neighbours-condition
|
|
||||||
"Generate code for a condition which refers to neighbours."
|
|
||||||
([tree]
|
|
||||||
(assert-type tree :NEIGHBOURS-CONDITION)
|
|
||||||
(case (first (second tree))
|
|
||||||
:NUMBER (read-string (second (second tree)))
|
|
||||||
:QUANTIFIER (generate-neighbours-condition tree (first (second (second tree))))
|
|
||||||
:QUALIFIER (cons (generate (second tree)) (rest (generate (nth tree 2))))))
|
|
||||||
([tree quantifier-type]
|
|
||||||
(let [quantifier (second tree)
|
|
||||||
pc (generate (nth tree 4))]
|
|
||||||
(case quantifier-type
|
|
||||||
:NUMBER (generate-neighbours-condition '= (read-string (second (second quantifier))) pc 1)
|
|
||||||
:SOME (generate-neighbours-condition '> 0 pc 1)
|
|
||||||
:MORE (let [value (generate (nth quantifier 3))]
|
|
||||||
(generate-neighbours-condition '> value pc 1))
|
|
||||||
:LESS (let [value (generate (nth quantifier 3))]
|
|
||||||
(generate-neighbours-condition '< value pc 1)))))
|
|
||||||
([comp1 quantity property-condition distance]
|
|
||||||
(list comp1
|
|
||||||
(list 'count
|
|
||||||
(list 'remove 'false?
|
|
||||||
(list 'map (list 'fn ['cell] property-condition)
|
|
||||||
(list 'mw-engine.utils/get-neighbours 'world 'cell distance))))
|
|
||||||
quantity))
|
|
||||||
([comp1 quantity property-condition]
|
|
||||||
(generate-neighbours-condition comp1 quantity property-condition 1)))
|
|
||||||
|
|
||||||
(defn generate-within-condition
|
|
||||||
"Generate code for a condition which refers to neighbours within a specified distance.
|
|
||||||
NOTE THAT there's clearly masses of commonality between this and
|
|
||||||
`generate-neighbours-condition`, and that some refactoring is almost certainly
|
|
||||||
desirable. It may be that it's better to simplify a `NEIGHBOURS-CONDITION`
|
|
||||||
into a `WITHIN-CONDITION` in the simplification stage."
|
|
||||||
([tree]
|
|
||||||
(assert-type tree :WITHIN-CONDITION)
|
|
||||||
(case (first (second tree))
|
|
||||||
:QUANTIFIER (generate-within-condition tree (first (second (second tree))))
|
|
||||||
:QUALIFIER (TODO "qualified within... help!")))
|
|
||||||
([tree quantifier-type]
|
|
||||||
(let [quantifier (second tree)
|
|
||||||
distance (generate (nth tree 4))
|
|
||||||
pc (generate (nth tree 6))]
|
|
||||||
(case quantifier-type
|
|
||||||
:NUMBER (generate-neighbours-condition
|
|
||||||
'=
|
|
||||||
(read-string (second (second quantifier)))
|
|
||||||
pc
|
|
||||||
distance)
|
|
||||||
:SOME (generate-neighbours-condition '> 0 pc distance)
|
|
||||||
:MORE (let [value (generate (nth quantifier 3))]
|
|
||||||
(generate-neighbours-condition '> value pc distance))
|
|
||||||
:LESS (let [value (generate (nth quantifier 3))]
|
|
||||||
(generate-neighbours-condition '< value pc distance))))))
|
|
||||||
|
|
||||||
;;; Flow rules. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; A flow rule DOES NOT return a modified cell; instead, it
|
|
||||||
;;; returns a PLAN to modify the world, in the form of a sequence of `flow`
|
|
||||||
;;; objects. See `mw-engine.flow`
|
|
||||||
;;;
|
|
||||||
;;; It is only when the plan is executed that the world is modified.
|
|
||||||
|
|
||||||
(defn flow-rule
|
|
||||||
"Generate a flow rule for this `quantity` of this `property` from this
|
|
||||||
`source` to this `destination`.
|
|
||||||
|
|
||||||
A flow rule **does not** return a modified cell; instead, it
|
|
||||||
returns a **plan** to modify the world, in the form of a sequence of
|
|
||||||
`flow` objects. See `mw-engine.flow`
|
|
||||||
|
|
||||||
It is only when the plan is executed that the world is modified."
|
|
||||||
[source property quantity-frag destinations]
|
|
||||||
(vary-meta
|
|
||||||
;; do macro-expansion here, because at least in theory I know what
|
|
||||||
;; macros are in scope here.
|
|
||||||
(macroexpand
|
|
||||||
(list 'fn ['cell 'world]
|
|
||||||
(list 'when
|
|
||||||
(list 'and
|
|
||||||
source
|
|
||||||
(list 'pos?
|
|
||||||
(list 'mw-engine.utils/get-num 'cell property)))
|
|
||||||
(list 'map
|
|
||||||
(list 'fn ['d]
|
|
||||||
{:source (list 'select-keys 'cell [:x :y])
|
|
||||||
:destination (list 'select-keys 'd [:x :y])
|
|
||||||
:property property
|
|
||||||
:quantity quantity-frag})
|
|
||||||
destinations))))
|
|
||||||
merge
|
|
||||||
{:rule-type
|
|
||||||
:flow}))
|
|
||||||
|
|
||||||
(defn generate-quantity-accessor
|
|
||||||
"Generate a code fragment which will generate the appropriate quantity of
|
|
||||||
the `property` specified in a rule, from this `q-clause`."
|
|
||||||
[q-clause property]
|
|
||||||
(case (first q-clause)
|
|
||||||
;; TODO :EXPRESSION still needed
|
|
||||||
:NUMBER (generate q-clause)
|
|
||||||
:PERCENTAGE (let [multiplier (/ (generate (second q-clause)) 100)]
|
|
||||||
(list '* multiplier
|
|
||||||
(list 'mw-engine.utils/get-num 'cell property)))
|
|
||||||
:SIMPLE-EXPRESSION (if (= (count q-clause) 2)
|
|
||||||
(generate-quantity-accessor (second q-clause)
|
|
||||||
property)
|
|
||||||
(throw (ex-info
|
|
||||||
(format
|
|
||||||
"Cannot yet handle q-clause of form: `%s`"
|
|
||||||
q-clause)
|
|
||||||
{:clause q-clause
|
|
||||||
:property property})))
|
|
||||||
:SOME (list 'rand (list 'mw-engine.utils/get-num 'cell property))
|
|
||||||
(throw (ex-info
|
|
||||||
(format "Unexpected QUANTITY type: `%s`" (first q-clause))
|
|
||||||
{:clause q-clause
|
|
||||||
:property property}))))
|
|
||||||
|
|
||||||
(defn generate-target-state-filter
|
|
||||||
[clause targets-frag]
|
|
||||||
(assert-type clause :DESTINATION)
|
|
||||||
(list 'filter
|
|
||||||
(list 'fn ['cell]
|
|
||||||
(generate-property-condition
|
|
||||||
(search-tree (search-tree clause :TARGET)
|
|
||||||
:PROPERTY-CONDITION)))
|
|
||||||
targets-frag))
|
|
||||||
|
|
||||||
(defn generate-dest-accessor
|
|
||||||
[clause]
|
|
||||||
(let [dc (search-tree clause :DETERMINER-CONDITION)
|
|
||||||
range (search-tree clause :RANGE)
|
|
||||||
distance (if range (generate (nth range 2)) 1)]
|
|
||||||
(list 'let ['candidates
|
|
||||||
(generate-target-state-filter
|
|
||||||
clause
|
|
||||||
(list 'mw-engine.utils/get-neighbours
|
|
||||||
'world 'cell distance))]
|
|
||||||
(if dc
|
|
||||||
(list 'list
|
|
||||||
(let [determiner (first
|
|
||||||
(second
|
|
||||||
(search-tree dc :DETERMINER)))
|
|
||||||
prop (generate (nth dc 2))]
|
|
||||||
(case determiner
|
|
||||||
:LEAST (list 'mw-engine.utils/get-least-cell
|
|
||||||
'candidates prop)
|
|
||||||
:MOST (list 'mw-engine.utils/get-most-cell
|
|
||||||
'candidates prop))))
|
|
||||||
'candidates))))
|
|
||||||
|
|
||||||
(defn generate-flow
|
|
||||||
[tree]
|
|
||||||
(assert-type tree :FLOW-RULE)
|
|
||||||
(let [clauses (reduce #(assoc %1 (first %2) %2) {} (rest tree))
|
|
||||||
source-accessor (generate (:SOURCE clauses))
|
|
||||||
property (generate (:SYMBOL clauses))
|
|
||||||
quantity (generate-quantity-accessor (second (:QUANTITY clauses)) property)
|
|
||||||
dest-accessor (generate-dest-accessor (:DESTINATION clauses))]
|
|
||||||
(flow-rule source-accessor property quantity dest-accessor)))
|
|
||||||
|
|
||||||
;;; Top level; only function anything outside this file (except tests) should
|
|
||||||
;;; really call.
|
|
||||||
|
|
||||||
(defn generate
|
|
||||||
"Generate code for this (fragment of a) parse tree"
|
|
||||||
[tree]
|
|
||||||
(if
|
|
||||||
(coll? tree)
|
|
||||||
(case (first tree)
|
|
||||||
:ACTIONS (generate-multiple-actions tree)
|
|
||||||
:COMPARATIVE (generate (second tree))
|
|
||||||
:COMPARATIVE-QUALIFIER (generate (second tree))
|
|
||||||
:CONDITION (generate-condition tree)
|
|
||||||
:CONDITIONS (generate-conditions tree)
|
|
||||||
:CONJUNCT-CONDITION (generate-conjunct-condition tree)
|
|
||||||
:DISJUNCT-CONDITION (generate-disjunct-condition tree)
|
|
||||||
:DISJUNCT-EXPRESSION (set (generate (second tree)))
|
|
||||||
:DISJUNCT-VALUE (map generate (rest tree))
|
|
||||||
:EQUIVALENCE '=
|
|
||||||
:EXPRESSION (generate (second tree))
|
|
||||||
:FLOW-RULE (generate-flow tree)
|
|
||||||
:LESS '<
|
|
||||||
:MORE '>
|
|
||||||
:NEGATED-QUALIFIER (case (generate (second tree))
|
|
||||||
= 'not=
|
|
||||||
> '<
|
|
||||||
< '>)
|
|
||||||
:NEIGHBOURS-CONDITION (generate-neighbours-condition tree)
|
|
||||||
:NUMERIC-EXPRESSION (generate-numeric-expression tree)
|
|
||||||
:NUMBER (read-string (second tree))
|
|
||||||
:OPERATOR (symbol (second tree))
|
|
||||||
:PROBABLE-ACTION (generate-probable-action tree)
|
|
||||||
:PROPERTY (list (generate (second tree)) 'cell) ;; dubious - may not be right
|
|
||||||
:PROPERTY-CONDITION (generate-property-condition tree)
|
|
||||||
:QUALIFIER (generate-qualifier tree)
|
|
||||||
:QUANTITY (generate (second tree))
|
|
||||||
:RULE (generate-rule tree)
|
|
||||||
:SIMPLE-ACTION (generate-simple-action tree)
|
|
||||||
:SOURCE (generate (second tree))
|
|
||||||
:SYMBOL (keyword (second tree))
|
|
||||||
:VALUE (generate (second tree))
|
|
||||||
:WITHIN-CONDITION (generate-within-condition tree)
|
|
||||||
(map generate tree))
|
|
||||||
tree))
|
|
92
src/mw_parser/simplifier.clj
Normal file
92
src/mw_parser/simplifier.clj
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
(ns mw-parser.simplifier
|
||||||
|
(:use mw-engine.utils
|
||||||
|
mw-parser.parser))
|
||||||
|
|
||||||
|
(declare simplify)
|
||||||
|
|
||||||
|
(defn simplify-qualifier
|
||||||
|
"Given that this `tree` fragment represents a qualifier, what
|
||||||
|
qualifier is that?"
|
||||||
|
[tree]
|
||||||
|
(cond
|
||||||
|
(empty? tree) nil
|
||||||
|
(and (coll? tree)
|
||||||
|
(member? (first tree) '(:EQUIVALENCE :COMPARATIVE))) tree
|
||||||
|
(coll? (first tree)) (or (simplify-qualifier (first tree))
|
||||||
|
(simplify-qualifier (rest tree)))
|
||||||
|
(coll? tree) (simplify-qualifier (rest tree))
|
||||||
|
true tree))
|
||||||
|
|
||||||
|
(defn simplify-second-of-two
|
||||||
|
"There are a number of possible simplifications such that if the `tree` has
|
||||||
|
only two elements, the second is semantically sufficient."
|
||||||
|
[tree]
|
||||||
|
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
|
||||||
|
|
||||||
|
|
||||||
|
(defn simplify-some
|
||||||
|
"'some' is the same as 'more than zero'"
|
||||||
|
[tree]
|
||||||
|
[:COMPARATIVE '> 0])
|
||||||
|
|
||||||
|
(defn simplify-none
|
||||||
|
"'none' is the same as 'zero'"
|
||||||
|
[tree]
|
||||||
|
[:COMPARATIVE '= 0])
|
||||||
|
|
||||||
|
(defn simplify-all
|
||||||
|
"'all' isn't actually the same as 'eight', because cells at the edges of the world have
|
||||||
|
fewer than eight neighbours; but it's a simplifying (ha!) assumption for now."
|
||||||
|
[tree]
|
||||||
|
[:COMPARATIVE '= 8])
|
||||||
|
|
||||||
|
(defn simplify-quantifier
|
||||||
|
"If this quantifier is a number, 'simplifiy' it into a comparative whose operator is '='
|
||||||
|
and whose quantity is that number. This is actually more complicated but makes generation easier."
|
||||||
|
[tree]
|
||||||
|
(if (number? (second tree)) [:COMPARATIVE '= (second tree)] (simplify (second tree))))
|
||||||
|
|
||||||
|
(defn simplify
|
||||||
|
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
|
||||||
|
semantically identical simpler fragments"
|
||||||
|
[tree]
|
||||||
|
(if
|
||||||
|
(coll? tree)
|
||||||
|
(case (first tree)
|
||||||
|
:SPACE nil
|
||||||
|
:QUALIFIER (simplify-qualifier tree)
|
||||||
|
:CONDITIONS (simplify-second-of-two tree)
|
||||||
|
:CONDITION (simplify-second-of-two tree)
|
||||||
|
:EXPRESSION (simplify-second-of-two tree)
|
||||||
|
:COMPARATIVE (simplify-second-of-two tree)
|
||||||
|
:QUANTIFIER (simplify-quantifier tree)
|
||||||
|
:VALUE (simplify-second-of-two tree)
|
||||||
|
:PROPERTY (simplify-second-of-two tree)
|
||||||
|
:ACTIONS (simplify-second-of-two tree)
|
||||||
|
:ACTION (simplify-second-of-two tree)
|
||||||
|
:ALL (simplify-all tree)
|
||||||
|
:SOME (simplify-some tree)
|
||||||
|
:NONE (simplify-none tree)
|
||||||
|
(remove nil? (map simplify tree)))
|
||||||
|
tree))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if state is climax and 4 neighbours have state equal to fire then 3 chance in 5 state should be fire"))
|
||||||
|
(simplify (parse-rule "if state is climax and no neighbours have state equal to fire then 3 chance in 5 state should be fire"))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if state is in grassland or pasture or heath and more than 4 neighbours have state equal to water then state should be village"))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if 6 neighbours have state equal to water then state should be village"))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if fertility is between 55 and 75 then state should be climax"))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if state is forest then state should be climax"))
|
||||||
|
|
||||||
|
|
||||||
|
(simplify (parse-rule "if state is in grassland or pasture or heath and more than 4 neighbours have state equal to water then state should be village"))
|
||||||
|
(simplify (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||||
|
(simplify (parse-rule "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"))
|
||||||
|
(simplify (parse-rule "if altitude is 100 or fertility is 25 then state should be heath"))
|
||||||
|
|
||||||
|
(simplify (parse-rule "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"))
|
||||||
|
(simplify (parse-rule "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"))
|
||||||
|
(simplify (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village"))
|
|
@ -1,90 +0,0 @@
|
||||||
(ns ^{:doc "Simplify a parse tree."
|
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-parser.simplify
|
|
||||||
(:require [mw-parser.utils :refer [search-tree]]))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;
|
|
||||||
;;;; mw-parser: a rule parser for MicroWorld.
|
|
||||||
;;;;
|
|
||||||
;;;; This program is free software; you can redistribute it and/or
|
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
|
||||||
;;;; as published by the Free Software Foundation; either version 2
|
|
||||||
;;;; of the License, or (at your option) any later version.
|
|
||||||
;;;;
|
|
||||||
;;;; This program is distributed in the hope that it will be useful,
|
|
||||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
;;;; GNU General Public License for more details.
|
|
||||||
;;;;
|
|
||||||
;;;; You should have received a copy of the GNU General Public License
|
|
||||||
;;;; along with this program; if not, write to the Free Software
|
|
||||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
;;;; USA.
|
|
||||||
;;;;
|
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(declare simplify)
|
|
||||||
|
|
||||||
(defn simplify-second-of-two
|
|
||||||
"There are a number of possible simplifications such that if the `tree` has
|
|
||||||
only two elements, the second is semantically sufficient."
|
|
||||||
[tree]
|
|
||||||
(if (= (count tree) 2) (simplify (nth tree 1)) tree))
|
|
||||||
|
|
||||||
(defn simplify-chained-list
|
|
||||||
"Some parse trees take the form
|
|
||||||
`[:X [:Y 1] :NOISE :NOISE [:X [:Y 2] :NOISE :NOISE [:X [:Y 3]]]]`
|
|
||||||
where what's wanted is `[:X [:Y 1] [:Y 2] [:Y 2]]` -- :DISJUNCT-VALUE is a case
|
|
||||||
in point. This takes such a parse `tree`, where `branch-tag` is the tag of
|
|
||||||
the enclosing form and `leaf-tag` is the tag of the form to be collected, and
|
|
||||||
returns the desired form."
|
|
||||||
[tree branch-tag leaf-tag]
|
|
||||||
(cons
|
|
||||||
(first tree)
|
|
||||||
(reverse
|
|
||||||
(loop [chain (rest tree) v '()]
|
|
||||||
(let [car (first chain)]
|
|
||||||
(cond (empty? chain) v
|
|
||||||
(coll? car) (let [caar (first car)]
|
|
||||||
(cond
|
|
||||||
(= branch-tag caar) (recur car v)
|
|
||||||
(= leaf-tag caar) (recur
|
|
||||||
(rest chain)
|
|
||||||
(cons (simplify car) v))
|
|
||||||
:else (recur (rest chain) v)))
|
|
||||||
:else (recur (rest chain) v)))))))
|
|
||||||
|
|
||||||
(defn simplify
|
|
||||||
"Simplify/canonicalise this `tree`. Opportunistically replace complex fragments with
|
|
||||||
semantically identical simpler fragments"
|
|
||||||
[tree]
|
|
||||||
(if
|
|
||||||
(coll? tree)
|
|
||||||
(case (first tree)
|
|
||||||
:ACTION (simplify-second-of-two tree)
|
|
||||||
:COMPARATIVE (simplify-second-of-two tree)
|
|
||||||
:CONDITION (simplify-second-of-two tree)
|
|
||||||
:CONDITIONS (simplify-second-of-two tree)
|
|
||||||
;; :DISJUNCT-EXPRESSION (simplify-chained-list tree :DISJUNCT-VALUE :VALUE)
|
|
||||||
:EXPRESSION (simplify-second-of-two tree)
|
|
||||||
:FLOW-CONDITIONS (simplify-second-of-two tree)
|
|
||||||
;; this is like simplify-second-of-two except if there isn't
|
|
||||||
;; a second element it returns nil
|
|
||||||
:LINE (if (= (count tree) 2) (simplify (nth tree 1)) tree)
|
|
||||||
:LINES (map simplify (rest tree))
|
|
||||||
:PROPERTY (simplify-second-of-two tree)
|
|
||||||
:PROPERTY-CONDITION-OR-EXPRESSION (simplify-second-of-two tree)
|
|
||||||
:STATE (list :PROPERTY-CONDITION
|
|
||||||
(list :SYMBOL "state")
|
|
||||||
'(:QUALIFIER
|
|
||||||
(:EQUIVALENCE
|
|
||||||
(:IS "is")))
|
|
||||||
(list :EXPRESSION
|
|
||||||
(list :VALUE (second tree))))
|
|
||||||
:VALUE (simplify-second-of-two tree)
|
|
||||||
;; default
|
|
||||||
(remove nil? (map simplify tree)))
|
|
||||||
tree))
|
|
|
@ -1,57 +0,0 @@
|
||||||
(ns ^{:doc "Utilities used in more than one namespace within the parser."
|
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-parser.utils)
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;
|
|
||||||
;;;; mw-parser: a rule parser for MicroWorld.
|
|
||||||
;;;;
|
|
||||||
;;;; This program is free software; you can redistribute it and/or
|
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
|
||||||
;;;; as published by the Free Software Foundation; either version 2
|
|
||||||
;;;; of the License, or (at your option) any later version.
|
|
||||||
;;;;
|
|
||||||
;;;; This program is distributed in the hope that it will be useful,
|
|
||||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
;;;; GNU General Public License for more details.
|
|
||||||
;;;;
|
|
||||||
;;;; You should have received a copy of the GNU General Public License
|
|
||||||
;;;; along with this program; if not, write to the Free Software
|
|
||||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
;;;; USA.
|
|
||||||
;;;;
|
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn suitable-fragment?
|
|
||||||
"Return `true` if `tree-fragment` appears to be a tree fragment of the expected `type`."
|
|
||||||
[tree-fragment type]
|
|
||||||
(and (coll? tree-fragment)
|
|
||||||
(keyword? type)
|
|
||||||
(= (first tree-fragment) type)))
|
|
||||||
|
|
||||||
(defn TODO
|
|
||||||
"Marker to indicate I'm not yet finished!"
|
|
||||||
[message]
|
|
||||||
message)
|
|
||||||
|
|
||||||
(defn assert-type
|
|
||||||
"If `tree-fragment` is not a tree fragment of the expected `type`, throw an exception."
|
|
||||||
[tree-fragment type]
|
|
||||||
(assert (suitable-fragment? tree-fragment type)
|
|
||||||
(throw (ex-info (format "Expected a %s fragment" type)
|
|
||||||
{:actual tree-fragment
|
|
||||||
:expected type}))))
|
|
||||||
|
|
||||||
(defn search-tree
|
|
||||||
"Return the first element of this tree which has this tag in a depth-first, left-to-right search"
|
|
||||||
[tree tag]
|
|
||||||
(cond
|
|
||||||
(= (first tree) tag) tree
|
|
||||||
:else (first
|
|
||||||
(remove nil?
|
|
||||||
(map
|
|
||||||
#(search-tree % tag)
|
|
||||||
(filter coll? (rest tree)))))))
|
|
24
test/mw_parser/bulk_test.clj
Normal file
24
test/mw_parser/bulk_test.clj
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
(ns mw-parser.bulk-test
|
||||||
|
(:use clojure.java.io)
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[mw-parser.bulk :refer :all]))
|
||||||
|
|
||||||
|
(deftest bulk-parsing-test
|
||||||
|
(testing "Bulk (file) parsing and compilation"
|
||||||
|
(is (= (count (parse-file (as-file "resources/rules.txt"))) 15)
|
||||||
|
"Should parse all rules and throw no exceptions")
|
||||||
|
(is (empty?
|
||||||
|
(remove #(= % 'fn)
|
||||||
|
(map first
|
||||||
|
(parse-file
|
||||||
|
(as-file "resources/rules.txt")))))
|
||||||
|
"all parsed rules should be lambda sexprs")
|
||||||
|
(is (= (count (compile-file (as-file "resources/rules.txt"))) 15)
|
||||||
|
"Should compile all rules and throw no exceptions")
|
||||||
|
(is (empty?
|
||||||
|
(remove ifn?
|
||||||
|
(map first
|
||||||
|
(compile-file
|
||||||
|
(as-file "resources/rules.txt")))))
|
||||||
|
"all compiled rules should be ifns")
|
||||||
|
))
|
471
test/mw_parser/core_test.clj
Normal file
471
test/mw_parser/core_test.clj
Normal file
|
@ -0,0 +1,471 @@
|
||||||
|
(ns mw-parser.core-test
|
||||||
|
(:use clojure.pprint
|
||||||
|
mw-engine.core
|
||||||
|
mw-engine.world)
|
||||||
|
(:require [clojure.test :refer :all]
|
||||||
|
[mw-parser.core :refer :all]))
|
||||||
|
|
||||||
|
(deftest primitives-tests
|
||||||
|
(testing "Simple functions supporting the parser"
|
||||||
|
(is (= (parse-simple-value '()) nil)
|
||||||
|
"if there's nothing to parse, return nil")
|
||||||
|
(is (= (first (parse-simple-value '("1234" "and" "that"))) 1234)
|
||||||
|
"a simple value is expected to be just a number.")
|
||||||
|
(is (= (first (parse-simple-value '("this" "and" "that"))) :this)
|
||||||
|
"or else just a keyword")
|
||||||
|
(is (= (first (parse-simple-value '("this" "and" "that") true))
|
||||||
|
'(get-int cell :this))
|
||||||
|
"...unless an integer is explicitly sought, in which case it should be something which gets an integer from the current cell")
|
||||||
|
(is (= (parse-value '()) nil)
|
||||||
|
"if there's nothing to parse, return nil")
|
||||||
|
(is (= (first (parse-value '("1234" "and" "that"))) 1234)
|
||||||
|
"a simple value is expected to be just a number.")
|
||||||
|
(is (= (first (parse-value '("this" "and" "that"))) :this)
|
||||||
|
"or else just a keyword")
|
||||||
|
(is (= (first (parse-value '("this" "and" "that") true))
|
||||||
|
'(get-int cell :this))
|
||||||
|
"...unless an integer is explicitly sought, in which case it should be something which gets an integer from the current cell")
|
||||||
|
(is (= (parse-property-value '()) nil)
|
||||||
|
"if there's nothing to parse, return nil")
|
||||||
|
(is (= (first (parse-property-value '("this" "and" "that"))) '(:this cell))
|
||||||
|
"Parsing a property value returns a code function to pull its value off the current cell")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(deftest rules-tests
|
||||||
|
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
|
||||||
|
(is (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"))
|
||||||
|
(is (parse-rule "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"))
|
||||||
|
(is (parse-rule "if altitude is 100 or fertility is 25 then state should be heath"))
|
||||||
|
(is (parse-rule "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"))
|
||||||
|
(is (parse-rule "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"))
|
||||||
|
(is (parse-rule "if state is grassland and 4 neighbours have state equal to water then state should be village"))
|
||||||
|
(is (parse-rule "if state is forest and fertility is between 55 and 75 then state should be climax"))
|
||||||
|
(is (parse-rule "if 6 neighbours have state equal to water then state should be village"))
|
||||||
|
(is (parse-rule "if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"))
|
||||||
|
(is (parse-rule "if state is climax and some neighbours have state equal to fire then 3 chance in 5 state should be fire"))
|
||||||
|
(is (parse-rule "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"))
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest exception-tests
|
||||||
|
(testing "Constructions which should cause exceptions to be thrown"
|
||||||
|
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||||
|
(parse-rule "the quick brown fox jumped over the lazy dog"))
|
||||||
|
"Exception thrown if rule text does not match grammar")
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
|
(parse-rule "if state is new then x should be 0"))
|
||||||
|
"Exception thrown on attempt to set 'x'")
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
|
(parse-rule "if state is new then y should be 0"))
|
||||||
|
"Exception thrown on attempt to set 'y'")
|
||||||
|
(is (thrown? Exception (compile-rule "if state is new then x should be 0"))
|
||||||
|
"Can't set x property to number, as this would break the world")
|
||||||
|
(is (thrown? Exception (compile-rule "if state is new then y should be 0"))
|
||||||
|
"Can't set y property to number, as this would break the world")
|
||||||
|
(is (thrown? Exception (compile-rule "if state is new then x should be heath"))
|
||||||
|
"Can't set x property to symbol, as this would break the world")
|
||||||
|
(is (thrown? Exception (compile-rule "if state is new then y should be heath"))
|
||||||
|
"Can't set y property to symbol, as this would break the world")
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest correctness-tests
|
||||||
|
(testing "Simplest possible rule"
|
||||||
|
(let [afn (compile-rule "if state is new then state should be grassland")]
|
||||||
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
|
{:state :grassland})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:state :forest} nil))))
|
||||||
|
"Rule doesn't fire when condition isn't met"))
|
||||||
|
|
||||||
|
(testing "Condition conjunction rule"
|
||||||
|
(let [afn (compile-rule "if state is new and altitude is 0 then state should be water")]
|
||||||
|
(is (= (apply afn (list {:state :new :altitude 0} nil))
|
||||||
|
{:state :water :altitude 0})
|
||||||
|
"Rule fires when conditions are met")
|
||||||
|
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
|
||||||
|
"Rule does not fire: second condition not met")
|
||||||
|
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
|
||||||
|
"Rule does not fire: first condition not met")))
|
||||||
|
|
||||||
|
(testing "Condition disjunction rule"
|
||||||
|
(let [afn (compile-rule "if state is new or state is waste then state should be grassland")]
|
||||||
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
|
{:state :grassland})
|
||||||
|
"Rule fires: first condition met")
|
||||||
|
(is (= (apply afn (list {:state :waste} nil))
|
||||||
|
{:state :grassland})
|
||||||
|
"Rule fires: second condition met")
|
||||||
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
|
"Rule does not fire: neither condition met")))
|
||||||
|
|
||||||
|
(testing "Simple negation rule"
|
||||||
|
(let [afn (compile-rule "if state is not new then state should be grassland")]
|
||||||
|
(is (nil? (apply afn (list {:state :new} nil)))
|
||||||
|
"Rule doesn't fire when condition isn't met")
|
||||||
|
(is (= (apply afn (list {:state :forest} nil))
|
||||||
|
{:state :grassland})
|
||||||
|
"Rule fires when condition is met")))
|
||||||
|
|
||||||
|
(testing "Can't set x or y properties")
|
||||||
|
|
||||||
|
(testing "Simple list membership rule"
|
||||||
|
(let [afn (compile-rule "if state is in heath or scrub or forest then state should be climax")]
|
||||||
|
(is (= (apply afn (list {:state :heath} nil))
|
||||||
|
{:state :climax})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (= (apply afn (list {:state :scrub} nil))
|
||||||
|
{:state :climax})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (= (apply afn (list {:state :forest} nil))
|
||||||
|
{:state :climax})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:state :grassland} nil)))
|
||||||
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
|
(testing "Negated list membership rule"
|
||||||
|
(let [afn (compile-rule "if state is not in heath or scrub or forest then state should be climax")]
|
||||||
|
(is (nil? (apply afn (list {:state :heath} nil)))
|
||||||
|
"Rule does not fire when condition is not met")
|
||||||
|
(is (nil? (apply afn (list {:state :scrub} nil)))
|
||||||
|
"Rule does not fire when condition is not met")
|
||||||
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
|
"Rule does not fire when condition is not met")
|
||||||
|
(is (= (apply afn (list {:state :grassland} nil))
|
||||||
|
{:state :climax})
|
||||||
|
"Rule fires when condition is met")))
|
||||||
|
|
||||||
|
(testing "Property is more than numeric-value"
|
||||||
|
(let [afn (compile-rule "if altitude is more than 200 then state should be snow")]
|
||||||
|
(is (= (apply afn (list {:altitude 201} nil))
|
||||||
|
{:state :snow :altitude 201})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:altitude 200} nil)))
|
||||||
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
|
(testing "Property is more than property"
|
||||||
|
(let [afn (compile-rule "if wolves are more than deer then deer should be 0")]
|
||||||
|
(is (= (apply afn (list {:deer 2 :wolves 3} nil))
|
||||||
|
{:deer 0 :wolves 3})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
|
||||||
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
|
(testing "Property is less than numeric-value"
|
||||||
|
(let [afn (compile-rule "if altitude is less than 10 then state should be water")]
|
||||||
|
(is (= (apply afn (list {:altitude 9} nil))
|
||||||
|
{:state :water :altitude 9})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:altitude 10} nil)))
|
||||||
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
|
(testing "Property is less than property"
|
||||||
|
(let [afn (compile-rule "if wolves are less than deer then deer should be deer - wolves")]
|
||||||
|
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
|
||||||
|
{:deer 1 :wolves 2})
|
||||||
|
"Rule fires when condition is met")
|
||||||
|
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
|
||||||
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
|
(testing "Number neighbours have property equal to value"
|
||||||
|
(let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water")
|
||||||
|
world (make-world 3 3)]
|
||||||
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
|
{:state :water :x 0 :y 0})
|
||||||
|
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell has eight neighbours, so rule does not fire."))
|
||||||
|
(let [afn (compile-rule "if 3 neighbours are new then state should be water")
|
||||||
|
world (make-world 3 3)]
|
||||||
|
;; 'are new' should be the same as 'have state equal to new'
|
||||||
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
|
{:state :water :x 0 :y 0})
|
||||||
|
"Rule fires when condition is met (in a new world all cells are new, corner cell has three neighbours)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell has eight neighbours, so rule does not fire.")))
|
||||||
|
|
||||||
|
(testing "Number neighbours have property more than numeric-value"
|
||||||
|
(let [afn (compile-rule "if 3 neighbours have altitude more than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
(testing "Number neighbours have property less than numeric-value"
|
||||||
|
(let [afn (compile-rule "if 5 neighbours have altitude less than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has two high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
(testing "More than number neighbours have property equal to numeric-value"
|
||||||
|
(let [afn (compile-rule "if more than 2 neighbours have altitude equal to 11 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
(testing "More than number neighbours have property equal to symbolic-value"
|
||||||
|
(let [afn (compile-rule "if more than 2 neighbours have state equal to grassland then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
||||||
|
(let [afn (compile-rule "if more than 2 neighbours are grassland then state should be beach")
|
||||||
|
;; 'are grassland' should mean the same as 'have state equal to grassland'.
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
||||||
|
)
|
||||||
|
|
||||||
|
(testing "Fewer than number neighbours have property equal to numeric-value"
|
||||||
|
(let [afn (compile-rule "if fewer than 3 neighbours have altitude equal to 11 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
(testing "Fewer than number neighbours have property equal to symbolic-value"
|
||||||
|
(let [afn (compile-rule "if fewer than 3 neighbours have state equal to grassland then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; some neighbours have property equal to value
|
||||||
|
(testing "Some neighbours have property equal to numeric-value"
|
||||||
|
(let [afn (compile-rule "if some neighbours have altitude equal to 11 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
(testing "Some neighbours have property equal to symbolic-value"
|
||||||
|
(let [afn (compile-rule "if some neighbours have state equal to grassland then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; more than number neighbours have property more than numeric-value
|
||||||
|
(testing "More than number neighbours have property more than symbolic-value"
|
||||||
|
(let [afn (compile-rule "if more than 2 neighbours have altitude more than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; fewer than number neighbours have property more than numeric-value
|
||||||
|
(testing "Fewer than number neighbours have property more than numeric-value"
|
||||||
|
(let [afn (compile-rule "if fewer than 3 neighbours have altitude more than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; some neighbours have property more than numeric-value
|
||||||
|
(testing "Some neighbours have property more than numeric-value"
|
||||||
|
(let [afn (compile-rule "if some neighbours have altitude more than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; more than number neighbours have property less than numeric-value
|
||||||
|
(testing "More than number neighbours have property less than numeric-value"
|
||||||
|
(let [afn (compile-rule "if more than 4 neighbours have altitude less than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only three low neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
|
;; fewer than number neighbours have property less than numeric-value
|
||||||
|
(testing "Fewer than number neighbours have property less than numeric-value"
|
||||||
|
(let [afn (compile-rule "if fewer than 4 neighbours have altitude less than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Centre cell has five low neighbours, so rule should not fire")
|
||||||
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
|
"Middle cell of the strip has only three low neighbours, so rule should fire.")))
|
||||||
|
|
||||||
|
;; some neighbours have property less than numeric-value
|
||||||
|
(testing "Some number neighbours have property less than numeric-value"
|
||||||
|
(let [afn (compile-rule "if some neighbours have altitude less than 10 then state should be beach")
|
||||||
|
world (transform-world
|
||||||
|
(make-world 3 3)
|
||||||
|
(list (compile-rule "if x is less than 2 then altitude should be 11")
|
||||||
|
(compile-rule "if x is 2 then altitude should be 0")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
|
"Left of world is all high, so rule should not fire.")))
|
||||||
|
|
||||||
|
|
||||||
|
;; 'single action' already tested in 'condition' tests above
|
||||||
|
;; action and actions
|
||||||
|
(testing "Conjunction of actions"
|
||||||
|
(let [afn (compile-rule "if state is new then state should be grassland and fertility should be 0")]
|
||||||
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
|
{:state :grassland :fertility 0})
|
||||||
|
"Both actions are executed")))
|
||||||
|
|
||||||
|
;; 'property should be symbolic-value' and 'property should be numeric-value'
|
||||||
|
;; already tested in tests above
|
||||||
|
|
||||||
|
;; number chance in number property should be value
|
||||||
|
(testing "Syntax of probability rule - action of real probability very hard to test"
|
||||||
|
(let [afn (compile-rule "if state is forest then 5 chance in 5 state should be climax")]
|
||||||
|
(is (= (:state (apply afn (list {:state :forest} nil))) :climax)
|
||||||
|
"five chance in five should fire every time"))
|
||||||
|
(let [afn (compile-rule "if state is forest then 0 chance in 5 state should be climax")]
|
||||||
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
|
"zero chance in five should never fire")))
|
||||||
|
|
||||||
|
;; property operator numeric-value
|
||||||
|
(testing "Arithmetic action: addition of number"
|
||||||
|
(let [afn (compile-rule "if state is climax then fertility should be fertility + 1")]
|
||||||
|
(is (= (:fertility
|
||||||
|
(apply afn (list {:state :climax :fertility 0} nil)))
|
||||||
|
1)
|
||||||
|
"Addition is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: addition of property value"
|
||||||
|
(let [afn (compile-rule "if state is climax then fertility should be fertility + leaf-fall")]
|
||||||
|
(is (= (:fertility
|
||||||
|
(apply afn
|
||||||
|
(list {:state :climax
|
||||||
|
:fertility 0
|
||||||
|
:leaf-fall 1} nil)))
|
||||||
|
1)
|
||||||
|
"Addition is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: subtraction of number"
|
||||||
|
(let [afn (compile-rule "if state is crop then fertility should be fertility - 1")]
|
||||||
|
(is (= (:fertility
|
||||||
|
(apply afn (list {:state :crop :fertility 2} nil)))
|
||||||
|
1)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: subtraction of property value"
|
||||||
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer - wolves")]
|
||||||
|
(is (= (:deer
|
||||||
|
(apply afn
|
||||||
|
(list {:deer 3
|
||||||
|
:wolves 2} nil)))
|
||||||
|
1)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: multiplication by number"
|
||||||
|
(let [afn (compile-rule "if deer are more than 1 then deer should be deer * 2")]
|
||||||
|
(is (= (:deer
|
||||||
|
(apply afn (list {:deer 2} nil)))
|
||||||
|
4)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: multiplication by property value"
|
||||||
|
(let [afn (compile-rule "if state is crop then deer should be deer * deer")]
|
||||||
|
(is (= (:deer
|
||||||
|
(apply afn
|
||||||
|
(list {:state :crop :deer 2} nil)))
|
||||||
|
4)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: division by number"
|
||||||
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / 2")]
|
||||||
|
(is (= (:deer
|
||||||
|
(apply afn (list {:deer 2 :wolves 1} nil)))
|
||||||
|
1)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
(testing "Arithmetic action: division by property value"
|
||||||
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / wolves")]
|
||||||
|
(is (= (:deer
|
||||||
|
(apply afn
|
||||||
|
(list {:deer 2 :wolves 2} nil)))
|
||||||
|
1)
|
||||||
|
"Action is executed")))
|
||||||
|
|
||||||
|
;; simple within distance
|
||||||
|
(testing "Number neighbours within distance have property equal to value"
|
||||||
|
(let [afn (compile-rule "if 8 neighbours within 2 have state equal to new then state should be water")
|
||||||
|
world (make-world 5 5)]
|
||||||
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
|
{:state :water :x 0 :y 0})
|
||||||
|
"Rule fires when condition is met (in a new world all cells are new, corner cell has eight neighbours within two)")
|
||||||
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
|
"Middle cell has twenty-four neighbours within two, so rule does not fire.")))
|
||||||
|
|
||||||
|
;; comparator within distance
|
||||||
|
(testing "More than number neighbours within distance have property equal to symbolic-value"
|
||||||
|
(let [afn (compile-rule "if more than 7 neighbours within 2 have state equal to grassland and more than 7 neighbours within 2 have state equal to water then state should be beach")
|
||||||
|
;; 5x5 world, strip of high ground two cells wide down left hand side
|
||||||
|
;; xxooo
|
||||||
|
;; xxooo
|
||||||
|
;; xxooo
|
||||||
|
;; xxooo
|
||||||
|
;; xxooo
|
||||||
|
world (transform-world
|
||||||
|
(make-world 5 5)
|
||||||
|
(list (compile-rule "if x is less than 2 then altitude should be 11 and state should be grassland")
|
||||||
|
(compile-rule "if x is more than 1 then altitude should be 0 and state should be water")))]
|
||||||
|
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
|
||||||
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))))
|
|
@ -1,514 +1,517 @@
|
||||||
(ns mw-parser.declarative-test
|
(ns mw-parser.declarative-test
|
||||||
(:require [clojure.string :refer [join]]
|
(:use clojure.pprint
|
||||||
[clojure.test :refer [deftest is testing]]
|
mw-engine.core
|
||||||
[mw-engine.core :refer [transform-world]]
|
mw-engine.world
|
||||||
[mw-engine.utils :refer [get-cell]]
|
mw-engine.utils)
|
||||||
[mw-engine.world :refer [make-world]]
|
(:require [clojure.test :refer :all]
|
||||||
[mw-parser.declarative :refer [compile parse]]
|
[mw-parser.declarative :refer :all]))
|
||||||
[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
|
(deftest rules-tests
|
||||||
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
|
(testing "Rule parser - does not test whether generated functions actually work, just that something is generated!"
|
||||||
(is (rule? (parse "if state is forest then state should be climax")))
|
(is (rule? (parse-rule "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-rule "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-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 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 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-rule "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-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 "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves")))
|
(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 "if state is forest and fertility is between 55 and 75 then state should be climax")))
|
(is (rule? (parse-rule "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-rule "if fertility is between 55 and 75 then state should be climax")))
|
||||||
(is (rule? (parse "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))))
|
(is (rule? (parse-rule "if altitude is less than 100 and state is forest then state should be climax and deer should be 3")))
|
||||||
|
))
|
||||||
|
|
||||||
(deftest neighbours-rules-tests
|
(deftest neighbours-rules-tests
|
||||||
(testing "Rules which relate to neighbours - hard!"
|
(testing "Rules which relate to neighbours - hard!"
|
||||||
(is (rule? (parse "if state is climax and some neighbours have state equal to fire then 3 chance in 5 state should be fire")))
|
(is (rule? (parse-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 "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 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-rule "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-rule "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-rule "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-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 "if state is grassland 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 "if 6 neighbours have state equal to water then state should be village")))))
|
(is (rule? (parse-rule "if 6 neighbours have state equal to water then state should be village")))
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest expressions-tests
|
||||||
|
(testing "Generating primitive expressions."
|
||||||
|
(is (generate '(:NUMERIC-EXPRESSION (:NUMBER "50"))) 50)
|
||||||
|
(is (generate '(:NUMERIC-EXPRESSION (:SYMBOL "sealevel")))
|
||||||
|
'(:sealevel cell))
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest lhs-generators-tests
|
||||||
|
(testing "Generating left-hand-side fragments of rule functions from appropriate fragments of parse trees"
|
||||||
|
(is (generate
|
||||||
|
'(:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")))
|
||||||
|
'(= (:state cell) :forest))
|
||||||
|
(is (generate
|
||||||
|
'(:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10")))
|
||||||
|
'(= (:fertility cell) 10))
|
||||||
|
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:LESS "less"]] (:NUMBER "10")))
|
||||||
|
'(< (:fertility cell) 10))
|
||||||
|
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:MORE "more"]] (:NUMBER "10")))
|
||||||
|
'(> (:fertility cell) 10))
|
||||||
|
(is (generate '(:CONJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:AND "and") (:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10"))))
|
||||||
|
'(and (= (:state cell) :forest) (= (:fertility cell) 10)))
|
||||||
|
(is (generate '(:DISJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:OR "or") (:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10"))))
|
||||||
|
'(or (= (:state cell) :forest) (= (:fertility cell) 10)))
|
||||||
|
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:DISJUNCT-EXPRESSION (:IN "in") (:DISJUNCT-VALUE (:SYMBOL "grassland") (:OR "or") (:DISJUNCT-VALUE (:SYMBOL "pasture") (:OR "or") (:DISJUNCT-VALUE (:SYMBOL "heath")))))))
|
||||||
|
'(let [value (:state cell)] (some (fn [i] (= i value)) (quote (:grassland :pasture :heath)))))
|
||||||
|
(is (generate '(:PROPERTY-CONDITION (:SYMBOL "altitude") [:EQUIVALENCE [:IS "is"]] (:RANGE-EXPRESSION (:BETWEEN "between") (:NUMERIC-EXPRESSION (:NUMBER "50")) (:AND "and") (:NUMERIC-EXPRESSION (:NUMBER "100")))))
|
||||||
|
'(let [lower (min 50 100) upper (max 50 100)] (and (>= (:altitude cell) lower) (<= (:altitude cell) upper))))
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest rhs-generators-tests
|
||||||
|
(testing "Generating right-hand-side fragments of rule functions from appropriate fragments of parse trees"
|
||||||
|
(is (generate
|
||||||
|
'(:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax")))
|
||||||
|
'(merge cell {:state :climax}))
|
||||||
|
(is (generate
|
||||||
|
'(:SIMPLE-ACTION (:SYMBOL "fertility") (:BECOMES "should be") (:NUMBER "10")))
|
||||||
|
'(merge cell {:fertility 10}))
|
||||||
|
))
|
||||||
|
|
||||||
|
(deftest full-generation-tests
|
||||||
|
(testing "Full rule generation from pre-parsed tree"
|
||||||
|
(is (generate '(:RULE (:IF "if") (:PROPERTY-CONDITION (:SYMBOL "state") [:EQUIVALENCE [:IS "is"]] (:SYMBOL "forest")) (:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax"))))
|
||||||
|
'(fn [cell world] (if (= (:state cell) :forest) (merge cell {:state :climax}))))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
(deftest exception-tests
|
(deftest exception-tests
|
||||||
(testing "Constructions which should cause exceptions to be thrown"
|
(testing "Constructions which should cause exceptions to be thrown"
|
||||||
(is (thrown-with-msg? Exception #"^Parse error at line.*"
|
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||||
(parse "the quick brown fox jumped over the lazy dog"))
|
(compile-rule "the quick brown fox jumped over the lazy dog"))
|
||||||
"Exception thrown if rule text does not match grammar")
|
"Exception thrown if rule text does not match grammar")
|
||||||
(is (thrown-with-msg? Exception #"^Parse error at line.*"
|
(is (thrown-with-msg? Exception #"^I did not understand.*"
|
||||||
(parse "if i have a cat on my lap then everything is fine"))
|
(compile-rule "if i have a cat on my lap then everything is fine"))
|
||||||
"Exception thrown if rule text does not match grammar")
|
"Exception thrown if rule text does not match grammar")
|
||||||
;; TODO: these two should be moved to generate-test; the exception should be
|
(is (thrown-with-msg?
|
||||||
;; being thrown (but isn't) in the generate phase.
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(is (thrown-with-msg?
|
(compile-rule "if state is new then x should be 0"))
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
"Exception thrown on attempt to set 'x'")
|
||||||
(generate (simplify (parse "if state is new then x should be 0")))
|
(is (thrown-with-msg?
|
||||||
"Exception thrown on attempt to set 'x'")
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(is (thrown-with-msg?
|
(compile-rule "if state is new then y should be 0"))
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
"Exception thrown on attempt to set 'y'")
|
||||||
(generate (simplify (parse "if state is new then y should be 0")))
|
))
|
||||||
"Exception thrown on attempt to set 'y'")))))
|
|
||||||
|
|
||||||
(deftest correctness-tests
|
(deftest correctness-tests
|
||||||
;; these are, in so far as possible, the same as the correctness-tests in core-tests - i.e., the two compilers
|
;; these are, in so far as possible, the same as the correctness-tests in core-tests - i.e., the two compilers
|
||||||
;; compile the same language.
|
;; compile the same language.
|
||||||
(testing "Simplest possible rule"
|
(testing "Simplest possible rule"
|
||||||
(let [afn (first (compile "if state is new then state should be grassland"))]
|
(let [afn (compile-rule "if state is new then state should be grassland")]
|
||||||
(is (= (apply afn (list {:state :new} nil))
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
{:state :grassland})
|
{:state :grassland})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (nil? (apply afn (list {:state :forest} nil)))
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
"Rule doesn't fire when condition isn't met")))
|
"Rule doesn't fire when condition isn't met")))
|
||||||
|
|
||||||
(testing "Condition conjunction rule"
|
(testing "Condition conjunction rule"
|
||||||
(let [afn (first (compile "if state is new and altitude is 0 then state should be water"))]
|
(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))
|
(is (= (apply afn (list {:state :new :altitude 0} nil))
|
||||||
{:state :water :altitude 0})
|
{:state :water :altitude 0})
|
||||||
"Rule fires when conditions are met")
|
"Rule fires when conditions are met")
|
||||||
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
|
(is (nil? (apply afn (list {:state :new :altitude 5} nil)))
|
||||||
"Rule does not fire: second condition not met")
|
"Rule does not fire: second condition not met")
|
||||||
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
|
(is (nil? (apply afn (list {:state :forest :altitude 0} nil)))
|
||||||
"Rule does not fire: first condition not met")))
|
"Rule does not fire: first condition not met")))
|
||||||
|
|
||||||
(testing "Condition disjunction rule"
|
(testing "Condition disjunction rule"
|
||||||
(let [afn (first (compile "if state is new or state is waste then state should be grassland"))]
|
(let [afn (compile-rule "if state is new or state is waste then state should be grassland")]
|
||||||
(is (= (apply afn (list {:state :new} nil))
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
{:state :grassland})
|
{:state :grassland})
|
||||||
"Rule fires: first condition met")
|
"Rule fires: first condition met")
|
||||||
(is (= (apply afn (list {:state :waste} nil))
|
(is (= (apply afn (list {:state :waste} nil))
|
||||||
{:state :grassland})
|
{:state :grassland})
|
||||||
"Rule fires: second condition met")
|
"Rule fires: second condition met")
|
||||||
(is (nil? (apply afn (list {:state :forest} nil)))
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
"Rule does not fire: neither condition met")))
|
"Rule does not fire: neither condition met")))
|
||||||
|
|
||||||
(testing "Simple negation rule"
|
(testing "Simple negation rule"
|
||||||
(let [afn (first (compile "if state is not new then state should be grassland"))]
|
(let [afn (compile-rule "if state is not new then state should be grassland")]
|
||||||
(is (nil? (apply afn (list {:state :new} nil)))
|
(is (nil? (apply afn (list {:state :new} nil)))
|
||||||
"Rule doesn't fire when condition isn't met")
|
"Rule doesn't fire when condition isn't met")
|
||||||
(is (= (apply afn (list {:state :forest} nil))
|
(is (= (apply afn (list {:state :forest} nil))
|
||||||
{:state :grassland})
|
{:state :grassland})
|
||||||
"Rule fires when condition is met")))
|
"Rule fires when condition is met")))
|
||||||
|
|
||||||
(testing "Can't set x or y properties"
|
(testing "Can't set x or y properties"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(first (compile "if state is new then x should be 0")))
|
(compile-rule "if state is new then x should be 0"))
|
||||||
"Exception thrown on attempt to set 'x'")
|
"Exception thrown on attempt to set 'x'")
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
Exception #"The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions"
|
||||||
(first (compile "if state is new then y should be 0")))
|
(compile-rule "if state is new then y should be 0"))
|
||||||
"Exception thrown on attempt to set 'y'"))
|
"Exception thrown on attempt to set 'y'"))
|
||||||
|
|
||||||
(testing "Simple list membership rule"
|
(testing "Simple list membership rule"
|
||||||
(let [afn (first (compile "if state is in heath or scrub or forest then state should be climax"))]
|
(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))
|
(is (= (apply afn (list {:state :heath} nil))
|
||||||
{:state :climax})
|
{:state :climax})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (= (apply afn (list {:state :scrub} nil))
|
(is (= (apply afn (list {:state :scrub} nil))
|
||||||
{:state :climax})
|
{:state :climax})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (= (apply afn (list {:state :forest} nil))
|
(is (= (apply afn (list {:state :forest} nil))
|
||||||
{:state :climax})
|
{:state :climax})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (nil? (apply afn (list {:state :grassland} nil)))
|
(is (nil? (apply afn (list {:state :grassland} nil)))
|
||||||
"Rule does not fire when condition is not met")))
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
(testing "Negated list membership rule"
|
(testing "Negated list membership rule"
|
||||||
(let [afn (first (compile "if state is not in heath or scrub or forest then state should be climax"))]
|
(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)))
|
(is (nil? (apply afn (list {:state :heath} nil)))
|
||||||
"Rule does not fire when condition is not met")
|
"Rule does not fire when condition is not met")
|
||||||
(is (nil? (apply afn (list {:state :scrub} nil)))
|
(is (nil? (apply afn (list {:state :scrub} nil)))
|
||||||
"Rule does not fire when condition is not met")
|
"Rule does not fire when condition is not met")
|
||||||
(is (nil? (apply afn (list {:state :forest} nil)))
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
"Rule does not fire when condition is not met")
|
"Rule does not fire when condition is not met")
|
||||||
(is (= (apply afn (list {:state :grassland} nil))
|
(is (= (apply afn (list {:state :grassland} nil))
|
||||||
{:state :climax})
|
{:state :climax})
|
||||||
"Rule fires when condition is met")))
|
"Rule fires when condition is met")))
|
||||||
|
|
||||||
(testing "Property is more than numeric-value"
|
(testing "Property is more than numeric-value"
|
||||||
(let [afn (first (compile "if altitude is more than 200 then state should be snow"))]
|
(let [afn (compile-rule "if altitude is more than 200 then state should be snow")]
|
||||||
(is (= (apply afn (list {:altitude 201} nil))
|
(is (= (apply afn (list {:altitude 201} nil))
|
||||||
{:state :snow :altitude 201})
|
{:state :snow :altitude 201})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (nil? (apply afn (list {:altitude 200} nil)))
|
(is (nil? (apply afn (list {:altitude 200} nil)))
|
||||||
"Rule does not fire when condition is not met")))
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
(testing "Property is more than property"
|
;; TODO: this one is very tricky and will require a rethink of the way conditions are parsed.
|
||||||
(let [afn (first (compile "if wolves are more than deer then deer should be 0"))]
|
;; (testing "Property is more than property"
|
||||||
(is (= (apply afn (list {:deer 2 :wolves 3} nil))
|
;; (let [afn (compile-rule "if wolves are more than deer then deer should be 0")]
|
||||||
{:deer 0 :wolves 3})
|
;; (is (= (apply afn (list {:deer 2 :wolves 3} nil))
|
||||||
"Rule fires when condition is met")
|
;; {:deer 0 :wolves 3})
|
||||||
(is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
|
;; "Rule fires when condition is met")
|
||||||
"Rule does not fire when condition is not 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"
|
(testing "Property is less than numeric-value"
|
||||||
(let [afn (first (compile "if altitude is less than 10 then state should be water"))]
|
(let [afn (compile-rule "if altitude is less than 10 then state should be water")]
|
||||||
(is (= (apply afn (list {:altitude 9} nil))
|
(is (= (apply afn (list {:altitude 9} nil))
|
||||||
{:state :water :altitude 9})
|
{:state :water :altitude 9})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (nil? (apply afn (list {:altitude 10} nil)))
|
(is (nil? (apply afn (list {:altitude 10} nil)))
|
||||||
"Rule does not fire when condition is not met")))
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
(testing "Property is less than property"
|
(testing "Property is less than property"
|
||||||
(let [afn (first (compile "if wolves are less than deer then deer should be deer - wolves"))]
|
(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))
|
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
|
||||||
{:deer 1 :wolves 2})
|
{:deer 1 :wolves 2})
|
||||||
"Rule fires when condition is met")
|
"Rule fires when condition is met")
|
||||||
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
|
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
|
||||||
"Rule does not fire when condition is not met")))
|
"Rule does not fire when condition is not met")))
|
||||||
|
|
||||||
(testing "Number neighbours have property equal to value"
|
(testing "Number neighbours have property equal to value"
|
||||||
(let [afn (first (compile "if 3 neighbours have state equal to new then state should be water"))
|
(let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water")
|
||||||
world (make-world 3 3)]
|
world (make-world 3 3)]
|
||||||
(is (= (apply afn (list {:x 0 :y 0} world))
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
{:state :water :x 0 :y 0})
|
{: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)")
|
"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)))
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Middle cell has eight neighbours, so rule does not fire."))
|
"Middle cell has eight neighbours, so rule does not fire."))
|
||||||
(let [afn (first (compile "if 3 neighbours are new then state should be water"))
|
(let [afn (compile-rule "if 3 neighbours are new then state should be water")
|
||||||
world (make-world 3 3)]
|
world (make-world 3 3)]
|
||||||
;; 'are new' and 'is new' should be the same as 'have state equal to new'
|
;; 'are new' and 'is new' should be the same as 'have state equal to new'
|
||||||
(is (= (apply afn (list {:x 0 :y 0} world))
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
{:state :water :x 0 :y 0})
|
{: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)")
|
"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)))
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Middle cell has eight neighbours, so rule does not fire."))
|
"Middle cell has eight neighbours, so rule does not fire."))
|
||||||
(let [afn (first (compile "if 3 neighbours is new then state should be water"))
|
(let [afn (compile-rule "if 3 neighbours is new then state should be water")
|
||||||
world (make-world 3 3)]
|
world (make-world 3 3)]
|
||||||
;; 'are new' and 'is new' should be the same as 'have state equal to new'
|
;; 'are new' and 'is new' should be the same as 'have state equal to new'
|
||||||
(is (= (apply afn (list {:x 0 :y 0} world))
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
{:state :water :x 0 :y 0})
|
{: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)")
|
"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)))
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Middle cell has eight neighbours, so rule does not fire.")))
|
"Middle cell has eight neighbours, so rule does not fire.")))
|
||||||
|
|
||||||
(testing "Number neighbours have property more than numeric-value"
|
(testing "Number neighbours have property more than numeric-value"
|
||||||
;; if 3 neighbours have altitude more than 10 then state should be beach
|
(let [afn (compile-rule "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
|
||||||
world (transform-world
|
(make-world 3 3)
|
||||||
(make-world 3 3)
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(compile (join "\n" ["if x is 2 then altitude should be 11"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
"if x is less than 2 then altitude should be 0"])))]
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
(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)")
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
(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.")))
|
||||||
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
|
|
||||||
|
|
||||||
(testing "Number neighbours have property less than numeric-value"
|
(testing "Number neighbours have property less than numeric-value"
|
||||||
(let [afn (first (compile "if 5 neighbours have altitude less than 10 then state should be beach"))
|
(let [afn (compile-rule "if 5 neighbours have altitude less than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile (join "\n" ["if x is 2 then altitude should be 11"
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
"if x is less than 2 then altitude should be 0"])))]
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(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)")
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
"Middle cell of the strip has two high neighbours, so rule should not fire.")))
|
"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"
|
(testing "More than number neighbours have property equal to numeric-value"
|
||||||
(let [afn (first (compile "if more than 2 neighbours have altitude equal to 11 then state should be beach"))
|
(let [afn (compile-rule "if more than 2 neighbours have altitude equal to 11 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile (join "\n" ["if x is 2 then altitude should be 11"
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
"if x is less than 2 then altitude should be 0"])))]
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(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)")
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
(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.")))
|
"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"
|
(testing "More than number neighbours have property equal to symbolic-value"
|
||||||
(let [afn (first (compile "if more than 2 neighbours have state equal to grassland then state should be beach"))
|
(let [afn (compile-rule "if more than 2 neighbours have state equal to grassland then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
(list "if x is 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
||||||
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
(let [afn (compile-rule "if more than 2 neighbours are grassland then state should be beach")
|
||||||
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
;; 'are grassland' should mean the same as 'have state equal to grassland'.
|
||||||
(let [afn (first (compile "if more than 2 neighbours are grassland then state should be beach"))
|
world (transform-world
|
||||||
;; 'are grassland' should mean the same as 'have state equal to grassland'.
|
(make-world 3 3)
|
||||||
world (transform-world
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
(make-world 3 3)
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
(compile (join "\n" (list "if x is 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
||||||
(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"
|
(testing "Fewer than number neighbours have property equal to numeric-value"
|
||||||
(let [afn (first (compile "if fewer than 3 neighbours have altitude equal to 11 then state should be beach"))
|
(let [afn (compile-rule "if fewer than 3 neighbours have altitude equal to 11 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile (join "\n" (list "if x is 2 then altitude should be 11"
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
(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)")
|
"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)))
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
|
|
||||||
(testing "Fewer than number neighbours have property equal to symbolic-value"
|
(testing "Fewer than number neighbours have property equal to symbolic-value"
|
||||||
(let [afn (first (compile "if fewer than 3 neighbours have state equal to grassland then state should be beach"))
|
(let [afn (compile-rule "if fewer than 3 neighbours have state equal to grassland then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
(list "if x is 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
||||||
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "Some neighbours have property equal to numeric-value"
|
||||||
(let [afn (first (compile "if some neighbours have altitude equal to 11 then state should be beach"))
|
(let [afn (compile-rule "if some neighbours have altitude equal to 11 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(list "if x is 2 then altitude should be 11"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "Some neighbours have property equal to symbolic-value"
|
||||||
(let [afn (first (compile "if some neighbours have state equal to grassland then state should be beach"))
|
(let [afn (compile-rule "if some neighbours have state equal to grassland then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
(list "if x is 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
(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
|
;; more than number neighbours have property more than numeric-value
|
||||||
(testing "More than number neighbours have property more than number"
|
(testing "More than number neighbours have property more than symbolic-value"
|
||||||
(let [afn (first (compile "if more than 2 neighbours have altitude more than 10 then state should be beach"))
|
(let [afn (compile-rule "if more than 2 neighbours have altitude more than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11 and state should be grassland")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0 and state should be water")))]
|
||||||
(list "if x is 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Middle cell of the strip has only two high neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "Fewer than number neighbours have property more than numeric-value"
|
||||||
(let [afn (first (compile "if fewer than 3 neighbours have altitude more than 10 then state should be beach"))
|
(let [afn (compile-rule "if fewer than 3 neighbours have altitude more than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(list "if x is 2 then altitude should be 11"
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
||||||
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Rule fires when condition is met (Middle cell of the strip has only two high neighbours)")
|
"Middle cell of world has three high neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "Some neighbours have property more than numeric-value"
|
||||||
(let [afn (first (compile "if some neighbours have altitude more than 10 then state should be beach"))
|
(let [afn (compile-rule "if some neighbours have altitude more than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(list "if x is 2 then altitude should be 11"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Left hand side of world has no high neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "More than number neighbours have property less than numeric-value"
|
||||||
(let [afn (first (compile "if more than 4 neighbours have altitude less than 10 then state should be beach"))
|
(let [afn (compile-rule "if more than 4 neighbours have altitude less than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(list "if x is 2 then altitude should be 11"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 2 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Middle cell of the strip has only three low neighbours, so rule should not fire.")))
|
||||||
(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"
|
(testing "Fewer than number neighbours have property less than numeric-value"
|
||||||
(let [afn (first (compile "if fewer than 4 neighbours have altitude less than 10 then state should be beach"))
|
(let [afn (compile-rule "if fewer than 4 neighbours have altitude less than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is less than 2 then altitude should be 0")))]
|
||||||
(list "if x is 2 then altitude should be 11"
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"if x is less than 2 then altitude should be 0"))))]
|
"Centre cell has five low neighbours, so rule should not fire")
|
||||||
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
(is (= (:state (apply afn (list {:x 2 :y 1} world))) :beach)
|
||||||
"Centre cell has five low neighbours, so rule should not fire")
|
"Middle cell of the strip has only three low neighbours, so rule should 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"
|
(testing "Some number neighbours have property less than numeric-value"
|
||||||
(let [afn (first (compile "if some neighbours have altitude less than 10 then state should be beach"))
|
(let [afn (compile-rule "if some neighbours have altitude less than 10 then state should be beach")
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 3 3)
|
(make-world 3 3)
|
||||||
(compile
|
(list (compile-rule "if x is less than 2 then altitude should be 11")
|
||||||
(join "\n"
|
(compile-rule "if x is 2 then altitude should be 0")))]
|
||||||
(list "if x is less than 2 then altitude should be 11"
|
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
||||||
"if x is 2 then altitude should be 0"))))]
|
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 1 :y 1} world))) :beach)
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 0 down right hand side)")
|
"Left of world is all high, so rule should not fire.")))
|
||||||
(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
|
;; 'single action' already tested in 'condition' tests above
|
||||||
;; action and actions
|
;; action and actions
|
||||||
(testing "Conjunction of actions"
|
(testing "Conjunction of actions"
|
||||||
(let [afn (first (compile "if state is new then state should be grassland and fertility should be 0"))]
|
(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))
|
(is (= (apply afn (list {:state :new} nil))
|
||||||
{:state :grassland :fertility 0})
|
{:state :grassland :fertility 0})
|
||||||
"Both actions are executed")))
|
"Both actions are executed")))
|
||||||
|
|
||||||
;; 'property should be symbolic-value' and 'property should be numeric-value'
|
;; 'property should be symbolic-value' and 'property should be numeric-value'
|
||||||
;; already tested in tests above
|
;; 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"
|
(testing "Syntax of probability rule - action of real probability very hard to test"
|
||||||
(let [afn (first (compile "if state is forest then 5 chance in 5 state should be climax"))]
|
(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)
|
(is (= (:state (apply afn (list {:state :forest} nil))) :climax)
|
||||||
"five chance in five should fire every time"))
|
"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"))]
|
(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)))
|
(is (nil? (apply afn (list {:state :forest} nil)))
|
||||||
"zero chance in five should never fire")))
|
"zero chance in five should never fire")))
|
||||||
|
|
||||||
;; property operator numeric-value
|
;; property operator numeric-value
|
||||||
(testing "Arithmetic action: addition of number"
|
(testing "Arithmetic action: addition of number"
|
||||||
(let [afn (first (compile "if state is climax then fertility should be fertility + 1"))]
|
(let [afn (compile-rule "if state is climax then fertility should be fertility + 1")]
|
||||||
(is (= (:fertility
|
(is (= (:fertility
|
||||||
(apply afn (list {:state :climax :fertility 0} nil)))
|
(apply afn (list {:state :climax :fertility 0} nil)))
|
||||||
1)
|
1)
|
||||||
"Addition is executed")))
|
"Addition is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: addition of property value"
|
(testing "Arithmetic action: addition of property value"
|
||||||
(let [afn (first (compile "if state is climax then fertility should be fertility + leaffall"))]
|
(let [afn (compile-rule "if state is climax then fertility should be fertility + leaf-fall")]
|
||||||
(is (= (:fertility
|
(is (= (:fertility
|
||||||
(apply afn
|
(apply afn
|
||||||
(list {:state :climax
|
(list {:state :climax
|
||||||
:fertility 0
|
:fertility 0
|
||||||
:leaffall 1} nil)))
|
:leaf-fall 1} nil)))
|
||||||
1)
|
1)
|
||||||
"Addition is executed")))
|
"Addition is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: subtraction of number"
|
(testing "Arithmetic action: subtraction of number"
|
||||||
(let [afn (first (compile "if state is crop then fertility should be fertility - 1"))]
|
(let [afn (compile-rule "if state is crop then fertility should be fertility - 1")]
|
||||||
(is (= (:fertility
|
(is (= (:fertility
|
||||||
(apply afn (list {:state :crop :fertility 2} nil)))
|
(apply afn (list {:state :crop :fertility 2} nil)))
|
||||||
1)
|
1)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: subtraction of property value"
|
(testing "Arithmetic action: subtraction of property value"
|
||||||
(let [afn (first (compile "if wolves are more than 0 then deer should be deer - wolves"))]
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer - wolves")]
|
||||||
(is (= (:deer
|
(is (= (:deer
|
||||||
(apply afn
|
(apply afn
|
||||||
(list {:deer 3
|
(list {:deer 3
|
||||||
:wolves 2} nil)))
|
:wolves 2} nil)))
|
||||||
1)
|
1)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: multiplication by number"
|
(testing "Arithmetic action: multiplication by number"
|
||||||
(let [afn (first (compile "if deer are more than 1 then deer should be deer * 2"))]
|
(let [afn (compile-rule "if deer are more than 1 then deer should be deer * 2")]
|
||||||
(is (= (:deer
|
(is (= (:deer
|
||||||
(apply afn (list {:deer 2} nil)))
|
(apply afn (list {:deer 2} nil)))
|
||||||
4)
|
4)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: multiplication by property value"
|
(testing "Arithmetic action: multiplication by property value"
|
||||||
(let [afn (first (compile "if state is crop then deer should be deer * deer"))]
|
(let [afn (compile-rule "if state is crop then deer should be deer * deer")]
|
||||||
(is (= (:deer
|
(is (= (:deer
|
||||||
(apply afn
|
(apply afn
|
||||||
(list {:state :crop :deer 2} nil)))
|
(list {:state :crop :deer 2} nil)))
|
||||||
4)
|
4)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: division by number"
|
(testing "Arithmetic action: division by number"
|
||||||
(let [afn (first (compile "if wolves are more than 0 then deer should be deer / 2"))]
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / 2")]
|
||||||
(is (= (:deer
|
(is (= (:deer
|
||||||
(apply afn (list {:deer 2 :wolves 1} nil)))
|
(apply afn (list {:deer 2 :wolves 1} nil)))
|
||||||
1)
|
1)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
(testing "Arithmetic action: division by property value"
|
(testing "Arithmetic action: division by property value"
|
||||||
(let [afn (first (compile "if wolves are more than 0 then deer should be deer / wolves"))]
|
(let [afn (compile-rule "if wolves are more than 0 then deer should be deer / wolves")]
|
||||||
(is (= (:deer
|
(is (= (:deer
|
||||||
(apply afn
|
(apply afn
|
||||||
(list {:deer 2 :wolves 2} nil)))
|
(list {:deer 2 :wolves 2} nil)))
|
||||||
1)
|
1)
|
||||||
"Action is executed")))
|
"Action is executed")))
|
||||||
|
|
||||||
;; simple within distance
|
;; simple within distance
|
||||||
(testing "Number neighbours within distance have property equal to value"
|
(testing "Number neighbours within distance have property equal to value"
|
||||||
(let [afn (first (compile "if 8 neighbours within 2 have state equal to new then state should be water"))
|
(let [afn (compile-rule "if 8 neighbours within 2 have state equal to new then state should be water")
|
||||||
world (make-world 5 5)]
|
world (make-world 5 5)]
|
||||||
(is (= (apply afn (list {:x 0 :y 0} world))
|
(is (= (apply afn (list {:x 0 :y 0} world))
|
||||||
{:state :water :x 0 :y 0})
|
{: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)")
|
"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)))
|
(is (nil? (apply afn (list {:x 1 :y 1} world)))
|
||||||
"Middle cell has twenty-four neighbours within two, so rule does not fire.")))
|
"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"
|
(testing "More than number neighbours within distance have property equal to symbolic-value"
|
||||||
(let [afn (first (compile "if more than 7 neighbours within 2 have state equal to grassland and more than 7 neighbours within 2 have state equal to water then state should be beach"))
|
(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
|
;; 5x5 world, strip of high ground two cells wide down left hand side
|
||||||
;; xxooo
|
;; xxooo
|
||||||
;; xxooo
|
;; xxooo
|
||||||
;; xxooo
|
;; xxooo
|
||||||
;; xxooo
|
;; xxooo
|
||||||
;; xxooo
|
;; xxooo
|
||||||
world (transform-world
|
world (transform-world
|
||||||
(make-world 5 5)
|
(make-world 5 5)
|
||||||
(compile
|
(list (compile-rule "if x is less than 2 then altitude should be 11 and state should be grassland")
|
||||||
(join "\n"
|
(compile-rule "if x is more than 1 then altitude should be 0 and state should be water")))]
|
||||||
(list "if state is new and x is less than 2 then altitude should be 11 and state should be grassland"
|
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
|
||||||
"if state is new and x is more than 1 then altitude should be 0 and state should be water"))))]
|
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
||||||
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
|
(is (nil? (apply afn (list {:x 0 :y 1} world)))
|
||||||
"Rule fires when condition is met (strip of altitude 11 down right hand side)")
|
"Middle cell of the strip has only two high neighbours, so rule should not fire."))
|
||||||
(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."))))
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
(ns mw-parser.flow-test
|
|
||||||
(:require ;; [clojure.pprint :as pprint]
|
|
||||||
[clojure.test :refer [deftest is testing]] ;; [mw-engine.core :refer [transform-world]]
|
|
||||||
[mw-parser.declarative :refer [parse]]
|
|
||||||
[mw-parser.simplify :refer [simplify]]))
|
|
||||||
|
|
||||||
(deftest parse-tests
|
|
||||||
(testing "flow-grammar"
|
|
||||||
(let [rule "flow 1 food from house having food more than 10 to house within 2 with least food"
|
|
||||||
expected '(:FLOW-RULE
|
|
||||||
(:FLOW "flow")
|
|
||||||
(:QUANTITY (:SIMPLE-EXPRESSION (:NUMBER "1")))
|
|
||||||
(:SYMBOL "food")
|
|
||||||
(:FROM "from")
|
|
||||||
(:SOURCE
|
|
||||||
(:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:WITH "having")
|
|
||||||
(:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than"))) (:NUMBER "10")))
|
|
||||||
(:TO-HOW (:TO "to"))
|
|
||||||
(:DESTINATION
|
|
||||||
(:TARGET
|
|
||||||
(:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:RANGE (:WITHIN "within") (:NUMBER "2")))
|
|
||||||
(:WITH "with")
|
|
||||||
(:DETERMINER-CONDITION (:DETERMINER (:LEAST "least")) (:SYMBOL "food"))))
|
|
||||||
|
|
||||||
actual (simplify (parse rule))]
|
|
||||||
|
|
||||||
(is (= actual expected) rule))
|
|
||||||
(let [rule "flow 10% food from house having food more than 10 to each house within 2 with food less than 4"
|
|
||||||
expected '(:FLOW-RULE
|
|
||||||
(:FLOW "flow")
|
|
||||||
(:QUANTITY (:PERCENTAGE (:NUMBER "10") "%"))
|
|
||||||
(:SYMBOL "food")
|
|
||||||
(:FROM "from")
|
|
||||||
(:SOURCE (:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"]))) (:WITH "having") (:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:MORE "more") (:THAN "than"))) (:NUMBER "10")))
|
|
||||||
(:TO-HOW (:TO-EACH (:TO "to") (:EACH "each")))
|
|
||||||
(:DESTINATION
|
|
||||||
(:TARGET (:PROPERTY-CONDITION (:SYMBOL "state") (:QUALIFIER (:EQUIVALENCE (:IS "is"))) (:EXPRESSION (:VALUE [:SYMBOL "house"])))
|
|
||||||
(:RANGE (:WITHIN "within") (:NUMBER "2")))
|
|
||||||
(:WITH "with") (:PROPERTY-CONDITION (:SYMBOL "food") (:QUALIFIER (:COMPARATIVE-QUALIFIER (:LESS "less") (:THAN "than"))) (:NUMBER "4"))))
|
|
||||||
actual (simplify (parse rule))]
|
|
||||||
(is (= actual expected) rule))))
|
|
|
@ -1,124 +0,0 @@
|
||||||
(ns mw-parser.generate-test
|
|
||||||
(:require [clojure.pprint :as pprint]
|
|
||||||
[clojure.test :refer [deftest is testing]]
|
|
||||||
[mw-engine.core :refer [apply-rule]]
|
|
||||||
[mw-engine.utils :refer [get-cell]]
|
|
||||||
[mw-parser.declarative :refer [compile parse]]
|
|
||||||
[mw-parser.generate :refer [generate]]
|
|
||||||
[mw-parser.simplify :refer [simplify]]))
|
|
||||||
|
|
||||||
(deftest expressions-tests
|
|
||||||
(testing "Generating primitive expressions."
|
|
||||||
(let [actual (generate '(:NUMERIC-EXPRESSION (:NUMBER "50")))
|
|
||||||
expected 50]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [actual (generate '(:NUMERIC-EXPRESSION (:SYMBOL "sealevel")))
|
|
||||||
expected '(:sealevel cell)]
|
|
||||||
(is (= actual expected)))))
|
|
||||||
|
|
||||||
(deftest lhs-generators-tests
|
|
||||||
(testing "Generating left-hand-side fragments of rule functions from appropriate fragments of parse trees"
|
|
||||||
(let [expected '(= (:state cell) (or (:forest cell) :forest))
|
|
||||||
actual (generate
|
|
||||||
'(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
[:EQUIVALENCE [:IS "is"]]
|
|
||||||
(:SYMBOL "forest")))]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(is (= (generate
|
|
||||||
'(:PROPERTY-CONDITION (:SYMBOL "fertility") [:EQUIVALENCE [:IS "is"]] (:NUMBER "10")))
|
|
||||||
'(= (:fertility cell) 10)))
|
|
||||||
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:LESS "less"]] (:NUMBER "10")))
|
|
||||||
'(< (:fertility cell) 10)))
|
|
||||||
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "fertility") [:COMPARATIVE [:MORE "more"]] (:NUMBER "10")))
|
|
||||||
'(> (:fertility cell) 10)))
|
|
||||||
(is (= (generate '(:CONJUNCT-CONDITION
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:SYMBOL "forest"))
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "fertility")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:NUMBER "10"))))
|
|
||||||
'(and (= (:state cell) (or (:forest cell) :forest)) (= (:fertility cell) 10))))
|
|
||||||
(is (= (generate '(:DISJUNCT-CONDITION (:PROPERTY-CONDITION (:SYMBOL "state") (:EQUIVALENCE (:IS "is")) (:SYMBOL "forest")) (:PROPERTY-CONDITION (:SYMBOL "fertility") (:EQUIVALENCE (:IS "is")) (:NUMBER "10"))))
|
|
||||||
'(or (= (:state cell) (or (:forest cell) :forest)) (= (:fertility cell) 10))))
|
|
||||||
(is (= (generate '(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:DISJUNCT-EXPRESSION
|
|
||||||
(:SYMBOL "heath")
|
|
||||||
(:SYMBOL "scrub")
|
|
||||||
(:SYMBOL "forest"))))
|
|
||||||
'(#{:scrub :forest :heath} (:state cell))))
|
|
||||||
(is (= (generate '(:PROPERTY-CONDITION (:SYMBOL "altitude") [:EQUIVALENCE [:IS "is"]] (:RANGE-EXPRESSION (:BETWEEN "between") (:NUMERIC-EXPRESSION (:NUMBER "50")) (:AND "and") (:NUMERIC-EXPRESSION (:NUMBER "100")))))
|
|
||||||
'(let [lower (min 50 100) upper (max 50 100)] (and (>= (:altitude cell) lower) (<= (:altitude cell) upper)))))))
|
|
||||||
|
|
||||||
(deftest rhs-generators-tests
|
|
||||||
(testing "Generating right-hand-side fragments of rule functions from appropriate fragments of parse trees"
|
|
||||||
(is (= (generate
|
|
||||||
'(:SIMPLE-ACTION (:SYMBOL "state") (:BECOMES "should be") (:SYMBOL "climax")))
|
|
||||||
'(merge cell {:state :climax})))
|
|
||||||
(is (= (generate
|
|
||||||
'(:SIMPLE-ACTION (:SYMBOL "fertility") (:BECOMES "should be") (:NUMBER "10")))
|
|
||||||
'(merge cell {:fertility 10})))))
|
|
||||||
|
|
||||||
(deftest full-generation-tests
|
|
||||||
(testing "Full rule generation from pre-parsed tree"
|
|
||||||
(let [rule '(:RULE
|
|
||||||
(:IF "if")
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:SYMBOL "forest"))
|
|
||||||
(:ACTIONS
|
|
||||||
(:SIMPLE-ACTION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:BECOMES "should be")
|
|
||||||
(:SYMBOL "climax"))))
|
|
||||||
expected '(fn* ([cell world]
|
|
||||||
(when
|
|
||||||
(= (:state cell) (or (:forest cell) :forest))
|
|
||||||
(merge cell {:state :climax}))))
|
|
||||||
actual (generate rule)
|
|
||||||
expected-meta {:rule-type :production}
|
|
||||||
actual-meta (meta actual)]
|
|
||||||
(is (= actual expected))
|
|
||||||
(is (= actual-meta expected-meta)))))
|
|
||||||
|
|
||||||
(deftest metadata-tests
|
|
||||||
(testing "Rules have correct metadata"
|
|
||||||
(let [expected :production
|
|
||||||
actual (:rule-type
|
|
||||||
(meta
|
|
||||||
(generate
|
|
||||||
(simplify
|
|
||||||
(parse "if state is house then state should be waste")))))]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected :flow
|
|
||||||
actual (:rule-type
|
|
||||||
(meta
|
|
||||||
(generate
|
|
||||||
(simplify
|
|
||||||
(parse "flow 10% food from house to house within 2 with least food")))))]
|
|
||||||
(is (= actual expected)))))
|
|
||||||
|
|
||||||
(deftest chance-bug-test
|
|
||||||
(testing "exception thrown when evaluating``"
|
|
||||||
(let [cell {:y 1, :generation 10,
|
|
||||||
:state :scrub, :gradient 85,
|
|
||||||
:x 1, :altitude 92}
|
|
||||||
world [[{:y 0, :state :new, :x 0} {:y 0, :state :new, :x 1} {:y 0, :state :new, :x 2}]
|
|
||||||
[{:y 1, :state :new, :x 0} cell {:y 1, :state :new, :x 2}]
|
|
||||||
[{:y 2, :state :new, :x 0} {:y 2, :state :new, :x 1} {:y 2, :state :new, :x 2}]]
|
|
||||||
rule (first (compile "if state is scrub then 1 chance in 5 state should be forest"))
|
|
||||||
expected #{:scrub :forest}
|
|
||||||
cell' (reduce
|
|
||||||
(fn [c i] (merge (or (apply-rule world c rule) c) {:i i}))
|
|
||||||
cell
|
|
||||||
(range 20))
|
|
||||||
actual (:state cell')]
|
|
||||||
(pprint/pprint cell')
|
|
||||||
(is (expected actual)))))
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
(ns mw-parser.simplify-test
|
|
||||||
(:require [clojure.test :refer [deftest is testing]]
|
|
||||||
[mw-parser.declarative :refer [parse]]
|
|
||||||
[mw-parser.simplify :refer [simplify]]
|
|
||||||
[mw-parser.utils :refer [search-tree]]))
|
|
||||||
|
|
||||||
((deftest disjunct-condition-test
|
|
||||||
(testing "Generation of disjunct conditions has been producing wrong
|
|
||||||
output -- in a way which didn't actually alter the
|
|
||||||
correctness of the rule -- since the beginning, and because
|
|
||||||
of inadequate and badly written tests, I didn't know it."
|
|
||||||
(let [expected '(:DISJUNCT-CONDITION
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:SYMBOL "forest"))
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "fertility")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:NUMBER "10")))
|
|
||||||
actual (simplify [:DISJUNCT-CONDITION
|
|
||||||
[:CONDITION
|
|
||||||
[:PROPERTY-CONDITION
|
|
||||||
[:PROPERTY [:SYMBOL "state"]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:EXPRESSION [:VALUE [:SYMBOL "forest"]]]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:OR "or"]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:CONDITIONS
|
|
||||||
[:CONDITION
|
|
||||||
[:PROPERTY-CONDITION
|
|
||||||
[:PROPERTY [:SYMBOL "fertility"]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:EXPRESSION [:VALUE [:NUMBER "10"]]]]]]])]
|
|
||||||
(is (= actual expected))))))
|
|
||||||
|
|
||||||
(deftest conjunct-condition-test
|
|
||||||
(testing "Conjunct conditions were failing in more or less the same way"
|
|
||||||
(let [expected '(:CONJUNCT-CONDITION
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "state")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:SYMBOL "forest"))
|
|
||||||
(:PROPERTY-CONDITION
|
|
||||||
(:SYMBOL "fertility")
|
|
||||||
(:QUALIFIER (:EQUIVALENCE (:IS "is")))
|
|
||||||
(:NUMBER "10")))
|
|
||||||
actual (simplify [:CONJUNCT-CONDITION
|
|
||||||
[:CONDITION
|
|
||||||
[:PROPERTY-CONDITION
|
|
||||||
[:PROPERTY [:SYMBOL "state"]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:EXPRESSION [:VALUE [:SYMBOL "forest"]]]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:AND "and"]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:CONDITIONS
|
|
||||||
[:CONDITION
|
|
||||||
[:PROPERTY-CONDITION
|
|
||||||
[:PROPERTY [:SYMBOL "fertility"]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:QUALIFIER [:EQUIVALENCE [:IS "is"]]]
|
|
||||||
[:SPACE " "]
|
|
||||||
[:EXPRESSION [:VALUE [:NUMBER "10"]]]]]]])]
|
|
||||||
(is (= actual expected)))))
|
|
||||||
|
|
||||||
((deftest unchained-disjuncts-test
|
|
||||||
(testing "Disjunct values should not be chained"
|
|
||||||
(let [wrong '(:DISJUNCT-EXPRESSION
|
|
||||||
(:IN "in")
|
|
||||||
(:DISJUNCT-VALUE
|
|
||||||
(:SYMBOL "heath")
|
|
||||||
(:DISJUNCT-VALUE
|
|
||||||
(:SYMBOL "scrub")
|
|
||||||
(:DISJUNCT-VALUE (:SYMBOL "forest")))))
|
|
||||||
parse-tree (search-tree
|
|
||||||
(parse
|
|
||||||
"if state is not in heath or scrub or forest then state should be climax")
|
|
||||||
:DISJUNCT-EXPRESSION)
|
|
||||||
actual (simplify parse-tree)]
|
|
||||||
(is (not (= wrong actual))))
|
|
||||||
(let [expected '(:DISJUNCT-EXPRESSION
|
|
||||||
(:SYMBOL "heath")
|
|
||||||
(:SYMBOL "scrub")
|
|
||||||
(:SYMBOL "forest"))
|
|
||||||
parse-tree (search-tree
|
|
||||||
(parse
|
|
||||||
"if state is not in heath or scrub or forest then state should be climax")
|
|
||||||
:DISJUNCT-EXPRESSION)
|
|
||||||
actual (simplify parse-tree)]
|
|
||||||
(is (= expected actual))))))
|
|
Loading…
Reference in a new issue