aboutsummaryrefslogtreecommitdiffstats
path: root/indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/StatementExpression.java
blob: 66a45cb75e2b14e57ce790b95cbfdd95173e2261 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.indexinglanguage.expressions;

import com.yahoo.document.DataType;
import com.yahoo.language.Linguistics;
import com.yahoo.language.process.Embedder;
import com.yahoo.language.simple.SimpleLinguistics;
import com.yahoo.vespa.indexinglanguage.ExpressionConverter;
import com.yahoo.vespa.indexinglanguage.ScriptParser;
import com.yahoo.vespa.indexinglanguage.ScriptParserContext;
import com.yahoo.vespa.indexinglanguage.parser.IndexingInput;
import com.yahoo.vespa.indexinglanguage.parser.ParseException;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Simon Thoresen Hult
 */
public final class StatementExpression extends ExpressionList<Expression> {

    /** The names of the fields consumed by this. */
    private final List<String> inputFields;

    /** The name of the (last) output field this statement will write to, or null if none */
    private String outputField;

    public StatementExpression(Expression... lst) {
        this(Arrays.asList(lst));
    }

    public StatementExpression(Iterable<Expression> lst) {
        this(filterList(lst), null);
    }

    private StatementExpression(Iterable<Expression> list, Object unused) {
        super(list, resolveInputType(list));
        inputFields = List.copyOf(InputExpression.InputFieldNameExtractor.runOn(this));
    }

    /** Returns the input fields which are (perhaps optionally) consumed by some expression in this statement. */
    public List<String> getInputFields() { return inputFields; }

    @Override
    public StatementExpression convertChildren(ExpressionConverter converter) {
        return new StatementExpression(asList().stream()
                                               .map(child -> converter.convert(child))
                                               .filter(Objects::nonNull)
                                               .toList());
    }

    @Override
    protected void doExecute(ExecutionContext context) {
        for (Expression expression : this) {
            context.execute(expression);
        }
    }

    @Override
    protected void doVerify(VerificationContext context) {
        for (Expression expression : this) {
            if (expression instanceof OutputExpression)
                outputField = ((OutputExpression)expression).getFieldName();
        }
        if (outputField != null)
            context.setOutputField(outputField);
        for (Expression expression : this)
            context.execute(expression);
    }

    private static DataType resolveInputType(Iterable<Expression> expressions) {
        for (Expression expression : expressions) {
            DataType type = expression.requiredInputType();
            if (type != null) return type;

            type = expression.createdOutputType();
            if (type != null) return null;
        }
        return null;
    }

    @Override
    public DataType createdOutputType() {
        for (int i = size(); --i >= 0; ) {
            DataType type = get(i).createdOutputType();
            if (type != null) return type;
        }
        return null;
    }

    @Override
    public String toString() {
        return asList().stream().map(Expression::toString).collect(Collectors.joining(" | "));
    }

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj) && obj instanceof StatementExpression;
    }

    /** Creates an expression with simple lingustics for testing */
    public static StatementExpression fromString(String expression) throws ParseException {
        return fromString(expression, new SimpleLinguistics(), Embedder.throwsOnUse.asMap());
    }

    public static StatementExpression fromString(String expression, Linguistics linguistics, Map<String, Embedder> embedders) throws ParseException {
        return newInstance(new ScriptParserContext(linguistics, embedders).setInputStream(new IndexingInput(expression)));
    }

    public static StatementExpression newInstance(ScriptParserContext config) throws ParseException {
        return ScriptParser.parseStatement(config);
    }

    private static List<Expression> filterList(Iterable<Expression> lst) {
        List<Expression> ret = new LinkedList<>();
        for (Expression exp : lst) {
            if (exp instanceof StatementExpression) {
                ret.addAll(filterList((StatementExpression)exp));
            } else if (exp != null) {
                ret.add(exp);
            }
        }
        return ret;
    }

}