iter 22b.4a.2: form-a parser arm for InstanceDef

This commit is contained in:
2026-05-09 22:05:49 +02:00
parent a1b4cd0063
commit da1746229d
+173 -3
View File
@@ -85,8 +85,8 @@
//! [`ailang_core::ast::Import::alias`].
use ailang_core::ast::{
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, Literal, Module,
ParamMode, Pattern, SuperclassRef, Suppress, Term,
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
Type, TypeDef,
};
use ailang_core::SCHEMA;
@@ -311,12 +311,13 @@ impl<'a> Parser<'a> {
"fn" => defs.push(Def::Fn(self.parse_fn()?)),
"const" => defs.push(Def::Const(self.parse_const()?)),
"class" => defs.push(Def::Class(self.parse_class()?)),
"instance" => defs.push(Def::Instance(self.parse_instance()?)),
other => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "module",
message: format!(
"unknown def head `{other}`; expected `data`, `fn`, `const`, `class`, or `import`"
"unknown def head `{other}`; expected `data`, `fn`, `const`, `class`, `instance`, or `import`"
),
pos,
});
@@ -847,6 +848,132 @@ impl<'a> Parser<'a> {
Ok(t)
}
// ---- instance def --------------------------------------------------
fn parse_instance(&mut self) -> Result<InstanceDef, ParseError> {
self.expect_lparen("instance-def")?;
self.expect_keyword("instance")?;
let mut class: Option<String> = None;
let mut type_: Option<Type> = None;
let mut doc: Option<String> = None;
let mut methods: Vec<InstanceMethod> = Vec::new();
loop {
match self.peek_head_ident() {
Some("class") => {
if class.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance-def",
message: "instance has duplicate `(class ...)` clause".into(),
pos,
});
}
self.expect_lparen("instance.class")?;
self.expect_keyword("class")?;
class = Some(self.expect_ident("instance class name")?);
self.expect_rparen("instance.class")?;
}
Some("type") => {
if type_.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance-def",
message: "instance has duplicate `(type ...)` clause".into(),
pos,
});
}
type_ = Some(self.parse_type_attr()?);
}
Some("doc") => {
if doc.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance-def",
message: "instance has duplicate `(doc ...)` clause".into(),
pos,
});
}
doc = Some(self.parse_doc()?);
}
Some("method") => {
methods.push(self.parse_instance_method()?);
}
Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance-def",
message: format!(
"unknown instance attribute `{other}`; expected `class`, `type`, `doc`, or `method`"
),
pos,
});
}
None => break,
}
}
self.expect_rparen("instance-def")?;
let class = class.ok_or_else(|| ParseError::Production {
production: "instance-def",
message: "instance is missing required `(class ...)` clause".into(),
pos: 0,
})?;
let type_ = type_.ok_or_else(|| ParseError::Production {
production: "instance-def",
message: "instance is missing required `(type ...)` clause".into(),
pos: 0,
})?;
Ok(InstanceDef {
class,
type_,
methods,
doc,
})
}
fn parse_instance_method(&mut self) -> Result<InstanceMethod, ParseError> {
self.expect_lparen("instance.method")?;
self.expect_keyword("method")?;
let name = self.expect_ident("instance method name")?;
let mut body: Option<Term> = None;
loop {
match self.peek_head_ident() {
Some("body") => {
if body.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance.method",
message: format!(
"instance method `{name}` has duplicate `(body ...)` clause"
),
pos,
});
}
body = Some(self.parse_body_attr()?);
}
Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "instance.method",
message: format!(
"unknown instance.method attribute `{other}`; expected `body`"
),
pos,
});
}
None => break,
}
}
self.expect_rparen("instance.method")?;
let body = body.ok_or_else(|| ParseError::Production {
production: "instance.method",
message: format!(
"instance method `{name}` is missing required `(body ...)` clause"
),
pos: 0,
})?;
Ok(InstanceMethod { name, body })
}
// ---- types ----------------------------------------------------------
fn parse_type(&mut self) -> Result<Type, ParseError> {
@@ -2056,4 +2183,47 @@ mod tests {
assert!(c.methods[0].default.is_some(),
"method default present");
}
#[test]
fn parses_minimal_instance_def() {
let src = r#"(module M
(instance
(class Foo)
(type (con Int))
(method m
(body 5))))"#;
let m = parse(src).expect("parse ok");
match &m.defs[0] {
ailang_core::ast::Def::Instance(i) => {
assert_eq!(i.class, "Foo");
match &i.type_ {
ailang_core::ast::Type::Con { name, args } => {
assert_eq!(name, "Int");
assert!(args.is_empty(), "Int has no args");
}
other => panic!("expected Type::Con, got {other:?}"),
}
assert!(i.doc.is_none());
assert_eq!(i.methods.len(), 1);
assert_eq!(i.methods[0].name, "m");
}
other => panic!("expected Def::Instance, got {other:?}"),
}
}
#[test]
fn parses_instance_def_with_doc() {
let src = r#"(module M
(instance
(class Foo)
(type (con Int))
(doc "instance for ints")
(method m (body 7))))"#;
let m = parse(src).expect("parse ok");
let i = match &m.defs[0] {
ailang_core::ast::Def::Instance(i) => i,
_ => panic!("expected Instance"),
};
assert_eq!(i.doc.as_deref(), Some("instance for ints"));
}
}