diff --git a/src/mw_parser/bulk.clj b/src/mw_parser/bulk.clj index 2dfa6c4..d841878 100644 --- a/src/mw_parser/bulk.clj +++ b/src/mw_parser/bulk.clj @@ -1,10 +1,9 @@ (ns ^{:doc "parse multiple rules from a stream, possibly a file." :author "Simon Brooke"} mw-parser.bulk - (:use mw-parser.core - mw-engine.utils - clojure.java.io - [clojure.string :only [split trim]]) + (:require [clojure.string :refer [split trim]] + [mw-engine.utils :refer [member?]] + [mw-parser.declarative :refer [compile-rule]]) (:import (java.io BufferedReader StringReader))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -30,7 +29,6 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (defn comment? "Is this `line` a comment?" [line] @@ -40,8 +38,8 @@ "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 (trim %)) (remove comment? (split string #"\n")))) + (map compile-rule + (remove comment? (split string #"\n")))) (defn parse-file "Parse rules from successive lines in the file loaded from this `filename`. diff --git a/src/mw_parser/declarative.clj b/src/mw_parser/declarative.clj index b86a32d..403e9de 100644 --- a/src/mw_parser/declarative.clj +++ b/src/mw_parser/declarative.clj @@ -150,24 +150,28 @@ (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. + 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." + Throws an exception if parsing fails." ([rule-text return-tuple?] - (assert (string? rule-text)) - (let [rule (trim rule-text) - tree (simplify (parse-rule rule)) - afn (if (rule? tree) (eval (generate tree)) - ;; else - (throw-parse-exception tree))] - (if return-tuple? - (list afn rule) - ;; else + (let [src (trim rule-text) + parse-tree (simplify (parse src)) + fn' (generate parse-tree) + afn (try + (if (= 'fn (first fn')) + (vary-meta (eval fn') merge (meta fn')) + (throw (Exception. (format "Parse of `%s` did not return a functionn" src)))) + (catch Exception any (throw (ex-info (.getMessage any) + {:src src + :parse parse-tree + :fn fn'}))))] + (if + return-tuple? + (list afn (trim rule-text)) afn))) ([rule-text] (compile-rule rule-text false))) - diff --git a/src/mw_parser/errors.clj b/src/mw_parser/errors.clj index 6e5efbe..ddc599e 100644 --- a/src/mw_parser/errors.clj +++ b/src/mw_parser/errors.clj @@ -32,13 +32,11 @@ ;; (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) @@ -47,7 +45,6 @@ (: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] diff --git a/src/mw_parser/generate.clj b/src/mw_parser/generate.clj index 4c2f0c6..af8649b 100644 --- a/src/mw_parser/generate.clj +++ b/src/mw_parser/generate.clj @@ -289,7 +289,7 @@ [source property quantity-frag destinations] (vary-meta (list 'fn ['cell 'world] - (list 'when (list 'and source (list 'pos? 'cell property)) + (list 'when (list 'and source (list 'pos? (list 'cell property))) (list 'map (list 'fn ['d] {:source (list 'select-keys 'cell [:x :y]) @@ -310,6 +310,12 @@ :NUMBER (generate q-clause) :PERCENTAGE (let [multiplier (/ (generate (second q-clause)) 100)] (list '* multiplier (list property 'cell))) + :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 property 'cell)) (throw (ex-info (format "Unexpected QUANTITY type: `%s`" (first q-clause)) @@ -343,30 +349,6 @@ :LEAST (list 'mw-engine.utils/get-least-cell 'candidates prop) :MOST (list 'mw-engine.utils/get-most-cell 'candidates prop)))) 'candidates)))) -;; (fn -;; [cell world] -;; (when -;; (and (= (:state cell) (or (:house cell) :house)) (pos? cell :food)) -;; (map -;; (fn -;; [d] -;; (assoc -;; {} -;; :source -;; (select-keys cell [:x :y]) -;; :destination -;; (select-keys d [:x :y]) -;; :property -;; :food -;; :quantity -;; (* 1/10 (:food cell))) -;; {}) -;; (let -;; [candidates -;; (filter -;; (fn [cell] (= (:state cell) (or (:house cell) :house))) -;; (mw-engine.utils/get-neighbours world cell 2))] -;; (list (mw-engine.utils/get-least-cell candidates :food)))))) (defn generate-flow [tree]