rukalang/
meta_builtins.rs

1use crate::meta_types::{tuple_head_type, tuple_tail_type};
2use crate::semantic::{BuiltinMetaCallKind, MetaArgKind, MetaType};
3use crate::syntax::ast::{Expr, TypeExpr};
4
5pub(crate) struct BuiltinMetaSpec {
6    pub kind: BuiltinMetaCallKind,
7    pub name: &'static str,
8    pub arity: usize,
9    pub arg_kinds: &'static [MetaArgKind],
10}
11
12pub(crate) enum TupleExprBuiltinError {
13    NotTupleExpr,
14    EmptyTuple,
15}
16
17pub(crate) enum TupleTypeBuiltinError {
18    NotTupleType,
19    EmptyTuple,
20}
21
22const STRING_STRING_ARGS: &[MetaArgKind] = &[MetaArgKind::String, MetaArgKind::String];
23const STRING_INT_ARGS: &[MetaArgKind] = &[MetaArgKind::String, MetaArgKind::Int];
24const EXPR_ARG: &[MetaArgKind] = &[MetaArgKind::Expr];
25const TYPE_ARG: &[MetaArgKind] = &[MetaArgKind::Type];
26
27const BUILTIN_META_SPECS: &[BuiltinMetaSpec] = &[
28    BuiltinMetaSpec {
29        kind: BuiltinMetaCallKind::StringConcat,
30        name: "std::string::concat",
31        arity: 2,
32        arg_kinds: STRING_STRING_ARGS,
33    },
34    BuiltinMetaSpec {
35        kind: BuiltinMetaCallKind::StringStartsWith,
36        name: "std::string::starts_with",
37        arity: 2,
38        arg_kinds: STRING_STRING_ARGS,
39    },
40    BuiltinMetaSpec {
41        kind: BuiltinMetaCallKind::StringDrop,
42        name: "std::string::drop",
43        arity: 2,
44        arg_kinds: STRING_INT_ARGS,
45    },
46    BuiltinMetaSpec {
47        kind: BuiltinMetaCallKind::StringTake,
48        name: "std::string::take",
49        arity: 2,
50        arg_kinds: STRING_INT_ARGS,
51    },
52    BuiltinMetaSpec {
53        kind: BuiltinMetaCallKind::TupleIsEmpty,
54        name: "std::tuple::is_empty",
55        arity: 1,
56        arg_kinds: EXPR_ARG,
57    },
58    BuiltinMetaSpec {
59        kind: BuiltinMetaCallKind::TupleHead,
60        name: "std::tuple::head",
61        arity: 1,
62        arg_kinds: EXPR_ARG,
63    },
64    BuiltinMetaSpec {
65        kind: BuiltinMetaCallKind::TupleTail,
66        name: "std::tuple::tail",
67        arity: 1,
68        arg_kinds: EXPR_ARG,
69    },
70    BuiltinMetaSpec {
71        kind: BuiltinMetaCallKind::TupleTypeHead,
72        name: "std::tuple::type_head",
73        arity: 1,
74        arg_kinds: TYPE_ARG,
75    },
76    BuiltinMetaSpec {
77        kind: BuiltinMetaCallKind::TupleTypeTail,
78        name: "std::tuple::type_tail",
79        arity: 1,
80        arg_kinds: TYPE_ARG,
81    },
82];
83
84pub(crate) fn builtin_spec(kind: BuiltinMetaCallKind) -> &'static BuiltinMetaSpec {
85    BUILTIN_META_SPECS
86        .iter()
87        .find(|spec| spec.kind == kind)
88        .expect("builtin kind should have spec")
89}
90
91pub(crate) fn builtin_spec_by_name(callee: &str) -> Option<&'static BuiltinMetaSpec> {
92    BUILTIN_META_SPECS.iter().find(|spec| spec.name == callee)
93}
94
95pub(crate) fn builtin_meta_call_name(kind: BuiltinMetaCallKind) -> &'static str {
96    builtin_spec(kind).name
97}
98
99pub(crate) fn builtin_meta_call_arity(kind: BuiltinMetaCallKind) -> usize {
100    builtin_spec(kind).arity
101}
102
103pub(crate) fn builtin_result_type(
104    kind: BuiltinMetaCallKind,
105    arg_types: &[Option<MetaType>],
106) -> Option<MetaType> {
107    match kind {
108        BuiltinMetaCallKind::StringConcat
109        | BuiltinMetaCallKind::StringDrop
110        | BuiltinMetaCallKind::StringTake => Some(MetaType::String),
111        BuiltinMetaCallKind::StringStartsWith | BuiltinMetaCallKind::TupleIsEmpty => {
112            Some(MetaType::Bool)
113        }
114        BuiltinMetaCallKind::TupleHead => builtin_tuple_head_result_type(arg_types),
115        BuiltinMetaCallKind::TupleTail => builtin_tuple_tail_result_type(arg_types),
116        BuiltinMetaCallKind::TupleTypeHead | BuiltinMetaCallKind::TupleTypeTail => {
117            Some(MetaType::Type)
118        }
119    }
120}
121
122fn builtin_tuple_head_result_type(arg_types: &[Option<MetaType>]) -> Option<MetaType> {
123    let [Some(MetaType::Expr(TypeExpr::Tuple(items)))] = arg_types else {
124        return None;
125    };
126    tuple_head_type(&TypeExpr::Tuple(items.clone())).map(MetaType::Expr)
127}
128
129fn builtin_tuple_tail_result_type(arg_types: &[Option<MetaType>]) -> Option<MetaType> {
130    let [Some(MetaType::Expr(TypeExpr::Tuple(items)))] = arg_types else {
131        return None;
132    };
133    tuple_tail_type(&TypeExpr::Tuple(items.clone())).map(MetaType::Expr)
134}
135
136pub(crate) fn string_concat(left: &str, right: &str) -> String {
137    format!("{left}{right}")
138}
139
140pub(crate) fn string_starts_with(value: &str, prefix: &str) -> bool {
141    value.starts_with(prefix)
142}
143
144pub(crate) fn string_drop(value: &str, count: usize) -> String {
145    value.chars().skip(count).collect()
146}
147
148pub(crate) fn string_take(value: &str, count: usize) -> String {
149    value.chars().take(count).collect()
150}
151
152pub(crate) fn tuple_expr_is_empty(expr: &Expr) -> Option<bool> {
153    match expr {
154        Expr::Tuple { items, .. } => Some(items.is_empty()),
155        _ => None,
156    }
157}
158
159pub(crate) fn tuple_expr_head(
160    expr: Expr,
161    ty: &TypeExpr,
162) -> Result<(Expr, TypeExpr), TupleExprBuiltinError> {
163    let Expr::Tuple { items, .. } = expr else {
164        return Err(TupleExprBuiltinError::NotTupleExpr);
165    };
166    let head_expr = items
167        .first()
168        .cloned()
169        .ok_or(TupleExprBuiltinError::EmptyTuple)?;
170    let head_ty = tuple_head_type(ty).ok_or(TupleExprBuiltinError::EmptyTuple)?;
171    Ok((head_expr, head_ty))
172}
173
174pub(crate) fn tuple_expr_tail(expr: Expr, ty: &TypeExpr) -> Option<(Expr, TypeExpr)> {
175    let Expr::Tuple { items, span } = expr else {
176        return None;
177    };
178    let tail_expr = if items.len() <= 1 {
179        Expr::Tuple {
180            items: Vec::new(),
181            span,
182        }
183    } else {
184        Expr::Tuple {
185            items: items[1..].to_vec(),
186            span,
187        }
188    };
189    let tail_ty = tuple_tail_type(ty)?;
190    Some((tail_expr, tail_ty))
191}
192
193pub(crate) fn tuple_type_head_builtin(ty: &TypeExpr) -> Result<TypeExpr, TupleTypeBuiltinError> {
194    tuple_head_type(ty).ok_or_else(|| {
195        if matches!(ty, TypeExpr::Tuple(_)) || matches!(ty, TypeExpr::Named(name) if name == "Unit")
196        {
197            TupleTypeBuiltinError::EmptyTuple
198        } else {
199            TupleTypeBuiltinError::NotTupleType
200        }
201    })
202}
203
204pub(crate) fn tuple_type_tail_builtin(ty: &TypeExpr) -> Result<TypeExpr, TupleTypeBuiltinError> {
205    tuple_tail_type(ty).ok_or(TupleTypeBuiltinError::NotTupleType)
206}