From 28da9555badd81cdb8e0dbc58e6df1ef3cdc9078 Mon Sep 17 00:00:00 2001
From: Simon Brooke
(defn get-neighbours-with-property-value
+ ([world x y depth property value comparator]
+ (filter #(apply comparator (list (get % property) value)) (get-neighbours world x y depth)))
([world x y depth property value]
- (filter #(= (get % property) value) (get-neighbours world x y depth)))
+ (get-neighbours-with-property-value world x y depth property value =))
([world cell depth property value]
(get-neighbours-with-property-value world (:x cell) (:y cell) depth
property value))
diff --git a/resources/public/docs/mw-parser/uberdoc.html b/resources/public/docs/mw-parser/uberdoc.html
index 199cb45..b3b9e2d 100644
--- a/resources/public/docs/mw-parser/uberdoc.html
+++ b/resources/public/docs/mw-parser/uberdoc.html
@@ -3035,29 +3035,36 @@ 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)
- (:import (java.io BufferedReader StringReader)))(defn parse-line [line] - (let [initial (first line)] - (cond - (member? initial '(nil \# \;)) nil - true (parse-rule line))))
Parse rules from lines returned by this reader. Ignore
- lines starting with ;;, but otherwise throw an exception if any
- line cannot be parsed.
(defn- parse-from-reader - [reader] - (remove nil? - (map parse-line - (line-seq reader))))
Parse rules from successive lines in the file loaded from this filename
Is this line a comment?
(defn comment? + [line] + (or (empty? (trim line)) (member? (first line) '(nil \# \;))))
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.
(defn parse-string + [string] + ;; TODO: tried to do this using with-open, but couldn't make it work. + (map parse-rule (remove comment? (split string #"\n"))))
Parse rules from successive lines in the file loaded from this filename.
+ Return a list of S-expressions.
(defn parse-file [filename] - (with-open [rdr (reader filename)] - (remove nil? - (map parse-line - (line-seq rdr)))))
Parse rules from successive lines in this string
(defn parse-string - [string] - (parse-from-reader (BufferedReader. (StringReader. string))))
A very simple parser which parses production rules of the following forms:
+ (parse-string (slurp filename)))Compile each non-comment line of this string into an executable anonymous
+ function, and return the sequence of such functions.
(defn compile-string + [string] + (map compile-rule (split string #"\n")))
Compile each non-comment line of the file indicated by this filename into
+ an executable anonymous function, and return the sequence of such functions.
(defn compile-file + [filename] + (compile-string (slurp filename)))
(let [lines + (doall (with-open [rdr (reader filename)] (line-seq rdr)))] + (map parse-line lines)))
+(defn parse-string
+ "Parse rules from successive lines in this string"
+ [string]
+ (parse-from-reader (BufferedReader. (StringReader. string))))
A very simple parser which parses production rules of the following forms:
In every case if the function cannot parse the desired construct from the front of the sequence of tokens it returns nil.
Parse a number.
-(defn- parse-numeric-value +
(defn parse-numeric-value [[value & remainder]] (if (re-matches re-number value) [(read-string value) remainder]))
Parse a token assumed to be the name of a property of the current cell, whose value is assumed to be an integer.
-(defn- parse-property-int +
(defn parse-property-int [[value & remainder]] (if value [(list 'get-int 'cell (keyword value)) remainder]))
Parse a token assumed to be the name of a property of the current cell.
-(defn- parse-property-value +
(defn parse-property-value [[value & remainder]] - (if value [(list (keyword value) 'cell) remainder]))
Parse a value from the first of these tokens. If expect-int is true, return
+ (if value [(list (keyword value) 'cell) remainder]))
Parse a token assumed to be a simple token value.
+(defn parse-token-value + [[value & remainder]] + (if value [(keyword value) remainder]))
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.
(defn- parse-simple-value +
(defn parse-simple-value
([tokens expect-int]
(or
(parse-numeric-value tokens)
(cond expect-int
(parse-property-int tokens)
- true (parse-property-value tokens))))
+ true (parse-token-value tokens))))
([tokens]
(parse-simple-value tokens false)))Parse a list of values from among these tokens. If expect-int is true, return
- an integer or something which will evaluate to an integer.
(defn- parse-disjunct-value + integers or things which will evaluate to integers. +
(defn parse-disjunct-value
[[OR token & tokens] expect-int]
(cond (member? OR '("or" "in"))
- (let [[others remainder] (parse-disjunct-value tokens expect-int)]
- [(cons
- (cond
- expect-int (first (parse-simple-value (list token) true))
- true (keyword token))
- others)
- remainder])
- true [nil (cons OR (cons token tokens))]))Parse a value from among these tokens. If expect-int is true, return
+ (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]))))
Parse a value from among these tokens. If expect-int is true, return
an integer or something which will evaluate to an integer.
(defn- parse-value +
(defn parse-value
([tokens expect-int]
(or
(parse-disjunct-value tokens expect-int)
@@ -3149,21 +3159,23 @@ front of the sequence of tokens it returns nil.
([tokens]
(parse-value tokens false)))Parses a condition of the form '[property] in [value] or [value]...'
(defn- parse-member-condition
- [[property IN & rest]]
- (if (= IN "in")
+ [[property IS IN & rest]]
+ (if (and (member? IS '("is" "are")) (= IN "in"))
(let [[l remainder] (parse-disjunct-value (cons "in" rest) false)]
[(list 'member? (keyword property) l) remainder])))Parse '[property] less than [value]'.
(defn- parse-less-condition - [[property LESS THAN value & rest]] - (cond (and (= LESS "less") (= THAN "than")) - [(list '< (list 'get-int 'cell (keyword property)) (read-string value)) rest]))
Parse '[property] more 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])))Parse '[property] more than [value]'.
(defn- parse-more-condition - [[property MORE THAN value & rest]] - (cond (and (= MORE "more") (= THAN "than")) - [(list '> (list 'get-int 'cell (keyword property)) (read-string value)) rest]))
(defn- parse-between-condition
- [[p BETWEEN v1 AND v2 & rest]]
- (cond (and (= BETWEEN "between") (= AND "and") (not (nil? v2)))
+ [[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))]
@@ -3178,10 +3190,6 @@ front of the sequence of tokens it returns nil.
(member? IS '("is" "are"))
(let [tokens (cons property (cons value rest))]
(cond
- (= value "in") (parse-member-condition tokens)
- (= value "between") (parse-between-condition tokens)
- (= value "more") (parse-more-condition tokens)
- (= value "less") (parse-less-condition tokens)
(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]))))Parse the negation of a simple condition.
(defn- parse-not-condition
@@ -3192,14 +3200,14 @@ front of the sequence of tokens it returns nil.
(let [[condition remainder] partial]
[(list 'not condition) remainder])))))(defn- gen-neighbours-condition
- [comparator quantity property value remainder]
+ [comparator quantity property value remainder comp2]
[(list comparator
(list 'count
- (list 'get-neighbours-with-property-value 'world 'cell
- (keyword property) (keyword-or-numeric value)))
+ (list 'get-neighbours-with-property-value 'world '(cell :x) '(cell :y)
+ (keyword property) (keyword-or-numeric value) comp2))
quantity)
remainder])Parse conditions of the form '...more than 6 neighbours are [condition]'
-(defn- parse-comparator-neighbours-condition +
(defn parse-comparator-neighbours-condition
[[MORE THAN n NEIGHBOURS have-or-are & rest]]
(let [quantity (first (parse-numeric-value (list n)))
comparator (cond (= MORE "more") '>
@@ -3212,21 +3220,21 @@ front of the sequence of tokens it returns nil.
(cond
(= have-or-are "are")
(let [[value & remainder] rest]
- (gen-neighbours-condition comparator quantity :state value remainder))
+ (gen-neighbours-condition comparator quantity :state value remainder =))
(= have-or-are "have")
(let [[property comp1 comp2 value & remainder] rest]
(cond (and (= comp1 "equal") (= comp2 "to"))
- (gen-neighbours-condition comparator quantity property value remainder)
-;; (and (= comp1 "more") (= comp2 "than"))
-;; (gen-neighbours-condition '> quantity property value remainder)
-;; (and (= comp1 "less") (= comp2 "than"))
-;; (gen-neighbours-condition '< quantity property value remainder)))))))(defn- parse-some-neighbours-condition + (gen-neighbours-condition comparator quantity property value remainder =) + (and (= comp1 "more") (= comp2 "than")) + (gen-neighbours-condition '> quantity property value remainder >) + (and (= comp1 "less") (= comp2 "than")) + (gen-neighbours-condition '< quantity property value remainder <)))))))
(defn parse-some-neighbours-condition
[[SOME NEIGHBOURS & rest]]
(cond
(and (= SOME "some") (= NEIGHBOURS "neighbours"))
(parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest))))Parse conditions of the form '...6 neighbours are condition'
-(defn- parse-simple-neighbours-condition +
(defn parse-simple-neighbours-condition
[[n NEIGHBOURS have-or-are & rest]]
(let [quantity (first (parse-numeric-value (list n)))]
(cond
@@ -3243,21 +3251,22 @@ front of the sequence of tokens it returns nil.
;; (gen-neighbours-condition '> quantity property value remainder)
;; (and (= comp1 "less") (= comp2 "than"))
;; (gen-neighbours-condition '< quantity property value remainder)))))))Parse conditions referring to neighbours
-(defn- parse-neighbours-condition +
(defn parse-neighbours-condition
[tokens]
(or
(parse-simple-neighbours-condition tokens)
(parse-comparator-neighbours-condition tokens)
(parse-some-neighbours-condition tokens)))Parse conditions of the form '[property] [comparison] [value]'.
-(defn- parse-simple-condition +
(defn parse-simple-condition
[tokens]
(or
(parse-neighbours-condition tokens)
(parse-member-condition tokens)
(parse-not-condition tokens)
- (parse-is-condition tokens)
(parse-less-condition tokens)
- (parse-more-condition tokens)))Parse '... or [condition]' from tokens, where left is the already parsed first disjunct.
Parse '... or [condition]' from tokens, where left is the already parsed first disjunct.
(defn- parse-disjunction-condition
[left tokens]
(let [partial (parse-conditions tokens)]
@@ -3291,10 +3300,11 @@ front of the sequence of tokens it returns nil.
(= be "be")
(member? operator '("+" "-" "*" "/")))
[(list 'merge (or previous 'cell)
- {(keyword prop1) (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]))Parse actions of the form '[property] should be [value].'
+ {(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]))Parse actions of the form '[property] should be [value].'
(defn- parse-set-action
[previous [property should be value & rest]]
(if (and (= should "should") (= be "be"))
@@ -3327,18 +3337,29 @@ front of the sequence of tokens it returns nil.
(if (= THEN "then")
(or
(parse-probability nil tokens)
- (parse-actions nil tokens))))Parse a complete rule from this string or sequence of string tokens.
+ (parse-actions nil tokens))))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.
(defn parse-rule
[line]
(cond
- (string? line) (parse-rule (split (triml line) #"\s+"))
- true (let [[left remainder] (parse-left-hand-side line)
+ (string? line)
+ (let [rule (parse-rule (split (triml line) #"\s+"))]
+ (cond rule rule
+ true (throw (Exception. (str "I did not understand '" line "'")))))
+ true
+ (let [[left remainder] (parse-left-hand-side line)
[right junk] (parse-right-hand-side remainder)]
- ;; there shouldn't be any junk (should be null)
- (list 'fn ['cell 'world] (list 'if left right)))))Parse this rule-text, a string conforming to the grammar of MicroWorld rules,
+ ;; TODO: there shouldn't be any junk (should be null)
+ (cond
+ (and left right (nil? junk))
+ (list 'fn ['cell 'world] (list 'if left right))))))
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.
Throws an exception if parsing fails.
(defn compile-rule
[rule-text]
(do
diff --git a/resources/public/docs/mw-ui/uberdoc.html b/resources/public/docs/mw-ui/uberdoc.html
index ade7351..e4ef147 100644
--- a/resources/public/docs/mw-ui/uberdoc.html
+++ b/resources/public/docs/mw-ui/uberdoc.html
@@ -3228,7 +3228,7 @@ net.brehaut.ClojureTools = (function (SH) {
(file-seq (clojure.java.io/file "resources/public/img/tiles"))))))(defn docs-page []
(layout/render "docs.html" {:title "Documentation"
- :parser (util/md->html "/md/parser.md")
+ :parser (util/md->html "/md/mw-parser.md")
:states (list-states)
:components ["mw-engine" "mw-parser" "mw-ui"]}))(defroutes home-routes
diff --git a/src/mw_ui/routes/home.clj b/src/mw_ui/routes/home.clj
index c20abd3..ddd3f02 100644
--- a/src/mw_ui/routes/home.clj
+++ b/src/mw_ui/routes/home.clj
@@ -23,7 +23,7 @@
(defn docs-page []
(layout/render "docs.html" {:title "Documentation"
- :parser (util/md->html "/md/parser.md")
+ :parser (util/md->html "/md/mw-parser.md")
:states (list-states)
:components ["mw-engine" "mw-parser" "mw-ui"]}))