48ffa293b49d46f3c033dde6132fca89ca8d7f56
[yangtools.git] / parser / 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.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.common.UnresolvedQName;
23 import org.opendaylight.yangtools.yang.model.api.PathExpression;
24 import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps;
25 import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
26 import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
27 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathLexer;
28 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser;
29 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Absolute_pathContext;
30 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Deref_exprContext;
31 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Deref_function_invocationContext;
32 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Descendant_pathContext;
33 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Node_identifierContext;
34 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Path_argContext;
35 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Path_equality_exprContext;
36 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Path_key_exprContext;
37 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Path_predicateContext;
38 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Path_strContext;
39 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Rel_path_keyexprContext;
40 import org.opendaylight.yangtools.yang.parser.antlr.LeafRefPathParser.Relative_pathContext;
41 import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
44 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
45 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
46 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
47 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
48 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
49 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
50 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
51 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
52 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
53 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Relative;
54 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
55 import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
56 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
57 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 class PathExpressionParser {
62     static final class Lenient extends PathExpressionParser {
63         private static final Logger LOG = LoggerFactory.getLogger(Lenient.class);
64
65         @Override
66         PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
67             try {
68                 return super.parseExpression(ctx, pathArg);
69             } catch (IllegalStateException | SourceException e) {
70                 LOG.warn("Failed to parse expression '{}'", pathArg, e);
71                 return new UnparsedPathExpression(pathArg, e);
72             }
73         }
74     }
75
76     private static final YangFunctionCallExpr CURRENT_CALL = YangFunctionCallExpr.of(
77         YangFunction.CURRENT.getIdentifier());
78
79     PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
80         final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(pathArg));
81         final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
82         final Path_argContext path = SourceExceptionParser.parse(lexer, parser, parser::path_arg,
83             ctx.sourceReference());
84
85         final ParseTree childPath = path.getChild(0);
86         final Steps steps;
87         if (childPath instanceof Path_strContext) {
88             steps = new LocationPathSteps(parsePathStr(ctx, pathArg, (Path_strContext) childPath));
89         } else if (childPath instanceof Deref_exprContext) {
90             final Deref_exprContext deref = (Deref_exprContext) childPath;
91             steps = new DerefSteps(parseRelative(ctx, pathArg,
92                 getChild(deref, 0, Deref_function_invocationContext.class).getChild(Relative_pathContext.class, 0)),
93                 parseRelative(ctx, pathArg, getChild(deref, deref.getChildCount() - 1, Relative_pathContext.class)));
94         } else {
95             throw new IllegalStateException("Unsupported child " + childPath);
96         }
97         return new ParsedPathExpression(steps, pathArg);
98     }
99
100     private static YangLocationPath parsePathStr(final StmtContext<?, ?, ?> ctx, final String pathArg,
101             final Path_strContext path) {
102         final ParseTree childPath = path.getChild(0);
103         if (childPath instanceof Absolute_pathContext) {
104             return parseAbsolute(ctx, pathArg, (Absolute_pathContext) childPath);
105         } else if (childPath instanceof Relative_pathContext) {
106             return parseRelative(ctx, pathArg, (Relative_pathContext) childPath);
107         } else {
108             throw new IllegalStateException("Unsupported child " + childPath);
109         }
110     }
111
112     private static Absolute parseAbsolute(final StmtContext<?, ?, ?> ctx, final String pathArg,
113             final Absolute_pathContext absolute) {
114         final List<Step> steps = new ArrayList<>();
115         fillSteps(ctx, pathArg, absolute, steps);
116         return YangLocationPath.absolute(steps);
117     }
118
119     private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
120             final Relative_pathContext relative) {
121         final int relativeChildren = relative.getChildCount();
122         verify(relativeChildren % 2 != 0, "Unexpected child count %s", relativeChildren);
123
124         final int stepCount = relativeChildren / 2;
125         final List<Step> steps = new ArrayList<>(stepCount);
126         for (int i = 0; i < stepCount; ++i) {
127             steps.add(YangXPathAxis.PARENT.asStep());
128         }
129         return parseRelative(ctx, pathArg, relative, steps);
130     }
131
132     private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
133             final Relative_pathContext relative, final List<Step> steps) {
134         final int relativeChildren = relative.getChildCount();
135         final Descendant_pathContext descendant = getChild(relative, relativeChildren - 1,
136             Descendant_pathContext.class);
137         final Node_identifierContext qname = getChild(descendant, 0, Node_identifierContext.class);
138         final int descandantChildren = descendant.getChildCount();
139         if (descandantChildren > 1) {
140             final List<YangExpr> predicates = new ArrayList<>(descandantChildren);
141             for (int i = 1; i < descandantChildren - 1; ++i) {
142                 predicates.add(parsePathPredicate(ctx, getChild(descendant, i, Path_predicateContext.class)));
143             }
144             steps.add(createChildStep(ctx, qname, predicates));
145             fillSteps(ctx, pathArg, getChild(descendant, descandantChildren - 1, Absolute_pathContext.class), steps);
146         } else {
147             steps.add(createChildStep(ctx, qname, ImmutableList.of()));
148         }
149
150         return YangLocationPath.relative(steps);
151     }
152
153     private static void fillSteps(final StmtContext<?, ?, ?> ctx, final String pathArg,
154             final Absolute_pathContext absolute, final List<Step> output) {
155
156         final List<YangExpr> predicates = new ArrayList<>();
157         Node_identifierContext qname = getChild(absolute, 1, Node_identifierContext.class);
158
159         final int children = absolute.getChildCount();
160         for (int i = 2; i < children; ++i) {
161             final ParseTree child = absolute.getChild(i);
162             if (child instanceof Node_identifierContext) {
163                 output.add(createChildStep(ctx, qname, predicates));
164                 predicates.clear();
165                 qname = (Node_identifierContext) child;
166             } else if (child instanceof Path_predicateContext) {
167                 predicates.add(parsePathPredicate(ctx, (Path_predicateContext) child));
168             }
169         }
170
171         output.add(createChildStep(ctx, qname, predicates));
172     }
173
174     private static <T> T getChild(final ParseTree parent, final int offset, final Class<T> clazz) {
175         final ParseTree child = parent.getChild(offset);
176         verify(clazz.isInstance(child), "Unexpected child %s at offset %s of %s when expecting %s", child, offset,
177             parent, clazz);
178         return clazz.cast(child);
179     }
180
181     private static YangBinaryExpr parsePathPredicate(final StmtContext<?, ?, ?> ctx,
182             final Path_predicateContext predicate) {
183         final Path_equality_exprContext eqExpr = verifyNotNull(predicate.path_equality_expr());
184         return YangBinaryOperator.EQUALS.exprWith(
185             createChildExpr(ctx, getChild(eqExpr, 0, Node_identifierContext.class)),
186             parsePathKeyExpr(ctx, verifyNotNull(eqExpr.path_key_expr())));
187     }
188
189     private static YangExpr parsePathKeyExpr(final StmtContext<?, ?, ?> ctx, final Path_key_exprContext expr) {
190         final Rel_path_keyexprContext relPath = verifyNotNull(expr.rel_path_keyexpr());
191         final int children = relPath.getChildCount();
192
193         // Process dots first
194         final List<Step> steps = new ArrayList<>();
195         int offset = 0;
196         while (offset < children - 1) {
197             final ParseTree child = relPath.getChild(offset);
198             if (child instanceof Node_identifierContext) {
199                 break;
200             }
201             if (child instanceof TerminalNode
202                     && ((TerminalNode) child).getSymbol().getType() == LeafRefPathLexer.DOTS) {
203                 steps.add(YangXPathAxis.PARENT.asStep());
204             }
205
206             ++offset;
207         }
208
209         // Process node identifiers
210         while (offset < children) {
211             final ParseTree child = relPath.getChild(offset);
212             if (child instanceof Node_identifierContext) {
213                 steps.add(createChildStep(ctx, (Node_identifierContext) child, ImmutableList.of()));
214             }
215             ++offset;
216         }
217
218         return steps.isEmpty() ? CURRENT_CALL : YangPathExpr.of(CURRENT_CALL, YangLocationPath.relative(steps));
219     }
220
221     private static YangQNameExpr createChildExpr(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
222         switch (qname.getChildCount()) {
223             case 1:
224                 return YangQNameExpr.of(UnresolvedQName.unqualified(qname.getText()).intern());
225             case 3:
226                 return YangQNameExpr.of(parseQName(ctx, qname));
227             default:
228                 throw new IllegalStateException("Unexpected shape " + qname.getText());
229         }
230     }
231
232     private static QNameStep createChildStep(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname,
233             final List<YangExpr> predicates) {
234         switch (qname.getChildCount()) {
235             case 1:
236                 return YangXPathAxis.CHILD.asStep(UnresolvedQName.unqualified(qname.getText()).intern(), predicates);
237             case 3:
238                 return YangXPathAxis.CHILD.asStep(parseQName(ctx, qname), predicates);
239             default:
240                 throw new IllegalStateException("Unexpected shape " + qname.getText());
241         }
242     }
243
244     private static @NonNull QName parseQName(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
245         return StmtContextUtils.parseNodeIdentifier(ctx, qname.getChild(0).getText(), qname.getChild(2).getText());
246     }
247 }