2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.xpath.impl;
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;
14 import com.google.common.base.VerifyException;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Iterators;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import java.util.ArrayDeque;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Deque;
25 import java.util.Iterator;
26 import java.util.LinkedHashSet;
27 import java.util.List;
29 import java.util.Optional;
31 import java.util.function.Function;
32 import javax.xml.xpath.XPathExpressionException;
33 import org.antlr.v4.runtime.BaseErrorListener;
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.RecognitionException;
38 import org.antlr.v4.runtime.Recognizer;
39 import org.antlr.v4.runtime.Token;
40 import org.antlr.v4.runtime.tree.ParseTree;
41 import org.antlr.v4.runtime.tree.TerminalNode;
42 import org.eclipse.jdt.annotation.Nullable;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.common.QNameModule;
45 import org.opendaylight.yangtools.yang.common.YangConstants;
46 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
47 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
48 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
49 import org.opendaylight.yangtools.yang.xpath.api.YangFilterExpr;
50 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
51 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
52 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
53 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
54 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
55 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
56 import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
57 import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
58 import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr;
59 import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
60 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
61 import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr;
62 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
63 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
64 import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType;
65 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
66 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.EqQuotedStringContext;
67 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.InstanceIdentifierContext;
68 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateContext;
69 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateExprContext;
70 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateContext;
71 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateExprContext;
72 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.NodeIdentifierContext;
73 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PathArgumentContext;
74 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosContext;
75 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.QuotedStringContext;
76 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbbreviatedStepContext;
77 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbsoluteLocationPathNorootContext;
78 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AdditiveExprContext;
79 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AndExprContext;
80 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AxisSpecifierContext;
81 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.EqualityExprContext;
82 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.ExprContext;
83 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FilterExprContext;
84 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionCallContext;
85 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionNameContext;
86 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.LocationPathContext;
87 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.MultiplicativeExprContext;
88 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NCNameContext;
89 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NameTestContext;
90 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NodeTestContext;
91 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.OrExprContext;
92 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PathExprNoRootContext;
93 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PredicateContext;
94 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PrimaryExprContext;
95 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.QNameContext;
96 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelationalExprContext;
97 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelativeLocationPathContext;
98 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.StepContext;
99 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnaryExprNoRootContext;
100 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnionExprNoRootContext;
101 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.VariableReferenceContext;
102 import org.slf4j.Logger;
103 import org.slf4j.LoggerFactory;
106 * ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
108 * @author Robert Varga
110 abstract class XPathParser<N extends YangNumberExpr<N, ?>> implements YangXPathParser {
111 private static final Logger LOG = LoggerFactory.getLogger(XPathParser.class);
112 private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
113 Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
114 private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
115 YangXPathNodeType.values()), YangXPathNodeType::toString);
116 private static final Map<String, YangXPathAxis> XPATH_AXES = Maps.uniqueIndex(Arrays.asList(YangXPathAxis.values()),
117 YangXPathAxis::toString);
118 private static final Map<QName, YangFunction> YANG_FUNCTIONS = Maps.uniqueIndex(Arrays.asList(
119 YangFunction.values()), YangFunction::getIdentifier);
121 // Cached for checks in hot path
122 private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
124 private final QNameSupport qnameSupport;
126 XPathParser(final QNameModule implicitNamespace, final Function<String, QNameModule> prefixes) {
127 qnameSupport = new QNameSupport(implicitNamespace, prefixes);
131 public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
132 // Create a parser and disconnect it from console error output
133 final xpathParser parser = new xpathParser(new CommonTokenStream(new xpathLexer(
134 CharStreams.fromString(xpath))));
135 parser.removeErrorListeners();
136 final List<XPathExpressionException> errors = new ArrayList<>();
137 parser.addErrorListener(new BaseErrorListener() {
139 public void syntaxError(final @Nullable Recognizer<?, ?> recognizer, final @Nullable Object offendingSymbol,
140 final int line, final int charPositionInLine, final @Nullable String msg,
141 final @Nullable RecognitionException cause) {
142 final XPathExpressionException ex = new XPathExpressionException(msg);
144 if (errors.isEmpty()) {
147 errors.get(0).addSuppressed(ex);
152 final YangExpr expr = parseExpr(parser.main().expr());
153 if (!errors.isEmpty()) {
157 return new AntlrYangXPathExpression(qnameSupport, expr, xpath);
161 * Parse and simplify an XPath expression in {@link ExprContext} representation.
163 * @param expr ANTLR ExprContext
164 * @return A {@link YangExpr}
165 * @throws NullPointerException if {@code expr} is null
166 * @throws IllegalArgumentException if {@code expr} references an unbound prefix
168 private YangExpr parseExpr(final ExprContext expr) {
169 final OrExprContext or = expr.orExpr();
170 final int size = or.getChildCount();
172 return parseAnd(getChild(or, AndExprContext.class, 0));
174 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
175 for (int i = 0; i < size; i += 2) {
176 tmp.add(parseAnd(getChild(or, AndExprContext.class, i)));
178 return YangNaryOperator.OR.exprWith(tmp);
182 * Create a {@link YangNumberExpr} backed by specified string.
184 * @param str String, matching {@link xpathParser#Number} production.
185 * @return number expression
186 * @throws NullPointerException if {@code str} is null
188 abstract N createNumber(String str);
191 * Create a {@link YangNumberExpr} representing the negated value of a number.
193 * @param number input number
194 * @return negated number expression
195 * @throws NullPointerException if {@code number} is null
197 abstract N negateNumber(N number);
199 private YangExpr parseAdditive(final AdditiveExprContext expr) {
200 final Iterator<ParseTree> it = expr.children.iterator();
201 final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
202 return it.hasNext() ? parseAdditiveExpr(first, it) : first;
205 private YangExpr parseAnd(final AndExprContext expr) {
206 final int size = expr.getChildCount();
208 return parseEquality(getChild(expr, EqualityExprContext.class, 0));
210 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
211 for (int i = 0; i < size; i += 2) {
212 tmp.add(parseEquality(getChild(expr, EqualityExprContext.class, i)));
214 return YangNaryOperator.AND.exprWith(tmp);
217 private YangExpr parseEquality(final EqualityExprContext expr) {
218 final Iterator<ParseTree> it = expr.children.iterator();
219 final YangExpr first = parseRelational(nextContext(it, RelationalExprContext.class));
220 return it.hasNext() ? parseEqualityExpr(first, it) : first;
223 private YangExpr parseFilter(final FilterExprContext expr) {
224 final Iterator<ParseTree> it = expr.children.iterator();
225 final YangExpr first = parsePrimary(nextContext(it, PrimaryExprContext.class));
226 return it.hasNext() ? YangFilterExpr.of(first, ImmutableList.copyOf(Iterators.transform(it,
227 tree -> parsePredicate(verifyTree(PredicateContext.class, tree)))))
231 private YangExpr parseFunctionCall(final FunctionCallContext expr) {
232 // We are mapping functions to RFC7950 YIN namespace, to keep us consistent with type/statement definitions
234 final FunctionNameContext name = getChild(expr, FunctionNameContext.class, 0);
236 switch (name.getChildCount()) {
238 parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
241 parsed = qnameSupport.createQName(name.getChild(0).getText(), name.getChild(2).getText());
244 throw illegalShape(name);
247 final List<YangExpr> args = ImmutableList.copyOf(Lists.transform(expr.expr(), this::parseExpr));
248 final YangFunction func = YANG_FUNCTIONS.get(parsed);
250 return Functions.functionToExpr(func, args);
253 checkArgument(!YangConstants.RFC6020_YIN_MODULE.equals(parsed.getModule()), "Unknown default function %s",
255 return YangFunctionCallExpr.of(parsed, args);
258 private YangLocationPath parseLocationPath(final LocationPathContext expr) {
259 verifyChildCount(expr, 1);
260 final ParseTree first = expr.getChild(0);
261 if (first instanceof RelativeLocationPathContext) {
262 return parseRelativeLocationPath((RelativeLocationPathContext) first);
265 final AbsoluteLocationPathNorootContext abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
266 verifyChildCount(abs, 2);
268 final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
269 switch (getTerminalType(abs, 0)) {
270 case xpathParser.PATHSEP:
272 case xpathParser.ABRPATH:
273 steps.addFirst(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
276 throw illegalShape(abs);
279 return YangLocationPath.of(true, steps);
282 private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
283 final ParseTree first = expr.getChild(0);
285 if (first instanceof UnaryExprNoRootContext) {
286 left = parseUnary((UnaryExprNoRootContext) first);
288 left = YangLocationPath.root();
290 if (expr.getChildCount() == 1) {
294 verifyChildCount(expr, 3);
295 final YangBinaryOperator operator = parseOperator(expr.getChild(1));
296 final YangExpr right = parseMultiplicative(getChild(expr, MultiplicativeExprContext.class, 2));
297 final Optional<YangExpr> simple = simplifyNumbers(operator, left, right);
298 return simple.isPresent() ? simple.get() : operator.exprWith(left, right);
301 private YangExpr parsePathExpr(final PathExprNoRootContext expr) {
302 final ParseTree first = expr.getChild(0);
303 if (first instanceof LocationPathContext) {
304 return parseLocationPath((LocationPathContext) first);
307 final YangExpr filter = parseFilter(verifyTree(FilterExprContext.class, first));
308 if (expr.getChildCount() == 1) {
312 verifyChildCount(expr, 3);
313 return parseOperator(expr.getChild(1)).exprWith(filter,
314 parseRelativeLocationPath(getChild(expr, RelativeLocationPathContext.class, 2)));
317 private YangExpr parsePredicate(final PredicateContext expr) {
318 verifyChildCount(expr, 3);
319 return parseExpr(getChild(expr, ExprContext.class, 1));
322 private YangExpr parsePrimary(final PrimaryExprContext expr) {
323 if (expr.getChildCount() == 3) {
324 return parseExpr(getChild(expr, ExprContext.class, 1));
327 verifyChildCount(expr, 1);
328 final ParseTree first = expr.getChild(0);
329 if (first instanceof TerminalNode) {
330 return parseTerminal((TerminalNode) first);
332 if (first instanceof FunctionCallContext) {
333 return parseFunctionCall((FunctionCallContext) first);
335 if (first instanceof VariableReferenceContext) {
336 return YangVariableReferenceExpr.of(parseQName(((VariableReferenceContext) first).qName()));
338 throw illegalShape(first);
341 private YangExpr parseRelational(final RelationalExprContext expr) {
342 final Iterator<ParseTree> it = expr.children.iterator();
343 final YangExpr first = parseAdditive(nextContext(it, AdditiveExprContext.class));
344 return it.hasNext() ? parseRelationalExpr(first, it) : first;
347 private Deque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
348 final Deque<Step> steps = new ArrayDeque<>(expr.getChildCount());
349 final Iterator<ParseTree> it = expr.children.iterator();
350 steps.add(parseStep(nextContext(it, StepContext.class)));
352 while (it.hasNext()) {
353 final ParseTree tree = it.next();
354 switch (verifyTerminal(tree).getSymbol().getType()) {
355 case xpathParser.PATHSEP:
357 case xpathParser.ABRPATH:
358 steps.add(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
361 throw illegalShape(tree);
364 // Parse step and add it if it's not SELF_STEP
365 final Step step = parseStep(nextContext(it, StepContext.class));
366 if (!SELF_STEP.equals(step)) {
374 private YangLocationPath parseRelativeLocationPath(final RelativeLocationPathContext expr) {
375 return YangLocationPath.of(false, parseLocationPathSteps(expr));
378 private YangExpr parseTerminal(final TerminalNode term) {
379 final String text = term.getText();
380 switch (term.getSymbol().getType()) {
381 case xpathParser.Literal:
382 // We have to strip quotes
383 return parseLiteral(text.substring(1, text.length() - 1));
384 case xpathParser.Number:
385 return createNumber(text);
387 throw illegalShape(term);
391 private YangLiteralExpr parseLiteral(final String text) {
392 if (text.isEmpty()) {
393 return YangLiteralExpr.empty();
395 if (text.charAt(0) == '/') {
396 return parseLocationLiteral(text);
398 return parseQNameLiteral(text);
401 private YangLiteralExpr parseLocationLiteral(final String text) {
402 final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(new xpathLexer(
403 CharStreams.fromString(text))));
404 parser.removeErrorListeners();
406 final InstanceIdentifierContext id = parser.instanceIdentifier();
407 final int length = id.getChildCount();
408 final List<Step> steps = new ArrayList<>(length / 2);
409 for (int i = 1; i < length; i += 2) {
410 steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i)));
413 return new InstanceIdentifierLiteralExpr(text, steps);
416 private Step parsePathArgument(final PathArgumentContext expr) {
417 final QName qname = parseInstanceIdentifierQName(getChild(expr, NodeIdentifierContext.class, 0));
418 switch (expr.getChildCount()) {
420 return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of());
422 return YangXPathAxis.CHILD.asStep(qname,
423 parsePathArgumentPredicate(getChild(expr, instanceIdentifierParser.PredicateContext.class, 1)));
425 throw illegalShape(expr);
429 private Collection<YangExpr> parsePathArgumentPredicate(final instanceIdentifierParser.PredicateContext expr) {
430 final ParseTree first = expr.getChild(0);
431 if (first instanceof LeafListPredicateContext) {
432 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(),
433 parseEqStringValue(getChild(((LeafListPredicateContext) first)
434 .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1))));
435 } else if (first instanceof PosContext) {
436 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(Functions.POSITION,
437 createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0)
441 final int length = expr.getChildCount();
442 final List<YangExpr> ret = new ArrayList<>(length);
443 for (int i = 0; i < length; ++i) {
444 final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
445 .getChild(KeyPredicateExprContext.class, 0);
446 ret.add(YangBinaryOperator.EQUALS.exprWith(
447 YangQNameExpr.of(parseInstanceIdentifierQName(getChild(pred, NodeIdentifierContext.class, 0))),
448 parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
455 private YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
456 return parseLiteral(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1,
457 instanceIdentifierParser.STRING).getText());
460 private QName parseInstanceIdentifierQName(final NodeIdentifierContext expr) {
461 return qnameSupport.createQName(verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(),
462 verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText());
465 private YangLiteralExpr parseQNameLiteral(final String text) {
466 final int firstColon = text.indexOf(':');
467 if (firstColon != -1) {
468 // If we have two colons this node cannot be interpreted as a QName -- this may explode at evaluation-time,
469 // but that's fine as it will just result in evaluation error. Users do have unit tests, right?
470 final int secondColon = text.indexOf(':', firstColon + 1);
471 if (secondColon == -1) {
472 final Optional<QNameModule> optNamespace = qnameSupport.resolvePrefix(text.substring(0, firstColon));
473 // If we cannot resolve the namespace at evaluation-time has to deal with it.
474 if (optNamespace.isPresent()) {
476 return new QNameLiteralExpr(text, QName.create(optNamespace.get(),
477 text.substring(firstColon + 1)));
478 } catch (IllegalArgumentException e) {
479 LOG.trace("Cannot interpret {} as a QName", text, e);
480 return YangLiteralExpr.of(text);
485 return YangLiteralExpr.of(text);
488 private YangExpr parseUnary(final UnaryExprNoRootContext expr) {
489 // any number of '-' and an union expr
490 final int size = verifyAtLeastChildren(expr, 1);
491 final YangExpr ret = parseUnion(getChild(expr, UnionExprNoRootContext.class, size - 1));
493 // Even number of '-' tokens cancel out
497 return ret instanceof YangNumberExpr ? negateNumber((N) ret) : YangNegateExpr.of(ret);
500 private YangExpr parseUnion(final UnionExprNoRootContext expr) {
501 final ParseTree first = expr.getChild(0);
503 if (first instanceof PathExprNoRootContext) {
504 path = parsePathExpr((PathExprNoRootContext) first);
505 if (expr.getChildCount() == 1) {
509 path = YangLocationPath.root();
512 verifyChildCount(expr, 3);
513 final YangExpr union = parseUnion(getChild(expr, UnionExprNoRootContext.class, 2));
515 // Deduplicate expressions so we do not perform useless unioning
516 final Set<YangExpr> expressions = new LinkedHashSet<>();
517 expressions.add(path);
518 if (union instanceof YangNaryExpr) {
519 // If the result is a union expression, integrate it into this expression
520 final YangNaryExpr nary = (YangNaryExpr) union;
521 if (nary.getOperator() == YangNaryOperator.UNION) {
522 expressions.addAll(nary.getExpressions());
524 expressions.add(union);
527 expressions.add(union);
530 return YangNaryOperator.UNION.exprWith(expressions);
533 private YangExpr parseAdditiveExpr(final YangExpr left, final Iterator<ParseTree> it) {
536 final YangBinaryOperator operator = nextOperator(it);
537 final YangExpr right = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
538 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
539 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
540 } while (it.hasNext());
545 private Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final YangExpr left,
546 final YangExpr right) {
547 if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) {
548 // Constant folding on numbers -- precision plays a role here
549 return simplifyNumbers(operator, (N) left, (N) right);
551 return Optional.empty();
554 Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final N left, final N right) {
557 return Optional.of(YangBooleanConstantExpr.of(left.getNumber().equals(right.getNumber())));
559 return Optional.of(YangBooleanConstantExpr.of(!left.getNumber().equals(right.getNumber())));
561 return Optional.empty();
565 private YangExpr parseEqualityExpr(final YangExpr left, final Iterator<ParseTree> it) {
568 final YangBinaryOperator operator = nextOperator(it);
569 final YangExpr right = parseRelational(nextContext(it, RelationalExprContext.class));
571 if (left.equals(right)) {
572 // Constant folding on expression level: equal expressions are result in equal results
575 return YangBooleanConstantExpr.TRUE;
577 return YangBooleanConstantExpr.FALSE;
583 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
584 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
585 } while (it.hasNext());
590 private YangExpr parseRelationalExpr(final YangExpr left, final Iterator<ParseTree> it) {
593 final YangBinaryOperator operator = nextOperator(it);
594 final YangExpr right = parseAdditive(nextContext(it, AdditiveExprContext.class));
595 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
596 ret = simple.isPresent() ? simple.get() : nextOperator(it).exprWith(ret, right);
597 } while (it.hasNext());
602 private QName parseQName(final QNameContext expr) {
603 switch (expr.getChildCount()) {
605 return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText());
607 return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText(),
608 getChild(expr, NCNameContext.class, 2).getText());
610 throw illegalShape(expr);
614 private Step parseStep(final StepContext expr) {
615 if (expr.getChildCount() == 1) {
616 final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
617 verifyChildCount(abbrev, 1);
618 switch (getTerminalType(abbrev, 0)) {
619 case xpathParser.DOT:
620 return YangXPathAxis.SELF.asStep();
621 case xpathParser.DOTDOT:
622 return YangXPathAxis.PARENT.asStep();
624 throw illegalShape(abbrev);
628 final int size = verifyAtLeastChildren(expr, 2);
629 final List<YangExpr> predicates = new ArrayList<>(size - 2);
630 for (int i = 2; i < size; ++i) {
631 predicates.add(parsePredicate(getChild(expr, PredicateContext.class, i)));
634 final YangXPathAxis axis = parseAxis(getChild(expr, AxisSpecifierContext.class, 0));
635 final NodeTestContext nodeTest = getChild(expr, NodeTestContext.class, 1);
636 switch (nodeTest.getChildCount()) {
638 final NameTestContext nameChild = getChild(nodeTest, NameTestContext.class, 0);
639 final ParseTree first = nameChild.getChild(0);
640 if (first instanceof TerminalNode) {
641 verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
642 return axis.asStep(predicates);
644 return axis.asStep(parseQName(verifyTree(QNameContext.class, first)), predicates);
646 return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
648 final String text = verifyToken(nodeTest, 2, xpathParser.Literal).getText();
649 return axis.asStep(text.substring(1, text.length() - 1), predicates);
651 throw illegalShape(nodeTest);
655 private static YangXPathAxis parseAxis(final AxisSpecifierContext expr) {
656 switch (expr.getChildCount()) {
658 return YangXPathAxis.CHILD;
660 verify(getTerminalType(expr, 0) == xpathParser.AT, "Unhandled axis specifier shape %s", expr);
661 return YangXPathAxis.ATTRIBUTE;
663 final String str = verifyTerminal(expr.getChild(0)).getText();
664 return verifyNotNull(XPATH_AXES.get(str), "Unhandled axis %s", str);
666 throw illegalShape(expr);
670 private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
671 return verifyTree(type, it.next());
674 private static YangBinaryOperator nextOperator(final Iterator<ParseTree> it) {
675 return parseOperator(it.next());
678 private static <T extends ParseTree> T getChild(final ParseTree parent, final Class<T> type, final int offset) {
679 return verifyTree(type, parent.getChild(offset));
682 private static Token verifyToken(final ParseTree parent, final int offset, final int expected) {
683 final TerminalNode node = verifyTerminal(parent.getChild(offset));
684 final Token ret = node.getSymbol();
685 final int type = ret.getType();
686 verify(type == expected, "Item %s has type %s, expected %s", node, type, expected);
690 private static int getTerminalType(final ParseTree parent, final int offset) {
691 return verifyTerminal(parent.getChild(offset)).getSymbol().getType();
694 private static YangXPathNodeType parseNodeType(final ParseTree tree) {
695 final String str = verifyTerminal(tree).getText();
696 return verifyNotNull(NODE_TYPES.get(str), "Unhandled node type %s", str);
699 private static YangBinaryOperator parseOperator(final ParseTree tree) {
700 final String str = verifyTerminal(tree).getText();
701 return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
704 private static void verifyChildCount(final ParseTree tree, final int expected) {
705 if (tree.getChildCount() != expected) {
706 throw illegalShape(tree);
710 private static int verifyAtLeastChildren(final ParseTree tree, final int expected) {
711 final int count = tree.getChildCount();
712 if (count < expected) {
713 throw illegalShape(tree);
718 private static TerminalNode verifyTerminal(final ParseTree tree) {
719 if (tree instanceof TerminalNode) {
720 return (TerminalNode) tree;
722 throw new VerifyException(String.format("'%s' is not a terminal node", tree.getText()));
725 private static <T extends ParseTree> T verifyTree(final Class<T> type, final ParseTree tree) {
726 if (type.isInstance(tree)) {
727 return type.cast(tree);
729 throw new VerifyException(String.format("'%s' does not have expected type %s", tree.getText(), type));
732 private static VerifyException illegalShape(final ParseTree tree) {
733 return new VerifyException(String.format("Invalid parser shape of '%s'", tree.getText()));