use crate::{ diag::Diagnostic, sexpr::{Atom, SExpr, SExprKind}, token::Span, }; const CURRENT_BETA_UNSUPPORTED: &str = "reserved but not supported in the current beta"; pub(crate) fn unsupported_reserved_type_diagnostic(file: &str, form: &SExpr) -> Option { if let Some(name) = expect_ident(form) { if is_generic_type_parameter_name(name) { return Some(unsupported_generic_type_parameter(file, form.span, name)); } } let items = expect_list(form)?; let head = items.first().and_then(expect_ident)?; match head { "map" => Some( Diagnostic::new( file, "UnsupportedMapType", format!("`map` types are {}", CURRENT_BETA_UNSUPPORTED), ) .with_span(form.span) .expected("supported concrete type") .found(render_type_form(form)) .hint( "use current concrete arrays, vectors, option/result, structs, enums, or scalars", ), ), "set" => Some( Diagnostic::new( file, "UnsupportedSetType", format!("`set` types are {}", CURRENT_BETA_UNSUPPORTED), ) .with_span(form.span) .expected("supported concrete type") .found(render_type_form(form)) .hint( "use current concrete arrays, vectors, option/result, structs, enums, or scalars", ), ), "vec" if items.len() != 2 => Some( Diagnostic::new( file, "UnsupportedGenericTypeParameter", format!("generic vector syntax is {}", CURRENT_BETA_UNSUPPORTED), ) .with_span(form.span) .expected("(vec i32), (vec i64), (vec f64), (vec bool), or (vec string)") .found(render_type_form(form)) .hint("choose one current concrete vector family explicitly"), ), _ => items .iter() .find_map(|item| unsupported_reserved_type_diagnostic(file, item)), } } pub(crate) fn unsupported_generic_function(file: &str, span: Span) -> Diagnostic { Diagnostic::new( file, "UnsupportedGenericFunction", format!( "generic function declarations are {}", CURRENT_BETA_UNSUPPORTED ), ) .with_span(span) .expected("(fn name ((arg ConcreteType) ...) -> ConcreteType body...)") .found("(type_params ...)") .hint("write a concrete function for each currently supported type family") } pub(crate) fn unsupported_generic_type_alias(file: &str, span: Span) -> Diagnostic { Diagnostic::new( file, "UnsupportedGenericTypeAlias", format!( "parameterized type aliases are {}", CURRENT_BETA_UNSUPPORTED ), ) .with_span(span) .expected("(type Alias ConcreteType)") .found("(type_params ...)") .hint("aliases remain transparent names for concrete supported target types") } pub(crate) fn unsupported_generic_type_parameter(file: &str, span: Span, name: &str) -> Diagnostic { Diagnostic::new( file, "UnsupportedGenericTypeParameter", format!( "generic type parameter `{}` is {}", name, CURRENT_BETA_UNSUPPORTED ), ) .with_span(span) .expected("concrete supported type") .found(name.to_string()) .hint("use a concrete promoted type such as `i32`, `string`, or `(vec i32)`") } pub(crate) fn unsupported_generic_standard_library_call( file: &str, span: Span, name: &str, ) -> Diagnostic { Diagnostic::new( file, "UnsupportedGenericStandardLibraryCall", format!( "generic standard-library call `{}` is {}", name, CURRENT_BETA_UNSUPPORTED ), ) .with_span(span) .expected("promoted concrete standard-library function") .found(name.to_string()) .hint("use current concrete families such as `std.vec.i32.empty`") } pub(crate) fn is_unsupported_generic_standard_library_call(name: &str) -> bool { matches!(name, "std.vec.empty" | "std.result.map") } pub(crate) fn is_generic_type_parameter_name(name: &str) -> bool { name.len() == 1 && name.bytes().all(|byte| byte.is_ascii_uppercase()) } fn expect_list(expr: &SExpr) -> Option<&[SExpr]> { match &expr.kind { SExprKind::List(items) => Some(items), _ => None, } } fn expect_ident(expr: &SExpr) -> Option<&str> { match &expr.kind { SExprKind::Atom(Atom::Ident(name)) => Some(name), _ => None, } } fn render_type_form(form: &SExpr) -> String { match &form.kind { SExprKind::Atom(Atom::Ident(name)) => name.clone(), SExprKind::Atom(Atom::Int(value)) => value.to_string(), SExprKind::Atom(Atom::I64(value)) => format!("{}i64", value), SExprKind::Atom(Atom::U32(value)) => format!("{}u32", value), SExprKind::Atom(Atom::U64(value)) => format!("{}u64", value), SExprKind::Atom(Atom::Float(value)) => value.to_string(), SExprKind::Atom(Atom::String(value)) => format!("{:?}", value), SExprKind::Atom(Atom::Arrow) => "->".to_string(), SExprKind::List(items) => { let parts = items.iter().map(render_type_form).collect::>(); format!("({})", parts.join(" ")) } } }