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;
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;
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.AbstractMap.SimpleImmutableEntry;
26 import java.util.ArrayDeque;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Deque;
30 import java.util.Iterator;
31 import java.util.LinkedHashSet;
32 import java.util.List;
34 import java.util.Map.Entry;
35 import java.util.Optional;
37 import javax.xml.xpath.XPathExpressionException;
38 import org.antlr.v4.runtime.CharStreams;
39 import org.antlr.v4.runtime.CommonTokenStream;
40 import org.antlr.v4.runtime.ParserRuleContext;
41 import org.antlr.v4.runtime.tree.ParseTree;
42 import org.antlr.v4.runtime.tree.TerminalNode;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.common.QNameModule;
45 import org.opendaylight.yangtools.yang.common.QualifiedQName;
46 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
47 import org.opendaylight.yangtools.yang.common.YangConstants;
48 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
49 import org.opendaylight.yangtools.yang.common.YangVersion;
50 import org.opendaylight.yangtools.yang.xpath.antlr.xpathLexer;
51 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser;
52 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbbreviatedStepContext;
53 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbsoluteLocationPathNorootContext;
54 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AdditiveExprContext;
55 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AndExprContext;
56 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AxisSpecifierContext;
57 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.EqualityExprContext;
58 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.ExprContext;
59 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FilterExprContext;
60 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionCallContext;
61 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionNameContext;
62 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.LocationPathContext;
63 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.MultiplicativeExprContext;
64 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NCNameContext;
65 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NameTestContext;
66 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NodeTestContext;
67 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.OrExprContext;
68 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PathExprNoRootContext;
69 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PredicateContext;
70 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PrimaryExprContext;
71 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.QNameContext;
72 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelationalExprContext;
73 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelativeLocationPathContext;
74 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.StepContext;
75 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnaryExprNoRootContext;
76 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnionExprNoRootContext;
77 import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.VariableReferenceContext;
78 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
79 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
80 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
81 import org.opendaylight.yangtools.yang.xpath.api.YangFilterExpr;
82 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
83 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
84 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
85 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
86 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
87 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
88 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
89 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
90 import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
91 import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
92 import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr;
93 import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
94 import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
95 import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr;
96 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
97 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
98 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
99 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
100 import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType;
101 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
104 * ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
106 * @author Robert Varga
108 abstract class AntlrXPathParser implements YangXPathParser {
109 static class Base extends AntlrXPathParser {
110 Base(final YangXPathMathMode mathMode) {
115 public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
116 final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
117 return new AntlrYangXPathExpression.Base(mathMode, result.getKey(), result.getValue(), xpath);
121 QNameStep createStep(final YangXPathAxis axis, final String localName,
122 final List<YangExpr> predicates) {
123 return axis.asStep(UnqualifiedQName.of(localName).intern(), predicates);
127 QNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
128 final List<YangExpr> predicates) {
129 return axis.asStep(QualifiedQName.of(prefix, localName).intern(), predicates);
133 QName createQName(final String localName) {
134 throw new UnsupportedOperationException();
138 QName createQName(final String prefix, final String localName) {
139 throw new UnsupportedOperationException();
143 static class Qualified extends Base implements QualifiedBound {
144 final YangNamespaceContext namespaceContext;
146 Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
148 this.namespaceContext = requireNonNull(namespaceContext);
152 public YangXPathExpression.QualifiedBound parseExpression(final String xpath) throws XPathExpressionException {
153 final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
154 return new AntlrYangXPathExpression.Qualified(mathMode, result.getKey(), result.getValue(), xpath,
159 final QName createQName(final String prefix, final String localName) {
160 return namespaceContext.createQName(prefix, localName);
164 ResolvedQNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
165 final List<YangExpr> predicates) {
166 return axis.asStep(createQName(prefix, localName), predicates);
170 static final class Unqualified extends Qualified implements UnqualifiedBound {
171 private final QNameModule defaultNamespace;
173 Unqualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
174 final QNameModule defaultNamespace) {
175 super(mathMode, namespaceContext);
176 this.defaultNamespace = requireNonNull(defaultNamespace);
180 public YangXPathExpression.UnqualifiedBound parseExpression(final String xpath)
181 throws XPathExpressionException {
182 final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
184 return new AntlrYangXPathExpression.Unqualified(mathMode, result.getKey(), result.getValue(), xpath,
185 namespaceContext, defaultNamespace);
189 QName createQName(final String localName) {
190 return QName.create(defaultNamespace, localName);
194 ResolvedQNameStep createStep(final YangXPathAxis axis, final String localName,
195 final List<YangExpr> predicates) {
196 return axis.asStep(QName.create(defaultNamespace, localName), predicates);
200 private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
201 Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
202 private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
203 YangXPathNodeType.values()), YangXPathNodeType::toString);
204 private static final Map<String, YangXPathAxis> XPATH_AXES = Maps.uniqueIndex(Arrays.asList(YangXPathAxis.values()),
205 YangXPathAxis::toString);
206 private static final Map<QName, YangFunction> YANG_FUNCTIONS = Maps.uniqueIndex(Arrays.asList(
207 YangFunction.values()), YangFunction::getIdentifier);
209 // Cached for checks in hot path
210 private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
212 final YangXPathMathMode mathMode;
213 private final YangXPathMathSupport mathSupport;
214 private final FunctionSupport functionSupport;
216 private YangVersion minimumYangVersion = YangVersion.VERSION_1;
218 AntlrXPathParser(final YangXPathMathMode mathMode) {
219 this.mathMode = requireNonNull(mathMode);
220 this.mathSupport = mathMode.getSupport();
221 this.functionSupport = new FunctionSupport(mathSupport);
224 abstract QName createQName(String localName);
226 abstract QName createQName(String prefix, String localName);
228 abstract QNameStep createStep(YangXPathAxis axis, String localName, List<YangExpr> predicates);
230 abstract QNameStep createStep(YangXPathAxis axis, String prefix, String localName, List<YangExpr> predicates);
232 private QNameStep createStep(final YangXPathAxis axis, final QNameContext expr, final List<YangExpr> predicates) {
233 switch (expr.getChildCount()) {
235 return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(), predicates);
237 return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(),
238 getChild(expr, NCNameContext.class, 2).getText(), predicates);
240 throw illegalShape(expr);
244 @SuppressWarnings("checkstyle:illegalCatch")
245 final Entry<YangVersion, YangExpr> parseExpr(final String xpath) throws XPathExpressionException {
246 // Create a parser and disconnect it from console error output
247 final xpathLexer lexer = new xpathLexer(CharStreams.fromString(xpath));
248 final xpathParser parser = new xpathParser(new CommonTokenStream(lexer));
250 final CapturingErrorListener listener = new CapturingErrorListener();
251 lexer.removeErrorListeners();
252 lexer.addErrorListener(listener);
253 parser.removeErrorListeners();
254 parser.addErrorListener(listener);
255 final ExprContext antlr = parser.main().expr();
256 listener.reportError();
258 // Reset our internal context
259 minimumYangVersion = YangVersion.VERSION_1;
263 expr = parseExpr(antlr);
264 } catch (RuntimeException e) {
265 throw new XPathExpressionException(e);
267 return new SimpleImmutableEntry<>(minimumYangVersion, expr);
271 * Parse and simplify an XPath expression in {@link ExprContext} representation.
273 * @param ctx Current parsing context
274 * @param expr ANTLR ExprContext
275 * @return A {@link YangExpr}
276 * @throws NullPointerException if {@code expr} is null
277 * @throws IllegalArgumentException if {@code expr} references an unbound prefix
279 private YangExpr parseExpr(final ExprContext expr) {
280 final OrExprContext or = expr.orExpr();
281 final int size = or.getChildCount();
283 return parseAnd(getChild(or, AndExprContext.class, 0));
285 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
286 for (int i = 0; i < size; i += 2) {
287 tmp.add(parseAnd(getChild(or, AndExprContext.class, i)));
289 return YangNaryOperator.OR.exprWith(tmp);
292 private YangExpr parseAdditive(final AdditiveExprContext expr) {
293 final Iterator<ParseTree> it = expr.children.iterator();
294 final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
295 return it.hasNext() ? parseAdditiveExpr(first, it) : first;
298 private YangExpr parseAnd(final AndExprContext expr) {
299 final int size = expr.getChildCount();
301 return parseEquality(getChild(expr, EqualityExprContext.class, 0));
303 final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
304 for (int i = 0; i < size; i += 2) {
305 tmp.add(parseEquality(getChild(expr, EqualityExprContext.class, i)));
307 return YangNaryOperator.AND.exprWith(tmp);
310 private YangExpr parseEquality(final EqualityExprContext expr) {
311 final Iterator<ParseTree> it = expr.children.iterator();
312 final YangExpr first = parseRelational(nextContext(it, RelationalExprContext.class));
313 return it.hasNext() ? parseEqualityExpr(first, it) : first;
316 private YangExpr parseFilter(final FilterExprContext expr) {
317 final Iterator<ParseTree> it = expr.children.iterator();
318 final YangExpr first = parsePrimary(nextContext(it, PrimaryExprContext.class));
319 return it.hasNext() ? YangFilterExpr.of(first, ImmutableList.copyOf(Iterators.transform(it,
320 tree -> parsePredicate(verifyTree(PredicateContext.class, tree)))))
324 private YangExpr parseFunctionCall(final FunctionCallContext expr) {
325 // We are mapping functions to RFC7950 YIN namespace, to keep us consistent with type/statement definitions
327 final FunctionNameContext name = getChild(expr, FunctionNameContext.class, 0);
329 switch (name.getChildCount()) {
331 parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
334 parsed = createQName(name.getChild(0).getText(), name.getChild(2).getText());
337 throw illegalShape(name);
340 final List<YangExpr> args = expr.expr().stream().map(this::parseExpr).collect(ImmutableList.toImmutableList());
341 final YangFunction func = YANG_FUNCTIONS.get(parsed);
343 if (minimumYangVersion.compareTo(func.getYangVersion()) < 0) {
344 minimumYangVersion = func.getYangVersion();
346 return functionSupport.functionToExpr(func, args);
349 checkArgument(!YangConstants.RFC6020_YIN_MODULE.equals(parsed.getModule()), "Unknown default function %s",
351 return YangFunctionCallExpr.of(parsed, args);
354 private YangLocationPath parseLocationPath(final LocationPathContext expr) {
355 verifyChildCount(expr, 1);
356 final ParseTree first = expr.getChild(0);
357 if (first instanceof RelativeLocationPathContext) {
358 return YangLocationPath.relative(parseLocationPathSteps((RelativeLocationPathContext) first));
361 final AbsoluteLocationPathNorootContext abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
362 verifyChildCount(abs, 2);
364 final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
365 parseStepShorthand(abs.getChild(0)).ifPresent(steps::addFirst);
367 return YangLocationPath.absolute(steps);
370 private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
371 final ParseTree first = expr.getChild(0);
373 if (first instanceof UnaryExprNoRootContext) {
374 left = parseUnary((UnaryExprNoRootContext) first);
376 left = YangLocationPath.root();
378 if (expr.getChildCount() == 1) {
382 verifyChildCount(expr, 3);
383 final YangBinaryOperator operator = parseOperator(expr.getChild(1));
384 final YangExpr right = parseMultiplicative(getChild(expr, MultiplicativeExprContext.class, 2));
385 final Optional<YangExpr> simple = simplifyNumbers(operator, left, right);
386 return simple.isPresent() ? simple.get() : operator.exprWith(left, right);
389 private YangExpr parsePathExpr(final PathExprNoRootContext expr) {
390 final ParseTree first = expr.getChild(0);
391 if (first instanceof LocationPathContext) {
392 return parseLocationPath((LocationPathContext) first);
395 final YangExpr filter = parseFilter(verifyTree(FilterExprContext.class, first));
396 if (expr.getChildCount() == 1) {
400 verifyChildCount(expr, 3);
401 final Deque<Step> steps = parseLocationPathSteps(getChild(expr, RelativeLocationPathContext.class, 2));
402 parseStepShorthand(expr.getChild(1)).ifPresent(steps::addFirst);
403 return YangPathExpr.of(filter, YangLocationPath.relative(steps));
406 private YangExpr parsePredicate(final PredicateContext expr) {
407 verifyChildCount(expr, 3);
408 return parseExpr(getChild(expr, ExprContext.class, 1));
411 private YangExpr parsePrimary(final PrimaryExprContext expr) {
412 if (expr.getChildCount() == 3) {
413 return parseExpr(getChild(expr, ExprContext.class, 1));
416 verifyChildCount(expr, 1);
417 final ParseTree first = expr.getChild(0);
418 if (first instanceof TerminalNode) {
419 return parseTerminal((TerminalNode) first);
421 if (first instanceof FunctionCallContext) {
422 return parseFunctionCall((FunctionCallContext) first);
424 if (first instanceof VariableReferenceContext) {
425 return YangVariableReferenceExpr.of(parseQName(((VariableReferenceContext) first).qName()));
427 throw illegalShape(first);
430 private YangExpr parseRelational(final RelationalExprContext expr) {
431 final Iterator<ParseTree> it = expr.children.iterator();
432 final YangExpr first = parseAdditive(nextContext(it, AdditiveExprContext.class));
433 return it.hasNext() ? parseRelationalExpr(first, it) : first;
436 private Deque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
437 final Deque<Step> steps = new ArrayDeque<>(expr.getChildCount());
438 final Iterator<ParseTree> it = expr.children.iterator();
439 addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
441 while (it.hasNext()) {
442 parseStepShorthand(it.next()).ifPresent(steps::add);
444 // Parse step and add it if it's not SELF_STEP
445 addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
451 private static void addNotSelfStep(final Deque<Step> steps, final Step step) {
452 if (!SELF_STEP.equals(step)) {
457 private YangExpr parseTerminal(final TerminalNode term) {
458 final String text = term.getText();
459 switch (term.getSymbol().getType()) {
460 case xpathParser.Literal:
461 // We have to strip quotes
462 return YangLiteralExpr.of(text.substring(1, text.length() - 1));
463 case xpathParser.Number:
464 return mathSupport.createNumber(text);
466 throw illegalShape(term);
470 private YangExpr parseUnary(final UnaryExprNoRootContext expr) {
471 // any number of '-' and an union expr
472 final int size = verifyAtLeastChildren(expr, 1);
473 final YangExpr ret = parseUnion(getChild(expr, UnionExprNoRootContext.class, size - 1));
475 // Even number of '-' tokens cancel out
478 return ret instanceof YangNumberExpr ? mathSupport.negateNumber((YangNumberExpr) ret) : YangNegateExpr.of(ret);
481 private YangExpr parseUnion(final UnionExprNoRootContext expr) {
482 final ParseTree first = expr.getChild(0);
484 if (first instanceof PathExprNoRootContext) {
485 path = parsePathExpr((PathExprNoRootContext) first);
486 if (expr.getChildCount() == 1) {
490 path = YangLocationPath.root();
493 verifyChildCount(expr, 3);
494 final YangExpr union = parseUnion(getChild(expr, UnionExprNoRootContext.class, 2));
496 // Deduplicate expressions so we do not perform useless unioning
497 final Set<YangExpr> expressions = new LinkedHashSet<>();
498 expressions.add(path);
499 if (union instanceof YangNaryExpr) {
500 // If the result is a union expression, integrate it into this expression
501 final YangNaryExpr nary = (YangNaryExpr) union;
502 if (nary.getOperator() == YangNaryOperator.UNION) {
503 expressions.addAll(nary.getExpressions());
505 expressions.add(union);
508 expressions.add(union);
511 return YangNaryOperator.UNION.exprWith(expressions);
514 private YangExpr parseAdditiveExpr(final YangExpr left, final Iterator<ParseTree> it) {
517 final YangBinaryOperator operator = nextOperator(it);
518 final YangExpr right = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
519 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
520 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
521 } while (it.hasNext());
526 private Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final YangExpr left,
527 final YangExpr right) {
528 if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) {
529 // Constant folding on numbers -- precision plays a role here
530 return mathSupport.tryEvaluate(operator, (YangNumberExpr)left, (YangNumberExpr)right);
532 return Optional.empty();
535 private YangExpr parseEqualityExpr(final YangExpr left, final Iterator<ParseTree> it) {
538 final YangBinaryOperator operator = nextOperator(it);
539 final YangExpr right = parseRelational(nextContext(it, RelationalExprContext.class));
541 if (left.equals(right)) {
542 // Constant folding on expression level: equal expressions are result in equal results
545 return YangBooleanConstantExpr.TRUE;
547 return YangBooleanConstantExpr.FALSE;
553 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
554 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
555 } while (it.hasNext());
560 private YangExpr parseRelationalExpr(final YangExpr left, final Iterator<ParseTree> it) {
563 final YangBinaryOperator operator = nextOperator(it);
564 final YangExpr right = parseAdditive(nextContext(it, AdditiveExprContext.class));
565 final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
566 ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
567 } while (it.hasNext());
572 private Step parseStep(final StepContext expr) {
573 if (expr.getChildCount() == 1) {
574 final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
575 verifyChildCount(abbrev, 1);
576 switch (getTerminalType(abbrev, 0)) {
577 case xpathParser.DOT:
578 return YangXPathAxis.SELF.asStep();
579 case xpathParser.DOTDOT:
580 return YangXPathAxis.PARENT.asStep();
582 throw illegalShape(abbrev);
586 final int size = verifyAtLeastChildren(expr, 2);
587 final List<YangExpr> predicates = new ArrayList<>(size - 2);
588 for (int i = 2; i < size; ++i) {
589 predicates.add(parsePredicate(getChild(expr, PredicateContext.class, i)));
592 final YangXPathAxis axis = parseAxis(getChild(expr, AxisSpecifierContext.class, 0));
593 final NodeTestContext nodeTest = getChild(expr, NodeTestContext.class, 1);
594 switch (nodeTest.getChildCount()) {
596 final NameTestContext nameChild = getChild(nodeTest, NameTestContext.class, 0);
597 final ParseTree first = nameChild.getChild(0);
598 if (first instanceof TerminalNode) {
599 verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
600 return axis.asStep(predicates);
602 return createStep(axis, verifyTree(QNameContext.class, first), predicates);
604 return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
606 final String text = verifyToken(nodeTest, 2, xpathParser.Literal).getText();
607 return axis.asStep(text.substring(1, text.length() - 1), predicates);
609 throw illegalShape(nodeTest);
613 private static YangXPathAxis parseAxis(final AxisSpecifierContext expr) {
614 switch (expr.getChildCount()) {
616 return YangXPathAxis.CHILD;
618 verify(getTerminalType(expr, 0) == xpathParser.AT, "Unhandled axis specifier shape %s", expr);
619 return YangXPathAxis.ATTRIBUTE;
621 final String str = verifyTerminal(expr.getChild(0)).getText();
622 return verifyNotNull(XPATH_AXES.get(str), "Unhandled axis %s", str);
624 throw illegalShape(expr);
628 private QName parseQName(final QNameContext expr) {
629 switch (expr.getChildCount()) {
631 return createQName(getChild(expr, NCNameContext.class, 0).getText());
633 return createQName(getChild(expr, NCNameContext.class, 0).getText(),
634 getChild(expr, NCNameContext.class, 2).getText());
636 throw illegalShape(expr);
640 private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
641 return verifyTree(type, it.next());
644 private static YangBinaryOperator nextOperator(final Iterator<ParseTree> it) {
645 return parseOperator(it.next());
648 private static int getTerminalType(final ParseTree parent, final int offset) {
649 return verifyTerminal(parent.getChild(offset)).getSymbol().getType();
652 private static YangXPathNodeType parseNodeType(final ParseTree tree) {
653 final String str = verifyTerminal(tree).getText();
654 return verifyNotNull(NODE_TYPES.get(str), "Unhandled node type %s", str);
657 private static YangBinaryOperator parseOperator(final ParseTree tree) {
658 final String str = verifyTerminal(tree).getText();
659 return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
662 private static Optional<Step> parseStepShorthand(final ParseTree tree) {
663 switch (verifyTerminal(tree).getSymbol().getType()) {
664 case xpathParser.PATHSEP:
665 return Optional.empty();
666 case xpathParser.ABRPATH:
667 return Optional.of(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
669 throw illegalShape(tree);