expr

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 27, 2026 License: MIT Imports: 7 Imported by: 0

README

internal/expr

Tiny parser and evaluator for the subset of FlexMatch property expressions used inside rule measurements and referenceValue strings.

Responsibility

Turn strings like

avg(flatten(teams[*].players.attributes[skill]))
set_intersection(players.attributes[modes])
teams[red].players.attributes[skill]
avg(players.attributes[items][sword])

into an AST (Parse) and evaluate that AST against a player roster (Eval). The syntax follows the AWS FlexMatch property-expression dialect. Rule evaluators (internal/rule) compose these calls when they need to compute a number or a set from the candidate match.

Contents

  • Parse(src string) (Node, error) — recursive-descent parser. Recognised forms:
    • numeric and quoted-string literals
    • players.attributes[<attr>] and players.attributes[<attr>][<key>] (the latter for string_number_map attributes)
    • players[playerId] and bare players (player IDs, for count)
    • teams[<name>].players..., including multiple names teams[a,b] and teams[*]; multi-team scopes group results per team (nested lists)
    • one-argument function calls: flatten, avg, min, max, sum, median, stddev, count, set_intersection
  • Eval(n Node, ctx *EvalContext) (Value, error) — evaluates a parsed node. EvalContext supplies the unsorted player pool, a team-name → players map, and a deterministic team order for teams[*].
  • Value — recursive value tree returned by Eval. Kinds:
    • KindNumber, KindString
    • KindList (a list of Values, which may itself contain lists for per-team nesting; numeric/string aggregations applied to a list of lists operate on each sublist individually, per FlexMatch)
    • KindNone — produced when an aggregate (avg, min, max) is applied to an empty list. Rule evaluators interpret this as "the rule is not yet evaluable" and skip the comparison rather than failing it, which is what lets the matchmaker grow a candidate match step by step.

Design notes

  • The grammar is intentionally narrow. The FlexMatch reference contains many more compositions than we strictly need; add them here as concrete rule examples motivate them, not speculatively.
  • The parser is hand-written and zero-dependency; benchmark before reaching for a parser generator.
  • Values share backing slices with the input where practical. Callers must not mutate slices read out of a Value.

Documentation

Overview

Package expr implements a parser and evaluator for FlexMatch property expressions used inside rule measurements and reference values, e.g. "avg(flatten(teams[*].players.attributes[skill]))" or "teams[red].players.attributes[skill]".

Values are modelled as a recursive tree (scalars or lists of Values) so that nested results such as List<List<number>> — produced by multi-team scopes like teams[*] — can be aggregated per sublist, matching FlexMatch semantics ("operations on a nested list operate on each sublist individually").

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type EvalContext

type EvalContext struct {
	Players     []core.Player
	TeamPlayers map[string][]core.Player
	TeamOrder   []string
}

EvalContext supplies the player population an expression operates over.

Players is the full pool (used when no team scope is given). TeamPlayers maps a team name to its members for teams[<name>] accesses. TeamOrder lists team names in a deterministic order, used to expand teams[*].

type FuncCall

type FuncCall struct {
	Name string
	Arg  Node
}

FuncCall is a one-argument function such as avg / min / max / sum / count / median / stddev / flatten / set_intersection.

type Kind

type Kind int

Kind identifies a Value's variant.

const (
	KindNone Kind = iota
	KindNumber
	KindString
	KindList
)

type Node

type Node interface {
	// contains filtered or unexported methods
}

Node is a parsed property expression AST node.

func Parse

func Parse(src string) (Node, error)

Parse parses a FlexMatch property expression. Recognised forms:

NUMBER                                  (e.g. 42, 3.14)
"STRING" / 'STRING'
players.attributes[<attr>]
players.attributes[<attr>][<key>]       (string_number_map index)
players[playerId]                       (player IDs)
players                                 (the players, for count(...))
teams[<name>].players...                (single team)
teams[<a>,<b>].players...               (multiple teams, grouped)
teams[*].players...                     (all teams, grouped)
<func>(<expr>)                          where <func> in {flatten, avg, min,
                                        max, sum, count, median, stddev,
                                        set_intersection}

type NumberLit

type NumberLit struct{ V float64 }

NumberLit is a numeric literal.

type PlayerAccess

type PlayerAccess struct {
	Teams    []string // explicit team names (empty = all players)
	AllTeams bool     // teams[*]
	Attr     string   // attribute name; "" means the whole player (ID)
	MapKey   string   // optional, for string_number_map
	HasIndex bool
}

PlayerAccess refers to player data within a team scope.

Team scoping:

Teams == nil, AllTeams == false  -> all players in the match, ungrouped
AllTeams == true ("*")           -> every team, grouped (List<List<...>>)
len(Teams) == 1                  -> one team, ungrouped (List<...>)
len(Teams)  > 1                  -> those teams, grouped (List<List<...>>)

Attr selects what to read from each player:

Attr == ""    -> the player itself (its ID); used by count(...players)
                 and teams[red].players[playerId].
Attr == name  -> players.attributes[name]. For string_number_map attrs an
                 optional [MapKey] index selects a single numeric value.

type StringLit

type StringLit struct{ V string }

StringLit is a string literal (quoted with single or double quotes).

type Value

type Value struct {
	Kind Kind
	Num  float64
	Str  string
	List []Value
}

Value is the result of evaluating an expression node. A Value is either a scalar (KindNumber/KindString), an absent value (KindNone), or a list of Values (KindList) which may itself contain lists for nested scopes.

func Eval

func Eval(n Node, ctx *EvalContext) (Value, error)

Eval evaluates a parsed expression node against ctx.

func ListOf added in v0.2.0

func ListOf(v []Value) Value

ListOf wraps a slice of Values (no copy).

func Number

func Number(v float64) Value

Number wraps a float64.

func NumberList

func NumberList(v []float64) Value

NumberList wraps a []float64 as a list of Number values.

func String

func String(v string) Value

String wraps a string.

func StringList

func StringList(v []string) Value

StringList wraps a []string as a list of String values.

func (Value) AsNumber

func (v Value) AsNumber() (float64, bool)

AsNumber returns v as a float64 if it is a scalar number.

func (Value) AsString

func (v Value) AsString() (string, bool)

AsString returns v as a string if it is a scalar string.

func (Value) FlattenNumbers

func (v Value) FlattenNumbers() ([]float64, bool)

FlattenNumbers deeply flattens any nesting of numeric values into a single []float64. It returns false if the value contains a non-numeric scalar.

func (Value) FlattenStrings

func (v Value) FlattenStrings() ([]string, bool)

FlattenStrings deeply flattens any nesting of string values into a single []string. It returns false if the value contains a non-string scalar.

func (Value) String

func (v Value) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL