speckl/compiler/specs/speckl-compile.speckdl
Scott Scoble 7230650628
Some checks failed
Deploy Landing Page to Storj / deploy (push) Failing after 2s
feat(compiler): self-hosting spec + generator multi-enum support
- Add speckl-compile.speckdl — compiler formally specified in SpeckDL
- Generator: multi-enum detection, value→enum mapping, trailing comma cleanup
- Emit blocks collapsed to single-line (parser limitation)
- Generated artifacts: provenance, BOMs, WAT, TypeScript (2 known bugs → engineering#8)
2026-05-03 14:42:03 -07:00

216 lines
7.2 KiB
Text

// speckl-compile.speckdl — The Speckl compiler, specified in SpeckDL
// This is the ultimate dogfood: the compiler compiles itself.
speck SpecklCompiler {
version: "0.4.0"
author: "sscoble"
license: "MIT"
// ─── Purpose ──────────────────────────────────────────────
// Transform a .speckdl specification into provenance, BOMs,
// and runnable artifacts (TypeScript, WebAssembly).
// ─── State ────────────────────────────────────────────────
state: {
phase: CompilerPhase,
sourcePath: String,
outputDir: String,
ast: AST,
errors: String[],
artifacts: ArtifactMap
}
init: {
phase := Idle,
sourcePath := "",
outputDir := "./out",
ast := {},
errors := [],
artifacts := {}
}
// ─── Events ───────────────────────────────────────────────
event CompilationStarted {
source: String,
output: String,
at: String
}
event PhaseCompleted {
phase: CompilerPhase,
durationMs: Nat,
at: String
}
event ArtifactGenerated {
kind: ArtifactKind,
path: String,
bytes: Nat,
at: String
}
event ErrorEmitted {
phase: CompilerPhase,
message: String,
line: Nat,
col: Nat,
at: String
}
// ─── Actions ──────────────────────────────────────────────
action compile(sourcePath: String, outputDir: String) {
require phase === Idle
require len(sourcePath) > 0
this.phase := Parsing
this.sourcePath := sourcePath
this.outputDir := outputDir
this.errors := []
emit CompilationStarted { source: sourcePath, output: outputDir, at: now() }
}
action parse() {
require phase === Parsing
require len(sourcePath) > 0
// Lexical analysis: tokenize .speckdl source
// Syntactic analysis: build AST (SpeckNode, MemberNode hierarchy)
// Semantic validation: type check, name resolution
this.phase := Parsed
this.ast := parseSpeckFile(sourcePath)
emit PhaseCompleted { phase: Parsing, durationMs: 0, at: now() }
}
action generateProvenance() {
require phase === Parsed || phase === TypeScriptGenerated || phase === WASMGenerated
// Emit PROV-O JSON-LD provenance graph
// Tracks: compiler version, source file, generation time, generator used
this.phase := ProvenanceGenerated
emit ArtifactGenerated { kind: Provenance, path: join(outputDir, ".prov.jsonld"), bytes: 0, at: now() }
}
action generateBOMs() {
require phase === ProvenanceGenerated || phase === Parsed
// Emit CycloneDX SBOM (software bill of materials)
// Emit SPDX BOM (license and provenance)
this.phase := BOMsGenerated
emit ArtifactGenerated { kind: CycloneDX, path: join(outputDir, ".specbom.cdx.json"), bytes: 0, at: now() }
emit ArtifactGenerated { kind: SPDX, path: join(outputDir, ".specbom.spdx.json"), bytes: 0, at: now() }
}
action generateTypeScript() {
require phase === BOMsGenerated || phase === Parsed
// Transform AST → TypeScript state machine class
// Actions become methods, guards become if-throws,
// state vars become properties, init becomes constructor
this.phase := TypeScriptGenerated
emit ArtifactGenerated { kind: TypeScript, path: join(outputDir, ".ts"), bytes: 0, at: now() }
}
action generateWASM() {
require phase === TypeScriptGenerated || phase === BOMsGenerated || phase === Parsed
// Transform AST → WebAssembly Text (WAT)
// Scalar state → WASM globals, Map/Set → host imports
// Actions become exports with parameter mapping
this.phase := WASMGenerated
emit ArtifactGenerated { kind: WebAssembly, path: join(outputDir, ".wat"), bytes: 0, at: now() }
}
action report() {
require phase === ProvenanceGenerated || phase === BOMsGenerated || phase === TypeScriptGenerated || phase === WASMGenerated
// Print summary: phases completed, artifacts emitted, errors found
// Return exit code 0 if no errors, 1 otherwise
this.phase := Done
emit PhaseCompleted { phase: Done, durationMs: 0, at: now() }
}
action reset() {
// Return to Idle for next compilation
this.phase := Idle
this.sourcePath := ""
this.outputDir := "./out"
this.ast := {}
this.errors := []
this.artifacts := {}
}
// ─── Constraints ──────────────────────────────────────────
constraint C1: len(errors) === 0 || phase === Done
// If there are errors, compilation must reach Done (with non-zero exit)
constraint C2: forall a in artifacts: a.bytes > 0
// Every generated artifact must have content
constraint C3: phase === Idle || len(sourcePath) > 0
// Non-idle phases require a source file
constraint C4: artifacts[TypeScript] exists → phase >= TypeScriptGenerated
// TypeScript artifact implies the phase was reached
constraint C5: artifacts[WebAssembly] exists → phase >= WASMGenerated
// WASM artifact implies the phase was reached
// ─── Temporal Verifications ───────────────────────────────
verify V1: always(implies(phase === Parsing, eventually(phase === Parsed || len(errors) > 0)))
// Parsing must complete or produce errors
verify V2: always(implies(phase === Parsed, eventually(phase === Done)))
// Parsed must eventually reach Done
verify V3: always(implies(phase === Done, forall a in artifacts: a.bytes > 0 || a.kind === ErrorLog))
// Done implies all non-error artifacts have content
// ─── Provenance ───────────────────────────────────────────
provenance {
wasGeneratedBy(speckl-compile, SpecklCompiler, "2026-05-03T19:00:00Z")
used(SpecklCompiler, speckdl-v0.4, "grammar")
used(SpecklCompiler, typescript-5.4, "codegen")
used(SpecklCompiler, wasm-spec-2.0, "codegen")
}
// ─── BOM ──────────────────────────────────────────────────
bom {
compiler: "speckl-compile@0.4.0"
solver: "z3@4.13.0"
runtime: "node@22.0.0"
}
// ─── Supporting Types ───────────────────────────────────────
// These would be defined in a standard library eventually
interface CompilerPhase {
Idle, Parsing, Parsed, ProvenanceGenerated, BOMsGenerated,
TypeScriptGenerated, WASMGenerated, Done
}
interface ArtifactKind {
Provenance, CycloneDX, SPDX, TypeScript, WebAssembly, ErrorLog
}
interface AST {
// Abstract syntax tree — opaque in this spec
// Actual structure defined by parser implementation
}
interface ArtifactMap {
// Map from ArtifactKind to generated file metadata
}
}