Added protection against null pointer exceptions in numeric properties.
This commit is contained in:
parent
b4f796aca4
commit
a436499d98
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,3 +9,5 @@ pom.xml
|
|||
.nrepl-port
|
||||
|
||||
doc/scratch.clj
|
||||
|
||||
src/mw_parser/scratch.clj
|
||||
|
|
46
README.md
46
README.md
|
@ -13,15 +13,49 @@ You can see MicroWorld in action [here](http://www.journeyman.cc/microworld/) -
|
|||
but please don't be mean to my poor little server. If you want to run big maps
|
||||
or complex rule-sets, please run it on your own machines.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
There are substantial changes in how rule functions are evaluated between 0.1.x
|
||||
versions of MicroWorld libraries and 0.3.x versions. In particular, in 0.3.x
|
||||
metadata is held on rule functions which is essential to the functioning of the
|
||||
engine. Consequently, you cannot mix 0.1.x and 0.3.x libraries: it will not work.
|
||||
|
||||
In particular the parser in actual use has changed in 0.3.x from the
|
||||
`mw-parser.core` parser to the `mw-parser.declarative` parser. The API of the
|
||||
parser is also substantially revised and is not backwards compatible, so if
|
||||
you have code written to use 0.1.x versions of this library it will need to be
|
||||
modified. I apologise for this. On the upside, the new parser API is much
|
||||
simpler.
|
||||
|
||||
## Usage
|
||||
|
||||
Main entry point is (parse-rule _string_), where string takes a form detailed
|
||||
in __[grammar](#grammar)__, below. If the rule is interpretted correctly the result will
|
||||
be the source code of a Clojure anonymous function; if the rule cannot be interpretted,
|
||||
an error 'I did not understand...' will be shown.
|
||||
Main entry point is (compile _string_), where string takes a form detailed
|
||||
in __[grammar](#grammar)__, below. If the rules represnted by the string are
|
||||
interpretted correctly, the result will be a a list of compiled Clojure
|
||||
anonymous functions; if the rule cannot be interpretted, an error 'I did not
|
||||
understand...' will be thrown.
|
||||
|
||||
The function (compile-rule _string_) is like parse-rule, except that it returns
|
||||
a compiled Clojure anonymous function.
|
||||
Each of these functions will have metadata including:
|
||||
|
||||
* `:rule-type` : the type of rule the function represents;
|
||||
* `:lisp` : the lisp source from which the function was compiled;
|
||||
* `:parse` : the parse-tree from which that lisp source was derived;
|
||||
* `:source` : the rule source from which the parse-tree was derived;
|
||||
* `:line : the one-based line number of the rule source in the source file.
|
||||
|
||||
The values of `:rule-type` currently supported are:
|
||||
|
||||
* `:production` : an if-then rule which transforms the properties of a single
|
||||
cell, based on the values of properties of that cell and optionally of its
|
||||
neighbours;
|
||||
* `:flow` : a flow rule which creates flows of values of a numeric property
|
||||
from one cell to other cells.
|
||||
|
||||
Values which it is intended will be supported include rules to create graphs
|
||||
which will enable the user to aggregate and interpret what is happening in
|
||||
the world. Types which it is envisaged will be supported include
|
||||
`:time-series`, `bar-graph` and perhaps others, but grammar for these has not
|
||||
yet been developed.
|
||||
|
||||
### Generated function and evaluation environment
|
||||
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; TODO: Either, when I first wrote this parser, I didn't adequately read the
|
||||
;;; Instaparse documentation, or Instaparse has advanced considerably since
|
||||
;;; then. Reading the documentation now, I could probably rewrite this to
|
||||
;;; eliminate the simplify step altogether, and that would be well worth doing.
|
||||
|
||||
(def ruleset-grammar
|
||||
"Experimental: parse a whole file in one go."
|
||||
(join "\n" ["LINES := LINE | LINE CR LINES;"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
(ns ^{:doc "Generate Clojure source from simplified parse trees."
|
||||
:author "Simon Brooke"}
|
||||
mw-parser.generate
|
||||
(:require [mw-parser.utils :refer [assert-type search-tree TODO]]))
|
||||
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]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
|
@ -36,7 +38,7 @@
|
|||
generate and return the appropriate rule as a function of two arguments."
|
||||
[tree]
|
||||
(assert-type tree :RULE)
|
||||
(vary-meta
|
||||
(vary-meta
|
||||
;; do macro-expansion here, because at least in theory I know what
|
||||
;; macros are in scope here.
|
||||
(macroexpand
|
||||
|
@ -131,7 +133,9 @@
|
|||
(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)))))
|
||||
(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,
|
||||
|
@ -161,6 +165,21 @@
|
|||
(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."
|
||||
|
@ -174,9 +193,7 @@
|
|||
total (generate (nth tree 2))
|
||||
action (generate-action (nth tree 3) others)]
|
||||
;; TODO: could almost certainly be done better with macro syntax
|
||||
(list 'if
|
||||
(list '< (list 'rand total) chances)
|
||||
action))))
|
||||
(trap-errors-in-dice-throw total chances action))))
|
||||
|
||||
(defn generate-action
|
||||
"From this `tree`, assumed to be a syntactically correct action,
|
||||
|
@ -211,10 +228,10 @@
|
|||
(assert-type tree :NUMERIC-EXPRESSION)
|
||||
(case (count tree)
|
||||
4 (let [[p operator expression] (rest tree)
|
||||
property (if (number? p) p (list p 'cell))]
|
||||
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 (keyword (second (second tree))) 'cell)
|
||||
:SYMBOL (list 'mw-engine.utils/get-num 'cell (generate (second tree)))
|
||||
(generate (second tree)))))
|
||||
|
||||
(defn generate-neighbours-condition
|
||||
|
|
Loading…
Reference in a new issue