Packages
caffeine_lang
6.2.1
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_lang/compiler.gleam
import caffeine_lang/analysis/dependency_validator
import caffeine_lang/analysis/vendor
import caffeine_lang/codegen/datadog
import caffeine_lang/codegen/dependency_graph
import caffeine_lang/codegen/generator_utils
import caffeine_lang/codegen/platforms
import caffeine_lang/codegen/relay
import caffeine_lang/codegen/relay_bundle
import caffeine_lang/codegen/relay_workflow
import caffeine_lang/errors
import caffeine_lang/frontend/pipeline
import caffeine_lang/linker/expectations
import caffeine_lang/linker/ir.{
type DepsValidated, type IntermediateRepresentation, type Linked,
type Resolved,
}
import caffeine_lang/linker/ir_builder
import caffeine_lang/linker/linker
import caffeine_lang/linker/measurements
import caffeine_lang/source_file.{
type ExpectationSource, type SourceFile, type VendorMeasurementSource,
SourceFile,
}
import caffeine_lang/standard_library/artifacts as stdlib_artifacts
import gleam/dict
import gleam/list
import gleam/option.{type Option}
import gleam/result
import gleam/string
import terra_madre/common
import terra_madre/render
import terra_madre/terraform
/// Output of the compilation process. Includes Terraform (always), the
/// dependency graph when relations exist, the relay's `signals.json`,
/// the GHA workflow, and the bundled relay Gleam project when any
/// expectation uses external-signal indicators, and any warnings the
/// codegen accumulated.
///
/// `relay_bundle` is a path -> contents map relative to the bundle root
/// (`build/relay/relay/` is the conventional drop location). The CLI is
/// responsible for writing the files; the compiler just produces them.
pub type CompilationOutput {
CompilationOutput(
terraform: String,
dependency_graph: Option(String),
relay_signals: Option(String),
relay_workflow: Option(String),
relay_bundle: Option(dict.Dict(String, String)),
warnings: List(String),
)
}
/// Compiles measurement sources and expectation sources into Terraform configuration.
/// Pure function — all file reading happens before this function is called.
pub fn compile(
measurements: List(VendorMeasurementSource),
expectations: List(SourceFile(ExpectationSource)),
) -> Result(CompilationOutput, errors.CompilationError) {
use irs <- result.try(run_parse_and_link(measurements, expectations))
use resolved_irs <- result.try(run_semantic_analysis(irs))
run_code_generation(resolved_irs)
}
/// Compiles from source strings directly (no file I/O).
/// Used for browser-based compilation. The vendor parameter specifies
/// which vendor the measurements belong to.
pub fn compile_from_strings(
measurements_source: String,
expectations_source: String,
expectations_path: String,
vendor vendor_string: String,
) -> Result(CompilationOutput, errors.CompilationError) {
use irs <- result.try(parse_from_strings(
measurements_source,
expectations_source,
expectations_path,
vendor_string,
))
use resolved_irs <- result.try(run_semantic_analysis(irs))
run_code_generation(resolved_irs)
}
// ==== Pipeline stages ====
fn run_parse_and_link(
measurements: List(VendorMeasurementSource),
expectations: List(SourceFile(ExpectationSource)),
) -> Result(List(IntermediateRepresentation(Linked)), errors.CompilationError) {
let slo_params = stdlib_artifacts.slo_params()
linker.link(measurements, expectations, slo_params:)
}
fn run_semantic_analysis(
irs: List(IntermediateRepresentation(Linked)),
) -> Result(List(IntermediateRepresentation(Resolved)), errors.CompilationError) {
use validated_irs <- result.try(
dependency_validator.validate_dependency_relations(irs),
)
validated_irs
|> list.map(resolve_indicators)
|> errors.from_results()
}
/// Vendor dispatch for indicator template resolution. Datadog uses template
/// substitution; unmeasured IRs (vendor = None) pass through unchanged.
@internal
pub fn resolve_indicators(
ir: IntermediateRepresentation(DepsValidated),
) -> Result(IntermediateRepresentation(Resolved), errors.CompilationError) {
case ir.vendor {
option.Some(vendor.Datadog) -> datadog.resolve_indicators(ir)
option.None -> Ok(ir.promote(ir))
}
}
fn run_code_generation(
resolved_irs: List(IntermediateRepresentation(Resolved)),
) -> Result(CompilationOutput, errors.CompilationError) {
// Filter out unmeasured IRs (vendor = None) before codegen.
// Unmeasured IRs participate in dependency graphs but not Terraform generation.
// Sort by unique_identifier for deterministic output.
let measured_irs =
resolved_irs
|> list.filter(fn(ir) { option.is_some(ir.vendor) })
|> list.sort(fn(a, b) {
string.compare(a.unique_identifier, b.unique_identifier)
})
// Datadog is the only platform today. Multi-vendor dispatch will return
// when a second `Platform` is added.
let platform = platforms.datadog_platform()
use #(all_resources, all_warnings) <- result.try(platform.generate_resources(
measured_irs,
))
let terraform_settings = platforms.terraform_settings(platform)
// Render boilerplate (terraform/provider/variable blocks) without resources.
let boilerplate_config =
terraform.Config(
terraform: option.Some(terraform_settings),
providers: [platforms.provider(platform)],
resources: [],
data_sources: [],
variables: platform.variables,
outputs: [],
locals: [],
modules: [],
)
let boilerplate = render.render_config(boilerplate_config)
// Build resource name → metadata lookup for source comments.
let metadata_by_name =
resolved_irs
|> list.flat_map(fn(ir) {
let base = common.sanitize_terraform_identifier(ir.unique_identifier)
[#(base, ir.metadata), #(base <> "_sli", ir.metadata)]
})
|> dict.from_list
// Render each resource with a source traceability comment.
let resource_sections =
all_resources
|> list.map(fn(resource) {
let rendered = generator_utils.render_resource_to_string(resource)
case dict.get(metadata_by_name, resource.name) {
Ok(metadata) ->
generator_utils.build_source_comment(metadata) <> "\n" <> rendered
Error(_) -> rendered
}
})
// Assemble final output: boilerplate + commented resources.
let terraform_output = case resource_sections {
[] -> boilerplate
sections -> {
let trimmed_boilerplate = string.drop_end(boilerplate, 1)
trimmed_boilerplate <> "\n\n" <> string.join(sections, "\n\n") <> "\n"
}
}
// Dependency graph is only useful when relations exist.
let has_deps =
resolved_irs
|> list.any(fn(ir) { option.is_some(ir.slo.depends_on) })
let graph = case has_deps {
True -> option.Some(dependency_graph.generate(resolved_irs))
False -> option.None
}
// Relay artifacts (signals.json, GHA workflow, bundled Gleam project) are
// emitted as a set whenever at least one expectation uses an external-
// signal indicator. Pure literal-query pipelines need no relay and skip
// all three.
let relay_signals = relay.generate(resolved_irs)
let #(relay_workflow_artifact, relay_bundle_artifact) = case relay_signals {
option.None -> #(option.None, option.None)
option.Some(_) -> #(
option.Some(relay_workflow.generate()),
relay_bundle.generate(),
)
}
Ok(CompilationOutput(
terraform: terraform_output,
dependency_graph: graph,
relay_signals: relay_signals,
relay_workflow: relay_workflow_artifact,
relay_bundle: relay_bundle_artifact,
warnings: all_warnings,
))
}
fn parse_from_strings(
measurements_source: String,
expectations_source: String,
expectations_path: String,
vendor_string: String,
) -> Result(List(IntermediateRepresentation(Linked)), errors.CompilationError) {
let slo_params = stdlib_artifacts.slo_params()
let reserved_labels = ir_builder.reserved_labels(slo_params)
use resolved_vendor <- result.try(
vendor.resolve_vendor(vendor_string)
|> result.replace_error(errors.linker_vendor_resolution_error(
msg: "unknown vendor '" <> vendor_string <> "'",
)),
)
use raw_measurements <- result.try(
pipeline.compile_measurements(SourceFile(
path: "browser/measurements.caffeine",
content: measurements_source,
)),
)
use raw_expectations <- result.try(
pipeline.compile_expects(SourceFile(
path: "browser/expectations.caffeine",
content: expectations_source,
)),
)
use validated_measurements <- result.try(measurements.validate_measurements(
raw_measurements,
slo_params,
))
let vendor_lookup =
raw_measurements
|> list.map(fn(bp) { #(bp.name, resolved_vendor) })
|> dict.from_list
use expectations_measurement_collection <- result.try(
expectations.validate_expectations(
raw_expectations,
validated_measurements,
slo_params: slo_params,
from: expectations_path,
),
)
ir_builder.build_all(
[#(expectations_measurement_collection, expectations_path)],
reserved_labels:,
vendor_lookup:,
slo_params:,
)
}