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`].
|
//! [`ailang_core::ast::Import::alias`].
|
||||||
|
|
||||||
use ailang_core::ast::{
|
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,
|
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
||||||
Type, TypeDef,
|
Type, TypeDef,
|
||||||
};
|
};
|
||||||
@@ -1149,15 +1149,49 @@ impl<'a> Parser<'a> {
|
|||||||
vars.push(self.expect_ident("type variable")?);
|
vars.push(self.expect_ident("type variable")?);
|
||||||
}
|
}
|
||||||
self.expect_rparen("forall vars")?;
|
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()?;
|
let body = self.parse_type()?;
|
||||||
self.expect_rparen("forall-type")?;
|
self.expect_rparen("forall-type")?;
|
||||||
Ok(Type::Forall {
|
Ok(Type::Forall {
|
||||||
vars,
|
vars,
|
||||||
constraints: vec![],
|
constraints,
|
||||||
body: Box::new(body),
|
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 ----------------------------------------------------------
|
// ---- terms ----------------------------------------------------------
|
||||||
|
|
||||||
fn parse_term(&mut self) -> Result<Term, ParseError> {
|
fn parse_term(&mut self) -> Result<Term, ParseError> {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
//! per level. Comments are NOT emitted.
|
//! per level. Comments are NOT emitted.
|
||||||
|
|
||||||
use ailang_core::ast::{
|
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,
|
InstanceMethod, Literal, Module, ParamMode, Pattern, SuperclassRef, Suppress, Term,
|
||||||
Type, TypeDef,
|
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) {
|
fn write_type(out: &mut String, t: &Type) {
|
||||||
match t {
|
match t {
|
||||||
Type::Var { name } => out.push_str(name),
|
Type::Var { name } => out.push_str(name),
|
||||||
@@ -374,13 +386,22 @@ fn write_type(out: &mut String, t: &Type) {
|
|||||||
}
|
}
|
||||||
out.push(')');
|
out.push(')');
|
||||||
}
|
}
|
||||||
Type::Forall { vars, constraints: _, body } => {
|
Type::Forall { vars, constraints, body } => {
|
||||||
out.push_str("(forall (vars");
|
out.push_str("(forall (vars");
|
||||||
for v in vars {
|
for v in vars {
|
||||||
out.push(' ');
|
out.push(' ');
|
||||||
out.push_str(v);
|
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);
|
write_type(out, body);
|
||||||
out.push(')');
|
out.push(')');
|
||||||
}
|
}
|
||||||
@@ -658,6 +679,40 @@ mod tests {
|
|||||||
round_trip(m, "class_with_superclass_and_default");
|
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]
|
#[test]
|
||||||
fn print_then_parse_round_trip_minimal_instance() {
|
fn print_then_parse_round_trip_minimal_instance() {
|
||||||
let m = Module {
|
let m = Module {
|
||||||
|
|||||||
Reference in New Issue
Block a user