mw-parser0.1.0-SNAPSHOTParser for production rules for MicroWorld engine dependencies
| (this space intentionally left almost blank) | ||||||
A very simple parser which parses production rules of the following forms:
It should also but does not yet parse rules of the form: | |||||||
it generates rules in the form expected by mw-engine.core | |||||||
(ns mw-parser.core
(:use mw-engine.utils
[clojure.string :only [split triml]])) | |||||||
(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.]*$") | ||||||
Parse '[property] is less than [value]'. | (defn parse-less-condition
[[property is less than value & rest]]
(cond (and (member? is '("is" "are")) (= less "less") (= than "than"))
[(list '< (list 'get-int 'cell (keyword property)) (read-string value)) rest])) | ||||||
Parse '[property] is more than [value]'. | (defn parse-more-condition
[[property is more than value & rest]]
(cond (and (member? is '("is" "are")) (= more "more") (= than "than"))
[(list '> (list 'get-int 'cell (keyword property)) (read-string value)) rest])) | ||||||
Parse clauses of the form 'x is y', but not 'x is more than y' or 'x is less than y'. It is necessary to disambiguate whether value is a numeric or keyword. | (defn parse-is-condition
[[property is value & rest]]
(cond (and (member? is '("is" "are"))
(not (member? value '("more" "less" "exactly" "not"))))
[(cond
(re-matches re-number value)(list '= (list 'get-int 'cell (keyword property)) (read-string value))
true (list '= (list (keyword property) 'cell) (keyword value)))
rest])) | ||||||
Parse the negation of a simple condition. | (defn parse-not-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]))))) | ||||||
Parse conditions of the form '[property] [comparison] [value]'. | (defn parse-simple-condition
[tokens]
(or (parse-is-condition tokens)
(parse-not-condition tokens)
(parse-less-condition tokens)
(parse-more-condition tokens))) | ||||||
Parse '... or [condition]' from | (defn parse-disjunction-condition
[left tokens]
(let [partial (parse-conditions tokens)]
(if
partial
(let [[right remainder] partial]
[(list 'or left right) remainder])))) | ||||||
Parse '... and [condition]' from | (defn parse-conjunction-condition
[left tokens]
(let [partial (parse-conditions tokens)]
(if partial
(let [[right remainder] partial]
[(list 'and left right) remainder])))) | ||||||
Parse conditions from | (defn parse-conditions
[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))))) | ||||||
Parse the left hand side ('if...') of a production rule. | (defn parse-left-hand-side [tokens] (if (= (first tokens) "if") (parse-conditions (rest 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")
(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].' | (defn parse-set-action
[previous [property should be value & rest]]
(if (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))) | |||||||
Parse actions from tokens. | (defn parse-actions
[previous tokens]
(let [[left remainder] (parse-simple-action previous tokens)]
(cond left
(cond (= (first remainder) "and")
(parse-actions left (rest remainder))
true (list left))))) | ||||||
Parse the right hand side ('then...') of a production rule. | (defn parse-right-hand-side
[tokens]
(if (= (first tokens) "then")
(parse-actions nil (rest tokens)))) | ||||||
Parse a complete rule from this string or sequence of string tokens. | (defn parse-rule
[line]
(cond
(string? line) (parse-rule (split (triml line) #"\s+"))
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))))) | ||||||