Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Simon Brooke 2023-07-09 10:10:32 +01:00
commit fe92045f14
4 changed files with 88 additions and 69 deletions

2
.gitignore vendored
View file

@ -4,3 +4,5 @@ buildall.tmp.*
target/ target/
pom.xml pom.xml
.nrepl-port

View file

@ -15,4 +15,4 @@
[org.clojure/tools.trace "0.7.11"] [org.clojure/tools.trace "0.7.11"]
[instaparse "1.4.12"] [instaparse "1.4.12"]
[mw-engine "0.1.6-SNAPSHOT"] [mw-engine "0.1.6-SNAPSHOT"]
]) [trptr/java-wrapper "0.2.3"]])

View file

@ -1,15 +1,14 @@
(ns mw-parser.declarative
(:require [mw-engine.utils :refer [member?]])
(:require [instaparse.core :as insta]))
(ns ^{:doc "A very simple parser which parses production rules." (ns ^{:doc "A very simple parser which parses production rules."
:author "Simon Brooke"} :author "Simon Brooke"}
mw-parser.declarative mw-parser.declarative
(:require [instaparse.core :as insta] (:require [instaparse.core :as insta]
[clojure.string :refer [split trim triml]] [clojure.string :refer [join trim]]
[mw-parser.errors :as pe] [mw-parser.errors :refer [throw-parse-exception]]
[mw-parser.generate :as pg] [mw-parser.generate :refer [generate]]
[mw-parser.simplify :as ps] [mw-parser.simplify :refer [simplify]]
[mw-parser.utils :refer [rule?]])) [mw-parser.utils :refer [rule?]]
[trptr.java-wrapper.locale :refer [get-default]])
(:import [java.util Locale]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; ;;;;
@ -34,67 +33,85 @@
;;;; ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def grammar (def grammar
;; in order to simplify translation into other natural languages, all "Basic rule language grammar.
;; TOKENS within the parser should be unambiguous
"RULE := IF SPACE CONDITIONS SPACE THEN SPACE ACTIONS; in order to simplify translation into other natural languages, all
CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | CONDITION ; TOKENS within the parser should be unambiguou."
DISJUNCT-CONDITION := CONDITION SPACE OR SPACE CONDITIONS; (join "\n" ["RULE := IF SPACE CONDITIONS SPACE THEN SPACE ACTIONS;"
CONJUNCT-CONDITION := CONDITION SPACE AND SPACE CONDITIONS; "CONDITIONS := DISJUNCT-CONDITION | CONJUNCT-CONDITION | CONDITION ;"
CONDITION := WITHIN-CONDITION | NEIGHBOURS-CONDITION | PROPERTY-CONDITION; "DISJUNCT-CONDITION := CONDITION SPACE OR SPACE CONDITIONS;"
WITHIN-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE WITHIN SPACE NUMBER SPACE IS SPACE PROPERTY-CONDITION-OR-EXPRESSION; "CONJUNCT-CONDITION := CONDITION SPACE AND SPACE CONDITIONS;"
NEIGHBOURS-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE IS SPACE PROPERTY-CONDITION | QUALIFIER SPACE NEIGHBOURS-CONDITION; "CONDITION := WITHIN-CONDITION | NEIGHBOURS-CONDITION | PROPERTY-CONDITION;"
PROPERTY-CONDITION-OR-EXPRESSION := PROPERTY-CONDITION | EXPRESSION; "WITHIN-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE WITHIN SPACE NUMBER SPACE IS SPACE PROPERTY-CONDITION-OR-EXPRESSION;"
PROPERTY-CONDITION := PROPERTY SPACE QUALIFIER SPACE EXPRESSION | VALUE; "NEIGHBOURS-CONDITION := QUANTIFIER SPACE NEIGHBOURS SPACE IS SPACE PROPERTY-CONDITION | QUALIFIER SPACE NEIGHBOURS-CONDITION;"
EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE; "PROPERTY-CONDITION-OR-EXPRESSION := PROPERTY-CONDITION | EXPRESSION;"
SIMPLE-EXPRESSION := QUALIFIER SPACE EXPRESSION | VALUE; "PROPERTY-CONDITION := PROPERTY SPACE QUALIFIER SPACE EXPRESSION | VALUE;"
DISJUNCT-EXPRESSION := IN SPACE DISJUNCT-VALUE; "EXPRESSION := SIMPLE-EXPRESSION | RANGE-EXPRESSION | NUMERIC-EXPRESSION | DISJUNCT-EXPRESSION | VALUE;"
RANGE-EXPRESSION := BETWEEN SPACE NUMERIC-EXPRESSION SPACE AND SPACE NUMERIC-EXPRESSION; "SIMPLE-EXPRESSION := QUALIFIER SPACE EXPRESSION | VALUE;"
NUMERIC-EXPRESSION := VALUE | VALUE SPACE OPERATOR SPACE NUMERIC-EXPRESSION; "DISJUNCT-EXPRESSION := IN SPACE DISJUNCT-VALUE;"
NEGATED-QUALIFIER := QUALIFIER SPACE NOT | NOT SPACE QUALIFIER; "RANGE-EXPRESSION := BETWEEN SPACE NUMERIC-EXPRESSION SPACE AND SPACE NUMERIC-EXPRESSION;"
COMPARATIVE-QUALIFIER := IS SPACE COMPARATIVE SPACE THAN | COMPARATIVE SPACE THAN; "NUMERIC-EXPRESSION := VALUE | VALUE SPACE OPERATOR SPACE NUMERIC-EXPRESSION;"
QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS SPACE QUALIFIER; "NEGATED-QUALIFIER := QUALIFIER SPACE NOT | NOT SPACE QUALIFIER;"
QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE SPACE THAN SPACE NUMBER; "COMPARATIVE-QUALIFIER := IS SPACE COMPARATIVE SPACE THAN | COMPARATIVE SPACE THAN;"
EQUIVALENCE := IS SPACE EQUAL | EQUAL | IS ; "QUALIFIER := COMPARATIVE-QUALIFIER | NEGATED-QUALIFIER | EQUIVALENCE | IS SPACE QUALIFIER;"
COMPARATIVE := MORE | LESS; "QUANTIFIER := NUMBER | SOME | NONE | ALL | COMPARATIVE SPACE THAN SPACE NUMBER;"
DISJUNCT-VALUE := VALUE | VALUE SPACE OR SPACE DISJUNCT-VALUE; "EQUIVALENCE := IS SPACE EQUAL | EQUAL | IS ;"
IF := 'if'; "COMPARATIVE := MORE | LESS;"
THEN := 'then'; "DISJUNCT-VALUE := VALUE | VALUE SPACE OR SPACE DISJUNCT-VALUE;"
THAN := 'than'; "PROPERTY := SYMBOL;"
OR := 'or'; "VALUE := SYMBOL | NUMBER;"
NOT := 'not'; "OPERATOR := '+' | '-' | '*' | '/';"
AND := 'and'; "NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';"
SOME := 'some'; "ACTIONS := ACTION | ACTION SPACE AND SPACE ACTIONS"
NONE := 'no'; "ACTION := SIMPLE-ACTION | PROBABLE-ACTION;"
ALL := 'all' "PROBABLE-ACTION := VALUE SPACE CHANCE-IN SPACE VALUE SPACE SIMPLE-ACTION;"
BETWEEN := 'between'; "SIMPLE-ACTION := SYMBOL SPACE BECOMES SPACE EXPRESSION;"
WITHIN := 'within'; "SPACE := #'\\s+';"]))
IN := 'in';
MORE := 'more' | 'greater';
LESS := 'less' | 'fewer';
OPERATOR := '+' | '-' | '*' | '/';
NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';
PROPERTY := SYMBOL;
VALUE := SYMBOL | NUMBER;
EQUAL := 'equal to';
IS := 'is' | 'are' | 'have' | 'has';
NUMBER := #'[0-9]+' | #'[0-9]+.[0-9]+';
SYMBOL := #'[a-z]+';
ACTIONS := ACTION | ACTION SPACE AND SPACE ACTIONS
ACTION := SIMPLE-ACTION | PROBABLE-ACTION;
PROBABLE-ACTION := VALUE SPACE CHANCE-IN SPACE VALUE SPACE SIMPLE-ACTION;
SIMPLE-ACTION := SYMBOL SPACE BECOMES SPACE EXPRESSION;
CHANCE-IN := 'chance in';
BECOMES := 'should be' | 'becomes';
SPACE := #' *'";
)
(def keywords-en
"English language keyword literals used in rules.
It's a long term aim that the rule language should be easy to
internationalise; this isn't a full solution but it's a step towards
a solution."
(join "\n" ["IF := 'if';"
"THEN := 'then';"
"THAN := 'than';"
"OR := 'or';"
"NOT := 'not';"
"AND := 'and';"
"SOME := 'some';"
"NONE := 'no';"
"ALL := 'all'"
"BETWEEN := 'between';"
"WITHIN := 'within';"
"IN := 'in';"
"MORE := 'more' | 'greater';"
"LESS := 'less' | 'fewer';"
"NEIGHBOURS := 'neighbour' | 'neighbor' | 'neighbours' | 'neighbors';"
"EQUAL := 'equal to';"
"IS := 'is' | 'are' | 'have' | 'has';"
"CHANCE-IN := 'chance in';"
"BECOMES := 'should be' | 'becomes';"
;; SYMBOL is in the per-language file so that languages that use
;; (e.g.) Cyrillic characters can change the definition.
"SYMBOL := #'[a-z]+';"
]))
(defn select-keywords-for-locale
"For now, just return `keywords-en`; plan is to have resource files of
keywords for different languages in a resource directory, but that isn't
done yet. It's probably not going to work easily for languages that use
non-latin alphabets, anyway."
([]
(select-keywords-for-locale (get-default)))
([^Locale locale]
keywords-en))
(def parse-rule (def parse-rule
"Parse the argument, assumed to be a string in the correct syntax, and return a parse tree." "Parse the argument, assumed to be a string in the correct syntax, and return a parse tree."
(insta/parser grammar)) (insta/parser (join "\n" [grammar (select-keywords-for-locale)])))
(defn compile-rule (defn compile-rule
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules, "Parse this `rule-text`, a string conforming to the grammar of MicroWorld rules,
@ -108,10 +125,10 @@
([rule-text return-tuple?] ([rule-text return-tuple?]
(assert (string? rule-text)) (assert (string? rule-text))
(let [rule (trim rule-text) (let [rule (trim rule-text)
tree (ps/simplify (parse-rule rule)) tree (simplify (parse-rule rule))
afn (if (rule? tree) (eval (pg/generate tree)) afn (if (rule? tree) (eval (generate tree))
;; else ;; else
(pe/throw-parse-exception tree))] (throw-parse-exception tree))]
(if return-tuple? (if return-tuple?
(list afn rule) (list afn rule)
;; else ;; else

0
src/mw_parser/flow.clj Normal file
View file