Merge branch 'master' of ../controller
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / path / PathExpressionParser.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path;
9
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12
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;
53
54 class PathExpressionParser {
55     static final class Lenient extends PathExpressionParser {
56         private static final Logger LOG = LoggerFactory.getLogger(Lenient.class);
57
58         @Override
59         PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
60             try {
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);
65             }
66         }
67     }
68
69     private static final YangFunctionCallExpr CURRENT_CALL = YangFunctionCallExpr.of(
70         YangFunction.CURRENT.getIdentifier());
71
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());
77
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);
84         } else {
85             throw new IllegalStateException("Unsupported child " + childPath);
86         }
87
88         return new ParsedPathExpression(location, pathArg);
89     }
90
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);
96     }
97
98     private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
99             final Relative_pathContext relative) {
100         final List<Step> steps = new ArrayList<>();
101
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());
106         }
107
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)));
116             }
117             steps.add(createChildStep(ctx, qname, predicates));
118             fillSteps(ctx, pathArg, getChild(descendant, descandantChildren - 1, Absolute_pathContext.class), steps);
119         } else {
120             steps.add(createChildStep(ctx, qname, ImmutableList.of()));
121         }
122
123         return YangLocationPath.relative(steps);
124     }
125
126     private static void fillSteps(final StmtContext<?, ?, ?> ctx, final String pathArg,
127             final Absolute_pathContext absolute, final List<Step> output) {
128
129         final List<YangExpr> predicates = new ArrayList<>();
130         Node_identifierContext qname = getChild(absolute, 1, Node_identifierContext.class);
131
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));
137                 predicates.clear();
138                 qname = (Node_identifierContext) child;
139             } else if (child instanceof Path_predicateContext) {
140                 predicates.add(parsePathPredicate(ctx, (Path_predicateContext) child));
141             }
142         }
143
144         output.add(createChildStep(ctx, qname, predicates));
145     }
146
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,
150             parent, clazz);
151         return clazz.cast(child);
152     }
153
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())));
160     }
161
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();
165
166         // Process dots first
167         final List<Step> steps = new ArrayList<>();
168         int offset = 0;
169         while (offset < children - 1) {
170             final ParseTree child = expr.getChild(offset);
171             if (child instanceof Node_identifierContext) {
172                 break;
173             }
174             if (child instanceof TerminalNode
175                     && ((TerminalNode) child).getSymbol().getType() == LeafRefPathLexer.DOTS) {
176                 steps.add(YangXPathAxis.PARENT.asStep());
177             }
178
179             ++offset;
180         }
181
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()));
187             }
188             ++offset;
189         }
190
191         return steps.isEmpty() ? CURRENT_CALL : YangPathExpr.of(CURRENT_CALL, YangLocationPath.relative(steps));
192     }
193
194     private static YangQNameExpr createChildExpr(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
195         switch (qname.getChildCount()) {
196             case 1:
197                 return YangQNameExpr.of(UnqualifiedQName.of(qname.getText()).intern());
198             case 3:
199                 return YangQNameExpr.of(parseQName(ctx, qname));
200             default:
201                 throw new IllegalStateException("Unexpected shape " + qname.getText());
202         }
203     }
204
205     private static QNameStep createChildStep(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname,
206             final List<YangExpr> predicates) {
207         switch (qname.getChildCount()) {
208             case 1:
209                 return YangXPathAxis.CHILD.asStep(UnqualifiedQName.of(qname.getText()).intern(), predicates);
210             case 3:
211                 return YangXPathAxis.CHILD.asStep(parseQName(ctx, qname), predicates);
212             default:
213                 throw new IllegalStateException("Unexpected shape " + qname.getText());
214         }
215     }
216
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());
219     }
220 }