package jdk.nashorn.internal.runtime.regexp.joni.ast;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.A;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.AQ;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.ASIS;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.DEL;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.PQ_Q;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.P_QQ;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.ReduceType.QQ;
import jdk.nashorn.internal.runtime.regexp.joni.Config;
import jdk.nashorn.internal.runtime.regexp.joni.ScanEnvironment;
import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
@SuppressWarnings("javadoc")
public final class QuantifierNode extends StateNode {
public Node target;
public int lower;
public int upper;
public boolean greedy;
public int targetEmptyInfo;
public Node headExact;
public Node nextHeadExact;
public boolean isRefered;
enum ReduceType {
ASIS,
DEL,
A,
AQ,
QQ,
P_QQ,
PQ_Q,
}
private final static ReduceType[][] REDUCE_TABLE = {
{DEL, A, A, QQ, AQ, ASIS},
{DEL, DEL, DEL, P_QQ, P_QQ, DEL},
{A, A, DEL, ASIS, P_QQ, DEL},
{DEL, AQ, AQ, DEL, AQ, AQ},
{DEL, DEL, DEL, DEL, DEL, DEL},
{ASIS, PQ_Q, DEL, AQ, AQ, DEL}
};
private final static String PopularQStr[] = new String[] {
"?", "*", "+", "??", "*?", "+?"
};
private final static String ReduceQStr[]= new String[] {
"", "", "*", "*?", "??", "+ and ??", "+? and ?"
};
public QuantifierNode(final int lower, final int upper, final boolean byNumber) {
this.lower = lower;
this.upper = upper;
greedy = true;
targetEmptyInfo = TargetInfo.ISNOT_EMPTY;
if (byNumber) {
setByNumber();
}
}
@Override
public int getType() {
return QTFR;
}
@Override
protected void setChild(final Node newChild) {
target = newChild;
}
@Override
protected Node getChild() {
return target;
}
public void setTarget(final Node tgt) {
target = tgt;
tgt.parent = this;
}
public StringNode convertToString(final int flag) {
final StringNode sn = new StringNode();
sn.flag = flag;
sn.swap(this);
return sn;
}
@Override
public String getName() {
return "Quantifier";
}
@Override
public String toString(final int level) {
final StringBuilder value = new StringBuilder(super.toString(level));
value.append("\n target: ").append(pad(target, level + 1));
value.append("\n lower: ").append(lower);
value.append("\n upper: ").append(upper);
value.append("\n greedy: ").append(greedy);
value.append("\n targetEmptyInfo: ").append(targetEmptyInfo);
value.append("\n headExact: ").append(pad(headExact, level + 1));
value.append("\n nextHeadExact: ").append(pad(nextHeadExact, level + 1));
value.append("\n isRefered: ").append(isRefered);
return value.toString();
}
public boolean isAnyCharStar() {
return greedy && isRepeatInfinite(upper) && target.getType() == CANY;
}
protected int popularNum() {
if (greedy) {
if (lower == 0) {
if (upper == 1) {
return 0;
} else if (isRepeatInfinite(upper)) {
return 1;
}
} else if (lower == 1) {
if (isRepeatInfinite(upper)) {
return 2;
}
}
} else {
if (lower == 0) {
if (upper == 1) {
return 3;
} else if (isRepeatInfinite(upper)) {
return 4;
}
} else if (lower == 1) {
if (isRepeatInfinite(upper)) {
return 5;
}
}
}
return -1;
}
protected void set(final QuantifierNode other) {
setTarget(other.target);
other.target = null;
lower = other.lower;
upper = other.upper;
greedy = other.greedy;
targetEmptyInfo = other.targetEmptyInfo;
headExact = other.headExact;
nextHeadExact = other.nextHeadExact;
isRefered = other.isRefered;
}
public void reduceNestedQuantifier(final QuantifierNode other) {
final int pnum = popularNum();
final int cnum = other.popularNum();
if (pnum < 0 || cnum < 0) {
return;
}
switch(REDUCE_TABLE[cnum][pnum]) {
case DEL:
set(other);
break;
case A:
setTarget(other.target);
lower = 0;
upper = REPEAT_INFINITE;
greedy = true;
break;
case AQ:
setTarget(other.target);
lower = 0;
upper = REPEAT_INFINITE;
greedy = false;
break;
case QQ:
setTarget(other.target);
lower = 0;
upper = 1;
greedy = false;
break;
case P_QQ:
setTarget(other);
lower = 0;
upper = 1;
greedy = false;
other.lower = 1;
other.upper = REPEAT_INFINITE;
other.greedy = true;
return;
case PQ_Q:
setTarget(other);
lower = 0;
upper = 1;
greedy = true;
other.lower = 1;
other.upper = REPEAT_INFINITE;
other.greedy = false;
return;
case ASIS:
setTarget(other);
return;
default:
break;
}
other.target = null;
}
@SuppressWarnings("fallthrough")
public int setQuantifier(final Node tgt, final boolean group, final ScanEnvironment env, final char[] chars, final int p, final int end) {
if (lower == 1 && upper == 1) {
return 1;
}
switch(tgt.getType()) {
case STR:
if (!group) {
final StringNode sn = (StringNode)tgt;
if (sn.canBeSplit()) {
final StringNode n = sn.splitLastChar();
if (n != null) {
setTarget(n);
return 2;
}
}
}
break;
case QTFR:
final QuantifierNode qnt = (QuantifierNode)tgt;
final int nestQNum = popularNum();
final int targetQNum = qnt.popularNum();
if (Config.USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR) {
if (!isByNumber() && !qnt.isByNumber() && env.syntax.warnReduntantNestedRepeat()) {
switch(REDUCE_TABLE[targetQNum][nestQNum]) {
case ASIS:
break;
case DEL:
env.reg.getWarnings().warn(new String(chars, p, end) +
" redundant nested repeat operator");
break;
default:
env.reg.getWarnings().warn(new String(chars, p, end) +
" nested repeat operator " + PopularQStr[targetQNum] +
" and " + PopularQStr[nestQNum] + " was replaced with '" +
ReduceQStr[REDUCE_TABLE[targetQNum][nestQNum].ordinal()] + "'");
}
}
}
if (targetQNum >= 0) {
if (nestQNum >= 0) {
reduceNestedQuantifier(qnt);
return 0;
} else if (targetQNum == 1 || targetQNum == 2) {
if (!isRepeatInfinite(upper) && upper > 1 && greedy) {
upper = lower == 0 ? 1 : lower;
}
}
}
default:
break;
}
setTarget(tgt);
return 0;
}
public static final int REPEAT_INFINITE = -1;
public static boolean isRepeatInfinite(final int n) {
return n == REPEAT_INFINITE;
}
}