rukalang/elab/
helpers.rs

1use super::prelude::*;
2
3pub(super) fn resolve_local(locals: &[(String, Ty)], name: &str) -> Result<Ty, ElabError> {
4    locals
5        .iter()
6        .rev()
7        .find_map(|(local, ty)| {
8            if local == name {
9                Some(ty.clone())
10            } else {
11                None
12            }
13        })
14        .ok_or_else(|| ElabError::UnknownIdentifier {
15            name: name.to_owned(),
16        })
17}
18
19pub(super) fn split_variant_path(path: &str) -> Option<(&str, &str)> {
20    let (head, tail) = path.split_once("::")?;
21    if tail.contains("::") {
22        return None;
23    }
24    Some((head, tail))
25}
26
27pub(super) fn is_type_param(param: &Param) -> bool {
28    param.ty.mode == OwnershipMode::View && matches!(param.ty.ty, TypeExpr::TypeKind)
29}
30
31pub(super) fn is_runtime_template(params: &[Param]) -> bool {
32    params
33        .iter()
34        .any(|param| is_type_param(param) || param.is_meta || param.is_variadic)
35}
36
37pub(super) fn type_param_names(params: &[Param]) -> Vec<String> {
38    params
39        .iter()
40        .filter(|param| is_type_param(param))
41        .map(|param| param.name.clone())
42        .collect()
43}
44
45pub(super) fn has_meta_params(params: &[Param]) -> bool {
46    params.iter().any(|param| param.is_meta)
47}
48
49pub(super) fn variadic_param_name(
50    name: &str,
51    params: &[Param],
52) -> Result<Option<String>, ElabError> {
53    let mut variadic = None;
54    for (index, param) in params.iter().enumerate() {
55        if !param.is_variadic {
56            continue;
57        }
58        if variadic.is_some() {
59            return Err(ElabError::MultipleVariadicParams {
60                name: name.to_owned(),
61            });
62        }
63        if index + 1 != params.len() {
64            return Err(ElabError::InvalidVariadicParam {
65                name: name.to_owned(),
66            });
67        }
68        if !matches!(&param.ty.ty, TypeExpr::Named(any_name) if any_name == "any") {
69            return Err(ElabError::InvalidVariadicParam {
70                name: name.to_owned(),
71            });
72        }
73        variadic = Some(param.name.clone());
74    }
75    Ok(variadic)
76}
77
78pub(super) fn runtime_params(params: &[Param]) -> Vec<Param> {
79    params
80        .iter()
81        .filter(|param| !is_type_param(param) && !param.is_meta)
82        .cloned()
83        .collect()
84}
85
86pub(super) fn canonical_nominal_name(ty: &Ty) -> String {
87    let (name, args) = match ty {
88        Ty::Struct { name, args } | Ty::Enum { name, args } => (name, args),
89        _ => {
90            return ty.to_string();
91        }
92    };
93    if args.is_empty() {
94        name.clone()
95    } else {
96        let rendered = args
97            .iter()
98            .map(render_type_name)
99            .collect::<Vec<_>>()
100            .join(", ");
101        format!("{name}[{rendered}]")
102    }
103}
104
105pub(super) fn render_type_name(ty: &Ty) -> String {
106    match ty {
107        Ty::Unit => "Unit".to_owned(),
108        Ty::U8 => "u8".to_owned(),
109        Ty::U16 => "u16".to_owned(),
110        Ty::U32 => "u32".to_owned(),
111        Ty::U64 => "u64".to_owned(),
112        Ty::I8 => "i8".to_owned(),
113        Ty::I16 => "i16".to_owned(),
114        Ty::I32 => "i32".to_owned(),
115        Ty::I64 => "i64".to_owned(),
116        Ty::F32 => "f32".to_owned(),
117        Ty::F64 => "f64".to_owned(),
118        Ty::String => "String".to_owned(),
119        Ty::Bool => "Bool".to_owned(),
120        Ty::Pointer(item) => format!("*{}", render_type_name(item)),
121        Ty::Option(item) => format!("Option[{}]", render_type_name(item)),
122        Ty::Array { item, len } => format!("[{}; {}]", render_type_name(item), len),
123        Ty::Slice(item) => format!("[{}]", render_type_name(item)),
124        Ty::Tuple(items) => {
125            let mut rendered = items
126                .iter()
127                .map(render_type_name)
128                .collect::<Vec<_>>()
129                .join(", ");
130            if items.len() == 1 {
131                rendered.push(',');
132            }
133            format!("({rendered})")
134        }
135        Ty::Struct { .. } | Ty::Enum { .. } => canonical_nominal_name(ty),
136        Ty::RefRo(_) | Ty::RefMut(_) => {
137            panic!("reference types are not valid in canonical runtime type names")
138        }
139    }
140}
141
142/// Infers generic type bindings by matching a type schema against a concrete type.
143pub(super) fn ty_to_type_expr(ty: &Ty) -> TypeExpr {
144    match ty {
145        Ty::Unit => TypeExpr::Named("Unit".to_owned()),
146        Ty::U8 => TypeExpr::Named("u8".to_owned()),
147        Ty::U16 => TypeExpr::Named("u16".to_owned()),
148        Ty::U32 => TypeExpr::Named("u32".to_owned()),
149        Ty::U64 => TypeExpr::Named("u64".to_owned()),
150        Ty::I8 => TypeExpr::Named("i8".to_owned()),
151        Ty::I16 => TypeExpr::Named("i16".to_owned()),
152        Ty::I32 => TypeExpr::Named("i32".to_owned()),
153        Ty::I64 => TypeExpr::Named("i64".to_owned()),
154        Ty::F32 => TypeExpr::Named("f32".to_owned()),
155        Ty::F64 => TypeExpr::Named("f64".to_owned()),
156        Ty::String => TypeExpr::Named("String".to_owned()),
157        Ty::Bool => TypeExpr::Named("Bool".to_owned()),
158        Ty::Pointer(item) => TypeExpr::Pointer {
159            item: Box::new(ty_to_type_expr(item)),
160        },
161        Ty::Option(item) => TypeExpr::Apply {
162            callee: "Option".to_owned(),
163            args: vec![ty_to_type_expr(item)],
164        },
165        Ty::Array { item, len } => TypeExpr::Array {
166            item: Box::new(ty_to_type_expr(item)),
167            len: *len,
168        },
169        Ty::Slice(item) => TypeExpr::Slice {
170            item: Box::new(ty_to_type_expr(item)),
171        },
172        Ty::Tuple(items) => TypeExpr::Tuple(items.iter().map(ty_to_type_expr).collect()),
173        Ty::Struct { .. } | Ty::Enum { .. } => TypeExpr::Named(canonical_nominal_name(ty)),
174        Ty::RefRo(_) | Ty::RefMut(_) => {
175            panic!("reference types are not valid in runtime type expression rewriting")
176        }
177    }
178}