Java tutorial
/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.dmn.feel.parser.feel11; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.kie.dmn.feel.lang.CompositeType; import org.kie.dmn.feel.lang.Type; import org.kie.dmn.feel.lang.ast.ASTBuilderFactory; import org.kie.dmn.feel.lang.ast.BaseNode; import org.kie.dmn.feel.lang.ast.BetweenNode; import org.kie.dmn.feel.lang.ast.BooleanNode; import org.kie.dmn.feel.lang.ast.ContextEntryNode; import org.kie.dmn.feel.lang.ast.DashNode; import org.kie.dmn.feel.lang.ast.FunctionInvocationNode; import org.kie.dmn.feel.lang.ast.InNode; import org.kie.dmn.feel.lang.ast.InfixOpNode; import org.kie.dmn.feel.lang.ast.InstanceOfNode; import org.kie.dmn.feel.lang.ast.ListNode; import org.kie.dmn.feel.lang.ast.NameDefNode; import org.kie.dmn.feel.lang.ast.NameRefNode; import org.kie.dmn.feel.lang.ast.QualifiedNameNode; import org.kie.dmn.feel.lang.ast.QuantifiedExpressionNode; import org.kie.dmn.feel.lang.ast.RangeNode; import org.kie.dmn.feel.lang.ast.TypeNode; import org.kie.dmn.feel.lang.ast.UnaryTestListNode; import org.kie.dmn.feel.lang.ast.UnaryTestNode; import org.kie.dmn.feel.lang.types.BuiltInType; import org.kie.dmn.feel.parser.feel11.FEEL_1_1Parser.RelExpressionValueContext; public class ASTBuilderVisitor extends FEEL_1_1BaseVisitor<BaseNode> { private ScopeHelper scopeHelper; private static class ScopeHelper { Deque<Map<String, Type>> stack; public ScopeHelper() { this.stack = new ArrayDeque<>(); this.stack.push(new HashMap<>()); } public void addTypes(Map<String, Type> inputTypes) { stack.peek().putAll(inputTypes); } public void addType(String name, Type type) { stack.peek().put(name, type); } public void pushScope() { stack.push(new HashMap<>()); } public void popScope() { stack.pop(); } public Optional<Type> resolveType(String name) { return stack.stream().map(scope -> Optional.ofNullable(scope.get(name))) .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty()).findFirst(); } } public ASTBuilderVisitor(Map<String, Type> inputTypes) { this.scopeHelper = new ScopeHelper(); this.scopeHelper.addTypes(inputTypes); } @Override public BaseNode visitNumberLiteral(FEEL_1_1Parser.NumberLiteralContext ctx) { return ASTBuilderFactory.newNumberNode(ctx); } @Override public BaseNode visitBoolLiteral(FEEL_1_1Parser.BoolLiteralContext ctx) { return ASTBuilderFactory.newBooleanNode(ctx); } @Override public BaseNode visitSignedUnaryExpressionPlus(FEEL_1_1Parser.SignedUnaryExpressionPlusContext ctx) { BaseNode node = visit(ctx.unaryExpressionNotPlusMinus()); return ASTBuilderFactory.newSignedUnaryNode(ctx, node); } @Override public BaseNode visitSignedUnaryExpressionMinus(FEEL_1_1Parser.SignedUnaryExpressionMinusContext ctx) { BaseNode node = visit(ctx.unaryExpression()); return ASTBuilderFactory.newSignedUnaryNode(ctx, node); } @Override public BaseNode visitNullLiteral(FEEL_1_1Parser.NullLiteralContext ctx) { return ASTBuilderFactory.newNullNode(ctx); } @Override public BaseNode visitStringLiteral(FEEL_1_1Parser.StringLiteralContext ctx) { return ASTBuilderFactory.newStringNode(ctx); } @Override public BaseNode visitPrimaryParens(FEEL_1_1Parser.PrimaryParensContext ctx) { return visit(ctx.expression()); } @Override public BaseNode visitPowExpression(FEEL_1_1Parser.PowExpressionContext ctx) { BaseNode left = visit(ctx.powerExpression()); BaseNode right = visit(ctx.filterPathExpression()); String op = ctx.op.getText(); return ASTBuilderFactory.newInfixOpNode(ctx, left, op, right); } @Override public BaseNode visitMultExpression(FEEL_1_1Parser.MultExpressionContext ctx) { BaseNode left = visit(ctx.multiplicativeExpression()); BaseNode right = visit(ctx.powerExpression()); String op = ctx.op.getText(); return ASTBuilderFactory.newInfixOpNode(ctx, left, op, right); } @Override public BaseNode visitAddExpression(FEEL_1_1Parser.AddExpressionContext ctx) { BaseNode left = visit(ctx.additiveExpression()); BaseNode right = visit(ctx.multiplicativeExpression()); String op = ctx.op.getText(); return ASTBuilderFactory.newInfixOpNode(ctx, left, op, right); } @Override public BaseNode visitRelExpressionBetween(FEEL_1_1Parser.RelExpressionBetweenContext ctx) { BaseNode value = visit(ctx.val); BaseNode start = visit(ctx.start); BaseNode end = visit(ctx.end); return ASTBuilderFactory.newBetweenNode(ctx, value, start, end); } @Override public BaseNode visitExpressionList(FEEL_1_1Parser.ExpressionListContext ctx) { List<BaseNode> exprs = new ArrayList<>(); for (int i = 0; i < ctx.getChildCount(); i++) { if (ctx.getChild(i) instanceof FEEL_1_1Parser.ExpressionContext) { exprs.add(visit(ctx.getChild(i))); } } return ASTBuilderFactory.newListNode(ctx, exprs); } @Override public BaseNode visitInterval(FEEL_1_1Parser.IntervalContext ctx) { BaseNode start = visit(ctx.start); BaseNode end = visit(ctx.end); RangeNode.IntervalBoundary low = ctx.low.getText().equals("[") ? RangeNode.IntervalBoundary.CLOSED : RangeNode.IntervalBoundary.OPEN; RangeNode.IntervalBoundary up = ctx.up.getText().equals("]") ? RangeNode.IntervalBoundary.CLOSED : RangeNode.IntervalBoundary.OPEN; return ASTBuilderFactory.newIntervalNode(ctx, low, start, end, up); } @Override public BaseNode visitPositiveUnaryTestIneq(FEEL_1_1Parser.PositiveUnaryTestIneqContext ctx) { BaseNode value = visit(ctx.endpoint()); String op = ctx.op.getText(); return ASTBuilderFactory.newUnaryTestNode(ctx, op, value); } @Override public BaseNode visitPositiveUnaryTests(FEEL_1_1Parser.PositiveUnaryTestsContext ctx) { List<BaseNode> tests = new ArrayList<>(); for (int i = 0; i < ctx.getChildCount(); i++) { if (ctx.getChild(i) instanceof FEEL_1_1Parser.PositiveUnaryTestContext || ctx.getChild(i) instanceof FEEL_1_1Parser.PrimaryContext) { tests.add(visit(ctx.getChild(i))); } } return ASTBuilderFactory.newListNode(ctx, tests); } @Override public BaseNode visitRelExpressionTestList(FEEL_1_1Parser.RelExpressionTestListContext ctx) { BaseNode value = visit(ctx.val); BaseNode list = visit(ctx.positiveUnaryTests()); return ASTBuilderFactory.newInNode(ctx, value, list); } @Override public BaseNode visitRelExpressionValue(RelExpressionValueContext ctx) { BaseNode value = visit(ctx.val); BaseNode test = visit(ctx.expression()); return ASTBuilderFactory.newInNode(ctx, value, test); } @Override public BaseNode visitPositiveUnaryTestDash(FEEL_1_1Parser.PositiveUnaryTestDashContext ctx) { return ASTBuilderFactory.newDashNode(ctx); } @Override public BaseNode visitCompExpression(FEEL_1_1Parser.CompExpressionContext ctx) { BaseNode left = visit(ctx.left); BaseNode right = visit(ctx.right); return ASTBuilderFactory.newInfixOpNode(ctx, left, ctx.op.getText(), right); } @Override public BaseNode visitCondOr(FEEL_1_1Parser.CondOrContext ctx) { BaseNode left = visit(ctx.left); BaseNode right = visit(ctx.right); return ASTBuilderFactory.newInfixOpNode(ctx, left, ctx.op.getText(), right); } @Override public BaseNode visitCondAnd(FEEL_1_1Parser.CondAndContext ctx) { BaseNode left = visit(ctx.left); BaseNode right = visit(ctx.right); return ASTBuilderFactory.newInfixOpNode(ctx, left, ctx.op.getText(), right); } @Override public BaseNode visitList(FEEL_1_1Parser.ListContext ctx) { if (ctx.expressionList() == null) { // empty list -> children are [ ] return ASTBuilderFactory.newListNode(ctx, new ArrayList<>()); } else { // returns actual list return visit(ctx.expressionList()); } } @Override public BaseNode visitNameDefinition(FEEL_1_1Parser.NameDefinitionContext ctx) { List<String> tokenStrs = new ArrayList<>(); List<Token> tokens = new ArrayList<>(); for (int i = 0; i < ctx.getChildCount(); i++) { visit(ctx.getChild(i)); } ParserHelper.getAllTokens(ctx, tokens); for (Token t : tokens) { tokenStrs.add(t.getText()); } return ASTBuilderFactory.newNameDefNode(ctx, tokenStrs); } @Override public BaseNode visitIterationNameDefinition(FEEL_1_1Parser.IterationNameDefinitionContext ctx) { List<String> tokenStrs = new ArrayList<>(); List<Token> tokens = new ArrayList<>(); for (int i = 0; i < ctx.getChildCount(); i++) { visit(ctx.getChild(i)); } ParserHelper.getAllTokens(ctx, tokens); for (Token t : tokens) { tokenStrs.add(t.getText()); } return ASTBuilderFactory.newNameDefNode(ctx, tokenStrs); } @Override public BaseNode visitKeyString(FEEL_1_1Parser.KeyStringContext ctx) { return ASTBuilderFactory.newStringNode(ctx); } @Override public BaseNode visitKeyName(FEEL_1_1Parser.KeyNameContext ctx) { return visit(ctx.nameDefinition()); } @Override public BaseNode visitContextEntry(FEEL_1_1Parser.ContextEntryContext ctx) { FEEL_1_1Parser.KeyContext key = ctx.key(); FEEL_1_1Parser.ExpressionContext expression = ctx.expression(); if (key == null || expression == null) { return null; } BaseNode name = visit(key); BaseNode value = visit(expression); if (value == null) return null; return ASTBuilderFactory.newContextEntry(ctx, name, value); } @Override public BaseNode visitContextEntries(FEEL_1_1Parser.ContextEntriesContext ctx) { List<BaseNode> nodes = new ArrayList<>(); scopeHelper.pushScope(); for (FEEL_1_1Parser.ContextEntryContext c : ctx.contextEntry()) { ContextEntryNode visited = (ContextEntryNode) visit(c); // forced cast similarly to visitFunctionDefinition() method if (visited != null) { nodes.add(visited); scopeHelper.addType(visited.getName().getText(), visited.getResultType()); } } scopeHelper.popScope(); return ASTBuilderFactory.newListNode(ctx, nodes); } @Override public BaseNode visitContext(FEEL_1_1Parser.ContextContext ctx) { ListNode list = ctx.contextEntries() != null ? (ListNode) visit(ctx.contextEntries()) : ASTBuilderFactory.newListNode(ctx, new ArrayList<>()); return ASTBuilderFactory.newContextNode(ctx, list); } @Override public BaseNode visitFormalParameters(FEEL_1_1Parser.FormalParametersContext ctx) { List<BaseNode> list = new ArrayList<>(); for (FEEL_1_1Parser.FormalParameterContext fpc : ctx.formalParameter()) { list.add(visit(fpc)); } return ASTBuilderFactory.newListNode(ctx, list); } @Override public BaseNode visitFormalParameter(FEEL_1_1Parser.FormalParameterContext ctx) { NameDefNode name = (NameDefNode) visit(ctx.nameDefinition()); TypeNode type = ctx.type() != null ? (TypeNode) visit(ctx.type()) : null; return ASTBuilderFactory.newFormalParameter(ctx, name, type); } @Override public BaseNode visitFunctionDefinition(FEEL_1_1Parser.FunctionDefinitionContext ctx) { ListNode parameters = null; if (ctx.formalParameters() != null) { parameters = (ListNode) visit(ctx.formalParameters()); } boolean external = ctx.external != null; BaseNode body = visit(ctx.body); return ASTBuilderFactory.newFunctionDefinition(ctx, parameters, external, body); } @Override public BaseNode visitIterationContext(FEEL_1_1Parser.IterationContextContext ctx) { NameDefNode name = (NameDefNode) visit(ctx.iterationNameDefinition()); BaseNode expr = visit(ctx.expression().get(0)); if (ctx.expression().size() == 1) { return ASTBuilderFactory.newIterationContextNode(ctx, name, expr); } else { BaseNode rangeEndExpr = visit(ctx.expression().get(1)); return ASTBuilderFactory.newIterationContextNode(ctx, name, expr, rangeEndExpr); } } @Override public BaseNode visitIterationContexts(FEEL_1_1Parser.IterationContextsContext ctx) { ArrayList<BaseNode> ctxs = new ArrayList<>(); for (FEEL_1_1Parser.IterationContextContext ic : ctx.iterationContext()) { ctxs.add(visit(ic)); } return ASTBuilderFactory.newListNode(ctx, ctxs); } @Override public BaseNode visitForExpression(FEEL_1_1Parser.ForExpressionContext ctx) { ListNode list = (ListNode) visit(ctx.iterationContexts()); BaseNode expr = visit(ctx.expression()); return ASTBuilderFactory.newForExpression(ctx, list, expr); } @Override public BaseNode visitQualifiedName(FEEL_1_1Parser.QualifiedNameContext ctx) { ArrayList<NameRefNode> parts = new ArrayList<>(); Type typeCursor = null; for (FEEL_1_1Parser.NameRefContext t : ctx.nameRef()) { String originalText = ParserHelper.getOriginalText(t); if (typeCursor == null) { typeCursor = scopeHelper.resolveType(originalText).orElse(BuiltInType.UNKNOWN); } else if (typeCursor instanceof CompositeType) { typeCursor = ((CompositeType) typeCursor).getFields().get(originalText); } else { // TODO throw error here? typeCursor = BuiltInType.UNKNOWN; } parts.add(ASTBuilderFactory.newNameRefNode(t, typeCursor)); } return parts.size() > 1 ? ASTBuilderFactory.newQualifiedNameNode(ctx, parts, typeCursor) : parts.get(0); } @Override public BaseNode visitIfExpression(FEEL_1_1Parser.IfExpressionContext ctx) { BaseNode c = visit(ctx.c); BaseNode t = visit(ctx.t); BaseNode e = visit(ctx.e); return ASTBuilderFactory.newIfExpression(ctx, c, t, e); } @Override public BaseNode visitQuantExprSome(FEEL_1_1Parser.QuantExprSomeContext ctx) { ListNode list = (ListNode) visit(ctx.iterationContexts()); BaseNode expr = visit(ctx.expression()); return ASTBuilderFactory.newQuantifiedExpression(ctx, QuantifiedExpressionNode.Quantifier.SOME, list, expr); } @Override public BaseNode visitQuantExprEvery(FEEL_1_1Parser.QuantExprEveryContext ctx) { ListNode list = (ListNode) visit(ctx.iterationContexts()); BaseNode expr = visit(ctx.expression()); return ASTBuilderFactory.newQuantifiedExpression(ctx, QuantifiedExpressionNode.Quantifier.EVERY, list, expr); } // TODO verify, this is never covered in test, possibly as qualifiedName visitor "ingest" it directly. @Override public BaseNode visitNameRef(FEEL_1_1Parser.NameRefContext ctx) { return ASTBuilderFactory.newNameRefNode(ctx, BuiltInType.UNKNOWN); } @Override public BaseNode visitPositionalParameters(FEEL_1_1Parser.PositionalParametersContext ctx) { List<BaseNode> params = new ArrayList<>(); for (FEEL_1_1Parser.ExpressionContext ec : ctx.expression()) { params.add(visit(ec)); } return ASTBuilderFactory.newListNode(ctx, params); } @Override public BaseNode visitNamedParameter(FEEL_1_1Parser.NamedParameterContext ctx) { NameDefNode name = (NameDefNode) visit(ctx.name); BaseNode value = visit(ctx.value); return ASTBuilderFactory.newNamedParameterNode(ctx, name, value); } @Override public BaseNode visitNamedParameters(FEEL_1_1Parser.NamedParametersContext ctx) { List<BaseNode> params = new ArrayList<>(); for (FEEL_1_1Parser.NamedParameterContext npc : ctx.namedParameter()) { params.add(visit(npc)); } return ASTBuilderFactory.newListNode(ctx, params); } @Override public BaseNode visitParametersEmpty(FEEL_1_1Parser.ParametersEmptyContext ctx) { return ASTBuilderFactory.newListNode(ctx, new ArrayList<>()); } @Override public BaseNode visitParametersNamed(FEEL_1_1Parser.ParametersNamedContext ctx) { return visit(ctx.namedParameters()); } @Override public BaseNode visitParametersPositional(FEEL_1_1Parser.ParametersPositionalContext ctx) { return visit(ctx.positionalParameters()); } @Override public BaseNode visitPrimaryName(FEEL_1_1Parser.PrimaryNameContext ctx) { BaseNode name = visit(ctx.qualifiedName()); if (ctx.parameters() != null) { ListNode params = (ListNode) visit(ctx.parameters()); return buildFunctionCall(ctx, name, params); } else { return name; } } private String getFunctionName(BaseNode name) { String functionName = null; if (name instanceof NameRefNode) { // simple name functionName = name.getText(); } else if (name instanceof QualifiedNameNode) { QualifiedNameNode qn = (QualifiedNameNode) name; functionName = qn.getParts().stream().map(p -> p.getText()).collect(Collectors.joining(" ")); } return functionName; } private BaseNode buildFunctionCall(ParserRuleContext ctx, BaseNode name, ListNode params) { String functionName = getFunctionName(name); return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } @Override public BaseNode visitUnaryTestsRoot(FEEL_1_1Parser.UnaryTestsRootContext ctx) { return visit(ctx.unaryTests()); } @Override public BaseNode visitUnaryTests_empty(FEEL_1_1Parser.UnaryTests_emptyContext ctx) { return ASTBuilderFactory.newUnaryTestListNode(ctx, Collections.singletonList(ASTBuilderFactory.newDashNode(ctx)), UnaryTestListNode.State.Positive); } @Override public BaseNode visitUnaryTests_positive(FEEL_1_1Parser.UnaryTests_positiveContext ctx) { ListNode list = (ListNode) visit(ctx.positiveUnaryTests()); return ASTBuilderFactory.newUnaryTestListNode(ctx, list.getElements(), UnaryTestListNode.State.Positive); } @Override public BaseNode visitUnaryTests_negated(FEEL_1_1Parser.UnaryTests_negatedContext ctx) { BaseNode name = ASTBuilderFactory.newNameRefNode(ctx, "not", BuiltInType.BOOLEAN); // negating a unary tests: BOOLEAN-type anyway ListNode value = (ListNode) visit(ctx.positiveUnaryTests()); return ASTBuilderFactory.newUnaryTestListNode(ctx, value.getElements(), UnaryTestListNode.State.Negated); } private BaseNode buildNotCall(ParserRuleContext ctx, BaseNode name, ListNode params) { // if a not() call is found, we have to differentiate between the boolean function // and the unary tests operator if (params.getElements().size() == 1) { // if it is a single parameter, we need to check if the type is boolean BaseNode param = params.getElements().get(0); if (param instanceof UnaryTestNode) { return ASTBuilderFactory.newUnaryTestNode(ctx, "not", params); } else if (param instanceof BooleanNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof NameRefNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof QuantifiedExpressionNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof InstanceOfNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof BetweenNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof InNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof InfixOpNode && ((InfixOpNode) param).isBoolean()) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof FunctionInvocationNode) { return ASTBuilderFactory.newFunctionInvocationNode(ctx, name, params); } else if (param instanceof RangeNode) { return ASTBuilderFactory.newUnaryTestNode(ctx, "not", params); } else if (param instanceof DashNode) { return ASTBuilderFactory.newUnaryTestNode(ctx, "not", params); } else { return ASTBuilderFactory.newUnaryTestNode(ctx, "not", params); } } else { return ASTBuilderFactory.newUnaryTestNode(ctx, "not", params); } } @Override public TypeNode visitType(FEEL_1_1Parser.TypeContext ctx) { return ASTBuilderFactory.newTypeNode(ctx); } @Override public BaseNode visitRelExpressionInstanceOf(FEEL_1_1Parser.RelExpressionInstanceOfContext ctx) { BaseNode expr = visit(ctx.val); TypeNode type = (TypeNode) visit(ctx.type()); return ASTBuilderFactory.newInstanceOfNode(ctx, expr, type); } @Override public BaseNode visitFilterPathExpression(FEEL_1_1Parser.FilterPathExpressionContext ctx) { if (ctx.filter != null) { BaseNode expr = visit(ctx.filterPathExpression()); BaseNode filter = visit(ctx.filter); expr = ASTBuilderFactory.newFilterExpressionNode(ctx, expr, filter); return expr; } else if (ctx.qualifiedName() != null) { BaseNode expr = visit(ctx.filterPathExpression()); BaseNode path = visit(ctx.qualifiedName()); return ASTBuilderFactory.newPathExpressionNode(ctx, expr, path); } else { return visit(ctx.unaryExpression()); } } @Override public BaseNode visitExpressionTextual(FEEL_1_1Parser.ExpressionTextualContext ctx) { BaseNode expr = visit(ctx.expr); return expr; } @Override public BaseNode visitUenpmPrimary(FEEL_1_1Parser.UenpmPrimaryContext ctx) { BaseNode expr = visit(ctx.primary()); if (ctx.qualifiedName() != null) { BaseNode path = visit(ctx.qualifiedName()); expr = ASTBuilderFactory.newPathExpressionNode(ctx, expr, path); } if (ctx.parameters() != null) { ListNode params = (ListNode) visit(ctx.parameters()); return buildFunctionCall(ctx, expr, params); } return expr; } @Override public BaseNode visitCompilation_unit(FEEL_1_1Parser.Compilation_unitContext ctx) { return visit(ctx.expression()); } }