2 //#if defined(FOUNDATION10) || defined(J2SE13)
5 *******************************************************************************
6 * Copyright (C) 2002-2009, International Business Machines Corporation and *
7 * others. All Rights Reserved. *
8 *******************************************************************************
10 package com.ibm.icu.dev.test.util;
12 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
19 import com.ibm.icu.text.UnicodeSet;
20 import java.util.Random;
23 private Map map = new HashMap();
24 private Set variables = new HashSet();
25 private Pick pick = null;
26 private Pick.Target target = null;
28 private Quoter quoter;
29 private Random random;
31 public String next() {
35 public String getInternal() {
36 return pick.getInternal(0, new HashSet());
40 + "weight = integer '%';"
41 + "range = '{' integer (',' integer?)? '}' weight*;"
43 + "star = '*' weight*;"
44 + "plus = '+' weight*;"
45 + "maybe = '?' weight?;"
46 + "quantifier = range | star | maybe | plus;"
47 + "core = string | unicodeSet | '(' alternation ')';"
48 + "sequence = (core quantifier*)+;"
49 + "alternation = sequence (weight? ('|' sequence weight?)+)?;"
50 + "rule = string '=' alternation;";
53 * Match 0 or more times
54 + Match 1 or more times
56 {n} Match exactly n times
57 {n,} Match at least n times
58 {n,m} Match at least n but not more than m times
64 public BNF(Random random, Quoter quoter) {
70 public BNF addRules(String rules) {
74 return this; // for chaining
77 public BNF complete() {
78 // check that the rules match the variables, except for $root in rules
79 Set ruleSet = map.keySet();
81 variables.add("$root");
82 variables.addAll(t.getLookedUpItems());
83 if (!ruleSet.equals(variables)) {
84 String msg = showDiff(variables, ruleSet);
85 if (msg.length() != 0) msg = "Error: Missing definitions for: " + msg;
86 String temp = showDiff(ruleSet, variables);
87 if (temp.length() != 0) temp = "Warning: Defined but not used: " + temp;
88 if (msg.length() == 0) msg = temp;
89 else if (temp.length() != 0) {
90 msg = msg + "; " + temp;
95 if (!ruleSet.equals(variables)) {
96 String msg = showDiff(variables, ruleSet);
97 if (msg.length() != 0) msg = "Missing definitions for: " + msg;
98 String temp = showDiff(ruleSet, variables);
99 if (temp.length() != 0) temp = "Defined but not used: " + temp;
100 if (msg.length() == 0) msg = temp;
101 else if (temp.length() != 0) {
102 msg = msg + "; " + temp;
107 // replace variables by definitions
108 Iterator it = ruleSet.iterator();
109 while (it.hasNext()) {
110 String key = (String) it.next();
111 Pick expression = (Pick) map.get(key);
112 Iterator it2 = ruleSet.iterator();
113 if (false && key.equals("$crlf")) {
114 System.out.println("debug") ;
116 while (it2.hasNext()) {
117 Object key2 = it2.next();
118 if (key.equals(key2)) continue;
119 Pick expression2 = (Pick) map.get(key2);
120 expression2.replace(key, expression);
123 pick = (Pick) map.get("$root");
124 target = Pick.Target.make(pick, random, quoter);
125 // TODO remove temp collections
129 String showDiff(Set a, Set b) {
130 Set temp = new HashSet();
133 if (temp.size() == 0) return "";
134 StringBuffer buffer = new StringBuffer();
135 Iterator it = temp.iterator();
136 while (it.hasNext()) {
137 if (buffer.length() != 0) buffer.append(", ");
138 buffer.append(it.next().toString());
140 return buffer.toString();
143 void error(String msg) {
144 throw new IllegalArgumentException(msg
145 + "\r\n" + t.toString());
148 private boolean addRule() {
150 if (type == Tokenizer.DONE) return false;
151 if (type != Tokenizer.STRING) error("missing weight");
152 String s = t.getString();
153 if (s.length() == 0 || s.charAt(0) != '$') error("missing $ in variable");
154 if (t.next() != '=') error("missing =");
155 int startBody = t.index;
156 Pick rule = getAlternation();
157 if (rule == null) error("missing expression");
158 t.addSymbol(s, t.getSource(), startBody, t.index);
159 if (t.next() != ';') error("missing ;");
160 return addPick(s, rule);
163 protected boolean addPick(String s, Pick rule) {
164 Object temp = map.get(s);
165 if (temp != null) error("duplicate variable");
166 if (rule.name == null) rule.name(s);
171 public BNF addSet(String variable, UnicodeSet set) {
173 String body = set.toString();
174 t.addSymbol(variable, body, 0, body.length());
175 addPick(variable, Pick.codePoint(set));
182 Pick qualify(Pick item) {
187 return new Pick.Quote(item);
189 return new Pick.Morph(item);
191 int weight = getWeight();
192 if (weight == NO_WEIGHT) weight = 50;
193 weights = new int[] {100-weight, weight};
194 return Pick.repeat(0, 1, weights, item);
196 weights = getWeights();
197 return Pick.repeat(1, maxRepeat, weights, item);
199 weights = getWeights();
200 return Pick.repeat(1, maxRepeat, weights, item);
202 if (t.next() != Tokenizer.NUMBER) error("missing number");
203 int start = (int) t.getNumber();
209 if (type == Tokenizer.NUMBER) {
210 end = (int)t.getNumber();
214 if (type != '}') error("missing }");
215 weights = getWeights();
216 return Pick.repeat(start, end, weights, item);
223 int token = t.next();
224 if (token == Tokenizer.STRING) {
225 String s = t.getString();
226 if (s.charAt(0) == '$') variables.add(s);
227 return Pick.string(s);
229 if (token == Tokenizer.UNICODESET) {
230 return Pick.codePoint(t.getUnicodeSet());
236 Pick temp = getAlternation();
238 if (token != ')') error("missing )");
243 Pick.Sequence result = null;
246 Pick item = getCore();
248 if (result != null) return result;
249 if (last != null) return last;
250 error("missing item");
252 // qualify it as many times as possible
256 item = qualify(item);
257 } while (item != oldItem);
262 if (result == null) result = Pick.makeSequence().and2(last);
263 result = result.and2(item);
268 // for simplicity, we just use recursive descent
269 Pick getAlternation() {
270 Pick.Alternation result = null;
272 int lastWeight = NO_WEIGHT;
274 Pick temp = getSequence();
275 if (temp == null) error("empty alternation");
276 int weight = getWeight();
277 if (weight == NO_WEIGHT) weight = 1;
282 if (result == null) result = Pick.makeAlternation().or2(lastWeight, last);
283 result = result.or2(weight, temp);
285 int token = t.next();
288 if (result != null) return result;
289 if (last != null) return last;
294 private static final int NO_WEIGHT = Integer.MIN_VALUE;
298 int token = t.next();
299 if (token != Tokenizer.NUMBER) {
303 weight = (int)t.getNumber();
305 if (token != '%') error("missing %");
310 ArrayList list = new ArrayList();
312 int weight = getWeight();
313 if (weight == NO_WEIGHT) break;
314 list.add(new Integer(weight));
316 if (list.size() == 0) return null;
317 int[] result = new int[list.size()];
318 for (int i = 0; i < list.size(); ++i) {
319 result[i] = ((Integer)list.get(i)).intValue();
324 public int getMaxRepeat() {
328 public BNF setMaxRepeat(int maxRepeat) {
329 this.maxRepeat = maxRepeat;