public OperatorNode<ExpressionOperator> convertExpr(ParseTree parseTree, Scope scope) {
        switch (getParseTreeIndex(parseTree)) {
            case yqlplusParser.RULE_vespa_grouping: {
                ParseTree firstChild = parseTree.getChild(0);
                if (getParseTreeIndex(firstChild) == yqlplusParser.RULE_annotation) {
                    ParseTree secondChild = parseTree.getChild(1);
                    OperatorNode<ExpressionOperator> annotation = convertExpr(((AnnotationContext) firstChild).map_expression(), scope);
                    OperatorNode<ExpressionOperator> expr = OperatorNode.create(toLocation(scope, secondChild),
                                                                                ExpressionOperator.VESPA_GROUPING, secondChild.getText());
                    List<String> names = annotation.getArgument(0);
                    List<OperatorNode<ExpressionOperator>> annotates = annotation.getArgument(1);
                    for (int i = 0; i < names.size(); ++i) {
                        expr.putAnnotation(names.get(i), readConstantExpression(annotates.get(i)));
                    }
                    return expr;
                } else {
                    return OperatorNode.create(toLocation(scope, firstChild), ExpressionOperator.VESPA_GROUPING,
                                               firstChild.getText());
                }
            }
            case yqlplusParser.RULE_null_operator:
                return OperatorNode.create(ExpressionOperator.NULL);
            case yqlplusParser.RULE_argument:
                return convertExpr(parseTree.getChild(0), scope);
            case yqlplusParser.RULE_fixed_or_parameter: {
                ParseTree firstChild = parseTree.getChild(0);
                if (getParseTreeIndex(firstChild) == yqlplusParser.INT) {
                    return OperatorNode.create(toLocation(scope, firstChild), ExpressionOperator.LITERAL, Integer.valueOf(firstChild.getText()));
                } else {
                    return convertExpr(firstChild, scope);
                }
            }
            case yqlplusParser.RULE_map_expression: {
                List<Property_name_and_valueContext> propertyList = ((Map_expressionContext)parseTree).property_name_and_value();
                List<String> names = new ArrayList<>(propertyList.size());
                List<OperatorNode<ExpressionOperator>> exprs = new ArrayList<>(propertyList.size());
                for (Property_name_and_valueContext child : propertyList) {
                    // : propertyName ':' expression[$expression::namespace] ->
                    // ^(PROPERTY propertyName expression)
                    names.add(StringUnescaper.unquote(child.getChild(0).getText()));
                    exprs.add(convertExpr(child.getChild(2), scope));
                }
                return OperatorNode.create(toLocation(scope, parseTree),ExpressionOperator.MAP, names, exprs);
            }
            case yqlplusParser.RULE_array_literal: {
                List<Constant_expressionContext> expressionList = ((Array_literalContext) parseTree).constant_expression();
                List<OperatorNode<ExpressionOperator>> values = new ArrayList<>(expressionList.size());
                for (Constant_expressionContext expr : expressionList) {
                    values.add(convertExpr(expr, scope));
                }
                return OperatorNode.create(toLocation(scope, expressionList.isEmpty()? parseTree:expressionList.get(0)), ExpressionOperator.ARRAY, values);
            }
            // dereferencedExpression: primaryExpression(indexref[in_select]| propertyref)*
            case yqlplusParser.RULE_dereferenced_expression: {
                Dereferenced_expressionContext dereferencedExpression = (Dereferenced_expressionContext) parseTree;
                Iterator<ParseTree> it = dereferencedExpression.children.iterator();
                OperatorNode<ExpressionOperator> result = convertExpr(it.next(), scope);
                while (it.hasNext()) {
                    ParseTree defTree = it.next();
                    if (getParseTreeIndex(defTree) == yqlplusParser.RULE_propertyref) {
                        // DOT nm=ID
                        result = OperatorNode.create(toLocation(scope, parseTree), ExpressionOperator.PROPREF, result, defTree.getChild(1).getText());
                    } else {
                        // indexref
                        result = OperatorNode.create(toLocation(scope, parseTree), ExpressionOperator.INDEX, result, convertExpr(defTree.getChild(1), scope));
                    }
                }
                return result;
            }
            case yqlplusParser.RULE_primary_expression: {
                // ^(CALL namespaced_name arguments)
                ParseTree firstChild = parseTree.getChild(0);
                switch (getParseTreeIndex(firstChild)) {
                    case yqlplusParser.RULE_fieldref: {
                        return convertExpr(firstChild, scope);
                    }
                    case yqlplusParser.RULE_call_expression: {
                        List<ArgumentContext> args = ((ArgumentsContext) firstChild.getChild(1)).argument();
                        List<OperatorNode<ExpressionOperator>> arguments = new ArrayList<>(args.size());
                        for (ArgumentContext argContext : args) {
                            arguments.add(convertExpr(argContext.expression(),scope));
                        }
                        return OperatorNode.create(toLocation(scope, parseTree), ExpressionOperator.CALL, scope.resolvePath(readName((Namespaced_nameContext) firstChild.getChild(0))), arguments);
                    }
                    case yqlplusParser.RULE_constant_expression:
                        return convertExpr(firstChild, scope);

                    case yqlplusParser.LPAREN:
                        return convertExpr(parseTree.getChild(1), scope);
                }
                break;
            }
            case yqlplusParser.RULE_parameter: {
                // external variable reference
                ParserRuleContext parameterContext = (ParserRuleContext) parseTree;
                IdentContext identContext = parameterContext.getRuleContext(IdentContext.class, 0);
                return OperatorNode.create(toLocation(scope, identContext), ExpressionOperator.VARREF, identContext.getText());
            }
            case yqlplusParser.RULE_annotate_expression: {
                //annotation logicalORExpression
                AnnotationContext annotateExpressionContext = ((Annotate_expressionContext)parseTree).annotation();
                OperatorNode<ExpressionOperator> annotation = convertExpr(annotateExpressionContext.map_expression(), scope);
                OperatorNode<ExpressionOperator> expr = convertExpr(parseTree.getChild(1), scope);
                List<String> names = annotation.getArgument(0);
                List<OperatorNode<ExpressionOperator>> annotates = annotation.getArgument(1);
                for (int i = 0; i < names.size(); ++i) {
                    expr.putAnnotation(names.get(i), readConstantExpression(annotates.get(i)));
                }
                return expr;
            }
            case yqlplusParser.RULE_expression: {
                return convertExpr(parseTree.getChild(0), scope);
            }
            case yqlplusParser.RULE_logical_AND_expression:
                Logical_AND_expressionContext andExpressionContext = (Logical_AND_expressionContext) parseTree;
                return readConjOp(ExpressionOperator.AND, andExpressionContext.equality_expression(), scope);
            case yqlplusParser.RULE_logical_OR_expression: {
                int childCount = parseTree.getChildCount();
                Logical_OR_expressionContext logicalORExpressionContext = (Logical_OR_expressionContext) parseTree;
                if (childCount > 1) {
                    return readConjOrOp(ExpressionOperator.OR, logicalORExpressionContext, scope);
                } else {
                    List<Equality_expressionContext> equalityExpressionList = ((Logical_AND_expressionContext) parseTree.getChild(0)).equality_expression();
                    if (equalityExpressionList.size() > 1) {
                        return readConjOp(ExpressionOperator.AND, equalityExpressionList, scope);
                    } else {
                        return convertExpr(equalityExpressionList.get(0), scope);
                    }
                }
            }
            case yqlplusParser.RULE_equality_expression: {
                Equality_expressionContext equalityExpression = (Equality_expressionContext) parseTree;
                Relational_expressionContext relationalExpressionContext = equalityExpression.relational_expression(0);
                OperatorNode<ExpressionOperator> expr = convertExpr(relationalExpressionContext, scope);
                In_not_in_targetContext inNotInTarget = equalityExpression.in_not_in_target();
                int childCount = equalityExpression.getChildCount();
                if (childCount == 1) {
                    return expr;
                }
                if (inNotInTarget != null) {
                    Literal_listContext literalListContext = inNotInTarget.literal_list();
                    boolean isIN = equalityExpression.IN() != null;
                    if (literalListContext == null) {
                        Select_statementContext selectStatementContext = inNotInTarget.select_statement();
                        OperatorNode<SequenceOperator> query = convertQuery(selectStatementContext, scope);
                        return OperatorNode.create(expr.getLocation(),isIN ? ExpressionOperator.IN_QUERY: ExpressionOperator.NOT_IN_QUERY, expr, query);
                    } else {
                        // we need to identify the type of the target; if it's a
                        // scalar we need to wrap it in a CREATE_ARRAY
                        // if it's already a CREATE ARRAY then it's fine, otherwise
                        // we need to know the variable type
                        // return readBinOp(node.getType() == yqlplusParser.IN ?
                        // ExpressionOperator.IN : ExpressionOperator.NOT_IN, node,
                        // scope);
                        return readBinOp(isIN ? ExpressionOperator.IN: ExpressionOperator.NOT_IN, equalityExpression.getChild(0), literalListContext, scope);
                    }

                } else {
                    ParseTree firstChild = equalityExpression.getChild(1);
                    if (equalityExpression.getChildCount() == 2) {
                        switch (getParseTreeIndex(firstChild)) {
                            case yqlplusParser.IS_NULL:
                                return readUnOp(ExpressionOperator.IS_NULL, relationalExpressionContext, scope);
                            case yqlplusParser.IS_NOT_NULL:
                                return readUnOp(ExpressionOperator.IS_NOT_NULL, relationalExpressionContext, scope);
                        }
                    } else {
                        switch (getParseTreeIndex(firstChild.getChild(0))) {
                            case yqlplusParser.EQ:
                                return readBinOp(ExpressionOperator.EQ, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.NEQ:
                                return readBinOp(ExpressionOperator.NEQ, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.LIKE:
                                return readBinOp(ExpressionOperator.LIKE, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.NOTLIKE:
                                return readBinOp(ExpressionOperator.NOT_LIKE, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.MATCHES:
                                return readBinOp(ExpressionOperator.MATCHES, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.NOTMATCHES:
                                return readBinOp(ExpressionOperator.NOT_MATCHES, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                            case yqlplusParser.CONTAINS:
                                return readBinOp(ExpressionOperator.CONTAINS, equalityExpression.getChild(0), equalityExpression.getChild(2), scope);
                        }
                    }

                }
                break;
            }
            case yqlplusParser.RULE_relational_expression: {
                Relational_expressionContext relationalExpressionContext = (Relational_expressionContext) parseTree;
                Relational_opContext opContext = relationalExpressionContext.relational_op();
                if (opContext != null) {
                    switch (getParseTreeIndex(relationalExpressionContext.relational_op().getChild(0))) {
                        case yqlplusParser.LT:
                            return readBinOp(ExpressionOperator.LT, parseTree, scope);
                        case yqlplusParser.LTEQ:
                            return readBinOp(ExpressionOperator.LTEQ, parseTree, scope);
                        case yqlplusParser.GT:
                            return readBinOp(ExpressionOperator.GT, parseTree, scope);
                        case yqlplusParser.GTEQ:
                            return readBinOp(ExpressionOperator.GTEQ, parseTree, scope);
                    }
                } else {
                    return convertExpr(relationalExpressionContext.additive_expression(0), scope);
                }
            }
            break;
            case yqlplusParser.RULE_additive_expression:
            case yqlplusParser.RULE_multiplicative_expression: {
                if (parseTree.getChildCount() > 1) {
                    String opStr = parseTree.getChild(1).getText();
                    switch (opStr) {
                        case "+":
                            return readBinOp(ExpressionOperator.ADD, parseTree, scope);
                        case "-":
                            return readBinOp(ExpressionOperator.SUB, parseTree, scope);
                        case "/":
                            return readBinOp(ExpressionOperator.DIV, parseTree, scope);
                        case "*":
                            return readBinOp(ExpressionOperator.MULT, parseTree, scope);
                        case "%":
                            return readBinOp(ExpressionOperator.MOD, parseTree, scope);
                        default:
                            if (parseTree.getChild(0) instanceof Unary_expressionContext) {
                                return convertExpr(parseTree.getChild(0), scope);
                            } else {
                                throw new ProgramCompileException(toLocation(scope, parseTree), "Unknown expression type: " + parseTree.toStringTree());
                            }
                    }
                } else {
                    if (parseTree.getChild(0) instanceof Unary_expressionContext) {
                        return convertExpr(parseTree.getChild(0), scope);
                    } else if (parseTree.getChild(0) instanceof Multiplicative_expressionContext) {
                        return convertExpr(parseTree.getChild(0), scope);
                    } else {
                        throw new ProgramCompileException(toLocation(scope, parseTree), "Unknown expression type: " + parseTree.getText());
                    }
                }
            }
            case yqlplusParser.RULE_unary_expression: {
                if (1 == parseTree.getChildCount()) {
                    return convertExpr(parseTree.getChild(0), scope);
                } else if (2 == parseTree.getChildCount()) {
                    if ("-".equals(parseTree.getChild(0).getText())) {
                        return readUnOp(ExpressionOperator.NEGATE, parseTree, scope);
                    } else if ("!".equals(parseTree.getChild(0).getText())) {
                        return readUnOp(ExpressionOperator.NOT, parseTree, scope);
                    }
                    throw new ProgramCompileException(toLocation(scope, parseTree),"Unknown unary operator " + parseTree.getText());
                } else {
                    throw new ProgramCompileException(toLocation(scope, parseTree),"Unknown child count " + parseTree.getChildCount() + " of " + parseTree.getText());
                }
            }
            case yqlplusParser.RULE_fieldref: {
                // all in-scope data sources should be defined in scope
                // the 'first' field in a namespaced reference must be:
                // - a field name if (and only if) there is exactly one data source
                // in scope OR
                // - an alias name, which will be followed by a field name
                // ^(FIELDREF<FieldReference>[$expression::namespace]
                // namespaced_name)
                List<String> path = readName((Namespaced_nameContext) parseTree.getChild(0));
                Location loc = toLocation(scope, parseTree.getChild(0));
                String alias = path.get(0);
                OperatorNode<ExpressionOperator> result;
                int start;
                if (scope.isCursor(alias)) {
                    if (path.size() > 1) {
                        result = OperatorNode.create(loc, ExpressionOperator.READ_FIELD, alias, path.get(1));
                        start = 2;
                    } else {
                        result = OperatorNode.create(loc, ExpressionOperator.READ_RECORD, alias);
                        start = 1;
                    }
                } else if (scope.getCursors().size() == 1) {
                    alias = scope.getCursors().iterator().next();
                    result = OperatorNode.create(loc, ExpressionOperator.READ_FIELD, alias, path.get(0));
                    start = 1;
                } else {
                    throw new ProgramCompileException(loc, "Unknown field or alias '%s'", alias);
                }
                for (int idx = start; idx < path.size(); ++idx) {
                    result = OperatorNode.create(loc, ExpressionOperator.PROPREF, result, path.get(idx));
                }
                return result;
            }
            case yqlplusParser.RULE_scalar_literal:
                return OperatorNode.create(toLocation(scope, parseTree), ExpressionOperator.LITERAL, convertLiteral((Scalar_literalContext) parseTree));
            case yqlplusParser.RULE_constant_expression:
                return convertExpr(parseTree.getChild(0), scope);
            case yqlplusParser.RULE_literal_list:
                if (getParseTreeIndex(parseTree.getChild(1)) == yqlplusParser.RULE_array_parameter) {
                    return convertExpr(parseTree.getChild(1), scope);
                } else {
                    List<Literal_elementContext> elements = ((Literal_listContext) parseTree).literal_element();
                    List<OperatorNode<ExpressionOperator>> values = new ArrayList<>(elements.size());
                    for (Literal_elementContext child : elements) {
                        values.add(convertExpr(child.getChild(0), scope));
                    }
                    return OperatorNode.create(toLocation(scope, elements.get(0)),ExpressionOperator.ARRAY, values);
                }
        }
        throw new ProgramCompileException(toLocation(scope, parseTree),
                                          "Unknown expression type: " + parseTree.getText());
	}
Translate the ANTLR grammar into the logical representation.
container-search/src/main/java/com/yahoo/search/yql/ProgramParser.java