iter 22b.4a.2: form-a parser arm for InstanceDef
This commit is contained in:
@@ -85,8 +85,8 @@
|
|||||||
//! [`ailang_core::ast::Import::alias`].
|
//! [`ailang_core::ast::Import::alias`].
|
||||||
|
|
||||||
use ailang_core::ast::{
|
use ailang_core::ast::{
|
||||||
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, Literal, Module,
|
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
|
||||||
ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
||||||
Type, TypeDef,
|
Type, TypeDef,
|
||||||
};
|
};
|
||||||
use ailang_core::SCHEMA;
|
use ailang_core::SCHEMA;
|
||||||
@@ -311,12 +311,13 @@ impl<'a> Parser<'a> {
|
|||||||
"fn" => defs.push(Def::Fn(self.parse_fn()?)),
|
"fn" => defs.push(Def::Fn(self.parse_fn()?)),
|
||||||
"const" => defs.push(Def::Const(self.parse_const()?)),
|
"const" => defs.push(Def::Const(self.parse_const()?)),
|
||||||
"class" => defs.push(Def::Class(self.parse_class()?)),
|
"class" => defs.push(Def::Class(self.parse_class()?)),
|
||||||
|
"instance" => defs.push(Def::Instance(self.parse_instance()?)),
|
||||||
other => {
|
other => {
|
||||||
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
||||||
return Err(ParseError::Production {
|
return Err(ParseError::Production {
|
||||||
production: "module",
|
production: "module",
|
||||||
message: format!(
|
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,
|
pos,
|
||||||
});
|
});
|
||||||
@@ -847,6 +848,132 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(t)
|
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 ----------------------------------------------------------
|
// ---- types ----------------------------------------------------------
|
||||||
|
|
||||||
fn parse_type(&mut self) -> Result<Type, ParseError> {
|
fn parse_type(&mut self) -> Result<Type, ParseError> {
|
||||||
@@ -2056,4 +2183,47 @@ mod tests {
|
|||||||
assert!(c.methods[0].default.is_some(),
|
assert!(c.methods[0].default.is_some(),
|
||||||
"method default present");
|
"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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user