1// SPDX-License-Identifier: MIT
2
3package expr
4
5import "math"
6
7// FuncInfo describes a function that expressions may call besides the
8// built-in ones (case, contains, endsWith, format, fromJSON, join,
9// startsWith, toJSON). The parser checks names and argument counts against
10// it; the implementation is supplied at evaluation time in
11// [EvalContext.Funcs].
12type FuncInfo struct {
13 // Name is matched case-insensitively.
14 Name string
15 // MinArgs and MaxArgs bound the number of arguments. A negative MaxArgs
16 // means no upper bound.
17 MinArgs int
18 MaxArgs int
19}
20
21// builtinFuncs are the functions of ExpressionConstants.WellKnownFunctions, keyed
22// by upper-cased name (see foldKey). They take precedence over caller-supplied FuncInfo for
23// validation.
24var builtinFuncs = map[string]FuncInfo{
25 "CASE": {"case", 3, math.MaxInt},
26 "CONTAINS": {"contains", 2, 2},
27 "ENDSWITH": {"endsWith", 2, 2},
28 "FORMAT": {"format", 1, 255},
29 "FROMJSON": {"fromJson", 1, 1},
30 "JOIN": {"join", 1, 2},
31 "STARTSWITH": {"startsWith", 2, 2},
32 "TOJSON": {"toJson", 1, 1},
33}
34
35// Parse parses the text of one expression (the inside of ${{ }}).
36//
37// contexts lists the context names the expression may reference, such as
38// "github" or "matrix"; funcs lists the extra functions it may call. Names
39// are case-insensitive. A reference to any other context or function is an
40// error, as is a call with a wrong number of arguments.
41//
42// The returned error is an [*Error]. An expression with no tokens, such as
43// "" or " ", is not an error: the reference returns nothing for it, and Parse
44// returns a nil [Node], which [Evaluate] rejects.
45func Parse(expr string, contexts []string, funcs []FuncInfo) (Node, error) {
46 return parseRunner(expr, contexts, funcs)
47}
48
49// arityError reports whether argCount is wrong for f, and how. name is the
50// upper-cased function name (see foldKey): case also needs an odd number of arguments.
51func arityError(name string, f FuncInfo, argCount int) (ErrorType, bool) {
52 switch {
53 case argCount < f.MinArgs:
54 return ErrorTooFewParameters, true
55 case f.MaxArgs >= 0 && argCount > f.MaxArgs:
56 return ErrorTooManyParameters, true
57 case name == "CASE" && argCount%2 == 0:
58 return ErrorEvenParameters, true
59 }
60 return 0, false
61}