View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.core.metapath;
28  
29  import gov.nist.secauto.metaschema.core.metapath.IComparison.Operator;
30  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Lexer;
31  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.AbbrevforwardstepContext;
32  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.AbbrevreversestepContext;
33  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.AdditiveexprContext;
34  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.AndexprContext;
35  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ArgumentlistContext;
36  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ArrowexprContext;
37  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.AxisstepContext;
38  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ComparisonexprContext;
39  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ContextitemexprContext;
40  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.EqnameContext;
41  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ExprContext;
42  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ForwardstepContext;
43  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.FunctioncallContext;
44  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.GeneralcompContext;
45  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.IntersectexceptexprContext;
46  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.LetexprContext;
47  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.LiteralContext;
48  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.MultiplicativeexprContext;
49  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.NumericliteralContext;
50  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.OrexprContext;
51  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.PathexprContext;
52  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.PostfixexprContext;
53  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.PredicateContext;
54  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.RelativepathexprContext;
55  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ReversestepContext;
56  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.SimpleletbindingContext;
57  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.SimpleletclauseContext;
58  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.StringconcatexprContext;
59  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.UnaryexprContext;
60  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.UnionexprContext;
61  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.ValuecompContext;
62  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.VarrefContext;
63  import gov.nist.secauto.metaschema.core.metapath.antlr.metapath10Parser.WildcardContext;
64  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
65  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
66  
67  import org.antlr.v4.runtime.ParserRuleContext;
68  import org.antlr.v4.runtime.Token;
69  import org.antlr.v4.runtime.tree.ParseTree;
70  import org.antlr.v4.runtime.tree.TerminalNode;
71  
72  import java.math.BigDecimal;
73  import java.math.BigInteger;
74  import java.util.ArrayList;
75  import java.util.List;
76  import java.util.ListIterator;
77  import java.util.Objects;
78  import java.util.function.BiFunction;
79  import java.util.function.Function;
80  import java.util.stream.Collectors;
81  import java.util.stream.Stream;
82  
83  import edu.umd.cs.findbugs.annotations.NonNull;
84  
85  @SuppressWarnings("PMD.CouplingBetweenObjects")
86  class BuildAstVisitor // NOPMD - this visitor has many methods
87      extends AbstractAstVisitor<IExpression> {
88  
89    @SuppressWarnings("null")
90    @Override
91    @NonNull
92    public IExpression visit(ParseTree tree) {
93      return super.visit(tree);
94    }
95  
96    // TODO: verify javadocs are accurate for the following n-ary functions.
97  
98    /**
99     * Parse the provided context as a simple n-ary phrase, which will be one of the
100    * following.
101    * <ol>
102    * <li><code>expr</code> for which the expr will be returned</li>
103    * <li><code>left (operator right)*</code> for which a collection of the left
104    * and right members will be returned based on what is provided by the supplier.
105    * </ol>
106    *
107    * @param <CONTEXT>
108    *          the context type to parse
109    * @param <NODE>
110    *          the type of expression
111    * @param context
112    *          the context instance
113    * @param supplier
114    *          a supplier that will instantiate an expression based on the provided
115    *          collection
116    * @return the left expression or the supplied expression for a collection
117    */
118   @NonNull
119   protected <CONTEXT extends ParserRuleContext, NODE extends IExpression> IExpression
120       handleNAiryCollection(
121           @NonNull CONTEXT context,
122           @NonNull Function<List<NODE>, IExpression> supplier) {
123     return handleNAiryCollection(context, 1, 2, (ctx, idx) -> {
124       // skip operator, since we know what it is
125       ParseTree tree = ctx.getChild(idx + 1);
126       @SuppressWarnings({ "unchecked", "null" })
127       @NonNull NODE node = (NODE) tree.accept(this);
128       return node;
129     }, supplier);
130   }
131 
132   /**
133    * Parse the provided context as a simple n-ary phrase, which will be one of the
134    * following.
135    * <ol>
136    * <li><code>expr</code> for which the expr will be returned</li>
137    * <li><code>left (operator right)*</code> for which a collection of the left
138    * and right members will be returned based on what is provided by the supplier.
139    * </ol>
140    *
141    * @param <CONTEXT>
142    *          the context type to parse
143    * @param <EXPRESSION>
144    *          the child expression type
145    * @param context
146    *          the context instance
147    * @param startIndex
148    *          the starting context child position
149    * @param step
150    *          the amount to advance the loop over the context children
151    * @param parser
152    *          a binary function used to parse the context children
153    * @param supplier
154    *          a supplier that will instantiate an expression based on the provided
155    *          collection
156    * @return the left expression or the supplied expression for a collection
157    */
158   @NonNull
159   protected <CONTEXT extends ParserRuleContext, EXPRESSION extends IExpression> IExpression
160       handleNAiryCollection(
161           @NonNull CONTEXT context,
162           int startIndex,
163           int step,
164           @NonNull BiFunction<CONTEXT, Integer, EXPRESSION> parser,
165           @NonNull Function<List<EXPRESSION>, IExpression> supplier) {
166     int numChildren = context.getChildCount();
167 
168     if (numChildren == 0) {
169       throw new IllegalStateException("there should always be a child expression");
170     } else if (startIndex > numChildren) {
171       throw new IllegalStateException("Start index is out of bounds");
172     }
173 
174     ParseTree leftTree = context.getChild(0);
175     @SuppressWarnings({ "unchecked", "null" })
176     @NonNull EXPRESSION leftResult = (EXPRESSION) leftTree.accept(this);
177 
178     IExpression retval;
179     if (numChildren == 1) {
180       retval = leftResult;
181     } else {
182       List<EXPRESSION> children = new ArrayList<>(numChildren - 1 / step);
183       children.add(leftResult);
184       for (int i = startIndex; i < numChildren; i = i + step) {
185         EXPRESSION result = parser.apply(context, i);
186         children.add(result);
187       }
188       IExpression result = ObjectUtils.notNull(supplier.apply(children));
189       retval = result;
190     }
191     return retval;
192   }
193 
194   /**
195    * Parse the provided context as a simple n-ary phrase, which will be one of the
196    * following.
197    * <ol>
198    * <li><code>expr</code> for which the expr will be returned</li>
199    * <li><code>left (operator right)*</code> for which a collection of the left
200    * and right members will be returned based on what is provided by the supplier.
201    * </ol>
202    *
203    * @param <CONTEXT>
204    *          the context type to parse
205    * @param context
206    *          the context instance
207    * @param startingIndex
208    *          the index of the first child expression, which must be a
209    *          non-negative value that is less than the number of children
210    * @param step
211    *          the amount to advance the loop over the context children
212    * @param parser
213    *          a trinary function used to parse the context children and supply a
214    *          result
215    * @return the left expression or the supplied expression
216    */
217   protected <CONTEXT extends ParserRuleContext> IExpression handleGroupedNAiry(
218       @NonNull CONTEXT context,
219       int startingIndex,
220       int step,
221       @NonNull ITriFunction<CONTEXT, Integer, IExpression, IExpression> parser) {
222     int numChildren = context.getChildCount();
223     if (startingIndex >= numChildren) {
224       throw new IndexOutOfBoundsException(
225           String.format("The starting index '%d' exceeds the child count '%d'",
226               startingIndex,
227               numChildren));
228     }
229 
230     IExpression retval = null;
231     if (numChildren > 0) {
232       ParseTree leftTree = context.getChild(startingIndex);
233       IExpression result = ObjectUtils.notNull(leftTree.accept(this));
234 
235       for (int i = startingIndex + 1; i < numChildren; i = i + step) {
236         result = parser.apply(context, i, result);
237       }
238       retval = result;
239     }
240     return retval;
241   }
242 
243   @Override
244   protected IExpression handleExpr(ExprContext ctx) {
245     return handleNAiryCollection(ctx, children -> {
246       assert children != null;
247       return new Metapath(children);
248     });
249   }
250 
251   @Override
252   protected IExpression handleOrexpr(OrexprContext ctx) {
253     return handleNAiryCollection(ctx, children -> {
254       assert children != null;
255       return new Or(children);
256     });
257   }
258 
259   @Override
260   protected IExpression handleAndexpr(AndexprContext ctx) {
261     return handleNAiryCollection(ctx, children -> {
262       assert children != null;
263       return new And(children);
264     });
265   }
266 
267   @Override
268   protected IExpression handleComparisonexpr(ComparisonexprContext ctx) { // NOPMD - ok
269     assert ctx.getChildCount() == 3;
270 
271     IExpression left = visit(ctx.getChild(0));
272     IExpression right = visit(ctx.getChild(2));
273 
274     // the operator
275     ParseTree operatorTree = ctx.getChild(1);
276     Object payload = operatorTree.getPayload();
277     Operator operator;
278 
279     IComparison retval;
280     if (payload instanceof GeneralcompContext) {
281       GeneralcompContext compContext = (GeneralcompContext) payload;
282       int type = ((TerminalNode) compContext.getChild(0)).getSymbol().getType();
283       switch (type) {
284       case metapath10Lexer.EQ:
285         operator = Operator.EQ;
286         break;
287       case metapath10Lexer.NE:
288         operator = Operator.NE;
289         break;
290       case metapath10Lexer.LT:
291         operator = Operator.LT;
292         break;
293       case metapath10Lexer.LE:
294         operator = Operator.LE;
295         break;
296       case metapath10Lexer.GT:
297         operator = Operator.GT;
298         break;
299       case metapath10Lexer.GE:
300         operator = Operator.GE;
301         break;
302       default:
303         throw new UnsupportedOperationException(((TerminalNode) compContext.getChild(0)).getSymbol().getText());
304       }
305       retval = new GeneralComparison(left, operator, right);
306     } else if (payload instanceof ValuecompContext) {
307       ValuecompContext compContext = (ValuecompContext) payload;
308       int type = ((TerminalNode) compContext.getChild(0)).getSymbol().getType();
309       switch (type) {
310       case metapath10Lexer.KW_EQ:
311         operator = Operator.EQ;
312         break;
313       case metapath10Lexer.KW_NE:
314         operator = Operator.NE;
315         break;
316       case metapath10Lexer.KW_LT:
317         operator = Operator.LT;
318         break;
319       case metapath10Lexer.KW_LE:
320         operator = Operator.LE;
321         break;
322       case metapath10Lexer.KW_GT:
323         operator = Operator.GT;
324         break;
325       case metapath10Lexer.KW_GE:
326         operator = Operator.GE;
327         break;
328       default:
329         throw new UnsupportedOperationException(((TerminalNode) compContext.getChild(0)).getSymbol().getText());
330       }
331       retval = new ValueComparison(left, operator, right);
332     } else {
333       throw new UnsupportedOperationException();
334     }
335     return retval;
336   }
337 
338   @Override
339   protected IExpression handleStringconcatexpr(StringconcatexprContext ctx) {
340     return handleNAiryCollection(ctx, children -> {
341       assert children != null;
342       return new StringConcat(children);
343     });
344   }
345 
346   @Override
347   protected IExpression handleAdditiveexpr(AdditiveexprContext context) {
348     return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> {
349       ParseTree operatorTree = ctx.getChild(idx);
350       ParseTree rightTree = ctx.getChild(idx + 1);
351       IExpression right = rightTree.accept(this);
352 
353       assert left != null;
354       assert right != null;
355 
356       int type = ((TerminalNode) operatorTree).getSymbol().getType();
357 
358       IExpression retval;
359       switch (type) {
360       case metapath10Lexer.PLUS:
361         retval = new Addition(left, right);
362         break;
363       case metapath10Lexer.MINUS:
364         retval = new Subtraction(left, right);
365         break;
366       default:
367         throw new UnsupportedOperationException(((TerminalNode) operatorTree).getSymbol().getText());
368       }
369       return retval;
370     });
371   }
372 
373   @Override
374   protected IExpression handleMultiplicativeexpr(MultiplicativeexprContext context) {
375     return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> {
376       ParseTree operatorTree = ctx.getChild(idx);
377       ParseTree rightTree = ctx.getChild(idx + 1);
378       IExpression right = rightTree.accept(this);
379 
380       assert left != null;
381       assert right != null;
382 
383       int type = ((TerminalNode) operatorTree).getSymbol().getType();
384       IExpression retval;
385       switch (type) {
386       case metapath10Lexer.STAR:
387         retval = new Multiplication(left, right);
388         break;
389       case metapath10Lexer.KW_DIV:
390         retval = new Division(left, right);
391         break;
392       case metapath10Lexer.KW_IDIV:
393         retval = new IntegerDivision(left, right);
394         break;
395       case metapath10Lexer.KW_MOD:
396         retval = new Modulo(left, right);
397         break;
398       default:
399         throw new UnsupportedOperationException(((TerminalNode) operatorTree).getSymbol().getText());
400       }
401       return retval;
402     });
403   }
404 
405   @Override
406   protected IExpression handleUnionexpr(UnionexprContext ctx) {
407     return handleNAiryCollection(ctx, children -> {
408       assert children != null;
409       return new Union(children);
410     });
411   }
412 
413   @Override
414   protected IExpression handleIntersectexceptexpr(IntersectexceptexprContext context) {
415     return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> {
416       ParseTree operatorTree = ctx.getChild(idx);
417       ParseTree rightTree = ctx.getChild(idx + 1);
418       IExpression right = rightTree.accept(this);
419 
420       assert left != null;
421       assert right != null;
422 
423       int type = ((TerminalNode) operatorTree).getSymbol().getType();
424 
425       IExpression retval;
426       switch (type) {
427       case metapath10Lexer.KW_INTERSECT:
428         retval = new Intersect(left, right);
429         break;
430       case metapath10Lexer.KW_EXCEPT:
431         retval = new Except(left, right);
432         break;
433       default:
434         throw new UnsupportedOperationException(((TerminalNode) operatorTree).getSymbol().getText());
435       }
436       return retval;
437     });
438   }
439 
440   @SuppressWarnings("resource")
441   @Override
442   protected IExpression handleArrowexpr(ArrowexprContext context) {
443     // TODO: handle new syntax
444 
445     return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> {
446       // the next child is "=>"
447       assert "=>".equals(ctx.getChild(idx).getText());
448 
449       FunctioncallContext fcCtx = ctx.getChild(FunctioncallContext.class, idx + 1);
450       String name = fcCtx.eqname().getText();
451       assert name != null;
452 
453       Stream<IExpression> args = parseArgumentList(ObjectUtils.notNull(fcCtx.argumentlist()));
454       args = Stream.concat(Stream.of(left), args);
455       assert args != null;
456 
457       return new FunctionCall(name, ObjectUtils.notNull(args.collect(Collectors.toUnmodifiableList())));
458     });
459   }
460 
461   @Override
462   protected IExpression handleUnaryexpr(UnaryexprContext ctx) {
463     int numChildren = ctx.getChildCount();
464     int negateCount = 0;
465 
466     int idx = 0;
467     for (; idx < numChildren - 1; idx++) {
468       ParseTree tree = ctx.getChild(idx);
469       int type = ((TerminalNode) tree).getSymbol().getType();
470       switch (type) {
471       case metapath10Lexer.PLUS:
472         break;
473       case metapath10Lexer.MINUS:
474         negateCount++;
475         break;
476       default:
477         throw new UnsupportedOperationException(((TerminalNode) tree).getSymbol().getText());
478       }
479     }
480 
481     ParseTree expr = ctx.getChild(0);
482     IExpression retval = expr.accept(this);
483     assert retval != null;
484     if (negateCount % 2 != 0) {
485       retval = new Negate(retval);
486     }
487     return retval;
488   }
489 
490   @Override
491   protected IExpression handlePathexpr(PathexprContext ctx) {
492     int numChildren = ctx.getChildCount();
493 
494     IExpression retval;
495     ParseTree tree = ctx.getChild(0);
496     if (tree instanceof TerminalNode) {
497       int type = ((TerminalNode) tree).getSymbol().getType();
498       switch (type) {
499       case metapath10Lexer.SLASH:
500         // a slash expression with optional path
501         if (numChildren == 2) {
502           // the optional path
503           ParseTree pathTree = ctx.getChild(1);
504           retval = new RootSlashPath(ObjectUtils.notNull(pathTree.accept(this)));
505         } else {
506           retval = new RootSlashOnlyPath();
507         }
508         break;
509       case metapath10Lexer.SS:
510         // a double slash expression with path
511         ParseTree pathTree = ctx.getChild(1);
512         IExpression node = pathTree.accept(this);
513         assert node != null;
514         retval = new RootDoubleSlashPath(node);
515         break;
516       default:
517         throw new UnsupportedOperationException(((TerminalNode) tree).getSymbol().getText());
518       }
519     } else {
520       // a relative expression or something else
521       retval = tree.accept(this);
522     }
523     return retval;
524   }
525 
526   @Override
527   protected IExpression handleRelativepathexpr(RelativepathexprContext context) {
528     return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> {
529       ParseTree operatorTree = ctx.getChild(idx);
530       ParseTree rightTree = ctx.getChild(idx + 1);
531       IExpression rightResult = rightTree.accept(this);
532 
533       assert left != null;
534       assert rightResult != null;
535 
536       int type = ((TerminalNode) operatorTree).getSymbol().getType();
537 
538       IExpression retval;
539       switch (type) {
540       case metapath10Lexer.SLASH:
541         retval = new RelativeSlashPath(left, rightResult);
542         break;
543       case metapath10Lexer.SS:
544         retval = new RelativeDoubleSlashPath(left, rightResult);
545         break;
546       default:
547         throw new UnsupportedOperationException(((TerminalNode) operatorTree).getSymbol().getText());
548       }
549       return retval;
550     });
551   }
552 
553   @SuppressWarnings("null")
554   @NonNull
555   protected IExpression parsePredicate(@NonNull PredicateContext context) {
556     // the expression is always the second child
557     ParseTree tree = context.getChild(1);
558     return tree.accept(this);
559   }
560 
561   @NonNull
562   protected List<IExpression> parsePredicates(@NonNull ParseTree context, int staringChild) {
563     int numChildren = context.getChildCount();
564     int numPredicates = numChildren - staringChild;
565 
566     List<IExpression> predicates;
567     if (numPredicates == 0) {
568       // no predicates
569       predicates = CollectionUtil.emptyList();
570     } else if (numPredicates == 1) {
571       // single predicate
572       PredicateContext predicate = ObjectUtils.notNull((PredicateContext) context.getChild(staringChild));
573       predicates = CollectionUtil.singletonList(parsePredicate(predicate));
574     } else {
575       // multiple predicates
576       predicates = new ArrayList<>(numPredicates);
577       for (int i = staringChild; i < numChildren; i++) {
578         PredicateContext predicate = ObjectUtils.notNull((PredicateContext) context.getChild(i));
579         predicates.add(parsePredicate(predicate));
580       }
581     }
582     return predicates;
583   }
584 
585   @Override
586   protected IExpression handlePostfixexpr(PostfixexprContext ctx) {
587     int numChildren = ctx.getChildCount();
588     ParseTree primaryTree = ctx.getChild(0);
589     IExpression retval = ObjectUtils.notNull(primaryTree.accept(this));
590 
591     List<IExpression> predicates = numChildren > 1 ? parsePredicates(ctx, 1) : CollectionUtil.emptyList();
592 
593     if (!predicates.isEmpty()) {
594       retval = new Predicate(retval, predicates);
595     }
596     return retval;
597 
598   }
599 
600   @Override
601   protected IExpression handleAxisstep(AxisstepContext ctx) {
602     IExpression step = ctx.getChild(0).accept(this);
603     assert step != null;
604 
605     ParseTree predicateTree = ctx.getChild(1);
606     assert predicateTree != null;
607 
608     List<IExpression> predicates = parsePredicates(predicateTree, 0);
609 
610     return predicates.isEmpty() ? step : new Predicate(step, predicates);
611   }
612 
613   @Override
614   protected IExpression handleForwardstep(ForwardstepContext ctx) {
615     assert ctx.getChildCount() == 2;
616 
617     Token token = (Token) ctx.forwardaxis().getChild(0).getPayload();
618 
619     Axis axis;
620     switch (token.getType()) {
621     case metapath10Lexer.KW_SELF:
622       axis = Axis.SELF;
623       break;
624     case metapath10Lexer.KW_CHILD:
625       axis = Axis.CHILDREN;
626       break;
627     case metapath10Lexer.KW_DESCENDANT:
628       axis = Axis.DESCENDANT;
629       break;
630     case metapath10Lexer.KW_DESCENDANT_OR_SELF:
631       axis = Axis.DESCENDANT_OR_SELF;
632       break;
633     default:
634       throw new UnsupportedOperationException(token.getText());
635     }
636     return new Step(axis, ObjectUtils.notNull(ctx.nametest().accept(this)));
637   }
638 
639   @Override
640   protected IExpression handleAbbrevforwardstep(AbbrevforwardstepContext ctx) {
641     int numChildren = ctx.getChildCount();
642 
643     IExpression retval;
644     if (numChildren == 1) {
645       ParseTree tree = ctx.getChild(0);
646       retval = new ModelInstance(ObjectUtils.notNull(tree.accept(this)));
647     } else {
648       // this is an AT test
649       ParseTree tree = ctx.getChild(1);
650       retval = new Flag(ObjectUtils.notNull(tree.accept(this)));
651 
652     }
653     return retval;
654   }
655 
656   @Override
657   protected IExpression handleReversestep(ReversestepContext ctx) {
658     assert ctx.getChildCount() == 2;
659 
660     Token token = (Token) ctx.reverseaxis().getChild(0).getPayload();
661 
662     Axis axis;
663     switch (token.getType()) {
664     case metapath10Lexer.KW_PARENT:
665       axis = Axis.PARENT;
666       break;
667     case metapath10Lexer.KW_ANCESTOR:
668       axis = Axis.ANCESTOR;
669       break;
670     case metapath10Lexer.KW_ANCESTOR_OR_SELF:
671       axis = Axis.ANCESTOR_OR_SELF;
672       break;
673     default:
674       throw new UnsupportedOperationException(token.getText());
675     }
676     return new Step(axis, ObjectUtils.notNull(ctx.nametest().accept(this)));
677   }
678 
679   @Override
680   protected IExpression handleAbbrevreversestep(AbbrevreversestepContext ctx) {
681     return Axis.PARENT;
682   }
683 
684   @Override
685   protected IExpression handleStringLiteral(LiteralContext ctx) {
686     ParseTree tree = ctx.getChild(0);
687     return new StringLiteral(ObjectUtils.notNull(tree.getText()));
688   }
689 
690   @Override
691   protected IExpression handleNumericLiteral(NumericliteralContext ctx) {
692     ParseTree tree = ctx.getChild(0);
693     Token token = (Token) tree.getPayload();
694     IExpression retval;
695     switch (token.getType()) {
696     case metapath10Lexer.IntegerLiteral:
697       retval = new IntegerLiteral(new BigInteger(token.getText()));
698       break;
699     case metapath10Lexer.DecimalLiteral:
700     case metapath10Lexer.DoubleLiteral:
701       retval = new DecimalLiteral(new BigDecimal(token.getText()));
702       break;
703     default:
704       throw new UnsupportedOperationException(token.getText());
705     }
706     return retval;
707   }
708 
709   @Override
710   protected IExpression handleContextitemexpr(ContextitemexprContext ctx) {
711     return ContextItem.instance();
712   }
713 
714   @NonNull
715   protected Stream<IExpression> parseArgumentList(@NonNull ArgumentlistContext context) {
716     int numChildren = context.getChildCount();
717 
718     Stream<IExpression> retval;
719     if (numChildren == 2) {
720       // just the OP CP tokens, which is an empty list
721       retval = Stream.empty();
722     } else {
723       retval = context.argument().stream()
724           .map(argument -> {
725             return argument.exprsingle().accept(this);
726           });
727     }
728     assert retval != null;
729 
730     return retval;
731   }
732 
733   @Override
734   protected IExpression handleFunctioncall(FunctioncallContext ctx) {
735     EqnameContext nameCtx = ctx.eqname();
736     String name = nameCtx.getText();
737 
738     assert name != null;
739 
740     return new FunctionCall(
741         name,
742         ObjectUtils.notNull(parseArgumentList(ObjectUtils.notNull(ctx.argumentlist()))
743             .collect(Collectors.toUnmodifiableList())));
744   }
745 
746   @Override
747   protected IExpression handleEqname(EqnameContext ctx) {
748     ParseTree tree = ctx.getChild(0);
749     String name = ((TerminalNode) tree).getText();
750 
751     assert name != null;
752 
753     return new Name(name);
754   }
755 
756   @Override
757   protected IExpression handleWildcard(WildcardContext ctx) {
758     return new Wildcard();
759   }
760 
761   @FunctionalInterface
762   interface ITriFunction<T, U, V, R> {
763 
764     R apply(T argT, U argU, V argV);
765 
766     default <W> ITriFunction<T, U, V, W> andThen(Function<? super R, ? extends W> after) {
767       Objects.requireNonNull(after);
768       return (T t, U u, V v) -> after.apply(apply(t, u, v));
769     }
770   }
771 
772   @Override
773   protected IExpression handleLet(LetexprContext context) {
774     @NonNull IExpression retval = ObjectUtils.notNull(context.exprsingle().accept(this));
775 
776     SimpleletclauseContext letClause = context.simpleletclause();
777     List<SimpleletbindingContext> clauses = letClause.simpleletbinding();
778 
779     ListIterator<SimpleletbindingContext> reverseListIterator = clauses.listIterator(clauses.size());
780     while (reverseListIterator.hasPrevious()) {
781       SimpleletbindingContext simpleCtx = reverseListIterator.previous();
782 
783       Name varName = (Name) simpleCtx.varname().accept(this);
784       IExpression boundExpression = simpleCtx.exprsingle().accept(this);
785 
786       assert varName != null;
787       assert boundExpression != null;
788 
789       retval = new Let(varName, boundExpression, retval); // NOPMD intended
790     }
791     return retval;
792   }
793 
794   @Override
795   protected IExpression handleVarref(VarrefContext ctx) {
796     Name varName = (Name) ctx.varname().accept(this);
797     assert varName != null;
798     return new VariableReference(varName);
799   }
800 }