All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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