//! echo-1 M0 data engine. //! //! Generates pure-Lua functions, executes them in a deterministic sandbox, //! filters to the clean-terminating observable ones, and writes a JSONL dataset //! where each record carries an I/O spec (synthesis), a prefix/hole/suffix spec //! (infilling), and held-out tests for execution-based verification. mod grammar; mod mutate; mod pipeline; mod sandbox; mod spec; mod value; mod verify; use std::io::{BufRead, Write}; use anyhow::Result; use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use spec::IoPair; #[derive(Parser)] #[command(name = "echo-data", about = "echo-1 M0 data engine")] struct Cli { #[command(subcommand)] cmd: Cmd, } #[derive(Subcommand)] enum Cmd { /// Generate a dataset of verified pure-Lua programs. Gen { /// Number of programs to keep. #[arg(short, long, default_value_t = 100)] count: usize, /// Master RNG seed (reproducible). #[arg(short, long, default_value_t = 1)] seed: u64, /// Fix difficulty 0..=4; omit to round-robin. #[arg(short, long)] difficulty: Option, /// Shown I/O examples per program. #[arg(long, default_value_t = 3)] examples: usize, /// Held-out tests per program. #[arg(long, default_value_t = 5)] tests: usize, /// Instruction budget per execution (deterministic timeout). #[arg(long, default_value_t = 200_000)] budget: i64, /// Output JSONL path. #[arg(short, long, default_value = "data/dataset.jsonl")] out: String, /// Skip the mutation gate (much faster; for bulk training data). #[arg(long, default_value_t = false)] no_gate: bool, }, /// Verify candidate programs from stdin against their tests (one JSON object /// per line: {"source":..., "tests":[{"input":[...],"output":...}]}). Emits /// one result object per line. Same sandbox/verifier used to label data. VerifyBatch { /// Instruction budget per execution. #[arg(long, default_value_t = 200_000)] budget: i64, }, } #[derive(Deserialize)] struct Candidate { source: String, tests: Vec, } #[derive(Serialize)] struct VerifyOut { pass: bool, verdict: String, } fn verify_batch(budget: i64) -> Result<()> { let sb = sandbox::Sandbox::new().map_err(|e| anyhow::anyhow!("init sandbox: {e}"))?; let stdin = std::io::stdin(); let stdout = std::io::stdout(); let mut out = std::io::BufWriter::new(stdout.lock()); for line in stdin.lock().lines() { let line = line?; if line.trim().is_empty() { continue; } let cand: Candidate = serde_json::from_str(&line)?; let verdict = verify::verify(&sb, &cand.source, &cand.tests, budget); let res = VerifyOut { pass: verdict == verify::Verdict::Pass, verdict: format!("{verdict:?}"), }; writeln!(out, "{}", serde_json::to_string(&res)?)?; } out.flush()?; Ok(()) } fn main() -> Result<()> { let cli = Cli::parse(); match cli.cmd { Cmd::Gen { count, seed, difficulty, examples, tests, budget, out, no_gate } => { pipeline::run(pipeline::GenParams { count, seed, fixed_difficulty: difficulty, n_examples: examples, n_tests: tests, budget, out, gate: !no_gate, }) } Cmd::VerifyBatch { budget } => verify_batch(budget), } }