
cdc.applic.expressions.parsing.Parser Maven / Gradle / Ivy
package cdc.applic.expressions.parsing;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import cdc.applic.expressions.LexicalException;
import cdc.applic.expressions.SyntacticException;
import cdc.applic.expressions.ast.AndNode;
import cdc.applic.expressions.ast.EqualNode;
import cdc.applic.expressions.ast.EquivalenceNode;
import cdc.applic.expressions.ast.FalseNode;
import cdc.applic.expressions.ast.GreaterNode;
import cdc.applic.expressions.ast.GreaterOrEqualNode;
import cdc.applic.expressions.ast.ImplicationNode;
import cdc.applic.expressions.ast.InNode;
import cdc.applic.expressions.ast.LessNode;
import cdc.applic.expressions.ast.LessOrEqualNode;
import cdc.applic.expressions.ast.NeitherGreaterNorEqualNode;
import cdc.applic.expressions.ast.NeitherLessNorEqualNode;
import cdc.applic.expressions.ast.Node;
import cdc.applic.expressions.ast.NotEqualNode;
import cdc.applic.expressions.ast.NotGreaterNode;
import cdc.applic.expressions.ast.NotInNode;
import cdc.applic.expressions.ast.NotLessNode;
import cdc.applic.expressions.ast.NotNode;
import cdc.applic.expressions.ast.OrNode;
import cdc.applic.expressions.ast.ParsingNode;
import cdc.applic.expressions.ast.RefNode;
import cdc.applic.expressions.ast.TrueNode;
import cdc.applic.expressions.ast.XorNode;
import cdc.applic.expressions.content.SItem;
import cdc.applic.expressions.content.UncheckedSet;
import cdc.applic.expressions.content.Value;
import cdc.applic.expressions.literals.Name;
import cdc.applic.expressions.literals.SName;
public class Parser extends AbstractParser {
/** The operators stack. */
private final Deque operators = new ArrayDeque<>();
/** The operands stack. */
private final Deque operands = new ArrayDeque<>();
public Parser() {
super();
}
/**
* Parses an expression and returns its syntax tree.
*
* @param expression The expression to parse.
* @return The corresponding syntax tree, if expression is valid.
* @throws LexicalException When {@code expression} is lexically invalid.
* @throws SyntacticException When {@code expression} is syntactically invalid.
*/
public ParsingNode parse(String expression) {
tokenizer.init(expression);
operands.clear();
operators.clear();
operators.push(OperatorType.SENTINEL);
tokenizer.next(true);
parseExpr();
expect("parse", true, TokenType.EPSILON);
return operands.getFirst();
}
private void popOperator() {
final OperatorType type = operators.getFirst();
if (type == OperatorType.AND) {
popAnd();
} else if (type == OperatorType.OR) {
popOr();
} else if (type == OperatorType.NOT) {
popNot();
} else if (type == OperatorType.IMPLICATION) {
popImplication();
} else if (type == OperatorType.XOR) {
popXor();
} else {
popEquivalence();
}
}
private void popAnd() {
operators.removeFirst();
final Node right = operands.removeFirst();
final Node left = operands.removeFirst();
final AndNode node = new AndNode(left, right);
operands.push(node);
}
private void popOr() {
operators.removeFirst();
final Node right = operands.removeFirst();
final Node left = operands.removeFirst();
final OrNode node = new OrNode(left, right);
operands.push(node);
}
private void popImplication() {
operators.removeFirst();
final Node right = operands.removeFirst();
final Node left = operands.removeFirst();
final ImplicationNode node = new ImplicationNode(left, right);
operands.push(node);
}
private void popXor() {
operators.removeFirst();
final Node right = operands.removeFirst();
final Node left = operands.removeFirst();
final XorNode node = new XorNode(left, right);
operands.push(node);
}
private void popEquivalence() {
operators.removeFirst();
final Node right = operands.removeFirst();
final Node left = operands.removeFirst();
final EquivalenceNode node = new EquivalenceNode(left, right);
operands.push(node);
}
private void popNot() {
operators.removeFirst();
final Node right = operands.removeFirst();
final NotNode node = new NotNode(right);
operands.push(node);
}
private void pushOperator(TokenType type) {
final OperatorType op = OperatorType.from(type);
while (operators.getFirst().getPrecedence() >= op.getPrecedence()) {
popOperator();
}
operators.push(op);
}
private void parseExpr() {
parsePExpr();
while (tokenizer.getTokenType().isBinary()) {
pushOperator(tokenizer.getTokenType());
tokenizer.next(true);
parsePExpr();
}
while (operators.getFirst() != OperatorType.SENTINEL) {
popOperator();
}
}
private void parsePExpr() {
switch (tokenizer.getTokenType()) {
case OPEN_PAREN:
parseOpenParen();
break;
case NOT:
operators.push(OperatorType.NOT);
tokenizer.next(true);
parseExpr();
break;
case TRUE:
operands.push(TrueNode.INSTANCE);
tokenizer.next(true);
break;
case FALSE:
operands.push(FalseNode.INSTANCE);
tokenizer.next(true);
break;
case TEXT:
case ESCAPED_TEXT:
parseBaseExpr();
break;
default:
throw newInvalidExpression(SyntacticException.Detail.UNEXPECTED_TOKEN,
"Unexpected token: " + tokenizer.getToken() + " in: '" + tokenizer.getExpression()
+ "' parsePExpr");
}
}
private void parseOpenParen() {
tokenizer.next(true);
operators.push(OperatorType.SENTINEL);
parseExpr();
expect("parseOpenParen", true, TokenType.CLOSE_PAREN);
operators.pop();
}
private void parseBaseExpr() {
final Name name;
final SName name1 = SName.of(tokenizer.getUnescapedText(), false);
tokenizer.next(true);
if (tokenizer.getTokenType() == TokenType.PATH_SEP) {
tokenizer.next(true);
check("parseBaseExpr", true, TEXT_TOKEN_TYPES);
final SName name2 = SName.of(tokenizer.getUnescapedText(), false);
name = Name.of(name1, name2);
tokenizer.next(true);
} else {
name = Name.of(name1);
}
switch (tokenizer.getTokenType()) {
case EQUAL:
parseEqual(name);
break;
case NOT_EQUAL:
parseNotEqual(name);
break;
case LESS:
parseLess(name);
break;
case NOT_LESS:
parseNotLess(name);
break;
case GREATER:
parseGreater(name);
break;
case NOT_GREATER:
parseNotGreater(name);
break;
case LESS_OR_EQUAL:
parseLessOrEqual(name);
break;
case NEITHER_LESS_NOR_EQUAL:
parseNeitherLessNorEqual(name);
break;
case GREATER_OR_EQUAL:
parseGreaterOrEqual(name);
break;
case NEITHER_GREATER_NOR_EQUAL:
parseNeitherGreaterNorEqual(name);
break;
case IN:
parseIn(name);
break;
case NOT_IN:
parseNotIn(name);
break;
default:
operands.push(new RefNode(name));
break;
}
}
private void parseEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseEqual", true, ITEM_TOKEN_TYPES);
operands.push(new EqualNode(name, value));
}
private void parseNotEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseNotEqual", true, ITEM_TOKEN_TYPES);
operands.push(new NotEqualNode(name, value));
}
private void parseLess(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseLess", true, ITEM_TOKEN_TYPES);
operands.push(new LessNode(name, value));
}
private void parseNotLess(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseNotLess", true, ITEM_TOKEN_TYPES);
operands.push(new NotLessNode(name, value));
}
private void parseGreater(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseFGreater", true, ITEM_TOKEN_TYPES);
operands.push(new GreaterNode(name, value));
}
private void parseNotGreater(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseNotGreater", true, ITEM_TOKEN_TYPES);
operands.push(new NotGreaterNode(name, value));
}
private void parseLessOrEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseLessOrEqual", true, ITEM_TOKEN_TYPES);
operands.push(new LessOrEqualNode(name, value));
}
private void parseNeitherLessNorEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseNeitherLessNorEqual", true, ITEM_TOKEN_TYPES);
operands.push(new NeitherLessNorEqualNode(name, value));
}
private void parseGreaterOrEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseGreaterOrEqual", true, ITEM_TOKEN_TYPES);
operands.push(new GreaterOrEqualNode(name, value));
}
private void parseNeitherGreaterNorEqual(Name name) {
tokenizer.next(false);
final Value value = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseNeitherGreaterNorEqual", true, ITEM_TOKEN_TYPES);
operands.push(new NeitherGreaterNorEqualNode(name, value));
}
private void parseIn(Name name) {
tokenizer.next(true);
final UncheckedSet items = parseSItemSet();
operands.push(new InNode(name, items));
}
private void parseNotIn(Name name) {
tokenizer.next(true);
final UncheckedSet items = parseSItemSet();
operands.push(new NotInNode(name, items));
}
private UncheckedSet parseSItemSet() {
final TokenType type = tokenizer.getTokenType();
final List items = new ArrayList<>();
if (type == TokenType.EMPTY_SET) {
expect("parseSItemSet", true, ITEM_SET_TOKEN_TYPES);
} else {
expect("parseSItemSet", false, ITEM_SET_TOKEN_TYPES);
if (tokenizer.getTokenType().isItem()) {
items.add(parseSItem());
}
while (tokenizer.getTokenType() == TokenType.ITEMS_SEP) {
tokenizer.next(false);
items.add(parseSItem());
}
expect("parseSItemSet", true, TokenType.CLOSE_SET);
}
return UncheckedSet.of(items);
}
private SItem parseSItem() {
final TokenType lowType = tokenizer.getTokenType();
final Value low = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
tokenizer.next(true);
if (tokenizer.getTokenType() == TokenType.TO && lowType.supportsRange()) {
tokenizer.next(false);
final Value high = SItemsParsing.createValue(tokenizer.getToken(), tokenizer.getExpression());
expect("parseSItem", true, lowType);
return SItemsParsing.createRange(low, high, tokenizer.getExpression());
} else {
return low;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy