From d1c992d49a6367e6a49f7a65a6b91a2a5625823f Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 10 May 2026 04:47:50 +0200 Subject: [PATCH] =?UTF-8?q?iter=2022-tidy.6.1:=20write=5Fclass=5Fdef=20+?= =?UTF-8?q?=20write=5Finstance=5Fdef=20=E2=80=94=20full=20Form-B=20project?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/ailang-prose/src/lib.rs | 123 ++++++++++++++++++----- crates/ailang-prose/tests/snapshot.rs | 5 + examples/test_22b3_default_e2e.prose.txt | 16 +++ 3 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 examples/test_22b3_default_e2e.prose.txt diff --git a/crates/ailang-prose/src/lib.rs b/crates/ailang-prose/src/lib.rs index e8302fe..fe69811 100644 --- a/crates/ailang-prose/src/lib.rs +++ b/crates/ailang-prose/src/lib.rs @@ -25,8 +25,8 @@ //! never fails on a well-formed AST. use ailang_core::ast::{ - ConstDef, Ctor, Def, FnDef, Import, Literal, Module, ParamMode, Pattern, Suppress, Term, Type, - TypeDef, + ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, InstanceDef, InstanceMethod, + Literal, Module, ParamMode, Pattern, Suppress, Term, Type, TypeDef, }; /// Render a [`Module`] as human-readable prose. @@ -77,28 +77,8 @@ fn write_def(out: &mut String, def: &Def, level: usize) { Def::Type(td) => write_type_def(out, td, level), Def::Fn(fd) => write_fn_def(out, fd, level), Def::Const(cd) => write_const_def(out, cd, level), - // ClassDef / InstanceDef render as a single-line placeholder - // so prose round-trip survives a module that contains them. - // The full Form-B projection (mirror of the Form-A schema, - // with method bodies re-indented and superclass / default - // markers spelled out) is queued post-22 — `crates/ailang-prose` - // is one-way (no parser by design), so the placeholder is - // informational only and the round-trip mediator does not - // read these back. - Def::Class(c) => { - indent(out, level); - out.push_str("// (class "); - out.push_str(&c.name); - out.push(' '); - out.push_str(&c.param); - out.push_str(") -- full Form-B projection deferred (post-22)\n"); - } - Def::Instance(i) => { - indent(out, level); - out.push_str("// (instance "); - out.push_str(&i.class); - out.push_str(") -- full Form-B projection deferred (post-22)\n"); - } + Def::Class(c) => write_class_def(out, c, level), + Def::Instance(i) => write_instance_def(out, i, level), } } @@ -341,6 +321,101 @@ fn write_const_def(out: &mut String, cd: &ConstDef, level: usize) { write_term(out, &cd.value, level); } +fn write_class_def(out: &mut String, c: &ClassDef, level: usize) { + write_doc(out, &c.doc, level); + indent(out, level); + out.push_str("class "); + out.push_str(&c.name); + out.push(' '); + out.push_str(&c.param); + if let Some(sc) = &c.superclass { + out.push_str(" extends "); + out.push_str(&sc.class); + } + out.push_str(" {\n"); + for m in &c.methods { + write_class_method(out, m, level + 1); + } + indent(out, level); + out.push('}'); +} + +fn write_class_method(out: &mut String, m: &ClassMethod, level: usize) { + indent(out, level); + out.push_str("fn "); + out.push_str(&m.name); + out.push('('); + if let Type::Fn { params, param_modes, ret, ret_mode, effects } = &m.ty { + for (i, p) in params.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + // Class methods carry types but no parameter names — name slots + // as `x`, `x1`, `x2`, … to keep the projection unambiguous + // without inventing names that could collide with the body. + if i == 0 { + out.push_str("x"); + } else { + out.push_str(&format!("x{i}")); + } + out.push_str(": "); + let mode = param_modes.get(i).copied().unwrap_or(ParamMode::Implicit); + write_mode_type(out, p, mode); + } + out.push_str(") -> "); + write_mode_type(out, ret, *ret_mode); + if !effects.is_empty() { + out.push_str(" with "); + for (i, e) in effects.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + out.push_str(e); + } + } + } else { + out.push_str(") -> "); + write_type(out, &m.ty); + } + if let Some(body) = &m.default { + out.push_str(" default {\n"); + indent(out, level + 1); + write_term(out, body, level + 1); + out.push('\n'); + indent(out, level); + out.push_str("}\n"); + } else { + out.push('\n'); + } +} + +fn write_instance_def(out: &mut String, i: &InstanceDef, level: usize) { + write_doc(out, &i.doc, level); + indent(out, level); + out.push_str("instance "); + out.push_str(&i.class); + out.push(' '); + write_type(out, &i.type_); + out.push_str(" {\n"); + for m in &i.methods { + write_instance_method(out, m, level + 1); + } + indent(out, level); + out.push('}'); +} + +fn write_instance_method(out: &mut String, m: &InstanceMethod, level: usize) { + indent(out, level); + out.push_str("fn "); + out.push_str(&m.name); + out.push_str(" {\n"); + indent(out, level + 1); + write_term(out, &m.body, level + 1); + out.push('\n'); + indent(out, level); + out.push_str("}\n"); +} + // ---- types ---------------------------------------------------------------- fn write_mode_type(out: &mut String, t: &Type, mode: ParamMode) { diff --git a/crates/ailang-prose/tests/snapshot.rs b/crates/ailang-prose/tests/snapshot.rs index cb0bfab..10c94fc 100644 --- a/crates/ailang-prose/tests/snapshot.rs +++ b/crates/ailang-prose/tests/snapshot.rs @@ -71,3 +71,8 @@ fn snapshot_rc_app_let_partial_drop_leak() { fn snapshot_bench_list_sum() { check_snapshot("bench_list_sum"); } + +#[test] +fn snapshot_test_22b3_default_e2e() { + check_snapshot("test_22b3_default_e2e"); +} diff --git a/examples/test_22b3_default_e2e.prose.txt b/examples/test_22b3_default_e2e.prose.txt new file mode 100644 index 0000000..acaf283 --- /dev/null +++ b/examples/test_22b3_default_e2e.prose.txt @@ -0,0 +1,16 @@ +// module test_22b3_default_e2e + +class D a { + fn dval(x: a) -> Int default { + |x: a| -> Int { + 99 + } + } +} + +instance D Int { +} + +fn main() -> Unit with IO { + do io/print_int(dval(0)) +}