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}