package org.dcache.util.expression;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.annotations.BuildParseTree;
import org.parboiled.annotations.Cached;
import org.parboiled.annotations.DontLabel;
import org.parboiled.annotations.SuppressNode;
import org.parboiled.support.Var;
import org.dcache.util.ByteUnit;
import static org.dcache.util.ByteUnit.*;
import static org.dcache.util.ByteUnits.isoPrefix;
@BuildParseTree
public abstract class ExpressionParser extends BaseParser<Expression>
{
public Rule Top() {
return sequence(If(), EOI);
}
@SuppressWarnings("InfiniteRecursion")
@SuppressFBWarnings(value="IL_INFINITE_RECURSIVE_LOOP",
justification = "Parboil injects code to prevent infinite loops")
Rule If() {
return sequence(Disjunction(),
optional(QUERY, If(), COLON, If(),
push(new Expression(Token.IF,
pop(2), pop(1), pop()))));
}
Rule Disjunction() {
return BinaryOperatorRule(Conjunction(), OR);
}
Rule Conjunction() {
return BinaryOperatorRule(Negation(), AND);
}
Rule Negation() {
return UnaryOperatorRule(Relational(), NOT);
}
Rule Relational() {
return BinaryOperatorRule(Additive(),
firstOf(EQ, NE, LE, LT, GE, GT));
}
Rule Additive() {
return BinaryOperatorRule(Multiplicative(), firstOf(PLUS, MINUS));
}
Rule Multiplicative() {
return BinaryOperatorRule(Match(), firstOf(MULT, DIV, MOD));
}
Rule Match() {
return BinaryOperatorRule(Unary(), firstOf(MATCH, NOT_MATCH));
}
Rule Unary() {
Var<Boolean> neg = new Var<>();
return sequence(neg.set(false),
zeroOrMore(firstOf(PLUS,
sequence(MINUS, neg.set(!neg.get()))
)
),
Power(),
neg.get()
? push(new Expression(Token.UMINUS, pop()))
: true);
}
Rule Power() {
return BinaryOperatorRule(Primary(), POWER);
}
Rule Primary() {
return firstOf(sequence(LPAR, If(), RPAR),
Literal(),
QualifiedIdentifier());
}
Rule Literal() {
return sequence(firstOf(sequence("true", testNot(LetterOrDigit()),
push(new Expression(Token.TRUE))),
sequence("false", testNot(LetterOrDigit()),
push(new Expression(Token.FALSE))),
Number(),
StringLiteral()),
Spacing());
}
Rule StringLiteral() {
return firstOf(sequence('"',
zeroOrMore(sequence(testNot(anyOf("\r\n\"\\")), ANY)
).suppressSubnodes(),
push(new Expression(Token.STRING_LITERAL,
match())),
'"'
),
sequence('\'',
zeroOrMore(sequence(testNot(anyOf("\r\n'\\")), ANY)
).suppressSubnodes(),
push(new Expression(Token.STRING_LITERAL,
match())),
'\''
)
);
}
Rule QualifiedIdentifier() {
return sequence(sequence(Identifier(),
zeroOrMore(ch('.'), Identifier())),
push(new Expression(Token.IDENTIFIER, match())),
Spacing());
}
Rule Identifier() {
return sequence(Letter(), zeroOrMore(LetterOrDigit()));
}
Rule Letter() {
return firstOf(charRange('a', 'z'), charRange('A', 'Z'), '_', '$');
}
Rule LetterOrDigit() {
return firstOf(charRange('a', 'z'), charRange('A', 'Z'), charRange('0', '9'), '_', '$');
}
@SuppressWarnings("InfiniteRecursion")
@SuppressFBWarnings(value = "IL_INFINITE_RECURSIVE_LOOP",
justification = "Parboil injects code to prevent infinite loops")
@Cached
Rule UnaryOperatorRule(Rule subRule, Rule operatorRule) {
Var<Token> op = new Var<>();
return firstOf(sequence(operatorRule,
op.set(Token.find(match().trim())),
UnaryOperatorRule(subRule, operatorRule),
push(new Expression(op.get(), pop()))),
subRule);
}
Rule BinaryOperatorRule(Rule subRule, Rule operatorRule) {
Var<Token> op = new Var<>();
return sequence(subRule,
zeroOrMore(operatorRule,
op.set(Token.find(match().trim())),
subRule,
push(new Expression(op.get(), pop(1), pop()))
)
);
}
Rule Number() {
return sequence(sequence(oneOrMore(Digit()),
optional(ch('.'), oneOrMore(Digit()))
),
push(new Expression(Double.parseDouble(match()))),
Spacing(),
optional(Unit(),
push(new Expression(Token.MULT,
pop(), pop()))
)
);
}
Rule Unit() {
return firstOf(UnitRule(KiB),
UnitRule(MiB),
UnitRule(GiB),
UnitRule(TiB),
UnitRule(PiB),
UnitRule(KB),
UnitRule("K", KB.toBytes(1L)),
UnitRule(MB),
UnitRule(GB),
UnitRule(TB),
UnitRule(PB)
);
}
Rule UnitRule(ByteUnit units) {
return UnitRule(isoPrefix().of(units), units.toBytes(1L));
}
Rule UnitRule(String s, long factor) {
return sequence(s, push(new Expression(factor)));
}
Rule Digit() {
return charRange('0', '9');
}
@SuppressNode
Rule Spacing() {
return zeroOrMore(anyOf(" \t\r\n\f"));
}
@SuppressNode
@DontLabel
Rule Terminal(String string) {
return sequence(string, Spacing()).label(string);
}
final Rule AND = Terminal(Token.AND.label);
final Rule COLON = Terminal(":");
final Rule DIV = Terminal(Token.DIV.label);
final Rule EQ = Terminal(Token.EQ.label);
final Rule GE = Terminal(Token.GE.label);
final Rule GT = Terminal(Token.GT.label);
final Rule LE = Terminal(Token.LE.label);
final Rule LPAR = Terminal("(");
final Rule LT = Terminal(Token.LT.label);
final Rule MATCH = Terminal(Token.MATCH.label);
final Rule MINUS = Terminal(Token.MINUS.label);
final Rule MOD = Terminal(Token.MOD.label);
final Rule MULT = Terminal(Token.MULT.label);
final Rule NE = Terminal(Token.NE.label);
final Rule NOT = Terminal(Token.NOT.label);
final Rule NOT_MATCH = Terminal(Token.NOT_MATCH.label);
final Rule OR = Terminal(Token.OR.label);
final Rule PLUS = Terminal(Token.PLUS.label);
final Rule POWER = Terminal(Token.POWER.label);
final Rule QUERY = Terminal("?");
final Rule RPAR = Terminal(")");
}