use crate::ParseError;
use crate::item::rewritten;
use crate::item::visibility;
use crate::parser::Parser;
use crate::signature::TypeStop;
use crate::signature::capture_type;
use crate::signature::rust_generic;
use crate::signature::starts_a_field_type;
use crate::signature::type_vars;
use crate::type_class::instance_decl;
use voltaic_ast::Constraint;
use voltaic_ast::Constructor;
use voltaic_ast::ConstructorFields;
use voltaic_ast::DataDecl;
use voltaic_ast::DeriveTemplate;
use voltaic_ast::ErrorVariant;
use voltaic_ast::ErrorsDecl;
use voltaic_ast::FieldDecl;
use voltaic_ast::Ident;
use voltaic_ast::Signature;
use voltaic_ast::Visibility;
use voltaic_diag::Code;
use voltaic_lexer::Keyword;
use voltaic_lexer::Span;
use voltaic_lexer::TokenKind;
use voltaic_lexer::Trivia;
pub(crate) fn constructor(
p: &mut Parser,
leading: Vec<voltaic_lexer::Trivia>,
) -> Result<Constructor, ParseError> {
let start = p.peek_span().0;
let name = p.ident("expected a constructor name")?;
let fields = constructor_fields(p)?;
Ok(Constructor {
name,
fields,
leading,
span: (start, p.prev_span().1),
})
}
pub(crate) fn constructor_fields(p: &mut Parser) -> Result<ConstructorFields, ParseError> {
if p.check(&TokenKind::LBrace) {
p.advance();
let mut fields = Vec::new();
loop {
p.skip_layout();
if p.check(&TokenKind::RBrace) {
break;
}
fields.push(field_decl(p)?);
p.skip_layout();
if !p.check(&TokenKind::Comma) {
break;
}
p.advance();
}
p.skip_layout();
p.expect(&TokenKind::RBrace, "expected `}` to close the record")?;
return Ok(ConstructorFields::Record(fields));
}
let mut types = Vec::new();
while starts_a_field_type(&p.peek().kind) {
types.push(capture_type(p, "expected a field type", TypeStop::Atom)?);
}
if types.is_empty() {
return Ok(ConstructorFields::Unit);
}
Ok(ConstructorFields::Tuple(types))
}
pub(crate) fn data_decl(
p: &mut Parser,
leading: Vec<Trivia>,
start: usize,
visibility: Visibility,
) -> Result<DataDecl, ParseError> {
let is_newtype = p.check(&TokenKind::Kw(Keyword::Newtype));
let is_union = p.check(&TokenKind::Kw(Keyword::Union));
p.advance();
let name = p.ident("expected a type name after `data`")?;
let mut constructors = Vec::new();
let mut derives = Vec::new();
let mut depth: usize = 0;
loop {
if depth > 0 {
p.skip_separators();
}
let opens_a_block =
p.check(&TokenKind::Newline) && p.peek_at(1).is_some_and(|t| t.kind == TokenKind::Indent);
if opens_a_block {
p.advance();
p.advance();
depth += 1;
continue;
}
if p.check(&TokenKind::Eq) || p.check(&TokenKind::Or) {
let row_leading = p.peek_leading();
p.advance();
constructors.push(constructor(p, row_leading)?);
continue;
}
if p.check(&TokenKind::Kw(Keyword::Deriving)) {
derives = r#deriving(p)?;
continue;
}
break;
}
for _closer in 0..depth {
p.skip_separators();
p.expect(&TokenKind::Dedent, "expected the declaration to end")?;
}
let mut decl = DataDecl {
name,
generics: Vec::new(),
constructors,
derives,
is_newtype,
is_union,
visibility,
span: (start, p.prev_span().1),
leading,
};
generalise_data(&mut decl);
newtype_shape(&decl)?;
Ok(decl)
}
pub(crate) fn derive_template(
p: &mut Parser,
leading: Vec<Trivia>,
start: usize,
) -> Result<DeriveTemplate, ParseError> {
p.expect(&TokenKind::Kw(Keyword::Deriving), "expected `deriving`")?;
let word = p.ident("expected `template` after `deriving`")?;
if word.text != "template" {
return Err(ParseError::with_help(
Code::DeriveTemplateShape,
"expected `template` after `deriving`",
"a `deriving` clause belongs indented under its declaration — `newtype Meters = Meters f64` then `deriving (Debug)`. At item position `deriving` begins a template: `deriving template Forward`",
word.span,
));
}
let name = p.ident("expected a name for the deriving template")?;
p.newline_with_trailing("expected a line break after the template's name")?;
p.expect(
&TokenKind::Indent,
"expected an indented `instance` as the template's body",
)?;
let body = instance_decl(p, Vec::new(), p.peek_span().0)?;
p.skip_separators();
p.expect(&TokenKind::Dedent, "expected the template's body to end")?;
if body.target.head.text != "Target" {
return Err(ParseError::with_help(
Code::DeriveTemplateShape,
"a deriving template's `instance` must be for `Target`",
"`Target` is the placeholder the declaration's own type name is substituted for — `instance Display Target`. An `instance` for a concrete type is not a template and belongs at module scope",
body.target.head.span,
));
}
Ok(DeriveTemplate {
name,
body,
span: (start, p.prev_span().1),
leading,
})
}
pub(crate) fn r#deriving(p: &mut Parser) -> Result<Vec<Ident>, ParseError> {
if !p.check(&TokenKind::Kw(Keyword::Deriving)) {
return Ok(Vec::new());
}
p.advance();
p.expect(&TokenKind::LParen, "expected `(` after `deriving`")?;
let mut traits = Vec::new();
loop {
p.skip_layout();
if p.check(&TokenKind::RParen) {
break;
}
let mut name = p.ident("expected a trait name")?;
while p.check(&TokenKind::ColonColon) {
p.advance();
let segment = p.ident("expected a name after `::`")?;
name.text.push_str("::");
name.text.push_str(&segment.text);
name.span.1 = segment.span.1;
}
traits.push(name);
p.skip_layout();
if !p.check(&TokenKind::Comma) {
break;
}
p.advance();
}
p.expect(
&TokenKind::RParen,
"expected `)` to close the `deriving` list",
)?;
Ok(traits)
}
pub(crate) fn error_variant(p: &mut Parser) -> Result<ErrorVariant, ParseError> {
let start = p.peek_span().0;
let is_from = match &p.peek().kind {
TokenKind::Ident(text) if text == "from" => true,
_ => false,
};
let read = if is_from {
p.advance();
let ty = capture_type(p, "expected a type after `from`", TypeStop::Greedy)?;
p.expect(
&TokenKind::Kw(Keyword::As),
"expected `as` between the type and the variant's name",
)?;
let named = p.ident("expected the variant's name after `as`")?;
(named, vec![ty], true)
} else {
let named = p.ident("expected a variant name")?;
let mut payload = Vec::new();
while starts_a_field_type(&p.peek().kind) {
payload.push(capture_type(p, "expected a payload type", TypeStop::Atom)?);
}
(named, payload, false)
};
let message = match &p.peek().kind {
TokenKind::Str { raw, .. } => raw.clone(),
_ => {
return Err(ParseError::expected(
"expected the variant's `Display` message, as a string literal",
p.peek_span(),
));
}
};
p.advance();
p.newline_with_trailing("expected a line break after the variant")?;
Ok(ErrorVariant {
name: read.0,
fields: read.1,
from: read.2,
message,
span: (start, p.prev_span().1),
})
}
pub(crate) fn errors_decl(
p: &mut Parser,
leading: Vec<Trivia>,
start: usize,
visibility: Visibility,
) -> Result<ErrorsDecl, ParseError> {
p.expect(&TokenKind::Kw(Keyword::Errors), "expected `errors`")?;
let name = p.ident("expected the error type's name")?;
p.newline_with_trailing("expected a line break after the error type's name")?;
p.expect_with(
Code::EmptyBody,
&TokenKind::Indent,
"expected indented error variants",
)?;
let mut variants = Vec::new();
loop {
p.skip_separators();
if p.check(&TokenKind::Dedent) || p.at_eof() {
break;
}
variants.push(error_variant(p)?);
}
p.expect(&TokenKind::Dedent, "expected the error variants to end")?;
if variants.is_empty() {
return Err(ParseError::r#with(
Code::EmptyBody,
"an `errors` declaration must have at least one variant",
(start, p.prev_span().1),
));
}
Ok(ErrorsDecl {
name,
variants,
visibility,
span: (start, p.prev_span().1),
leading,
})
}
pub(crate) fn field_decl(p: &mut Parser) -> Result<FieldDecl, ParseError> {
let start = p.peek_span().0;
let leading = p.peek_leading();
let marker = visibility(p)?;
let name = p.ident("expected a field name")?;
p.expect(&TokenKind::Colon, "expected `:` after the field name")?;
let ty = capture_type(p, "expected a field type", TypeStop::Greedy)?;
Ok(FieldDecl {
name,
ty,
visibility: marker,
leading,
span: (start, p.prev_span().1),
})
}
pub(crate) fn generalise(sig: &mut Signature, constraints: &[Constraint], reserved: &[String]) {
if sig.r#given.is_some() {
return ();
}
let mut texts: Vec<&str> = sig
.params
.iter()
.filter_map(|param| param.ty.as_ref().map(|t| t.text.as_str()))
.collect();
if let Some(ret) = &sig.ret {
texts.push(ret.text.as_str());
}
let found: Vec<String> = type_vars(&texts)
.into_iter()
.filter(|v| !reserved.contains(v))
.collect();
sig.bounds = constraints
.iter()
.filter(|c| !reserved.contains(&c.var.text))
.map(|c| (rust_generic(&c.var.text), c.trait_name.text.clone()))
.collect();
if found.is_empty() {
return ();
}
sig.generics = found.iter().map(|v| rust_generic(v)).collect();
for var in &found {
if var.starts_with('\'') {
continue;
}
let subject = rust_generic(var);
for param in &mut sig.params {
if let Some(ty) = &mut param.ty {
ty.text = rewritten(&ty.text, var, &subject);
}
}
if let Some(ret) = &mut sig.ret {
ret.text = rewritten(&ret.text, var, &subject);
}
}
();
}
pub(crate) fn generalise_data(d: &mut DataDecl) {
let mut texts: Vec<String> = Vec::new();
for c in &d.constructors {
match &c.fields {
ConstructorFields::Unit => (),
ConstructorFields::Tuple(ts) => texts.extend(ts.iter().map(|t| t.text.clone())),
ConstructorFields::Record(fs) => texts.extend(fs.iter().map(|f| f.ty.text.clone())),
};
}
let refs: Vec<&str> = texts.iter().map(String::as_str).collect();
let found = type_vars(&refs);
if found.is_empty() {
return ();
}
d.generics = found.iter().map(|v| rust_generic(v)).collect();
for c in &mut d.constructors {
let types: Vec<&mut String> = match &mut c.fields {
ConstructorFields::Unit => Vec::new(),
ConstructorFields::Tuple(ts) => ts.iter_mut().map(|t| &mut t.text).collect(),
ConstructorFields::Record(fs) => fs.iter_mut().map(|f| &mut f.ty.text).collect(),
};
for text in types {
for v in &found {
*text = rewritten(text, v, &rust_generic(v));
}
}
}
();
}
pub(crate) fn newtype_shape(decl: &DataDecl) -> Result<(), ParseError> {
if !decl.is_newtype {
return Ok(());
}
let count = decl.constructors.len();
if count > 1 {
let message = format!(
"a `newtype` has exactly one constructor, and this declaration has {} — write `data` for a choice between constructors",
count
);
return Err(ParseError::r#with(
Code::NewtypeShape,
message,
decl.constructors[1].span,
));
}
let shape: (Span, Vec<Span>) = match decl.constructors.first() {
None => (decl.name.span, Vec::new()),
Some(only) => {
let spans = match &only.fields {
ConstructorFields::Unit => Vec::new(),
ConstructorFields::Tuple(types) => types.iter().map(|t| t.span).collect(),
ConstructorFields::Record(field_decls) => field_decls.iter().map(|f| f.span).collect(),
};
(only.span, spans)
}
};
let blame = shape.0;
let fields = shape.1;
let n = fields.len();
match n {
1 => Ok(()),
0 => Err(ParseError::r#with(
Code::NewtypeShape,
"a `newtype` wraps exactly one field, and this declaration wraps none — write `data` for a type with no payload",
blame,
)),
_ => {
let message = format!(
"a `newtype` wraps exactly one field, and this declaration wraps {} — write `data` for a constructor with more than one field",
n
);
Err(ParseError::r#with(Code::NewtypeShape, message, fields[1]))
}
}
}