QuantifierNode.java revision 953:221a84ef44c0
1193323Sed/*
2193323Sed * Permission is hereby granted, free of charge, to any person obtaining a copy of
3193323Sed * this software and associated documentation files (the "Software"), to deal in
4193323Sed * the Software without restriction, including without limitation the rights to
5193323Sed * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
6193323Sed * of the Software, and to permit persons to whom the Software is furnished to do
7193323Sed * so, subject to the following conditions:
8193323Sed *
9193323Sed * The above copyright notice and this permission notice shall be included in all
10193323Sed * copies or substantial portions of the Software.
11193323Sed *
12193323Sed * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13193323Sed * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14193323Sed * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15249423Sdim * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16249423Sdim * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17193323Sed * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18193323Sed * SOFTWARE.
19263508Sdim */
20193323Sedpackage jdk.nashorn.internal.runtime.regexp.joni.ast;
21212904Sdim
22193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.A;
23193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.AQ;
24193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.ASIS;
25193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.DEL;
26193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.PQ_Q;
27193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.P_QQ;
28193323Sedimport static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.QQ;
29193323Sed
30193323Sedimport jdk.nashorn.internal.runtime.regexp.joni.Config;
31212904Sdimimport jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
32193323Sedimport jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
33206124Srdivacky
34206124Srdivackypublic final class QuantifierNode extends StateNode {
35206124Srdivacky
36206124Srdivacky    public Node target;
37193323Sed    public int lower;
38193323Sed    public int upper;
39193323Sed    public boolean greedy;
40193323Sed
41249423Sdim    public int targetEmptyInfo;
42249423Sdim
43249423Sdim    public Node headExact;
44193323Sed    public Node nextHeadExact;
45193323Sed    public boolean isRefered;           /* include called node. don't eliminate even if {0} */
46193323Sed
47193323Sed    enum ReduceType {
48193323Sed        ASIS,       /* as is */
49193323Sed        DEL,        /* delete parent */
50193323Sed        A,          /* to '*'    */
51193323Sed        AQ,         /* to '*?'   */
52193323Sed        QQ,         /* to '??'   */
53193323Sed        P_QQ,       /* to '+)??' */
54193323Sed        PQ_Q,       /* to '+?)?' */
55193323Sed    }
56193323Sed
57193323Sed    private final static ReduceType[][] REDUCE_TABLE = {
58193323Sed            {DEL,     A,      A,      QQ,     AQ,     ASIS}, /* '?'  */
59193323Sed            {DEL,     DEL,    DEL,    P_QQ,   P_QQ,   DEL},  /* '*'  */
60193323Sed            {A,       A,      DEL,    ASIS,   P_QQ,   DEL},  /* '+'  */
61200581Srdivacky            {DEL,     AQ,     AQ,     DEL,    AQ,     AQ},   /* '??' */
62193323Sed            {DEL,     DEL,    DEL,    DEL,    DEL,    DEL},  /* '*?' */
63212904Sdim            {ASIS,    PQ_Q,   DEL,    AQ,     AQ,     DEL}   /* '+?' */
64193323Sed    };
65193323Sed
66193323Sed    private final static String PopularQStr[] = new String[] {
67193323Sed            "?", "*", "+", "??", "*?", "+?"
68193323Sed    };
69193323Sed
70193323Sed    private final static String ReduceQStr[]= new String[] {
71193323Sed            "", "", "*", "*?", "??", "+ and ??", "+? and ?"
72193323Sed    };
73193323Sed
74193323Sed
75193323Sed    public QuantifierNode(final int lower, final int upper, final boolean byNumber) {
76193323Sed        this.lower = lower;
77193323Sed        this.upper = upper;
78193323Sed        greedy = true;
79193323Sed        targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
80193323Sed
81193323Sed        if (byNumber) setByNumber();
82200581Srdivacky    }
83193323Sed
84193323Sed    @Override
85193323Sed    public int getType() {
86193323Sed        return QTFR;
87193323Sed    }
88193323Sed
89226633Sdim    @Override
90193323Sed    protected void setChild(final Node newChild) {
91193323Sed        target = newChild;
92193323Sed    }
93193323Sed
94193323Sed    @Override
95193323Sed    protected Node getChild() {
96193323Sed        return target;
97193323Sed    }
98193323Sed
99193323Sed    public void setTarget(final Node tgt) {
100193323Sed        target = tgt;
101193323Sed        tgt.parent = this;
102193323Sed    }
103202878Srdivacky
104202878Srdivacky    public StringNode convertToString(final int flag) {
105202878Srdivacky        final StringNode sn = new StringNode();
106193323Sed        sn.flag = flag;
107193323Sed        sn.swap(this);
108193323Sed        return sn;
109212904Sdim    }
110193323Sed
111212904Sdim    @Override
112212904Sdim    public String getName() {
113193323Sed        return "Quantifier";
114193323Sed    }
115193323Sed
116193323Sed    @Override
117193323Sed    public String toString(final int level) {
118193323Sed        final StringBuilder value = new StringBuilder(super.toString(level));
119193323Sed        value.append("\n  target: " + pad(target, level + 1));
120193323Sed        value.append("\n  lower: " + lower);
121193323Sed        value.append("\n  upper: " + upper);
122193323Sed        value.append("\n  greedy: " + greedy);
123198090Srdivacky        value.append("\n  targetEmptyInfo: " + targetEmptyInfo);
124198090Srdivacky        value.append("\n  headExact: " + pad(headExact, level + 1));
125193323Sed        value.append("\n  nextHeadExact: " + pad(nextHeadExact, level + 1));
126193323Sed        value.append("\n  isRefered: " + isRefered);
127198090Srdivacky
128198090Srdivacky        return value.toString();
129198090Srdivacky    }
130193323Sed
131193323Sed    public boolean isAnyCharStar() {
132193323Sed        return greedy && isRepeatInfinite(upper) && target.getType() == CANY;
133193323Sed    }
134193323Sed
135193323Sed    /* ?:0, *:1, +:2, ??:3, *?:4, +?:5 */
136193323Sed    protected int popularNum() {
137193323Sed        if (greedy) {
138193323Sed            if (lower == 0) {
139193323Sed                if (upper == 1) return 0;
140193323Sed                else if (isRepeatInfinite(upper)) return 1;
141193323Sed            } else if (lower == 1) {
142193323Sed                if (isRepeatInfinite(upper)) return 2;
143193323Sed            }
144193323Sed        } else {
145193323Sed            if (lower == 0) {
146193323Sed                if (upper == 1) return 3;
147193323Sed                else if (isRepeatInfinite(upper)) return 4;
148193323Sed            } else if (lower == 1) {
149193323Sed                if (isRepeatInfinite(upper)) return 5;
150193323Sed            }
151193323Sed        }
152193323Sed        return -1;
153193323Sed    }
154193323Sed
155193323Sed    protected void set(final QuantifierNode other) {
156193323Sed        setTarget(other.target);
157193323Sed        other.target = null;
158193323Sed        lower = other.lower;
159193323Sed        upper = other.upper;
160193323Sed        greedy = other.greedy;
161        targetEmptyInfo = other.targetEmptyInfo;
162
163        //setHeadExact(other.headExact);
164        //setNextHeadExact(other.nextHeadExact);
165        headExact = other.headExact;
166        nextHeadExact = other.nextHeadExact;
167        isRefered = other.isRefered;
168    }
169
170    public void reduceNestedQuantifier(final QuantifierNode other) {
171        final int pnum = popularNum();
172        final int cnum = other.popularNum();
173
174        if (pnum < 0 || cnum < 0) return;
175
176        switch(REDUCE_TABLE[cnum][pnum]) {
177        case DEL:
178            // no need to set the parent here...
179            // swap ?
180            set(other); // *pnode = *cnode; ???
181            break;
182
183        case A:
184            setTarget(other.target);
185            lower = 0;
186            upper = REPEAT_INFINITE;
187            greedy = true;
188            break;
189
190        case AQ:
191            setTarget(other.target);
192            lower = 0;
193            upper = REPEAT_INFINITE;
194            greedy = false;
195            break;
196
197        case QQ:
198            setTarget(other.target);
199            lower = 0;
200            upper = 1;
201            greedy = false;
202            break;
203
204        case P_QQ:
205            setTarget(other);
206            lower = 0;
207            upper = 1;
208            greedy = false;
209            other.lower = 1;
210            other.upper = REPEAT_INFINITE;
211            other.greedy = true;
212            return;
213
214        case PQ_Q:
215            setTarget(other);
216            lower = 0;
217            upper = 1;
218            greedy = true;
219            other.lower = 1;
220            other.upper = REPEAT_INFINITE;
221            other.greedy = false;
222            return;
223
224        case ASIS:
225            setTarget(other);
226            return;
227        }
228        // ??? remove the parent from target ???
229        other.target = null; // remove target from reduced quantifier
230    }
231
232    @SuppressWarnings("fallthrough")
233    public int setQuantifier(final Node tgt, final boolean group, final ScanEnvironment env, final char[] chars, final int p, final int end) {
234        if (lower == 1 && upper == 1) return 1;
235
236        switch(tgt.getType()) {
237
238        case STR:
239            if (!group) {
240                final StringNode sn = (StringNode)tgt;
241                if (sn.canBeSplit()) {
242                    final StringNode n = sn.splitLastChar();
243                    if (n != null) {
244                        setTarget(n);
245                        return 2;
246                    }
247                }
248            }
249            break;
250
251        case QTFR:
252            /* check redundant double repeat. */
253            /* verbose warn (?:.?)? etc... but not warn (.?)? etc... */
254            final QuantifierNode qnt = (QuantifierNode)tgt;
255            final int nestQNum = popularNum();
256            final int targetQNum = qnt.popularNum();
257
258            if (Config.USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR) {
259                if (!isByNumber() && !qnt.isByNumber() && env.syntax.warnReduntantNestedRepeat()) {
260                    switch(REDUCE_TABLE[targetQNum][nestQNum]) {
261                    case ASIS:
262                        break;
263
264                    case DEL:
265                        env.reg.getWarnings().warn(new String(chars, p, end) +
266                                " redundant nested repeat operator");
267                        break;
268
269                    default:
270                        env.reg.getWarnings().warn(new String(chars, p, end) +
271                                " nested repeat operator " + PopularQStr[targetQNum] +
272                                " and " + PopularQStr[nestQNum] + " was replaced with '" +
273                                ReduceQStr[REDUCE_TABLE[targetQNum][nestQNum].ordinal()] + "'");
274                    }
275                }
276            } // USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR
277
278            if (targetQNum >= 0) {
279                if (nestQNum >= 0) {
280                    reduceNestedQuantifier(qnt);
281                    return 0;
282                } else if (targetQNum == 1 || targetQNum == 2) { /* * or + */
283                    /* (?:a*){n,m}, (?:a+){n,m} => (?:a*){n,n}, (?:a+){n,n} */
284                    if (!isRepeatInfinite(upper) && upper > 1 && greedy) {
285                        upper = lower == 0 ? 1 : lower;
286                    }
287                }
288            }
289
290        default:
291            break;
292        }
293
294        setTarget(tgt);
295        return 0;
296    }
297
298    public static final int REPEAT_INFINITE         = -1;
299    public static boolean isRepeatInfinite(final int n) {
300        return n == REPEAT_INFINITE;
301    }
302
303}
304