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))))) | ||||||