Deprecate simple DataTreeFactory.create()
[yangtools.git] / xpath / yang-xpath-impl / src / main / java / org / opendaylight / yangtools / yang / xpath / impl / AntlrXPathParser.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.xpath.impl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static com.google.common.base.Verify.verifyNotNull;
13 import static java.util.Objects.requireNonNull;
14 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild;
15 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape;
16 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyAtLeastChildren;
17 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyChildCount;
18 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTerminal;
19 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken;
20 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTree;
21
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.Iterators;
24 import com.google.common.collect.Maps;
25 import java.util.ArrayDeque;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Iterator;
29 import java.util.LinkedHashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import javax.xml.xpath.XPathExpressionException;
34 import org.antlr.v4.runtime.CharStreams;
35 import org.antlr.v4.runtime.CommonTokenStream;
36 import org.antlr.v4.runtime.ParserRuleContext;
37 import org.antlr.v4.runtime.tree.ParseTree;
38 import org.antlr.v4.runtime.tree.TerminalNode;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.common.QNameModule;
41 import org.opendaylight.yangtools.yang.common.UnresolvedQName;
42 import org.opendaylight.yangtools.yang.common.YangConstants;
43 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
44 import org.opendaylight.yangtools.yang.common.YangVersion;
45 import org.opendaylight.yangtools.yang.xpath.antlr.xpathLexer;
46 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser;
47 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbbreviatedStepContext;
48 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbsoluteLocationPathNorootContext;
49 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AdditiveExprContext;
50 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AndExprContext;
51 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AxisSpecifierContext;
52 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.EqualityExprContext;
53 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.ExprContext;
54 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FilterExprContext;
55 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionCallContext;
56 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionNameContext;
57 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.LocationPathContext;
58 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.MultiplicativeExprContext;
59 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NCNameContext;
60 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NameTestContext;
61 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NodeTestContext;
62 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PathExprNoRootContext;
63 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PredicateContext;
64 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PrimaryExprContext;
65 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.QNameContext;
66 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelationalExprContext;
67 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelativeLocationPathContext;
68 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.StepContext;
69 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnaryExprNoRootContext;
70 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnionExprNoRootContext;
71 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.VariableReferenceContext;
72 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
73 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
74 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
75 import org.opendaylight.yangtools.yang.xpath.api.YangFilterExpr;
76 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
77 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
78 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
79 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
80 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
81 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
82 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
83 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
84 import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
85 import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
86 import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr;
87 import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
88 import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
89 import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr;
90 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
91 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
92 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
93 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
94 import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType;
95 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
96
97 /**
98  * ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
99  *
100  * @author Robert Varga
101  */
102 abstract class AntlrXPathParser implements YangXPathParser {
103     static class Base extends AntlrXPathParser {
104         Base(final YangXPathMathMode mathMode) {
105             super(mathMode);
106         }
107
108         @Override
109         public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
110             final var result = parseExpr(xpath);
111             return new AntlrYangXPathExpression.Base(mathMode, result.minimumYangVersion, result.expression, xpath);
112         }
113
114         @Override
115         QNameStep createStep(final YangXPathAxis axis, final String localName, final List<YangExpr> predicates) {
116             return axis.asStep(UnresolvedQName.Unqualified.of(localName).intern(), predicates);
117         }
118
119         @Override
120         QNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
121                 final List<YangExpr> predicates) {
122             return axis.asStep(UnresolvedQName.Qualified.of(prefix, localName).intern(), predicates);
123         }
124
125         @Override
126         QName createQName(final String localName) {
127             throw new UnsupportedOperationException();
128         }
129
130         @Override
131         QName createQName(final String prefix, final String localName) {
132             throw new UnsupportedOperationException();
133         }
134     }
135
136     static class Qualified extends Base implements QualifiedBound {
137         final YangNamespaceContext namespaceContext;
138
139         Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
140             super(mathMode);
141             this.namespaceContext = requireNonNull(namespaceContext);
142         }
143
144         @Override
145         public YangXPathExpression.QualifiedBound parseExpression(final String xpath) throws XPathExpressionException {
146             final var result = parseExpr(xpath);
147             return new AntlrYangXPathExpression.Qualified(mathMode, result.minimumYangVersion, result.expression, xpath,
148                 result.haveLiteral ? namespaceContext : null);
149         }
150
151         @Override
152         final QName createQName(final String prefix, final String localName) {
153             return namespaceContext.createQName(prefix, localName);
154         }
155
156         @Override
157         ResolvedQNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
158                 final List<YangExpr> predicates) {
159             return axis.asStep(createQName(prefix, localName), predicates);
160         }
161     }
162
163     static final class Unqualified extends Qualified implements UnqualifiedBound {
164         private final QNameModule defaultNamespace;
165
166         Unqualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
167                 final QNameModule defaultNamespace) {
168             super(mathMode, namespaceContext);
169             this.defaultNamespace = requireNonNull(defaultNamespace);
170         }
171
172         @Override
173         public YangXPathExpression.UnqualifiedBound parseExpression(final String xpath)
174                 throws XPathExpressionException {
175             final var result = parseExpr(xpath);
176             return new AntlrYangXPathExpression.Unqualified(mathMode, result.minimumYangVersion, result.expression,
177                 xpath, result.haveLiteral ? namespaceContext : null, defaultNamespace);
178         }
179
180         @Override
181         QName createQName(final String localName) {
182             return QName.create(defaultNamespace, localName);
183         }
184
185         @Override
186         ResolvedQNameStep createStep(final YangXPathAxis axis, final String localName,
187                 final List<YangExpr> predicates) {
188             return axis.asStep(QName.create(defaultNamespace, localName), predicates);
189         }
190     }
191
192     private static final class ParseExprResult {
193         final YangVersion minimumYangVersion;
194         final YangExpr expression;
195         final boolean haveLiteral;
196
197         ParseExprResult(final YangVersion minimumYangVersion, final YangExpr expression, final boolean haveLiteral) {
198             this.minimumYangVersion = requireNonNull(minimumYangVersion);
199             this.expression = requireNonNull(expression);
200             this.haveLiteral = haveLiteral;
201         }
202     }
203
204     private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
205         Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
206     private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
207         YangXPathNodeType.values()), YangXPathNodeType::toString);
208     private static final Map<String, YangXPathAxis> XPATH_AXES = Maps.uniqueIndex(Arrays.asList(YangXPathAxis.values()),
209         YangXPathAxis::toString);
210     private static final Map<QName, YangFunction> YANG_FUNCTIONS = Maps.uniqueIndex(Arrays.asList(
211         YangFunction.values()), YangFunction::getIdentifier);
212
213     // Cached for checks in hot path
214     private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
215
216     final YangXPathMathMode mathMode;
217     private final YangXPathMathSupport mathSupport;
218     private final FunctionSupport functionSupport;
219
220     private YangVersion minimumYangVersion = YangVersion.VERSION_1;
221     private boolean haveLiteral = false;
222
223     AntlrXPathParser(final YangXPathMathMode mathMode) {
224         this.mathMode = requireNonNull(mathMode);
225         mathSupport = mathMode.getSupport();
226         functionSupport = new FunctionSupport(mathSupport);
227     }
228
229     abstract QName createQName(String localName);
230
231     abstract QName createQName(String prefix, String localName);
232
233     abstract QNameStep createStep(YangXPathAxis axis, String localName, List<YangExpr> predicates);
234
235     abstract QNameStep createStep(YangXPathAxis axis, String prefix, String localName, List<YangExpr> predicates);
236
237     private QNameStep createStep(final YangXPathAxis axis, final QNameContext expr, final List<YangExpr> predicates) {
238         return switch (expr.getChildCount()) {
239             case 1 -> createStep(axis, getChild(expr, NCNameContext.class, 0).getText(), predicates);
240             case 3 -> createStep(axis, getChild(expr, NCNameContext.class, 0).getText(),
241                     getChild(expr, NCNameContext.class, 2).getText(), predicates);
242             default -> throw illegalShape(expr);
243         };
244     }
245
246     @SuppressWarnings("checkstyle:illegalCatch")
247     final ParseExprResult parseExpr(final String xpath) throws XPathExpressionException {
248         // Create a parser and disconnect it from console error output
249         final var lexer = new xpathLexer(CharStreams.fromString(xpath));
250         final var parser = new xpathParser(new CommonTokenStream(lexer));
251
252         final var listener = new CapturingErrorListener();
253         lexer.removeErrorListeners();
254         lexer.addErrorListener(listener);
255         parser.removeErrorListeners();
256         parser.addErrorListener(listener);
257         final var antlr = parser.main().expr();
258         listener.reportError();
259
260         // Reset our internal context
261         minimumYangVersion = YangVersion.VERSION_1;
262         haveLiteral = false;
263
264         final YangExpr expr;
265         try {
266             expr = parseExpr(antlr);
267         } catch (RuntimeException e) {
268             throw new XPathExpressionException(e);
269         }
270         return new ParseExprResult(minimumYangVersion, expr, haveLiteral);
271     }
272
273     /**
274      * Parse and simplify an XPath expression in {@link ExprContext} representation.
275      *
276      * @param ctx Current parsing context
277      * @param expr ANTLR ExprContext
278      * @return A {@link YangExpr}
279      * @throws NullPointerException if {@code expr} is null
280      * @throws IllegalArgumentException if {@code expr} references an unbound prefix
281      */
282     private YangExpr parseExpr(final ExprContext expr) {
283         final var or = expr.orExpr();
284         final int size = or.getChildCount();
285         if (size == 1) {
286             return parseAnd(getChild(or, AndExprContext.class, 0));
287         }
288         final var tmp = new ArrayList<YangExpr>((size + 1) / 2);
289         for (int i = 0; i < size; i += 2) {
290             tmp.add(parseAnd(getChild(or, AndExprContext.class, i)));
291         }
292         return YangNaryOperator.OR.exprWith(tmp);
293     }
294
295     private YangExpr parseAdditive(final AdditiveExprContext expr) {
296         final var it = expr.children.iterator();
297         final var first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
298         return it.hasNext() ? parseAdditiveExpr(first, it) : first;
299     }
300
301     private YangExpr parseAnd(final AndExprContext expr) {
302         final int size = expr.getChildCount();
303         if (size == 1) {
304             return parseEquality(getChild(expr, EqualityExprContext.class, 0));
305         }
306         final var tmp = new ArrayList<YangExpr>((size + 1) / 2);
307         for (int i = 0; i < size; i += 2) {
308             tmp.add(parseEquality(getChild(expr, EqualityExprContext.class, i)));
309         }
310         return YangNaryOperator.AND.exprWith(tmp);
311     }
312
313     private YangExpr parseEquality(final EqualityExprContext expr) {
314         final var it = expr.children.iterator();
315         final var first = parseRelational(nextContext(it, RelationalExprContext.class));
316         return it.hasNext() ? parseEqualityExpr(first, it) : first;
317     }
318
319     private YangExpr parseFilter(final FilterExprContext expr) {
320         final var it = expr.children.iterator();
321         final var first = parsePrimary(nextContext(it, PrimaryExprContext.class));
322         return it.hasNext() ? YangFilterExpr.of(first, ImmutableList.copyOf(Iterators.transform(it,
323             tree ->  parsePredicate(verifyTree(PredicateContext.class, tree)))))
324                 : first;
325     }
326
327     private YangExpr parseFunctionCall(final FunctionCallContext expr) {
328         // We are mapping functions to RFC7950 YIN namespace, to keep us consistent with type/statement definitions
329
330         final var name = getChild(expr, FunctionNameContext.class, 0);
331         final var parsed = switch (name.getChildCount()) {
332             case 1 -> QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
333             case 3 -> createQName(name.getChild(0).getText(), name.getChild(2).getText());
334             default -> throw illegalShape(name);
335         };
336         final var args = expr.expr().stream().map(this::parseExpr).collect(ImmutableList.toImmutableList());
337         final var func = YANG_FUNCTIONS.get(parsed);
338         if (func != null) {
339             if (minimumYangVersion.compareTo(func.getYangVersion()) < 0) {
340                 minimumYangVersion = func.getYangVersion();
341             }
342
343             final var funcExpr = functionSupport.functionToExpr(func, args);
344             if (funcExpr instanceof YangLiteralExpr) {
345                 haveLiteral = true;
346             }
347             return funcExpr;
348         }
349
350         checkArgument(!YangConstants.RFC6020_YIN_MODULE.equals(parsed.getModule()), "Unknown default function %s",
351             parsed);
352         return YangFunctionCallExpr.of(parsed, args);
353     }
354
355     private YangLocationPath parseLocationPath(final LocationPathContext expr) {
356         verifyChildCount(expr, 1);
357         final var first = expr.getChild(0);
358         if (first instanceof RelativeLocationPathContext relativeLocation) {
359             return YangLocationPath.relative(parseLocationPathSteps(relativeLocation));
360         }
361
362         final var abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
363         verifyChildCount(abs, 2);
364
365         final var steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
366         parseStepShorthand(abs.getChild(0)).ifPresent(steps::addFirst);
367
368         return YangLocationPath.absolute(steps);
369     }
370
371     private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
372         final var first = expr.getChild(0);
373         final var left = first instanceof UnaryExprNoRootContext unary ? parseUnary(unary) : YangLocationPath.root();
374         if (expr.getChildCount() == 1) {
375             return left;
376         }
377
378         verifyChildCount(expr, 3);
379         final var operator = parseOperator(expr.getChild(1));
380         final var right = parseMultiplicative(getChild(expr, MultiplicativeExprContext.class, 2));
381         final var simple = simplifyNumbers(operator, left, right);
382         return simple.isPresent() ? simple.orElseThrow() : operator.exprWith(left, right);
383     }
384
385     private YangExpr parsePathExpr(final PathExprNoRootContext expr) {
386         final var first = expr.getChild(0);
387         if (first instanceof LocationPathContext location) {
388             return parseLocationPath(location);
389         }
390
391         final var filter = parseFilter(verifyTree(FilterExprContext.class, first));
392         if (expr.getChildCount() == 1) {
393             return filter;
394         }
395
396         verifyChildCount(expr, 3);
397         final var steps = parseLocationPathSteps(getChild(expr, RelativeLocationPathContext.class, 2));
398         parseStepShorthand(expr.getChild(1)).ifPresent(steps::addFirst);
399         return YangPathExpr.of(filter, YangLocationPath.relative(steps));
400     }
401
402     private YangExpr parsePredicate(final PredicateContext expr) {
403         verifyChildCount(expr, 3);
404         return parseExpr(getChild(expr, ExprContext.class, 1));
405     }
406
407     private YangExpr parsePrimary(final PrimaryExprContext expr) {
408         if (expr.getChildCount() == 3) {
409             return parseExpr(getChild(expr, ExprContext.class, 1));
410         }
411
412         verifyChildCount(expr, 1);
413         final var first = expr.getChild(0);
414         return switch (first) {
415             case TerminalNode terminal -> parseTerminal(terminal);
416             case FunctionCallContext function -> parseFunctionCall(function);
417             case VariableReferenceContext variable -> YangVariableReferenceExpr.of(parseQName(variable.qName()));
418             default -> throw illegalShape(first);
419         };
420     }
421
422     private YangExpr parseRelational(final RelationalExprContext expr) {
423         final var it = expr.children.iterator();
424         final var first = parseAdditive(nextContext(it, AdditiveExprContext.class));
425         return it.hasNext() ? parseRelationalExpr(first, it) : first;
426     }
427
428     private ArrayDeque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
429         final var steps = new ArrayDeque<Step>(expr.getChildCount());
430         final var it = expr.children.iterator();
431         addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
432
433         while (it.hasNext()) {
434             parseStepShorthand(it.next()).ifPresent(steps::add);
435
436             // Parse step and add it if it's not SELF_STEP
437             addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
438         }
439
440         return steps;
441     }
442
443     private static void addNotSelfStep(final ArrayDeque<Step> steps, final Step step) {
444         if (!SELF_STEP.equals(step)) {
445             steps.add(step);
446         }
447     }
448
449     private YangExpr parseTerminal(final TerminalNode term) {
450         final var text = term.getText();
451         return switch (term.getSymbol().getType()) {
452             case xpathParser.Literal -> {
453                 // We have to strip quotes
454                 haveLiteral = true;
455                 yield YangLiteralExpr.of(text.substring(1, text.length() - 1));
456             }
457             case xpathParser.Number -> mathSupport.createNumber(text);
458             default -> throw illegalShape(term);
459         };
460     }
461
462     private YangExpr parseUnary(final UnaryExprNoRootContext expr) {
463         // any number of '-' and an union expr
464         final int size = verifyAtLeastChildren(expr, 1);
465         final var ret = parseUnion(getChild(expr, UnionExprNoRootContext.class, size - 1));
466         if (size % 2 != 0) {
467             // Even number of '-' tokens cancel out
468             return ret;
469         }
470         return ret instanceof YangNumberExpr number ? mathSupport.negateNumber(number) : YangNegateExpr.of(ret);
471     }
472
473     private YangExpr parseUnion(final UnionExprNoRootContext expr) {
474         final var first = expr.getChild(0);
475         final YangExpr path;
476         if (first instanceof PathExprNoRootContext noRoot) {
477             path = parsePathExpr(noRoot);
478             if (expr.getChildCount() == 1) {
479                 return path;
480             }
481         } else {
482             path = YangLocationPath.root();
483         }
484
485         verifyChildCount(expr, 3);
486         final var union = parseUnion(getChild(expr, UnionExprNoRootContext.class, 2));
487
488         // Deduplicate expressions so we do not perform useless unioning
489         final var expressions = new LinkedHashSet<YangExpr>();
490         expressions.add(path);
491         if (union instanceof YangNaryExpr nary && nary.getOperator() == YangNaryOperator.UNION) {
492             expressions.addAll(nary.getExpressions());
493         } else {
494             expressions.add(union);
495         }
496
497         return YangNaryOperator.UNION.exprWith(expressions);
498     }
499
500     private YangExpr parseAdditiveExpr(final YangExpr left, final Iterator<ParseTree> it) {
501         var ret = left;
502         do {
503             final var operator = nextOperator(it);
504             final var right = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
505             final var simple = simplifyNumbers(operator, ret, right);
506             ret = simple.isPresent() ? simple.orElseThrow() : operator.exprWith(ret, right);
507         } while (it.hasNext());
508
509         return ret;
510     }
511
512     private Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final YangExpr left,
513             final YangExpr right) {
514         if (left instanceof YangNumberExpr leftNumber && right instanceof YangNumberExpr rightNumber) {
515             // Constant folding on numbers -- precision plays a role here
516             return mathSupport.tryEvaluate(operator, leftNumber, rightNumber);
517         }
518         return Optional.empty();
519     }
520
521     private YangExpr parseEqualityExpr(final YangExpr left, final Iterator<ParseTree> it) {
522         var ret = left;
523         do {
524             final var operator = nextOperator(it);
525             final var right = parseRelational(nextContext(it, RelationalExprContext.class));
526
527             if (left.equals(right)) {
528                 // Constant folding on expression level: equal expressions are result in equal results
529                 switch (operator) {
530                     case EQUALS:
531                         return YangBooleanConstantExpr.TRUE;
532                     case NOT_EQUALS:
533                         return YangBooleanConstantExpr.FALSE;
534                     default:
535                         break;
536                 }
537             }
538
539             final var simple = simplifyNumbers(operator, ret, right);
540             ret = simple.isPresent() ? simple.orElseThrow() : operator.exprWith(ret, right);
541         } while (it.hasNext());
542
543         return ret;
544     }
545
546     private YangExpr parseRelationalExpr(final YangExpr left, final Iterator<ParseTree> it) {
547         var ret = left;
548         do {
549             final var operator = nextOperator(it);
550             final var right = parseAdditive(nextContext(it, AdditiveExprContext.class));
551             final var simple = simplifyNumbers(operator, ret, right);
552             ret = simple.isPresent() ? simple.orElseThrow() : operator.exprWith(ret, right);
553         } while (it.hasNext());
554
555         return ret;
556     }
557
558     private Step parseStep(final StepContext expr) {
559         if (expr.getChildCount() == 1) {
560             final var abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
561             verifyChildCount(abbrev, 1);
562             return switch (getTerminalType(abbrev, 0)) {
563                 case xpathParser.DOT -> YangXPathAxis.SELF.asStep();
564                 case xpathParser.DOTDOT -> YangXPathAxis.PARENT.asStep();
565                 default -> throw illegalShape(abbrev);
566             };
567         }
568
569         final int size = verifyAtLeastChildren(expr, 2);
570         final var predicates = new ArrayList<YangExpr>(size - 2);
571         for (int i = 2; i < size; ++i) {
572             predicates.add(parsePredicate(getChild(expr, PredicateContext.class, i)));
573         }
574
575         final var axis = parseAxis(getChild(expr, AxisSpecifierContext.class, 0));
576         final var nodeTest = getChild(expr, NodeTestContext.class, 1);
577         return switch (nodeTest.getChildCount()) {
578             case 1 -> {
579                 final var nameChild = getChild(nodeTest, NameTestContext.class, 0);
580                 final var first = nameChild.getChild(0);
581                 if (first instanceof TerminalNode terminal) {
582                     verify(terminal.getSymbol().getType() == xpathParser.MUL);
583                     yield axis.asStep(predicates);
584                 }
585                 yield createStep(axis, verifyTree(QNameContext.class, first), predicates);
586             }
587             case 3 -> axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
588             case 4 -> {
589                 final var text = verifyToken(nodeTest, 2, xpathParser.Literal).getText();
590                 yield axis.asStep(text.substring(1, text.length() - 1), predicates);
591             }
592             default -> throw illegalShape(nodeTest);
593         };
594     }
595
596     private static YangXPathAxis parseAxis(final AxisSpecifierContext expr) {
597         return switch (expr.getChildCount()) {
598             case 0 -> YangXPathAxis.CHILD;
599             case 1 -> {
600                 verify(getTerminalType(expr, 0) == xpathParser.AT, "Unhandled axis specifier shape %s", expr);
601                 yield YangXPathAxis.ATTRIBUTE;
602             }
603             case 2 -> {
604                 final var str = verifyTerminal(expr.getChild(0)).getText();
605                 yield verifyNotNull(XPATH_AXES.get(str), "Unhandled axis %s", str);
606             }
607             default -> throw illegalShape(expr);
608         };
609     }
610
611     private QName parseQName(final QNameContext expr) {
612         return switch (expr.getChildCount()) {
613             case 1 -> createQName(getChild(expr, NCNameContext.class, 0).getText());
614             case 3 -> createQName(getChild(expr, NCNameContext.class, 0).getText(),
615                     getChild(expr, NCNameContext.class, 2).getText());
616             default -> throw illegalShape(expr);
617         };
618     }
619
620     private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
621         return verifyTree(type, it.next());
622     }
623
624     private static YangBinaryOperator nextOperator(final Iterator<ParseTree> it) {
625         return parseOperator(it.next());
626     }
627
628     private static int getTerminalType(final ParseTree parent, final int offset) {
629         return verifyTerminal(parent.getChild(offset)).getSymbol().getType();
630     }
631
632     private static YangXPathNodeType parseNodeType(final ParseTree tree) {
633         final var str = verifyTerminal(tree).getText();
634         return verifyNotNull(NODE_TYPES.get(str), "Unhandled node type %s", str);
635     }
636
637     private static YangBinaryOperator parseOperator(final ParseTree tree) {
638         final var str = verifyTerminal(tree).getText();
639         return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
640     }
641
642     private static Optional<Step> parseStepShorthand(final ParseTree tree) {
643         return switch (verifyTerminal(tree).getSymbol().getType()) {
644             case xpathParser.PATHSEP -> Optional.empty();
645             case xpathParser.ABRPATH -> Optional.of(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
646             default -> throw illegalShape(tree);
647         };
648     }
649 }