Thu, 16 May 2013 19:52:39 +0200
8012359: Increase code coverage in Joni
Reviewed-by: jlaskey, lagergren
1 /*
2 * Permission is hereby granted, free of charge, to any person obtaining a copy of
3 * this software and associated documentation files (the "Software"), to deal in
4 * the Software without restriction, including without limitation the rights to
5 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
6 * of the Software, and to permit persons to whom the Software is furnished to do
7 * so, subject to the following conditions:
8 *
9 * The above copyright notice and this permission notice shall be included in all
10 * copies or substantial portions of the Software.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 * SOFTWARE.
19 */
20 package jdk.nashorn.internal.runtime.regexp.joni.ast;
22 import jdk.nashorn.internal.runtime.regexp.joni.Config;
23 import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
24 import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
26 import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.*;
28 public final class QuantifierNode extends StateNode {
30 public Node target;
31 public int lower;
32 public int upper;
33 public boolean greedy;
35 public int targetEmptyInfo;
37 public Node headExact;
38 public Node nextHeadExact;
39 public boolean isRefered; /* include called node. don't eliminate even if {0} */
41 enum ReduceType {
42 ASIS, /* as is */
43 DEL, /* delete parent */
44 A, /* to '*' */
45 AQ, /* to '*?' */
46 QQ, /* to '??' */
47 P_QQ, /* to '+)??' */
48 PQ_Q, /* to '+?)?' */
49 }
51 private final static ReduceType[][] REDUCE_TABLE = {
52 {DEL, A, A, QQ, AQ, ASIS}, /* '?' */
53 {DEL, DEL, DEL, P_QQ, P_QQ, DEL}, /* '*' */
54 {A, A, DEL, ASIS, P_QQ, DEL}, /* '+' */
55 {DEL, AQ, AQ, DEL, AQ, AQ}, /* '??' */
56 {DEL, DEL, DEL, DEL, DEL, DEL}, /* '*?' */
57 {ASIS, PQ_Q, DEL, AQ, AQ, DEL} /* '+?' */
58 };
60 private final static String PopularQStr[] = new String[] {
61 "?", "*", "+", "??", "*?", "+?"
62 };
64 private final static String ReduceQStr[]= new String[] {
65 "", "", "*", "*?", "??", "+ and ??", "+? and ?"
66 };
69 public QuantifierNode(int lower, int upper, boolean byNumber) {
70 this.lower = lower;
71 this.upper = upper;
72 greedy = true;
73 targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
75 if (byNumber) setByNumber();
76 }
78 @Override
79 public int getType() {
80 return QTFR;
81 }
83 @Override
84 protected void setChild(Node newChild) {
85 target = newChild;
86 }
88 @Override
89 protected Node getChild() {
90 return target;
91 }
93 public void setTarget(Node tgt) {
94 target = tgt;
95 tgt.parent = this;
96 }
98 public StringNode convertToString(int flag) {
99 StringNode sn = new StringNode();
100 sn.flag = flag;
101 sn.swap(this);
102 return sn;
103 }
105 @Override
106 public String getName() {
107 return "Quantifier";
108 }
110 @Override
111 public String toString(int level) {
112 StringBuilder value = new StringBuilder(super.toString(level));
113 value.append("\n target: " + pad(target, level + 1));
114 value.append("\n lower: " + lower);
115 value.append("\n upper: " + upper);
116 value.append("\n greedy: " + greedy);
117 value.append("\n targetEmptyInfo: " + targetEmptyInfo);
118 value.append("\n headExact: " + pad(headExact, level + 1));
119 value.append("\n nextHeadExact: " + pad(nextHeadExact, level + 1));
120 value.append("\n isRefered: " + isRefered);
122 return value.toString();
123 }
125 public boolean isAnyCharStar() {
126 return greedy && isRepeatInfinite(upper) && target.getType() == CANY;
127 }
129 /* ?:0, *:1, +:2, ??:3, *?:4, +?:5 */
130 protected int popularNum() {
131 if (greedy) {
132 if (lower == 0) {
133 if (upper == 1) return 0;
134 else if (isRepeatInfinite(upper)) return 1;
135 } else if (lower == 1) {
136 if (isRepeatInfinite(upper)) return 2;
137 }
138 } else {
139 if (lower == 0) {
140 if (upper == 1) return 3;
141 else if (isRepeatInfinite(upper)) return 4;
142 } else if (lower == 1) {
143 if (isRepeatInfinite(upper)) return 5;
144 }
145 }
146 return -1;
147 }
149 protected void set(QuantifierNode other) {
150 setTarget(other.target);
151 other.target = null;
152 lower = other.lower;
153 upper = other.upper;
154 greedy = other.greedy;
155 targetEmptyInfo = other.targetEmptyInfo;
157 //setHeadExact(other.headExact);
158 //setNextHeadExact(other.nextHeadExact);
159 headExact = other.headExact;
160 nextHeadExact = other.nextHeadExact;
161 isRefered = other.isRefered;
162 }
164 public void reduceNestedQuantifier(QuantifierNode other) {
165 int pnum = popularNum();
166 int cnum = other.popularNum();
168 if (pnum < 0 || cnum < 0) return;
170 switch(REDUCE_TABLE[cnum][pnum]) {
171 case DEL:
172 // no need to set the parent here...
173 // swap ?
174 set(other); // *pnode = *cnode; ???
175 break;
177 case A:
178 setTarget(other.target);
179 lower = 0;
180 upper = REPEAT_INFINITE;
181 greedy = true;
182 break;
184 case AQ:
185 setTarget(other.target);
186 lower = 0;
187 upper = REPEAT_INFINITE;
188 greedy = false;
189 break;
191 case QQ:
192 setTarget(other.target);
193 lower = 0;
194 upper = 1;
195 greedy = false;
196 break;
198 case P_QQ:
199 setTarget(other);
200 lower = 0;
201 upper = 1;
202 greedy = false;
203 other.lower = 1;
204 other.upper = REPEAT_INFINITE;
205 other.greedy = true;
206 return;
208 case PQ_Q:
209 setTarget(other);
210 lower = 0;
211 upper = 1;
212 greedy = true;
213 other.lower = 1;
214 other.upper = REPEAT_INFINITE;
215 other.greedy = false;
216 return;
218 case ASIS:
219 setTarget(other);
220 return;
221 }
222 // ??? remove the parent from target ???
223 other.target = null; // remove target from reduced quantifier
224 }
226 public int setQuantifier(Node tgt, boolean group, ScanEnvironment env, char[] chars, int p, int end) {
227 if (lower == 1 && upper == 1) return 1;
229 switch(tgt.getType()) {
231 case STR:
232 if (!group) {
233 StringNode sn = (StringNode)tgt;
234 if (sn.canBeSplit()) {
235 StringNode n = sn.splitLastChar();
236 if (n != null) {
237 setTarget(n);
238 return 2;
239 }
240 }
241 }
242 break;
244 case QTFR:
245 /* check redundant double repeat. */
246 /* verbose warn (?:.?)? etc... but not warn (.?)? etc... */
247 QuantifierNode qnt = (QuantifierNode)tgt;
248 int nestQNum = popularNum();
249 int targetQNum = qnt.popularNum();
251 if (Config.USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR) {
252 if (!isByNumber() && !qnt.isByNumber() && env.syntax.warnReduntantNestedRepeat()) {
253 switch(REDUCE_TABLE[targetQNum][nestQNum]) {
254 case ASIS:
255 break;
257 case DEL:
258 env.reg.getWarnings().warn(new String(chars, p, end) +
259 " redundant nested repeat operator");
260 break;
262 default:
263 env.reg.getWarnings().warn(new String(chars, p, end) +
264 " nested repeat operator " + PopularQStr[targetQNum] +
265 " and " + PopularQStr[nestQNum] + " was replaced with '" +
266 ReduceQStr[REDUCE_TABLE[targetQNum][nestQNum].ordinal()] + "'");
267 }
268 }
269 } // USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
271 if (targetQNum >= 0) {
272 if (nestQNum >= 0) {
273 reduceNestedQuantifier(qnt);
274 return 0;
275 } else if (targetQNum == 1 || targetQNum == 2) { /* * or + */
276 /* (?:a*){n,m}, (?:a+){n,m} => (?:a*){n,n}, (?:a+){n,n} */
277 if (!isRepeatInfinite(upper) && upper > 1 && greedy) {
278 upper = lower == 0 ? 1 : lower;
279 }
280 }
281 }
283 default:
284 break;
285 }
287 setTarget(tgt);
288 return 0;
289 }
291 public static final int REPEAT_INFINITE = -1;
292 public static boolean isRepeatInfinite(int n) {
293 return n == REPEAT_INFINITE;
294 }
296 }