diff --git a/README.md b/README.md index 59eb55b..84cf94b 100644 --- a/README.md +++ b/README.md @@ -137,4 +137,5 @@ Have fun! Copyright © 2014 [Simon Brooke](mailto:simon@journeyman.cc) -Distributed under the terms of the [GNU General Public License v2](http://www.gnu.org/licenses/gpl-2.0.html) \ No newline at end of file +Distributed under the terms of the [GNU General Public License v2] +(http://www.gnu.org/licenses/gpl-2.0.html) \ No newline at end of file diff --git a/resources/public/docs/mw-parser/uberdoc.html b/resources/public/docs/mw-parser/uberdoc.html index d40132a..4b6e319 100644 --- a/resources/public/docs/mw-parser/uberdoc.html +++ b/resources/public/docs/mw-parser/uberdoc.html @@ -3029,7 +3029,7 @@ net.brehaut.ClojureTools = (function (SH) { }; })(SyntaxHighlighter); mw-parser -- Marginalia

mw-parser

0.1.0-SNAPSHOT


Parser for production rules for MicroWorld engine

-

dependencies

org.clojure/clojure
1.5.1
mw-engine
0.1.0-SNAPSHOT



(this space intentionally left almost blank)
 

parse multiple rules from a stream, possibly a file - although the real +

dependencies

org.clojure/clojure
1.5.1
org.clojure/tools.trace
0.7.8
mw-engine
0.1.0-SNAPSHOT



(this space intentionally left almost blank)
 

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
@@ -3097,7 +3097,10 @@ more complex issue which I don't yet know how to address.

(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.]*$")

If this token appears to represent an explicit number, return that number; +

(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'")

If this token appears to represent an explicit number, return that number; otherwise, make a keyword of it and return that.

(defn- keyword-or-numeric
   [token]
@@ -3138,7 +3141,11 @@ front of the sequence of tokens it returns nil.

(parse-property-int 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 + (parse-simple-value tokens false)))

Parse a single value from this single token and return just the generated + code, not a pair.

+
(defn gen-token-value
+  [token expect-int]
+  (first (parse-simple-value (list token) expect-int)))

Parse a list of values from among these tokens. If expect-int is true, return integers or things which will evaluate to integers.

(defn parse-disjunct-value
   [[OR token & tokens] expect-int]
@@ -3203,56 +3210,81 @@ front of the sequence of tokens it returns nil.

([comp1 quantity property value remainder comp2 distance] [(list comp1 (list 'count - (list 'get-neighbours-with-property-value 'world '(cell :x) '(cell :y) 1 + (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)))

Parse conditions of the form '...more than 6 neighbours are [condition]'

(defn parse-comparator-neighbours-condition
-  [[MORE THAN n NEIGHBOURS have-or-are & rest]]
+  [[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]
-          (gen-neighbours-condition comparator quantity :state value remainder =))
+        (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]
+        (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 =)
+            (gen-neighbours-condition comparator quantity property 
+                                      value remainder = dist)
             (and (= comp1 "more") (= comp2 "than"))
-            (gen-neighbours-condition comparator quantity property value remainder >)
+            (gen-neighbours-condition comparator quantity property 
+                                      value remainder > dist)
             (and (= comp1 "less") (= comp2 "than"))
-            (gen-neighbours-condition comparator quantity property value remainder <)))))))
+ (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))))

Parse conditions of the form '...6 neighbours are [condition]'

(defn parse-simple-neighbours-condition
-  [[n NEIGHBOURS have-or-are & rest]]
+  [[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]
-          (gen-neighbours-condition '= quantity :state value remainder =))
+        (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]
+        (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 =)
+            (gen-neighbours-condition '= quantity property value remainder = 
+                                      dist)
             (and (= comp1 "more") (= comp2 "than"))
-            (gen-neighbours-condition '= quantity property value remainder >)
+            (gen-neighbours-condition '= quantity property value remainder > 
+                                      dist)
             (and (= comp1 "less") (= comp2 "than"))
-            (gen-neighbours-condition '= quantity property value remainder <)))))))

Parse conditions referring to neighbours

+ (gen-neighbours-condition '= quantity property value remainder < + dist)))))))

Parse conditions referring to neighbours

(defn parse-neighbours-condition
   [tokens]
   (or
@@ -3297,9 +3329,13 @@ front of the sequence of tokens it returns nil.

(parse-conditions tokens)))

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'.

(defn- parse-arithmetic-action 
-  [previous [prop1 should be prop2 operator value & rest]]
-  (if (and (= should "should")
-           (= be "be")
+  [previous [prop1 SHOULD BE prop2 operator value & rest]]
+  (cond
+    (member? prop2 '("x" "y"))
+    (throw 
+      (Exception. reserved-properties-error))
+    (and (= SHOULD "should")
+           (= BE "be")
            (member? operator '("+" "-" "*" "/")))
     [(list 'merge (or previous 'cell)
            {(keyword prop1) (list 'int 
@@ -3308,8 +3344,12 @@ front of the sequence of tokens it returns nil.

(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"))
+  [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]
@@ -3349,7 +3389,7 @@ front of the sequence of tokens it returns nil.

(string? line) (let [rule (parse-rule (split (triml line) #"\s+"))] (cond rule rule - true (throw (Exception. (str "I did not understand '" line "'"))))) + true (throw (Exception. (format bad-parse-error line))))) true (let [[left remainder] (parse-left-hand-side line) [right junk] (parse-right-hand-side remainder)]