Контекстно-зависимая реализация ANTLR4 ParseTreeVisitor - PullRequest
1 голос
/ 10 марта 2020

Я работаю над проектом, в котором мы переносим огромное количество (более 12000) просмотров в Hadoop / Impala из Oracle. Я написал небольшую утилиту Java для извлечения DDL-представления из Oracle и хотел бы использовать ANTLR4 для обхода AST и генерации DDL-выражения, совместимого с Impala.

Большая часть работы относительно просто, включает только переписывание некоторых Oracle специфических c синтаксических причуд в стиле Impala. Однако я столкнулся с проблемой, когда я не уверен, что у меня есть лучший ответ: у нас есть ряд особых случаев, когда значения из поля даты извлекаются в нескольких вызовах вложенных функций. Например, следующее извлекает день из поля Date:

TO_NUMBER(TO_CHAR(d.R_DATE , 'DD' ))

У меня есть грамматика ANTLR4, объявленная для Oracle SQL, и, следовательно, я получаю обратный вызов посетителя, когда он достигает TO_NUMBER и TO_CHAR также, но я хотел бы иметь специальную обработку для этого особого случая.

Нет ли другого способа, кроме реализации метода-обработчика для внешней функции, а затем прибегнуть к ручному обходу вложенной структуры, чтобы увидеть

У меня есть что-то похожее в сгенерированном классе Visitor:

    @Override
    public String visitNumber_function(PlSqlParser.Number_functionContext ctx) {

        // FIXME: seems to be dodgy code, can it be improved? 
        String functionName = ctx.name.getText();
        if (functionName.equalsIgnoreCase("TO_NUMBER")) {

            final int childCount = ctx.getChildCount();
            if (childCount == 4) {

                final int functionNameIndex = 0;
                final int openRoundBracketIndex = 1;
                final int encapsulatedValueIndex = 2;
                final int closeRoundBracketIndex = 3;

                ParseTree encapsulated = ctx.getChild(encapsulatedValueIndex);
                if (encapsulated instanceof TerminalNode) {
                    throw new IllegalStateException("TerminalNode is found at: " + encapsulatedValueIndex);
                }

                String customDateConversionOrNullOnOtherType =
                        customDateConversionFromToNumberAndNestedToChar(encapsulated);

                if (customDateConversionOrNullOnOtherType != null) {
                    // the child node contained our expected child element, so return the converted value
                    return customDateConversionOrNullOnOtherType;
                }
                // otherwise the child was something unexpected, signalled by null
                // so simply fall-back to the default handler
            }
        }

        // some other numeric function, default handling
        return super.visitNumber_function(ctx);
    }

    private String customDateConversionFromToNumberAndNestedToChar(ParseTree parseTree) {
        // ...
    }

1 Ответ

0 голосов
/ 19 марта 2020

Для тех, кто сталкивается с той же проблемой, путь к go выглядит следующим образом:

  1. изменение определения грамматики и введение пользовательских подтипов для инкапсулированного выражения вложенной функции ,

  2. Затем можно подключиться к обработке точно в нужном месте дерева разбора.

  3. Использование второго пользовательского ParseTreeVisitor, который фиксирует значения вызова функции и делегирует обработку остальной части поддерева основному, «внешнему» ParseTreeVisitor.

Как только второй пользовательский элемент ParseTreeVisitor завершил посещение всех вложенных элементов ParseTree, у меня была нужная контекстная информация, и все поддерево посетили должным образом.

...