-//##header\r
-//#if defined(FOUNDATION10) || defined(J2SE13)\r
-//#else\r
-/*\r
- *******************************************************************************\r
- * Copyright (C) 2002-2009, International Business Machines Corporation and *\r
- * others. All Rights Reserved. *\r
- *******************************************************************************\r
- */\r
-package com.ibm.icu.dev.test.util;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-\r
-import com.ibm.icu.text.UnicodeSet;\r
-import java.util.Random;\r
-\r
-public class BNF {\r
- private Map map = new HashMap();\r
- private Set variables = new HashSet();\r
- private Pick pick = null;\r
- private Pick.Target target = null;\r
- private Tokenizer t;\r
- private Quoter quoter;\r
- private Random random;\r
- \r
- public String next() {\r
- return target.next();\r
- }\r
- \r
- public String getInternal() {\r
- return pick.getInternal(0, new HashSet());\r
- }\r
- \r
- /*\r
- + "weight = integer '%';"\r
- + "range = '{' integer (',' integer?)? '}' weight*;"\r
- + "quote = '@';"\r
- + "star = '*' weight*;"\r
- + "plus = '+' weight*;"\r
- + "maybe = '?' weight?;"\r
- + "quantifier = range | star | maybe | plus;"\r
- + "core = string | unicodeSet | '(' alternation ')';"\r
- + "sequence = (core quantifier*)+;"\r
- + "alternation = sequence (weight? ('|' sequence weight?)+)?;"\r
- + "rule = string '=' alternation;"; \r
- \r
- \r
- * Match 0 or more times\r
- + Match 1 or more times\r
- ? Match 1 or 0 times\r
- {n} Match exactly n times\r
- {n,} Match at least n times\r
- {n,m} Match at least n but not more than m times \r
- \r
- \r
-\r
- */\r
- \r
- public BNF(Random random, Quoter quoter) {\r
- this.random = random;\r
- this.quoter = quoter;\r
- t = new Tokenizer();\r
- }\r
- \r
- public BNF addRules(String rules) {\r
- t.setSource(rules); \r
- while (addRule()) {\r
- }\r
- return this; // for chaining\r
- }\r
- \r
- public BNF complete() {\r
- // check that the rules match the variables, except for $root in rules\r
- Set ruleSet = map.keySet();\r
- // add also \r
- variables.add("$root");\r
- variables.addAll(t.getLookedUpItems());\r
- if (!ruleSet.equals(variables)) {\r
- String msg = showDiff(variables, ruleSet);\r
- if (msg.length() != 0) msg = "Error: Missing definitions for: " + msg;\r
- String temp = showDiff(ruleSet, variables);\r
- if (temp.length() != 0) temp = "Warning: Defined but not used: " + temp;\r
- if (msg.length() == 0) msg = temp;\r
- else if (temp.length() != 0) {\r
- msg = msg + "; " + temp; \r
- }\r
- error(msg); \r
- } \r
- \r
- if (!ruleSet.equals(variables)) {\r
- String msg = showDiff(variables, ruleSet);\r
- if (msg.length() != 0) msg = "Missing definitions for: " + msg;\r
- String temp = showDiff(ruleSet, variables);\r
- if (temp.length() != 0) temp = "Defined but not used: " + temp;\r
- if (msg.length() == 0) msg = temp;\r
- else if (temp.length() != 0) {\r
- msg = msg + "; " + temp; \r
- }\r
- error(msg); \r
- } \r
- \r
- // replace variables by definitions\r
- Iterator it = ruleSet.iterator();\r
- while (it.hasNext()) {\r
- String key = (String) it.next();\r
- Pick expression = (Pick) map.get(key);\r
- Iterator it2 = ruleSet.iterator(); \r
- if (false && key.equals("$crlf")) {\r
- System.out.println("debug") ;\r
- }\r
- while (it2.hasNext()) {\r
- Object key2 = it2.next();\r
- if (key.equals(key2)) continue;\r
- Pick expression2 = (Pick) map.get(key2);\r
- expression2.replace(key, expression);\r
- }\r
- }\r
- pick = (Pick) map.get("$root");\r
- target = Pick.Target.make(pick, random, quoter);\r
- // TODO remove temp collections\r
- return this;\r
- }\r
- \r
- String showDiff(Set a, Set b) {\r
- Set temp = new HashSet();\r
- temp.addAll(a);\r
- temp.removeAll(b);\r
- if (temp.size() == 0) return "";\r
- StringBuffer buffer = new StringBuffer();\r
- Iterator it = temp.iterator();\r
- while (it.hasNext()) {\r
- if (buffer.length() != 0) buffer.append(", ");\r
- buffer.append(it.next().toString());\r
- }\r
- return buffer.toString();\r
- }\r
- \r
- void error(String msg) {\r
- throw new IllegalArgumentException(msg\r
- + "\r\n" + t.toString());\r
- }\r
- \r
- private boolean addRule() {\r
- int type = t.next();\r
- if (type == Tokenizer.DONE) return false;\r
- if (type != Tokenizer.STRING) error("missing weight");\r
- String s = t.getString();\r
- if (s.length() == 0 || s.charAt(0) != '$') error("missing $ in variable");\r
- if (t.next() != '=') error("missing =");\r
- int startBody = t.index;\r
- Pick rule = getAlternation();\r
- if (rule == null) error("missing expression");\r
- t.addSymbol(s, t.getSource(), startBody, t.index);\r
- if (t.next() != ';') error("missing ;"); \r
- return addPick(s, rule);\r
- }\r
-\r
- protected boolean addPick(String s, Pick rule) {\r
- Object temp = map.get(s);\r
- if (temp != null) error("duplicate variable");\r
- if (rule.name == null) rule.name(s);\r
- map.put(s, rule);\r
- return true;\r
- }\r
- \r
- public BNF addSet(String variable, UnicodeSet set) {\r
- if (set != null) {\r
- String body = set.toString();\r
- t.addSymbol(variable, body, 0, body.length()); \r
- addPick(variable, Pick.codePoint(set));\r
- }\r
- return this;\r
- }\r
- \r
- int maxRepeat = 99;\r
- \r
- Pick qualify(Pick item) {\r
- int[] weights;\r
- int type = t.next();\r
- switch(type) {\r
- case '@': \r
- return new Pick.Quote(item);\r
- case '~': \r
- return new Pick.Morph(item);\r
- case '?': \r
- int weight = getWeight();\r
- if (weight == NO_WEIGHT) weight = 50;\r
- weights = new int[] {100-weight, weight};\r
- return Pick.repeat(0, 1, weights, item);\r
- case '*': \r
- weights = getWeights();\r
- return Pick.repeat(1, maxRepeat, weights, item);\r
- case '+': \r
- weights = getWeights();\r
- return Pick.repeat(1, maxRepeat, weights, item);\r
- case '{':\r
- if (t.next() != Tokenizer.NUMBER) error("missing number");\r
- int start = (int) t.getNumber();\r
- int end = start;\r
- type = t.next();\r
- if (type == ',') {\r
- end = maxRepeat;\r
- type = t.next();\r
- if (type == Tokenizer.NUMBER) {\r
- end = (int)t.getNumber();\r
- type = t.next();\r
- }\r
- }\r
- if (type != '}') error("missing }");\r
- weights = getWeights();\r
- return Pick.repeat(start, end, weights, item);\r
- }\r
- t.backup();\r
- return item;\r
- }\r
- \r
- Pick getCore() {\r
- int token = t.next();\r
- if (token == Tokenizer.STRING) {\r
- String s = t.getString();\r
- if (s.charAt(0) == '$') variables.add(s);\r
- return Pick.string(s);\r
- }\r
- if (token == Tokenizer.UNICODESET) {\r
- return Pick.codePoint(t.getUnicodeSet()); \r
- }\r
- if (token != '(') {\r
- t.backup();\r
- return null;\r
- }\r
- Pick temp = getAlternation();\r
- token = t.next();\r
- if (token != ')') error("missing )"); \r
- return temp; \r
- }\r
- \r
- Pick getSequence() {\r
- Pick.Sequence result = null;\r
- Pick last = null;\r
- while (true) {\r
- Pick item = getCore();\r
- if (item == null) {\r
- if (result != null) return result;\r
- if (last != null) return last;\r
- error("missing item");\r
- }\r
- // qualify it as many times as possible\r
- Pick oldItem;\r
- do {\r
- oldItem = item;\r
- item = qualify(item);\r
- } while (item != oldItem);\r
- // add it in\r
- if (last == null) {\r
- last = item;\r
- } else {\r
- if (result == null) result = Pick.makeSequence().and2(last); \r
- result = result.and2(item);\r
- }\r
- }\r
- }\r
- \r
- // for simplicity, we just use recursive descent\r
- Pick getAlternation() {\r
- Pick.Alternation result = null;\r
- Pick last = null;\r
- int lastWeight = NO_WEIGHT;\r
- while (true) {\r
- Pick temp = getSequence();\r
- if (temp == null) error("empty alternation");\r
- int weight = getWeight();\r
- if (weight == NO_WEIGHT) weight = 1;\r
- if (last == null) {\r
- last = temp;\r
- lastWeight = weight;\r
- } else {\r
- if (result == null) result = Pick.makeAlternation().or2(lastWeight, last);\r
- result = result.or2(weight, temp); \r
- }\r
- int token = t.next();\r
- if (token != '|') {\r
- t.backup();\r
- if (result != null) return result;\r
- if (last != null) return last;\r
- }\r
- } \r
- }\r
- \r
- private static final int NO_WEIGHT = Integer.MIN_VALUE;\r
- \r
- int getWeight() { \r
- int weight;\r
- int token = t.next();\r
- if (token != Tokenizer.NUMBER) {\r
- t.backup();\r
- return NO_WEIGHT;\r
- }\r
- weight = (int)t.getNumber();\r
- token = t.next();\r
- if (token != '%') error("missing %");\r
- return weight;\r
- }\r
- \r
- int[] getWeights() {\r
- ArrayList list = new ArrayList();\r
- while (true) {\r
- int weight = getWeight();\r
- if (weight == NO_WEIGHT) break;\r
- list.add(new Integer(weight));\r
- }\r
- if (list.size() == 0) return null;\r
- int[] result = new int[list.size()];\r
- for (int i = 0; i < list.size(); ++i) {\r
- result[i] = ((Integer)list.get(i)).intValue();\r
- }\r
- return result;\r
- }\r
-\r
- public int getMaxRepeat() {\r
- return maxRepeat;\r
- }\r
-\r
- public BNF setMaxRepeat(int maxRepeat) {\r
- this.maxRepeat = maxRepeat;\r
- return this;\r
- }\r
-}\r
-//#endif\r
+//##header J2SE15
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+/*
+ *******************************************************************************
+ * Copyright (C) 2002-2009, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+package com.ibm.icu.dev.test.util;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import com.ibm.icu.text.UnicodeSet;
+import java.util.Random;
+
+public class BNF {
+ private Map map = new HashMap();
+ private Set variables = new HashSet();
+ private Pick pick = null;
+ private Pick.Target target = null;
+ private Tokenizer t;
+ private Quoter quoter;
+ private Random random;
+
+ public String next() {
+ return target.next();
+ }
+
+ public String getInternal() {
+ return pick.getInternal(0, new HashSet());
+ }
+
+ /*
+ + "weight = integer '%';"
+ + "range = '{' integer (',' integer?)? '}' weight*;"
+ + "quote = '@';"
+ + "star = '*' weight*;"
+ + "plus = '+' weight*;"
+ + "maybe = '?' weight?;"
+ + "quantifier = range | star | maybe | plus;"
+ + "core = string | unicodeSet | '(' alternation ')';"
+ + "sequence = (core quantifier*)+;"
+ + "alternation = sequence (weight? ('|' sequence weight?)+)?;"
+ + "rule = string '=' alternation;";
+
+
+ * Match 0 or more times
+ + Match 1 or more times
+ ? Match 1 or 0 times
+ {n} Match exactly n times
+ {n,} Match at least n times
+ {n,m} Match at least n but not more than m times
+
+
+
+ */
+
+ public BNF(Random random, Quoter quoter) {
+ this.random = random;
+ this.quoter = quoter;
+ t = new Tokenizer();
+ }
+
+ public BNF addRules(String rules) {
+ t.setSource(rules);
+ while (addRule()) {
+ }
+ return this; // for chaining
+ }
+
+ public BNF complete() {
+ // check that the rules match the variables, except for $root in rules
+ Set ruleSet = map.keySet();
+ // add also
+ variables.add("$root");
+ variables.addAll(t.getLookedUpItems());
+ if (!ruleSet.equals(variables)) {
+ String msg = showDiff(variables, ruleSet);
+ if (msg.length() != 0) msg = "Error: Missing definitions for: " + msg;
+ String temp = showDiff(ruleSet, variables);
+ if (temp.length() != 0) temp = "Warning: Defined but not used: " + temp;
+ if (msg.length() == 0) msg = temp;
+ else if (temp.length() != 0) {
+ msg = msg + "; " + temp;
+ }
+ error(msg);
+ }
+
+ if (!ruleSet.equals(variables)) {
+ String msg = showDiff(variables, ruleSet);
+ if (msg.length() != 0) msg = "Missing definitions for: " + msg;
+ String temp = showDiff(ruleSet, variables);
+ if (temp.length() != 0) temp = "Defined but not used: " + temp;
+ if (msg.length() == 0) msg = temp;
+ else if (temp.length() != 0) {
+ msg = msg + "; " + temp;
+ }
+ error(msg);
+ }
+
+ // replace variables by definitions
+ Iterator it = ruleSet.iterator();
+ while (it.hasNext()) {
+ String key = (String) it.next();
+ Pick expression = (Pick) map.get(key);
+ Iterator it2 = ruleSet.iterator();
+ if (false && key.equals("$crlf")) {
+ System.out.println("debug") ;
+ }
+ while (it2.hasNext()) {
+ Object key2 = it2.next();
+ if (key.equals(key2)) continue;
+ Pick expression2 = (Pick) map.get(key2);
+ expression2.replace(key, expression);
+ }
+ }
+ pick = (Pick) map.get("$root");
+ target = Pick.Target.make(pick, random, quoter);
+ // TODO remove temp collections
+ return this;
+ }
+
+ String showDiff(Set a, Set b) {
+ Set temp = new HashSet();
+ temp.addAll(a);
+ temp.removeAll(b);
+ if (temp.size() == 0) return "";
+ StringBuffer buffer = new StringBuffer();
+ Iterator it = temp.iterator();
+ while (it.hasNext()) {
+ if (buffer.length() != 0) buffer.append(", ");
+ buffer.append(it.next().toString());
+ }
+ return buffer.toString();
+ }
+
+ void error(String msg) {
+ throw new IllegalArgumentException(msg
+ + "\r\n" + t.toString());
+ }
+
+ private boolean addRule() {
+ int type = t.next();
+ if (type == Tokenizer.DONE) return false;
+ if (type != Tokenizer.STRING) error("missing weight");
+ String s = t.getString();
+ if (s.length() == 0 || s.charAt(0) != '$') error("missing $ in variable");
+ if (t.next() != '=') error("missing =");
+ int startBody = t.index;
+ Pick rule = getAlternation();
+ if (rule == null) error("missing expression");
+ t.addSymbol(s, t.getSource(), startBody, t.index);
+ if (t.next() != ';') error("missing ;");
+ return addPick(s, rule);
+ }
+
+ protected boolean addPick(String s, Pick rule) {
+ Object temp = map.get(s);
+ if (temp != null) error("duplicate variable");
+ if (rule.name == null) rule.name(s);
+ map.put(s, rule);
+ return true;
+ }
+
+ public BNF addSet(String variable, UnicodeSet set) {
+ if (set != null) {
+ String body = set.toString();
+ t.addSymbol(variable, body, 0, body.length());
+ addPick(variable, Pick.codePoint(set));
+ }
+ return this;
+ }
+
+ int maxRepeat = 99;
+
+ Pick qualify(Pick item) {
+ int[] weights;
+ int type = t.next();
+ switch(type) {
+ case '@':
+ return new Pick.Quote(item);
+ case '~':
+ return new Pick.Morph(item);
+ case '?':
+ int weight = getWeight();
+ if (weight == NO_WEIGHT) weight = 50;
+ weights = new int[] {100-weight, weight};
+ return Pick.repeat(0, 1, weights, item);
+ case '*':
+ weights = getWeights();
+ return Pick.repeat(1, maxRepeat, weights, item);
+ case '+':
+ weights = getWeights();
+ return Pick.repeat(1, maxRepeat, weights, item);
+ case '{':
+ if (t.next() != Tokenizer.NUMBER) error("missing number");
+ int start = (int) t.getNumber();
+ int end = start;
+ type = t.next();
+ if (type == ',') {
+ end = maxRepeat;
+ type = t.next();
+ if (type == Tokenizer.NUMBER) {
+ end = (int)t.getNumber();
+ type = t.next();
+ }
+ }
+ if (type != '}') error("missing }");
+ weights = getWeights();
+ return Pick.repeat(start, end, weights, item);
+ }
+ t.backup();
+ return item;
+ }
+
+ Pick getCore() {
+ int token = t.next();
+ if (token == Tokenizer.STRING) {
+ String s = t.getString();
+ if (s.charAt(0) == '$') variables.add(s);
+ return Pick.string(s);
+ }
+ if (token == Tokenizer.UNICODESET) {
+ return Pick.codePoint(t.getUnicodeSet());
+ }
+ if (token != '(') {
+ t.backup();
+ return null;
+ }
+ Pick temp = getAlternation();
+ token = t.next();
+ if (token != ')') error("missing )");
+ return temp;
+ }
+
+ Pick getSequence() {
+ Pick.Sequence result = null;
+ Pick last = null;
+ while (true) {
+ Pick item = getCore();
+ if (item == null) {
+ if (result != null) return result;
+ if (last != null) return last;
+ error("missing item");
+ }
+ // qualify it as many times as possible
+ Pick oldItem;
+ do {
+ oldItem = item;
+ item = qualify(item);
+ } while (item != oldItem);
+ // add it in
+ if (last == null) {
+ last = item;
+ } else {
+ if (result == null) result = Pick.makeSequence().and2(last);
+ result = result.and2(item);
+ }
+ }
+ }
+
+ // for simplicity, we just use recursive descent
+ Pick getAlternation() {
+ Pick.Alternation result = null;
+ Pick last = null;
+ int lastWeight = NO_WEIGHT;
+ while (true) {
+ Pick temp = getSequence();
+ if (temp == null) error("empty alternation");
+ int weight = getWeight();
+ if (weight == NO_WEIGHT) weight = 1;
+ if (last == null) {
+ last = temp;
+ lastWeight = weight;
+ } else {
+ if (result == null) result = Pick.makeAlternation().or2(lastWeight, last);
+ result = result.or2(weight, temp);
+ }
+ int token = t.next();
+ if (token != '|') {
+ t.backup();
+ if (result != null) return result;
+ if (last != null) return last;
+ }
+ }
+ }
+
+ private static final int NO_WEIGHT = Integer.MIN_VALUE;
+
+ int getWeight() {
+ int weight;
+ int token = t.next();
+ if (token != Tokenizer.NUMBER) {
+ t.backup();
+ return NO_WEIGHT;
+ }
+ weight = (int)t.getNumber();
+ token = t.next();
+ if (token != '%') error("missing %");
+ return weight;
+ }
+
+ int[] getWeights() {
+ ArrayList list = new ArrayList();
+ while (true) {
+ int weight = getWeight();
+ if (weight == NO_WEIGHT) break;
+ list.add(new Integer(weight));
+ }
+ if (list.size() == 0) return null;
+ int[] result = new int[list.size()];
+ for (int i = 0; i < list.size(); ++i) {
+ result[i] = ((Integer)list.get(i)).intValue();
+ }
+ return result;
+ }
+
+ public int getMaxRepeat() {
+ return maxRepeat;
+ }
+
+ public BNF setMaxRepeat(int maxRepeat) {
+ this.maxRepeat = maxRepeat;
+ return this;
+ }
+}
+//#endif