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 xpathLexer lexer = new xpathLexer(CharStreams.fromString(xpath));
134 final xpathParser parser = new xpathParser(new CommonTokenStream(lexer));
136 final List<XPathExpressionException> errors = new ArrayList<>();
137 final BaseErrorListener listener = 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 lexer.removeErrorListeners();
153 lexer.addErrorListener(listener);
154 parser.removeErrorListeners();
155 parser.addErrorListener(listener);
157 final YangExpr expr = parseExpr(parser.main().expr());
158 if (!errors.isEmpty()) {
162 return new AntlrYangXPathExpression(qnameSupport, expr, xpath);
166 * Parse and simplify an XPath expression in {@link ExprContext} representation.
168 * @param expr ANTLR ExprContext
169 * @return A {@link YangExpr}
170 * @throws NullPointerException if {@code expr} is null
171 * @throws IllegalArgumentException if {@code expr} references an unbound prefix
173 private YangExpr parseExpr(final ExprContext expr) {
174 final OrExprContext or = expr.orExpr();
175 final int size = or.getChildCount();
177 return parseAnd(getChild(or, AndExprContext.class, 0));
179 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
180 for (int i = 0; i < size; i += 2) {
181 tmp.add(parseAnd(getChild(or, AndExprContext.class, i)));
183 return YangNaryOperator.OR.exprWith(tmp);
187 * Create a {@link YangNumberExpr} backed by specified string.
189 * @param str String, matching {@link xpathParser#Number} production.
190 * @return number expression
191 * @throws NullPointerException if {@code str} is null
193 abstract N createNumber(String str);
196 * Create a {@link YangNumberExpr} representing the negated value of a number.
198 * @param number input number
199 * @return negated number expression
200 * @throws NullPointerException if {@code number} is null
202 abstract N negateNumber(N number);
204 private YangExpr parseAdditive(final AdditiveExprContext expr) {
205 final Iterator<ParseTree> it = expr.children.iterator();
206 final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
207 return it.hasNext() ? parseAdditiveExpr(first, it) : first;
210 private YangExpr parseAnd(final AndExprContext expr) {
211 final int size = expr.getChildCount();
213 return parseEquality(getChild(expr, EqualityExprContext.class, 0));
215 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
216 for (int i = 0; i < size; i += 2) {
217 tmp.add(parseEquality(getChild(expr, EqualityExprContext.class, i)));
219 return YangNaryOperator.AND.exprWith(tmp);
222 private YangExpr parseEquality(final EqualityExprContext expr) {
223 final Iterator<ParseTree> it = expr.children.iterator();
224 final YangExpr first = parseRelational(nextContext(it, RelationalExprContext.class));
225 return it.hasNext() ? parseEqualityExpr(first, it) : first;
228 private YangExpr parseFilter(final FilterExprContext expr) {
229 final Iterator<ParseTree> it = expr.children.iterator();
230 final YangExpr first = parsePrimary(nextContext(it, PrimaryExprContext.class));
231 return it.hasNext() ? YangFilterExpr.of(first, ImmutableList.copyOf(Iterators.transform(it,
232 tree -> parsePredicate(verifyTree(PredicateContext.class, tree)))))
236 private YangExpr parseFunctionCall(final FunctionCallContext expr) {
237 // We are mapping functions to RFC7950 YIN namespace, to keep us consistent with type/statement definitions
239 final FunctionNameContext name = getChild(expr, FunctionNameContext.class, 0);
241 switch (name.getChildCount()) {
243 parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
246 parsed = qnameSupport.createQName(name.getChild(0).getText(), name.getChild(2).getText());
249 throw illegalShape(name);
252 final List<YangExpr> args = ImmutableList.copyOf(Lists.transform(expr.expr(), this::parseExpr));
253 final YangFunction func = YANG_FUNCTIONS.get(parsed);
255 return Functions.functionToExpr(func, args);
258 checkArgument(!YangConstants.RFC6020_YIN_MODULE.equals(parsed.getModule()), "Unknown default function %s",
260 return YangFunctionCallExpr.of(parsed, args);
263 private YangLocationPath parseLocationPath(final LocationPathContext expr) {
264 verifyChildCount(expr, 1);
265 final ParseTree first = expr.getChild(0);
266 if (first instanceof RelativeLocationPathContext) {
267 return parseRelativeLocationPath((RelativeLocationPathContext) first);
270 final AbsoluteLocationPathNorootContext abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
271 verifyChildCount(abs, 2);
273 final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
274 switch (getTerminalType(abs, 0)) {
275 case xpathParser.PATHSEP:
277 case xpathParser.ABRPATH:
278 steps.addFirst(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
281 throw illegalShape(abs);
284 return YangLocationPath.of(true, steps);
287 private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
288 final ParseTree first = expr.getChild(0);
290 if (first instanceof UnaryExprNoRootContext) {
291 left = parseUnary((UnaryExprNoRootContext) first);
293 left = YangLocationPath.root();
295 if (expr.getChildCount() == 1) {
299 verifyChildCount(expr, 3);
300 final YangBinaryOperator operator = parseOperator(expr.getChild(1));
301 final YangExpr right = parseMultiplicative(getChild(expr, MultiplicativeExprContext.class, 2));
302 final Optional<YangExpr> simple = simplifyNumbers(operator, left, right);
303 return simple.isPresent() ? simple.get() : operator.exprWith(left, right);
306 private YangExpr parsePathExpr(final PathExprNoRootContext expr) {
307 final ParseTree first = expr.getChild(0);
308 if (first instanceof LocationPathContext) {
309 return parseLocationPath((LocationPathContext) first);
312 final YangExpr filter = parseFilter(verifyTree(FilterExprContext.class, first));
313 if (expr.getChildCount() == 1) {
317 verifyChildCount(expr, 3);
318 return parseOperator(expr.getChild(1)).exprWith(filter,
319 parseRelativeLocationPath(getChild(expr, RelativeLocationPathContext.class, 2)));
322 private YangExpr parsePredicate(final PredicateContext expr) {
323 verifyChildCount(expr, 3);
324 return parseExpr(getChild(expr, ExprContext.class, 1));
327 private YangExpr parsePrimary(final PrimaryExprContext expr) {
328 if (expr.getChildCount() == 3) {
329 return parseExpr(getChild(expr, ExprContext.class, 1));
332 verifyChildCount(expr, 1);
333 final ParseTree first = expr.getChild(0);
334 if (first instanceof TerminalNode) {
335 return parseTerminal((TerminalNode) first);
337 if (first instanceof FunctionCallContext) {
338 return parseFunctionCall((FunctionCallContext) first);
340 if (first instanceof VariableReferenceContext) {
341 return YangVariableReferenceExpr.of(parseQName(((VariableReferenceContext) first).qName()));
343 throw illegalShape(first);
346 private YangExpr parseRelational(final RelationalExprContext expr) {
347 final Iterator<ParseTree> it = expr.children.iterator();
348 final YangExpr first = parseAdditive(nextContext(it, AdditiveExprContext.class));
349 return it.hasNext() ? parseRelationalExpr(first, it) : first;
352 private Deque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
353 final Deque<Step> steps = new ArrayDeque<>(expr.getChildCount());
354 final Iterator<ParseTree> it = expr.children.iterator();
355 steps.add(parseStep(nextContext(it, StepContext.class)));
357 while (it.hasNext()) {
358 final ParseTree tree = it.next();
359 switch (verifyTerminal(tree).getSymbol().getType()) {
360 case xpathParser.PATHSEP:
362 case xpathParser.ABRPATH:
363 steps.add(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
366 throw illegalShape(tree);
369 // Parse step and add it if it's not SELF_STEP
370 final Step step = parseStep(nextContext(it, StepContext.class));
371 if (!SELF_STEP.equals(step)) {
379 private YangLocationPath parseRelativeLocationPath(final RelativeLocationPathContext expr) {
380 return YangLocationPath.of(false, parseLocationPathSteps(expr));
383 private YangExpr parseTerminal(final TerminalNode term) {
384 final String text = term.getText();
385 switch (term.getSymbol().getType()) {
386 case xpathParser.Literal:
387 // We have to strip quotes
388 return parseLiteral(text.substring(1, text.length() - 1));
389 case xpathParser.Number:
390 return createNumber(text);
392 throw illegalShape(term);
396 private YangLiteralExpr parseLiteral(final String text) {
397 if (text.isEmpty()) {
398 return YangLiteralExpr.empty();
400 if (text.charAt(0) == '/') {
401 return parseLocationLiteral(text);
403 return parseQNameLiteral(text);
406 private YangLiteralExpr parseLocationLiteral(final String text) {
407 final xpathLexer lexer = new xpathLexer(CharStreams.fromString(text));
408 final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer));
409 lexer.removeErrorListeners();
410 parser.removeErrorListeners();
412 // FIXME: add listeners
414 final InstanceIdentifierContext id = parser.instanceIdentifier();
415 final int length = id.getChildCount();
416 final List<Step> steps = new ArrayList<>(length / 2);
417 for (int i = 1; i < length; i += 2) {
418 steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i)));
421 return new InstanceIdentifierLiteralExpr(text, steps);
424 private Step parsePathArgument(final PathArgumentContext expr) {
425 final QName qname = parseInstanceIdentifierQName(getChild(expr, NodeIdentifierContext.class, 0));
426 switch (expr.getChildCount()) {
428 return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of());
430 return YangXPathAxis.CHILD.asStep(qname,
431 parsePathArgumentPredicate(getChild(expr, instanceIdentifierParser.PredicateContext.class, 1)));
433 throw illegalShape(expr);
437 private Collection<YangExpr> parsePathArgumentPredicate(final instanceIdentifierParser.PredicateContext expr) {
438 final ParseTree first = expr.getChild(0);
439 if (first instanceof LeafListPredicateContext) {
440 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(),
441 parseEqStringValue(getChild(((LeafListPredicateContext) first)
442 .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1))));
443 } else if (first instanceof PosContext) {
444 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(Functions.POSITION,
445 createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0)
449 final int length = expr.getChildCount();
450 final List<YangExpr> ret = new ArrayList<>(length);
451 for (int i = 0; i < length; ++i) {
452 final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
453 .getChild(KeyPredicateExprContext.class, 0);
454 ret.add(YangBinaryOperator.EQUALS.exprWith(
455 YangQNameExpr.of(parseInstanceIdentifierQName(getChild(pred, NodeIdentifierContext.class, 0))),
456 parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
463 private YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
464 return parseLiteral(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1,
465 instanceIdentifierParser.STRING).getText());
468 private QName parseInstanceIdentifierQName(final NodeIdentifierContext expr) {
469 return qnameSupport.createQName(verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(),
470 verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText());
473 private YangLiteralExpr parseQNameLiteral(final String text) {
474 final int firstColon = text.indexOf(':');
475 if (firstColon != -1) {
476 // If we have two colons this node cannot be interpreted as a QName -- this may explode at evaluation-time,
477 // but that's fine as it will just result in evaluation error. Users do have unit tests, right?
478 final int secondColon = text.indexOf(':', firstColon + 1);
479 if (secondColon == -1) {
480 final Optional<QNameModule> optNamespace = qnameSupport.resolvePrefix(text.substring(0, firstColon));
481 // If we cannot resolve the namespace at evaluation-time has to deal with it.
482 if (optNamespace.isPresent()) {
484 return new QNameLiteralExpr(text, QName.create(optNamespace.get(),
485 text.substring(firstColon + 1)));
486 } catch (IllegalArgumentException e) {
487 LOG.trace("Cannot interpret {} as a QName", text, e);
488 return YangLiteralExpr.of(text);
493 return YangLiteralExpr.of(text);
496 private YangExpr parseUnary(final UnaryExprNoRootContext expr) {
497 // any number of '-' and an union expr
498 final int size = verifyAtLeastChildren(expr, 1);
499 final YangExpr ret = parseUnion(getChild(expr, UnionExprNoRootContext.class, size - 1));
501 // Even number of '-' tokens cancel out
505 return ret instanceof YangNumberExpr ? negateNumber((N) ret) : YangNegateExpr.of(ret);
508 private YangExpr parseUnion(final UnionExprNoRootContext expr) {
509 final ParseTree first = expr.getChild(0);
511 if (first instanceof PathExprNoRootContext) {
512 path = parsePathExpr((PathExprNoRootContext) first);
513 if (expr.getChildCount() == 1) {
517 path = YangLocationPath.root();
520 verifyChildCount(expr, 3);
521 final YangExpr union = parseUnion(getChild(expr, UnionExprNoRootContext.class, 2));
523 // Deduplicate expressions so we do not perform useless unioning
524 final Set<YangExpr> expressions = new LinkedHashSet<>();
525 expressions.add(path);
526 if (union instanceof YangNaryExpr) {
527 // If the result is a union expression, integrate it into this expression
528 final YangNaryExpr nary = (YangNaryExpr) union;
529 if (nary.getOperator() == YangNaryOperator.UNION) {
530 expressions.addAll(nary.getExpressions());
532 expressions.add(union);
535 expressions.add(union);
538 return YangNaryOperator.UNION.exprWith(expressions);
541 private YangExpr parseAdditiveExpr(final YangExpr left, final Iterator<ParseTree> it) {
544 final YangBinaryOperator operator = nextOperator(it);
545 final YangExpr right = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
546 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
547 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
548 } while (it.hasNext());
553 private Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final YangExpr left,
554 final YangExpr right) {
555 if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) {
556 // Constant folding on numbers -- precision plays a role here
557 return simplifyNumbers(operator, (N) left, (N) right);
559 return Optional.empty();
562 Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final N left, final N right) {
565 return Optional.of(YangBooleanConstantExpr.of(left.getNumber().equals(right.getNumber())));
567 return Optional.of(YangBooleanConstantExpr.of(!left.getNumber().equals(right.getNumber())));
569 return Optional.empty();
573 private YangExpr parseEqualityExpr(final YangExpr left, final Iterator<ParseTree> it) {
576 final YangBinaryOperator operator = nextOperator(it);
577 final YangExpr right = parseRelational(nextContext(it, RelationalExprContext.class));
579 if (left.equals(right)) {
580 // Constant folding on expression level: equal expressions are result in equal results
583 return YangBooleanConstantExpr.TRUE;
585 return YangBooleanConstantExpr.FALSE;
591 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
592 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
593 } while (it.hasNext());
598 private YangExpr parseRelationalExpr(final YangExpr left, final Iterator<ParseTree> it) {
601 final YangBinaryOperator operator = nextOperator(it);
602 final YangExpr right = parseAdditive(nextContext(it, AdditiveExprContext.class));
603 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
604 ret = simple.isPresent() ? simple.get() : nextOperator(it).exprWith(ret, right);
605 } while (it.hasNext());
610 private QName parseQName(final QNameContext expr) {
611 switch (expr.getChildCount()) {
613 return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText());
615 return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText(),
616 getChild(expr, NCNameContext.class, 2).getText());
618 throw illegalShape(expr);
622 private Step parseStep(final StepContext expr) {
623 if (expr.getChildCount() == 1) {
624 final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
625 verifyChildCount(abbrev, 1);
626 switch (getTerminalType(abbrev, 0)) {
627 case xpathParser.DOT:
628 return YangXPathAxis.SELF.asStep();
629 case xpathParser.DOTDOT:
630 return YangXPathAxis.PARENT.asStep();
632 throw illegalShape(abbrev);
636 final int size = verifyAtLeastChildren(expr, 2);
637 final List<YangExpr> predicates = new ArrayList<>(size - 2);
638 for (int i = 2; i < size; ++i) {
639 predicates.add(parsePredicate(getChild(expr, PredicateContext.class, i)));
642 final YangXPathAxis axis = parseAxis(getChild(expr, AxisSpecifierContext.class, 0));
643 final NodeTestContext nodeTest = getChild(expr, NodeTestContext.class, 1);
644 switch (nodeTest.getChildCount()) {
646 final NameTestContext nameChild = getChild(nodeTest, NameTestContext.class, 0);
647 final ParseTree first = nameChild.getChild(0);
648 if (first instanceof TerminalNode) {
649 verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
650 return axis.asStep(predicates);
652 return axis.asStep(parseQName(verifyTree(QNameContext.class, first)), predicates);
654 return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
656 final String text = verifyToken(nodeTest, 2, xpathParser.Literal).getText();
657 return axis.asStep(text.substring(1, text.length() - 1), predicates);
659 throw illegalShape(nodeTest);
663 private static YangXPathAxis parseAxis(final AxisSpecifierContext expr) {
664 switch (expr.getChildCount()) {
666 return YangXPathAxis.CHILD;
668 verify(getTerminalType(expr, 0) == xpathParser.AT, "Unhandled axis specifier shape %s", expr);
669 return YangXPathAxis.ATTRIBUTE;
671 final String str = verifyTerminal(expr.getChild(0)).getText();
672 return verifyNotNull(XPATH_AXES.get(str), "Unhandled axis %s", str);
674 throw illegalShape(expr);
678 private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
679 return verifyTree(type, it.next());
682 private static YangBinaryOperator nextOperator(final Iterator<ParseTree> it) {
683 return parseOperator(it.next());
686 private static <T extends ParseTree> T getChild(final ParseTree parent, final Class<T> type, final int offset) {
687 return verifyTree(type, parent.getChild(offset));
690 private static Token verifyToken(final ParseTree parent, final int offset, final int expected) {
691 final TerminalNode node = verifyTerminal(parent.getChild(offset));
692 final Token ret = node.getSymbol();
693 final int type = ret.getType();
694 verify(type == expected, "Item %s has type %s, expected %s", node, type, expected);
698 private static int getTerminalType(final ParseTree parent, final int offset) {
699 return verifyTerminal(parent.getChild(offset)).getSymbol().getType();
702 private static YangXPathNodeType parseNodeType(final ParseTree tree) {
703 final String str = verifyTerminal(tree).getText();
704 return verifyNotNull(NODE_TYPES.get(str), "Unhandled node type %s", str);
707 private static YangBinaryOperator parseOperator(final ParseTree tree) {
708 final String str = verifyTerminal(tree).getText();
709 return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
712 private static void verifyChildCount(final ParseTree tree, final int expected) {
713 if (tree.getChildCount() != expected) {
714 throw illegalShape(tree);
718 private static int verifyAtLeastChildren(final ParseTree tree, final int expected) {
719 final int count = tree.getChildCount();
720 if (count < expected) {
721 throw illegalShape(tree);
726 private static TerminalNode verifyTerminal(final ParseTree tree) {
727 if (tree instanceof TerminalNode) {
728 return (TerminalNode) tree;
730 throw new VerifyException(String.format("'%s' is not a terminal node", tree.getText()));
733 private static <T extends ParseTree> T verifyTree(final Class<T> type, final ParseTree tree) {
734 if (type.isInstance(tree)) {
735 return type.cast(tree);
737 throw new VerifyException(String.format("'%s' does not have expected type %s", tree.getText(), type));
740 private static VerifyException illegalShape(final ParseTree tree) {
741 return new VerifyException(String.format("Invalid parser shape of '%s'", tree.getText()));