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