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

com.github.harbby.dsxparser.DSXParser Maven / Gradle / Ivy

The newest version!
package com.github.harbby.dsxparser;

import com.github.harbby.dsxparser.antlr4.SqlBaseLexer;
import com.github.harbby.dsxparser.antlr4.SqlBaseParser;
import com.github.harbby.dsxparser.function.CharFunc;
import com.github.harbby.dsxparser.function.IsNotNull;
import com.github.harbby.dsxparser.function.IsNull;
import com.github.harbby.dsxparser.tree.DereferenceExpression;
import com.github.harbby.dsxparser.tree.Expression;
import com.github.harbby.dsxparser.tree.Identifier;
import com.github.harbby.dsxparser.tree.Node;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

public class DSXParser {
    private static final Logger logger = LoggerFactory.getLogger(DSXParser.class);
    private final Map funcMap;

    private DSXParser(Map funcMap) {
        this.funcMap = Map.copyOf(funcMap);
    }

    private static final BaseErrorListener LEXER_ERROR_LISTENER = new BaseErrorListener() {
        @Override
        public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String message, RecognitionException e) {
            throw new ParsingException(message, e, line, charPositionInLine);
        }
    };

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private boolean disableBuiltinFunctions = false;
        Map funcMap = new HashMap<>();

        public Builder register(Class funcClass) {
            requireNonNull(funcClass, "funcClass is null");
            UserFuncWrapper wrapper = UserFuncWrapper.of(funcClass);
            UserFuncWrapper old = funcMap.put(wrapper.getFuncName(), wrapper);
            if (old != null) {
                logger.warn("Duplicate register func {} {}", wrapper.getFuncName(), funcClass);
            }
            return this;
        }

        public Builder disableBuiltinFunctions() {
            this.disableBuiltinFunctions = true;
            return this;
        }

        public Builder register(List> userFuncList) {
            for (var f : userFuncList) {
                this.register(f);
            }
            return this;
        }

        public DSXParser build() {
            if (!disableBuiltinFunctions) {
                this.register(IsNull.class);
                this.register(IsNotNull.class);
                this.register(CharFunc.class);
            }
            return new DSXParser(funcMap);
        }
    }

    public Expression parseExpression(String expression) throws ParsingException {
        return (Expression) invokeParser("expression", expression, SqlBaseParser::singleExpression);
    }

    private Node invokeParser(String name, String sql, Function parseFunction) throws ParsingException {
        try {
            SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
            SqlBaseParser parser = new SqlBaseParser(tokenStream);

            //parser.addParseListener(new PostProcessor(Arrays.asList(parser.getRuleNames())));

            lexer.removeErrorListeners();
            lexer.addErrorListener(LEXER_ERROR_LISTENER);

            parser.removeErrorListeners();
            parser.addErrorListener(LEXER_ERROR_LISTENER);

            ParserRuleContext tree;
            try {
                // first, try parsing with potentially faster SLL mode
                parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
                tree = parseFunction.apply(parser);
            } catch (ParseCancellationException ex) {
                // if we fail, parse with LL mode
                tokenStream.seek(0); // rewind input stream
                parser.reset();

                parser.getInterpreter().setPredictionMode(PredictionMode.LL);
                tree = parseFunction.apply(parser);
            }

            return new AstBuilder(this.funcMap).visit(tree);
        } catch (StackOverflowError e) {
            throw new ParsingException(name + " is too large (stack overflow while parsing)");
        }
    }

    public static Set analyzeFromFields(Expression expression) {
        Deque stack = new LinkedList<>();
        Set set = new LinkedHashSet<>();
        stack.add(expression);
        Node it;
        while ((it = stack.poll()) != null) {
            if (it instanceof DereferenceExpression exp) {
                set.add(exp.toString());
            } else if (it instanceof Identifier exp) {
                set.add(exp.toString());
            } else {
                List children = it.getChildren();
                assert !(children instanceof LinkedList);
                int size = children.size();
                for (int i = 1; i <= size; i++) {
                    stack.addFirst(children.get(size - i));
                }
            }
        }
        return set;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy