Packages
caffeine_lang
2.0.5
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
src/caffeine_query_language/resolver.gleam
import caffeine_lang/common/errors.{type CompilationError}
import caffeine_query_language/parser.{
type Exp, type ExpContainer, Div, ExpContainer, OperatorExpr, TimeSliceExpr,
}
import gleam/float
/// Supported primitive query types that can be resolved from CQL expressions.
/// Each primitive represents a specific SLO calculation pattern.
pub type Primitives {
/// Good over total requires a top level division operator.
/// Represents an SLO where the numerator is the "good" events
/// and the denominator is the "total" events.
GoodOverTotal(numerator: Exp, denominator: Exp)
TimeSlice(
comparator: Comparator,
interval_in_seconds: Int,
threshold: Float,
query: String,
)
}
/// Set of valid comparison operators.
/// FUTURE: possible we may want a good ol' '==' 🤷♂️. Not yet atleast and YAGNI.
pub type Comparator {
// <
LessThan
// <=
LessThanOrEqualTo
// >
GreaterThan
// >=
GreaterThanOrEqualTo
}
/// Converst a comparator to a string
@internal
pub fn comparator_to_string(comparator: Comparator) {
case comparator {
LessThan -> "\"<\""
LessThanOrEqualTo -> "\"<=\""
GreaterThan -> "\">\""
GreaterThanOrEqualTo -> "\">=\""
}
}
/// Converts a parser Comparator to a resolver Comparator.
fn convert_comparator(comp: parser.Comparator) -> Comparator {
case comp {
parser.LessThan -> LessThan
parser.LessThanOrEqualTo -> LessThanOrEqualTo
parser.GreaterThan -> GreaterThan
parser.GreaterThanOrEqualTo -> GreaterThanOrEqualTo
}
}
/// Resolves a parsed CQL expression into a primitive type.
/// Supports GoodOverTotal (division at the top level) and TimeSlice.
/// Returns an error if the expression doesn't match a known primitive pattern.
@internal
pub fn resolve_primitives(
exp_container: ExpContainer,
) -> Result(Primitives, CompilationError) {
case exp_container {
ExpContainer(exp) ->
case exp {
OperatorExpr(left, right, Div) -> {
// Check that neither operand contains a time_slice expression
case contains_time_slice(left) || contains_time_slice(right) {
True ->
Error(errors.CQLResolverError(
msg: "time_slice cannot be used as an operand. It must be the entire expression.",
))
False -> Ok(GoodOverTotal(left, right))
}
}
TimeSliceExpr(spec) ->
Ok(TimeSlice(
comparator: convert_comparator(spec.comparator),
interval_in_seconds: float.truncate(spec.interval_seconds),
threshold: spec.threshold,
query: spec.query,
))
_ ->
Error(errors.CQLResolverError(
msg: "Invalid expression. Expected a top level division operator or time_slice.",
))
}
}
}
/// Checks if an expression contains a time_slice expression anywhere.
fn contains_time_slice(exp: Exp) -> Bool {
case exp {
TimeSliceExpr(_) -> True
OperatorExpr(left, right, _) ->
contains_time_slice(left) || contains_time_slice(right)
parser.Primary(parser.PrimaryExp(inner)) -> contains_time_slice(inner)
parser.Primary(parser.PrimaryWord(_)) -> False
}
}