MVP: AILang-Sprache mit JSON-AST, Typchecker, LLVM-IR-Backend
Erste lauffähige Iteration. examples/sum.ail.json wird zu nativem Binary kompiliert und druckt 55 (Summe 1..10) als End-to-End-Test. Architektur: - ailang-core: hashbares JSON-AST + canonical-form + pretty-printer - ailang-check: monomorpher HM-Subset + Effekt-Set-Tracking - ailang-codegen: LLVM-IR-Text-Emitter (kein libllvm-link) - ail: CLI mit check/manifest/render/describe/emit-ir/build/builtins Designentscheidungen sind in docs/DESIGN.md dokumentiert; der Verlauf in docs/JOURNAL.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
//! `ail` — CLI für AILang.
|
||||
//!
|
||||
//! Subcommands sind so geschnitten, dass jedes einzelne Tool dem LLM einen
|
||||
//! kleinen, fokussierten Kontext liefert (manifest = Übersicht; describe =
|
||||
//! Detail; emit-ir = exakte Maschinensicht; build = Pipeline-Validierung).
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "ail", version, about = "AILang toolchain")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
cmd: Cmd,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Cmd {
|
||||
/// Lädt ein Modul und gibt eine kompakte Symboltabelle aus.
|
||||
Manifest { path: PathBuf },
|
||||
/// Gibt das Modul in Textform aus (Pretty-Printer).
|
||||
Render { path: PathBuf },
|
||||
/// Gibt eine einzelne Definition als JSON oder Pretty-Text aus.
|
||||
Describe {
|
||||
path: PathBuf,
|
||||
name: String,
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
},
|
||||
/// Typprüft ein Modul.
|
||||
Check { path: PathBuf },
|
||||
/// Schreibt LLVM IR (.ll) für das Modul.
|
||||
EmitIr {
|
||||
path: PathBuf,
|
||||
#[arg(short, long)]
|
||||
out: Option<PathBuf>,
|
||||
},
|
||||
/// Komplette Pipeline: check + emit-ir + clang -> Binary.
|
||||
Build {
|
||||
path: PathBuf,
|
||||
#[arg(short, long)]
|
||||
out: Option<PathBuf>,
|
||||
/// Optimierung (z. B. `-O2`); default `-O0` für Debugbarkeit.
|
||||
#[arg(long, default_value = "-O0")]
|
||||
opt: String,
|
||||
},
|
||||
/// Listet eingebaute Operationen mit ihren Signaturen.
|
||||
Builtins,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
match cli.cmd {
|
||||
Cmd::Manifest { path } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
print!("{}", ailang_core::pretty::manifest(&m));
|
||||
}
|
||||
Cmd::Render { path } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
print!("{}", ailang_core::pretty::module(&m));
|
||||
}
|
||||
Cmd::Describe { path, name, json } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
let def = m
|
||||
.defs
|
||||
.iter()
|
||||
.find(|d| d.name() == name)
|
||||
.with_context(|| format!("no def `{name}` in module `{}`", m.name))?;
|
||||
if json {
|
||||
let s = serde_json::to_string_pretty(def)?;
|
||||
println!("{s}");
|
||||
} else {
|
||||
// Pretty-form: render module mit nur dieser Def.
|
||||
let one = ailang_core::Module {
|
||||
schema: m.schema.clone(),
|
||||
name: m.name.clone(),
|
||||
imports: vec![],
|
||||
defs: vec![def.clone()],
|
||||
};
|
||||
let h = ailang_core::def_hash(def);
|
||||
println!("hash: {h}");
|
||||
print!("{}", ailang_core::pretty::module(&one));
|
||||
}
|
||||
}
|
||||
Cmd::Check { path } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
let r = ailang_check::check(&m)?;
|
||||
println!("ok ({} symbols)", r.symbols.len());
|
||||
}
|
||||
Cmd::EmitIr { path, out } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
ailang_check::check(&m)?;
|
||||
let ir = ailang_codegen::emit_ir(&m)?;
|
||||
match out {
|
||||
Some(p) => {
|
||||
std::fs::write(&p, ir)?;
|
||||
eprintln!("wrote {}", p.display());
|
||||
}
|
||||
None => print!("{ir}"),
|
||||
}
|
||||
}
|
||||
Cmd::Build { path, out, opt } => {
|
||||
let m = ailang_core::load_module(&path)?;
|
||||
ailang_check::check(&m)?;
|
||||
let ir = ailang_codegen::emit_ir(&m)?;
|
||||
let tmpdir = std::env::temp_dir().join(format!("ailang-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&tmpdir)?;
|
||||
let ll_path = tmpdir.join(format!("{}.ll", m.name));
|
||||
std::fs::write(&ll_path, &ir)?;
|
||||
let out_bin = out.unwrap_or_else(|| {
|
||||
Path::new(".").join(&m.name).with_extension("")
|
||||
});
|
||||
let status = std::process::Command::new("clang")
|
||||
.arg(&opt)
|
||||
.arg("-o")
|
||||
.arg(&out_bin)
|
||||
.arg(&ll_path)
|
||||
.status()
|
||||
.context("running clang")?;
|
||||
if !status.success() {
|
||||
anyhow::bail!(
|
||||
"clang failed (status {}); ll at {}",
|
||||
status,
|
||||
ll_path.display()
|
||||
);
|
||||
}
|
||||
eprintln!("built {}", out_bin.display());
|
||||
}
|
||||
Cmd::Builtins => {
|
||||
for (n, sig) in ailang_check::builtins::list() {
|
||||
println!("{n:<16} {sig}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user