2 * Copyright (c) 2019 PANTHEON.tech, 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.parser.rfc7950.stmt.path;
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
13 import com.google.common.collect.ImmutableList;
14 import java.util.ArrayList;
15 import java.util.List;
16 import org.antlr.v4.runtime.CharStreams;
17 import org.antlr.v4.runtime.CommonTokenStream;
18 import org.antlr.v4.runtime.tree.ParseTree;
19 import org.antlr.v4.runtime.tree.TerminalNode;
20 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathLexer;
21 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser;
22 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Absolute_pathContext;
23 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Descendant_pathContext;
24 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Node_identifierContext;
25 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_argContext;
26 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_equality_exprContext;
27 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_key_exprContext;
28 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_predicateContext;
29 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Rel_path_keyexprContext;
30 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Relative_pathContext;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
33 import org.opendaylight.yangtools.yang.model.api.PathExpression;
34 import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
37 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
38 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
39 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
40 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
41 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
42 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
43 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
44 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
45 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
46 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Relative;
47 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
48 import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
49 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
50 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 class PathExpressionParser {
55 static final class Lenient extends PathExpressionParser {
56 private static final Logger LOG = LoggerFactory.getLogger(Lenient.class);
59 PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
61 return super.parseExpression(ctx, pathArg);
62 } catch (IllegalStateException | SourceException e) {
63 LOG.warn("Failed to parse expression '{}'", pathArg, e);
64 return new UnparsedPathExpression(pathArg, e);
69 private static final YangFunctionCallExpr CURRENT_CALL = YangFunctionCallExpr.of(
70 YangFunction.CURRENT.getIdentifier());
72 PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
73 final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(pathArg));
74 final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
75 final Path_argContext path = SourceExceptionParser.parse(lexer, parser, parser::path_arg,
76 ctx.getStatementSourceReference());
78 final ParseTree childPath = path.getChild(0);
79 final YangLocationPath location;
80 if (childPath instanceof Absolute_pathContext) {
81 location = parseAbsolute(ctx, pathArg, (Absolute_pathContext) childPath);
82 } else if (childPath instanceof Relative_pathContext) {
83 location = parseRelative(ctx, pathArg, (Relative_pathContext) childPath);
85 throw new IllegalStateException("Unsupported child " + childPath);
88 return new ParsedPathExpression(location, pathArg);
91 private static Absolute parseAbsolute(final StmtContext<?, ?, ?> ctx, final String pathArg,
92 final Absolute_pathContext absolute) {
93 final List<Step> steps = new ArrayList<>();
94 fillSteps(ctx, pathArg, absolute, steps);
95 return YangLocationPath.absolute(steps);
98 private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
99 final Relative_pathContext relative) {
100 final List<Step> steps = new ArrayList<>();
102 final int relativeChildren = relative.getChildCount();
103 verify(relativeChildren % 2 != 0, "Unexpected child count %s", relativeChildren);
104 for (int i = 0; i < relativeChildren / 2; ++i) {
105 steps.add(YangXPathAxis.PARENT.asStep());
108 final Descendant_pathContext descendant = getChild(relative, relativeChildren - 1,
109 Descendant_pathContext.class);
110 final Node_identifierContext qname = getChild(descendant, 0, Node_identifierContext.class);
111 final int descandantChildren = descendant.getChildCount();
112 if (descandantChildren > 1) {
113 final List<YangExpr> predicates = new ArrayList<>(descandantChildren);
114 for (int i = 1; i < descandantChildren - 1; ++i) {
115 predicates.add(parsePathPredicate(ctx, getChild(descendant, i, Path_predicateContext.class)));
117 steps.add(createChildStep(ctx, qname, predicates));
118 fillSteps(ctx, pathArg, getChild(descendant, descandantChildren - 1, Absolute_pathContext.class), steps);
120 steps.add(createChildStep(ctx, qname, ImmutableList.of()));
123 return YangLocationPath.relative(steps);
126 private static void fillSteps(final StmtContext<?, ?, ?> ctx, final String pathArg,
127 final Absolute_pathContext absolute, final List<Step> output) {
129 final List<YangExpr> predicates = new ArrayList<>();
130 Node_identifierContext qname = getChild(absolute, 1, Node_identifierContext.class);
132 final int children = absolute.getChildCount();
133 for (int i = 2; i < children; ++i) {
134 final ParseTree child = absolute.getChild(i);
135 if (child instanceof Node_identifierContext) {
136 output.add(createChildStep(ctx, qname, predicates));
138 qname = (Node_identifierContext) child;
139 } else if (child instanceof Path_predicateContext) {
140 predicates.add(parsePathPredicate(ctx, (Path_predicateContext) child));
144 output.add(createChildStep(ctx, qname, predicates));
147 private static <T> T getChild(final ParseTree parent, final int offset, final Class<T> clazz) {
148 final ParseTree child = parent.getChild(offset);
149 verify(clazz.isInstance(child), "Unexpected child %s at offset %s of %s when expecting %s", child, offset,
151 return clazz.cast(child);
154 private static YangBinaryExpr parsePathPredicate(final StmtContext<?, ?, ?> ctx,
155 final Path_predicateContext predicate) {
156 final Path_equality_exprContext eqExpr = verifyNotNull(predicate.path_equality_expr());
157 return YangBinaryOperator.EQUALS.exprWith(
158 createChildExpr(ctx, getChild(eqExpr, 0, Node_identifierContext.class)),
159 parsePathKeyExpr(ctx, verifyNotNull(eqExpr.path_key_expr())));
162 private static YangExpr parsePathKeyExpr(final StmtContext<?, ?, ?> ctx, final Path_key_exprContext expr) {
163 final Rel_path_keyexprContext relPath = verifyNotNull(expr.rel_path_keyexpr());
164 final int children = relPath.getChildCount();
166 // Process dots first
167 final List<Step> steps = new ArrayList<>();
169 while (offset < children - 1) {
170 final ParseTree child = expr.getChild(offset);
171 if (child instanceof Node_identifierContext) {
174 if (child instanceof TerminalNode
175 && ((TerminalNode) child).getSymbol().getType() == LeafRefPathLexer.DOTS) {
176 steps.add(YangXPathAxis.PARENT.asStep());
182 // Process node identifiers
183 while (offset < children) {
184 final ParseTree child = expr.getChild(offset);
185 if (child instanceof Node_identifierContext) {
186 steps.add(createChildStep(ctx, (Node_identifierContext) child, ImmutableList.of()));
191 return steps.isEmpty() ? CURRENT_CALL : YangPathExpr.of(CURRENT_CALL, YangLocationPath.relative(steps));
194 private static YangQNameExpr createChildExpr(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
195 switch (qname.getChildCount()) {
197 return YangQNameExpr.of(UnqualifiedQName.of(qname.getText()).intern());
199 return YangQNameExpr.of(parseQName(ctx, qname));
201 throw new IllegalStateException("Unexpected shape " + qname.getText());
205 private static QNameStep createChildStep(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname,
206 final List<YangExpr> predicates) {
207 switch (qname.getChildCount()) {
209 return YangXPathAxis.CHILD.asStep(UnqualifiedQName.of(qname.getText()).intern(), predicates);
211 return YangXPathAxis.CHILD.asStep(parseQName(ctx, qname), predicates);
213 throw new IllegalStateException("Unexpected shape " + qname.getText());
217 private static QName parseQName(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
218 return StmtContextUtils.parseNodeIdentifier(ctx, qname.getChild(0).getText(), qname.getChild(2).getText());