Packages
caffeine_lang
2.0.2
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/generator.gleam
import caffeine_query_language/parser.{
type Exp, type Operator, type Primary, PrimaryExp, PrimaryWord, Word,
}
import caffeine_query_language/resolver
import gleam/dict
import gleam/float
import gleam/int
import gleam/list
import gleam/option.{type Option}
import gleam/result
import gleam/string
import terra_madre/hcl
/// Converts an expression AST node to its string representation.
@internal
pub fn exp_to_string(exp: Exp) -> String {
case exp {
parser.Primary(primary:) -> primary_to_string(primary, option.None)
parser.TimeSliceExpr(spec) ->
"time_slice("
<> spec.query
<> " "
<> comparator_to_string(spec.comparator)
<> " "
<> float_to_string(spec.threshold)
<> " per "
<> float_to_string(spec.interval_seconds)
<> "s)"
parser.OperatorExpr(numerator:, denominator:, operator:) -> {
// Check if this entire expression tree is a path (all divisions with path-like components)
case operator, is_path_expression(exp) {
parser.Div, True -> {
// This is a path, render without spaces
exp_to_string_no_spaces(exp)
}
_, _ -> {
// Normal expression with spaces
let left =
exp_to_string_with_context(numerator, option.Some(operator), True)
let right =
exp_to_string_with_context(denominator, option.Some(operator), False)
let op = operator_to_datadog_query(operator)
left <> " " <> op <> " " <> right
}
}
}
}
}
fn comparator_to_string(comparator: parser.Comparator) -> String {
case comparator {
parser.LessThan -> "<"
parser.LessThanOrEqualTo -> "<="
parser.GreaterThan -> ">"
parser.GreaterThanOrEqualTo -> ">="
}
}
fn float_to_string(f: Float) -> String {
// Check if it's a whole number (no fractional part)
let truncated = float.truncate(f)
let is_whole = int.to_float(truncated) == f
case is_whole {
True -> int.to_string(truncated)
False -> float.to_string(f)
}
}
// Check if an expression is a path (all divisions with simple word components)
// A path expression starts with a field name ending in colon (like http.url_details.path:)
fn is_path_expression(exp: Exp) -> Bool {
// First check if the leftmost component is a field name (ends with :)
case get_leftmost_word(exp) {
option.Some(w) -> {
case string.ends_with(w, ":") {
True -> all_divisions(exp)
False -> False
}
}
option.None -> False
}
}
// Get the leftmost word in an expression tree
fn get_leftmost_word(exp: Exp) -> Option(String) {
case exp {
parser.Primary(parser.PrimaryWord(parser.Word(w))) -> option.Some(w)
parser.Primary(parser.PrimaryExp(inner_exp)) -> get_leftmost_word(inner_exp)
parser.TimeSliceExpr(_) -> option.None
parser.OperatorExpr(left, _, _) -> get_leftmost_word(left)
}
}
// Check if an expression is all divisions (no other operators)
fn all_divisions(exp: Exp) -> Bool {
case exp {
parser.Primary(_) -> True
parser.OperatorExpr(left, right, parser.Div) ->
all_divisions(left) && all_divisions(right)
_ -> False
}
}
// Convert expression to string without spaces (for paths)
fn exp_to_string_no_spaces(exp: Exp) -> String {
case exp {
parser.Primary(parser.PrimaryWord(parser.Word(w))) -> w
parser.OperatorExpr(left, right, parser.Div) ->
exp_to_string_no_spaces(left) <> "/" <> exp_to_string_no_spaces(right)
_ -> exp_to_string(exp)
}
}
fn exp_to_string_with_context(
exp: Exp,
parent_op: Option(Operator),
_is_left: Bool,
) -> String {
case exp {
parser.Primary(primary:) -> primary_to_string(primary, parent_op)
parser.TimeSliceExpr(_) -> exp_to_string(exp)
parser.OperatorExpr(numerator:, denominator:, operator:) -> {
// Check if this is a path expression to avoid adding spaces
// This is important when the division is part of a larger expression (e.g., with AND)
case operator, is_path_expression(exp) {
parser.Div, True -> exp_to_string_no_spaces(exp)
_, _ -> {
// Not a path, render with spaces
let left =
exp_to_string_with_context(numerator, option.Some(operator), True)
let right =
exp_to_string_with_context(denominator, option.Some(operator), False)
let op = operator_to_datadog_query(operator)
left <> " " <> op <> " " <> right
}
}
}
}
}
fn primary_to_string(primary: Primary, _parent_op: Option(Operator)) -> String {
case primary {
PrimaryWord(word:) -> word.value
PrimaryExp(exp:) -> {
// Always preserve explicit parentheses from the original input
"(" <> exp_to_string(exp) <> ")"
}
}
}
/// Converts a CQL operator to its Datadog query string representation.
@internal
pub fn operator_to_datadog_query(operator: parser.Operator) -> String {
case operator {
parser.Add -> "+"
parser.Sub -> "-"
parser.Mul -> "*"
parser.Div -> "/"
}
}
/// Transform an expression tree by substituting word values using a dictionary.
/// Words found in the dictionary are replaced with their corresponding values.
/// Words not found in the dictionary are left unchanged.
@internal
pub fn substitute_words(
exp: Exp,
substitutions: dict.Dict(String, String),
) -> Exp {
case exp {
parser.Primary(PrimaryWord(Word(name))) -> {
let value = dict.get(substitutions, name) |> result.unwrap(name)
parser.Primary(PrimaryWord(Word(value)))
}
parser.Primary(PrimaryExp(inner)) ->
parser.Primary(PrimaryExp(substitute_words(inner, substitutions)))
parser.TimeSliceExpr(spec) -> {
// Substitute in the query string if it matches a key
let query =
dict.get(substitutions, spec.query) |> result.unwrap(spec.query)
parser.TimeSliceExpr(parser.TimeSliceExp(..spec, query: query))
}
parser.OperatorExpr(left, right, op) ->
parser.OperatorExpr(
substitute_words(left, substitutions),
substitute_words(right, substitutions),
op,
)
}
}
/// Extracts all word names from an expression AST.
/// Returns a list of unique word strings found in the expression.
@internal
pub fn extract_words(exp: Exp) -> List(String) {
case exp {
parser.Primary(PrimaryWord(Word(name))) -> [name]
parser.Primary(PrimaryExp(inner)) -> extract_words(inner)
parser.TimeSliceExpr(_) -> []
parser.OperatorExpr(left, right, _) ->
list.append(extract_words(left), extract_words(right))
|> list.unique
}
}
/// Strips outer parentheses from a string if they wrap the entire expression.
/// E.g., "(a + b)" -> "a + b", but "(a + b) * c" stays unchanged.
fn strip_outer_parens(s: String) -> String {
let trimmed = string.trim(s)
case string.starts_with(trimmed, "(") && string.ends_with(trimmed, ")") {
True -> {
// Check if these parens actually wrap the whole expression
let inner = string.slice(trimmed, 1, string.length(trimmed) - 2)
// Verify parens are balanced in the inner part
case is_balanced(inner, 0) {
True -> inner
False -> trimmed
}
}
False -> trimmed
}
}
/// Check if parentheses are balanced in a string.
fn is_balanced(s: String, depth: Int) -> Bool {
case string.pop_grapheme(s) {
Error(_) -> depth == 0
Ok(#("(", rest)) -> is_balanced(rest, depth + 1)
Ok(#(")", rest)) ->
case depth {
0 -> False
// Closing without matching open
_ -> is_balanced(rest, depth - 1)
}
Ok(#(_, rest)) -> is_balanced(rest, depth)
}
}
/// Represents a single named query for TimeSlice formulas.
pub type NamedQuery {
NamedQuery(name: String, query: String)
}
/// Represents a resolved SLO query, either GoodOverTotal or TimeSlice.
pub type ResolvedSloQuery {
ResolvedGoodOverTotal(numerator: String, denominator: String)
ResolvedTimeSlice(
comparator: String,
interval_seconds: Int,
threshold: Float,
/// The formula expression (e.g., "build_time + deploy_time")
formula_expression: String,
/// List of named queries referenced by the formula
queries: List(NamedQuery),
)
}
/// Represents the SLO type for Datadog terraform generation.
pub type SloType {
MetricSlo
TimeSliceSlo
}
/// Resolved SLO with HCL blocks ready for terraform generation.
pub type ResolvedSloHcl {
ResolvedSloHcl(slo_type: SloType, blocks: List(hcl.Block))
}
/// Parse a value expression, resolve to primitive, substitute words,
/// and return the resolved SLO query type.
@internal
pub fn resolve_slo_query_typed(
value_expr: String,
substitutions: dict.Dict(String, String),
) -> Result(ResolvedSloQuery, String) {
case parser.parse_expr(value_expr) {
Error(err) -> Error("Parse error: " <> err)
Ok(exp_container) ->
case resolver.resolve_primitives(exp_container) {
Ok(resolver.GoodOverTotal(numerator_exp, denominator_exp)) -> {
let numerator_str =
substitute_words(numerator_exp, substitutions) |> exp_to_string
let denominator_str =
substitute_words(denominator_exp, substitutions) |> exp_to_string
Ok(ResolvedGoodOverTotal(numerator_str, denominator_str))
}
Ok(resolver.TimeSlice(comparator, interval_seconds, threshold, query)) -> {
let comparator_str = case comparator {
resolver.LessThan -> "<"
resolver.LessThanOrEqualTo -> "<="
resolver.GreaterThan -> ">"
resolver.GreaterThanOrEqualTo -> ">="
}
// Parse the query as an expression to extract word references
// The query could be a single word like "query1" or a formula like "(a + b)"
case parser.parse_expr(query) {
Ok(query_exp_container) -> {
let query_exp = query_exp_container.exp
let words = extract_words(query_exp)
// Build named queries by looking up each word in substitutions
let named_queries =
words
|> list.filter_map(fn(word) {
case dict.get(substitutions, word) {
Ok(resolved) -> Ok(NamedQuery(word, resolved))
Error(_) -> Error(Nil)
}
})
// If no substitutions were found, the query is likely a literal metric query
// In that case, use "query1" as the formula expression and the query as-is
case named_queries {
[] ->
Ok(
ResolvedTimeSlice(
comparator_str,
interval_seconds,
threshold,
"query1",
[NamedQuery("query1", query)],
),
)
_ -> {
// Use the original query string as the formula expression
// Strip outer parentheses if they wrap the entire expression
let formula_expr = strip_outer_parens(query)
Ok(ResolvedTimeSlice(
comparator_str,
interval_seconds,
threshold,
formula_expr,
named_queries,
))
}
}
}
Error(_) -> {
// If parsing fails, treat as a single literal query (backwards compat)
let resolved_query =
dict.get(substitutions, query) |> result.unwrap(query)
Ok(
ResolvedTimeSlice(
comparator_str,
interval_seconds,
threshold,
"query1",
[NamedQuery("query1", resolved_query)],
),
)
}
}
}
Error(err) -> Error("Resolution error: " <> err.msg)
}
}
}
/// Parse a value expression, resolve to GoodOverTotal primitive, substitute words,
/// and return the numerator and denominator as strings.
/// Panics if parsing or resolution fails.
@internal
pub fn resolve_slo_query(
value_expr: String,
substitutions: dict.Dict(String, String),
) -> #(String, String) {
case resolve_slo_query_typed(value_expr, substitutions) {
Ok(ResolvedGoodOverTotal(numerator, denominator)) -> #(
numerator,
denominator,
)
_ -> #("", "")
}
}
/// Parse a value expression, resolve to primitive, substitute words,
/// and return HCL blocks ready for Datadog terraform generation.
@internal
pub fn resolve_slo_to_hcl(
value_expr: String,
substitutions: dict.Dict(String, String),
) -> Result(ResolvedSloHcl, String) {
case resolve_slo_query_typed(value_expr, substitutions) {
Ok(ResolvedGoodOverTotal(numerator, denominator)) -> {
let query_block =
hcl.simple_block("query", [
#("numerator", hcl.StringLiteral(numerator)),
#("denominator", hcl.StringLiteral(denominator)),
])
Ok(ResolvedSloHcl(MetricSlo, [query_block]))
}
Ok(ResolvedTimeSlice(
comparator,
interval_seconds,
threshold,
formula_expression,
named_queries,
)) -> {
// Generate a metric_query block for each named query
let inner_query_blocks =
named_queries
|> list.map(fn(nq) {
let metric_query_block =
hcl.Block(
type_: "metric_query",
labels: [],
attributes: dict.from_list([
#("data_source", hcl.StringLiteral("metrics")),
#("name", hcl.StringLiteral(nq.name)),
#("query", hcl.StringLiteral(nq.query)),
]),
blocks: [],
)
hcl.Block(type_: "query", labels: [], attributes: dict.new(), blocks: [
metric_query_block,
])
})
let formula_block =
hcl.Block(
type_: "formula",
labels: [],
attributes: dict.from_list([
#("formula_expression", hcl.StringLiteral(formula_expression)),
]),
blocks: [],
)
let outer_query_block =
hcl.Block(type_: "query", labels: [], attributes: dict.new(), blocks: [
formula_block,
..inner_query_blocks
])
let time_slice_block =
hcl.Block(
type_: "time_slice",
labels: [],
attributes: dict.from_list([
#("comparator", hcl.StringLiteral(comparator)),
#("query_interval_seconds", hcl.IntLiteral(interval_seconds)),
#("threshold", hcl.FloatLiteral(threshold)),
]),
blocks: [outer_query_block],
)
let sli_specification_block =
hcl.Block(
type_: "sli_specification",
labels: [],
attributes: dict.new(),
blocks: [time_slice_block],
)
Ok(ResolvedSloHcl(TimeSliceSlo, [sli_specification_block]))
}
Error(err) -> Error(err)
}
}