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