001 (ns ^{:doc "A very simple parser which parses flow rules."
002 :author "Simon Brooke"}
003 mw-parser.flow
004 (:require [clojure.string :refer [join]]
005 [mw-parser.declarative :refer [build-parser]]
006 [mw-parser.simplify :refer [simplify-second-of-two]]))
007
008 (def flow-grammar
009 "Grammar for flow rules.
010
011 My initial conception of this would be that production rules
012 (if-then rules) and flow rules (flow-from-to rules) would be
013 entirely separate, presented to the parser as separate text
014 files, and parsed and compiled by different chains of functions.
015
016 This appears not to be necessary. Flow rules are easy to parse
017 with the same parser as production rules -- a lot of the grammar
018 is intentionally common -- and the rules are easily discriminated
019 at the compilation ('generate') stage.
020
021 The basic rule I want to be able to compile at this stage is the 'mutual
022 aid' rule:
023
024 `flow 1 food from house having food > 1 to house with least food within 2`
025 "
026 (join "\n" ["FLOW-RULE := FLOW SPACE QUANTITY SPACE PROPERTY SPACE FROM SPACE SOURCE SPACE TO-HOW SPACE DESTINATION;"
027 "PERCENTAGE := NUMBER #'%';"
028 "QUANTITY := PERCENTAGE | NUMBER | EXPRESSION | SOME;"
029 "SOURCE := STATE | STATE SPACE WITH SPACE CONDITIONS;"
030 "DESTINATION := STATE | STATE SPACE WITH SPACE FLOW-CONDITIONS | STATE SPACE WITHIN SPACE VALUE SPACE WITH SPACE FLOW-CONDITIONS;"
031 "DETERMINER := MOST | LEAST;"
032 "DETERMINER-CONDITION := DETERMINER SPACE PROPERTY | DETERMINER SPACE PROPERTY;"
033 "FLOW-CONDITIONS := DETERMINER-CONDITION | CONDITIONS"
034 "STATE := SYMBOL;"
035 "TO-HOW := TO | TO-EACH | TO-FIRST;"
036 "TO-EACH := TO SPACE EACH | TO SPACE ALL;"
037 "TO-FIRST := TO SPACE FIRST"]))
038
039 (def parse-flow
040 "Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
041 (build-parser flow-grammar))
042
043 (defn simplify-flow
044 [tree]
045 (if (coll? tree)
046 (case (first tree)
047 :CONDITION (simplify-second-of-two tree)
048 :CONDITIONS (simplify-second-of-two tree)
049 :DETERMINER (simplify-second-of-two tree)
050 ;; :DETERMINER-CONDITION (simplify-determiner-condition tree)
051 :EXPRESSION (simplify-second-of-two tree)
052 :FLOW nil
053 ;; :FLOW-CONDITIONS (simplify-second-of-two tree)
054 :PROPERTY (simplify-second-of-two tree)
055 :PROPERTY-CONDITION-OR-EXPRESSION (simplify-second-of-two tree)
056 :SPACE nil
057 :QUANTITY (simplify-second-of-two tree)
058 :STATE (list :PROPERTY-CONDITION
059 (list :SYMBOL "state")
060 '(:QUALIFIER
061 (:EQUIVALENCE
062 (:IS "is")))
063 (list :EXPRESSION
064 (list :VALUE (second tree))))
065 (remove nil? (map simplify-flow tree)))
066 tree))
067