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