iter 22b.4a.4.5: form-a printer+parser arms for Type::Forall constraints
This commit is contained in:
@@ -85,7 +85,7 @@
|
||||
//! [`ailang_core::ast::Import::alias`].
|
||||
|
||||
use ailang_core::ast::{
|
||||
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
|
||||
Arm, ClassDef, ClassMethod, Constraint, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
|
||||
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
||||
Type, TypeDef,
|
||||
};
|
||||
@@ -1149,15 +1149,49 @@ impl<'a> Parser<'a> {
|
||||
vars.push(self.expect_ident("type variable")?);
|
||||
}
|
||||
self.expect_rparen("forall vars")?;
|
||||
// optional (constraints (constraint <Class> <type>)+ )
|
||||
let constraints = if let Some("constraints") = self.peek_head_ident() {
|
||||
self.parse_forall_constraints_clause()?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let body = self.parse_type()?;
|
||||
self.expect_rparen("forall-type")?;
|
||||
Ok(Type::Forall {
|
||||
vars,
|
||||
constraints: vec![],
|
||||
constraints,
|
||||
body: Box::new(body),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_forall_constraints_clause(&mut self) -> Result<Vec<Constraint>, ParseError> {
|
||||
self.expect_lparen("forall.constraints")?;
|
||||
self.expect_keyword("constraints")?;
|
||||
let mut out = Vec::new();
|
||||
while !matches!(self.peek(), Some(Token { tok: Tok::RParen, .. })) {
|
||||
out.push(self.parse_constraint()?);
|
||||
}
|
||||
if out.is_empty() {
|
||||
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
||||
return Err(ParseError::Production {
|
||||
production: "forall.constraints",
|
||||
message: "(constraints …) clause must contain at least one (constraint …)".into(),
|
||||
pos,
|
||||
});
|
||||
}
|
||||
self.expect_rparen("forall.constraints")?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn parse_constraint(&mut self) -> Result<Constraint, ParseError> {
|
||||
self.expect_lparen("constraint")?;
|
||||
self.expect_keyword("constraint")?;
|
||||
let class = self.expect_ident("constraint class name")?;
|
||||
let type_ = self.parse_type()?;
|
||||
self.expect_rparen("constraint")?;
|
||||
Ok(Constraint { class, type_ })
|
||||
}
|
||||
|
||||
// ---- terms ----------------------------------------------------------
|
||||
|
||||
fn parse_term(&mut self) -> Result<Term, ParseError> {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! per level. Comments are NOT emitted.
|
||||
|
||||
use ailang_core::ast::{
|
||||
Arm, ClassDef, ClassMethod, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
|
||||
Arm, ClassDef, ClassMethod, Constraint, ConstDef, Ctor, Def, FnDef, Import, InstanceDef,
|
||||
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
||||
Type, TypeDef,
|
||||
};
|
||||
@@ -336,6 +336,18 @@ fn write_fn_type_slot(out: &mut String, t: &Type, mode: ParamMode) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iter 22b.4a.4.5: print one `(constraint <Class> <type>)` pair, used
|
||||
/// inside the optional `(constraints …)` clause of a `(forall …)`. The
|
||||
/// clause itself is omitted entirely when `Type::Forall.constraints` is
|
||||
/// empty so pre-22b.2 forall fixtures stay bit-identical.
|
||||
fn write_constraint(out: &mut String, c: &Constraint) {
|
||||
out.push_str("(constraint ");
|
||||
out.push_str(&c.class);
|
||||
out.push(' ');
|
||||
write_type(out, &c.type_);
|
||||
out.push(')');
|
||||
}
|
||||
|
||||
fn write_type(out: &mut String, t: &Type) {
|
||||
match t {
|
||||
Type::Var { name } => out.push_str(name),
|
||||
@@ -374,13 +386,22 @@ fn write_type(out: &mut String, t: &Type) {
|
||||
}
|
||||
out.push(')');
|
||||
}
|
||||
Type::Forall { vars, constraints: _, body } => {
|
||||
Type::Forall { vars, constraints, body } => {
|
||||
out.push_str("(forall (vars");
|
||||
for v in vars {
|
||||
out.push(' ');
|
||||
out.push_str(v);
|
||||
}
|
||||
out.push_str(") ");
|
||||
out.push(')');
|
||||
if !constraints.is_empty() {
|
||||
out.push_str(" (constraints");
|
||||
for c in constraints {
|
||||
out.push(' ');
|
||||
write_constraint(out, c);
|
||||
}
|
||||
out.push(')');
|
||||
}
|
||||
out.push(' ');
|
||||
write_type(out, body);
|
||||
out.push(')');
|
||||
}
|
||||
@@ -658,6 +679,40 @@ mod tests {
|
||||
round_trip(m, "class_with_superclass_and_default");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_then_parse_round_trip_forall_with_constraint() {
|
||||
let m = Module {
|
||||
schema: ailang_core::SCHEMA.to_string(),
|
||||
name: "M".into(),
|
||||
imports: vec![],
|
||||
defs: vec![Def::Fn(ailang_core::ast::FnDef {
|
||||
name: "f".into(),
|
||||
ty: Type::Forall {
|
||||
vars: vec!["a".into()],
|
||||
constraints: vec![ailang_core::ast::Constraint {
|
||||
class: "Show".into(),
|
||||
type_: Type::Var { name: "a".into() },
|
||||
}],
|
||||
body: Box::new(Type::Fn {
|
||||
params: vec![Type::Var { name: "a".into() }],
|
||||
param_modes: vec![ParamMode::Implicit],
|
||||
ret: Box::new(Type::Con {
|
||||
name: "Str".into(),
|
||||
args: vec![],
|
||||
}),
|
||||
ret_mode: ParamMode::Implicit,
|
||||
effects: vec![],
|
||||
}),
|
||||
},
|
||||
params: vec!["x".into()],
|
||||
body: Term::Var { name: "x".into() },
|
||||
doc: None,
|
||||
suppress: vec![],
|
||||
})],
|
||||
};
|
||||
round_trip(m, "forall_with_constraint");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_then_parse_round_trip_minimal_instance() {
|
||||
let m = Module {
|
||||
|
||||
Reference in New Issue
Block a user