Skip to main content

tl_types/
convert.rs

1// ThinkingLanguage — TypeExpr (AST) to Type (internal) conversion
2// Licensed under MIT OR Apache-2.0
3
4use tl_ast::TypeExpr;
5
6use crate::{Type, TypeEnv};
7
8/// Convert an AST type expression into the internal Type representation.
9pub fn convert_type_expr(texpr: &TypeExpr) -> Type {
10    convert_type_expr_with_params(texpr, &[])
11}
12
13/// Convert an AST type expression, resolving type aliases from the environment.
14pub fn convert_type_expr_with_env(texpr: &TypeExpr, env: &TypeEnv) -> Type {
15    convert_type_expr_impl(texpr, &[], Some(env))
16}
17
18/// Convert an AST type expression, recognizing type parameters from the given list.
19pub fn convert_type_expr_with_params(texpr: &TypeExpr, type_params: &[String]) -> Type {
20    convert_type_expr_impl(texpr, type_params, None)
21}
22
23fn convert_type_expr_impl(texpr: &TypeExpr, type_params: &[String], env: Option<&TypeEnv>) -> Type {
24    match texpr {
25        TypeExpr::Named(name) => {
26            if type_params.contains(name) {
27                Type::TypeParam(name.clone())
28            } else if let Some(env) = env {
29                // Check for recursive alias
30                if env.is_resolving_alias(name) {
31                    return Type::Error; // Break cycle
32                }
33                if let Some((alias_params, alias_value)) = env.lookup_type_alias(name)
34                    && alias_params.is_empty()
35                {
36                    return convert_type_expr_impl(alias_value, type_params, Some(env));
37                }
38                convert_named(name)
39            } else {
40                convert_named(name)
41            }
42        }
43        TypeExpr::Generic { name, args } => {
44            // Check if it's a type alias with type params
45            if let Some(env) = env
46                && let Some((alias_params, alias_value)) = env.lookup_type_alias(name).cloned()
47                && !alias_params.is_empty()
48                && alias_params.len() == args.len()
49            {
50                // Substitute type params — for now, just resolve the alias value
51                return convert_type_expr_impl(&alias_value, type_params, Some(env));
52            }
53            convert_generic_impl(name, args, type_params, env)
54        }
55        TypeExpr::Optional(inner) => {
56            Type::Option(Box::new(convert_type_expr_impl(inner, type_params, env)))
57        }
58        TypeExpr::Function {
59            params,
60            return_type,
61        } => Type::Function {
62            params: params
63                .iter()
64                .map(|p| convert_type_expr_impl(p, type_params, env))
65                .collect(),
66            ret: Box::new(convert_type_expr_impl(return_type, type_params, env)),
67        },
68    }
69}
70
71fn convert_named(name: &str) -> Type {
72    match name {
73        "int" | "int64" => Type::Int,
74        "float" | "float64" => Type::Float,
75        "string" => Type::String,
76        "bool" => Type::Bool,
77        "none" => Type::None,
78        "any" => Type::Any,
79        "unit" | "void" => Type::Unit,
80        "table" => Type::Table {
81            name: None,
82            columns: None,
83        },
84        "tensor" => Type::Tensor,
85        "pipeline" => Type::Pipeline,
86        "decimal" => Type::Decimal,
87        other => {
88            // Could be a struct or enum name — treated as struct by default.
89            // The checker resolves which it actually is.
90            Type::Struct(other.to_string())
91        }
92    }
93}
94
95#[allow(dead_code)]
96fn convert_generic(name: &str, args: &[TypeExpr]) -> Type {
97    convert_generic_with_params(name, args, &[])
98}
99
100fn convert_generic_with_params(name: &str, args: &[TypeExpr], type_params: &[String]) -> Type {
101    convert_generic_impl(name, args, type_params, None)
102}
103
104fn convert_generic_impl(
105    name: &str,
106    args: &[TypeExpr],
107    type_params: &[String],
108    env: Option<&TypeEnv>,
109) -> Type {
110    match name {
111        "list" if args.len() == 1 => {
112            Type::List(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
113        }
114        "map" if args.len() == 1 => {
115            Type::Map(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
116        }
117        "set" if args.len() == 1 => {
118            Type::Set(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
119        }
120        "option" if args.len() == 1 => {
121            Type::Option(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
122        }
123        "result" if args.len() == 2 => Type::Result(
124            Box::new(convert_type_expr_impl(&args[0], type_params, env)),
125            Box::new(convert_type_expr_impl(&args[1], type_params, env)),
126        ),
127        "generator" if args.len() == 1 => {
128            Type::Generator(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
129        }
130        "task" if args.len() == 1 => {
131            Type::Task(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
132        }
133        "channel" if args.len() == 1 => {
134            Type::Channel(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
135        }
136        "stream" if args.len() == 1 => {
137            Type::Stream(Box::new(convert_type_expr_impl(&args[0], type_params, env)))
138        }
139        "table" if args.len() == 1 => {
140            if let TypeExpr::Named(schema) = &args[0] {
141                Type::Table {
142                    name: Some(schema.clone()),
143                    columns: None,
144                }
145            } else {
146                Type::Table {
147                    name: None,
148                    columns: None,
149                }
150            }
151        }
152        _ => {
153            // Unknown generic — could be user-defined generic struct.
154            Type::Struct(name.to_string())
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use tl_ast::TypeExpr;
163
164    #[test]
165    fn test_convert_primitives() {
166        assert_eq!(convert_type_expr(&TypeExpr::Named("int".into())), Type::Int);
167        assert_eq!(
168            convert_type_expr(&TypeExpr::Named("float".into())),
169            Type::Float
170        );
171        assert_eq!(
172            convert_type_expr(&TypeExpr::Named("string".into())),
173            Type::String
174        );
175        assert_eq!(
176            convert_type_expr(&TypeExpr::Named("bool".into())),
177            Type::Bool
178        );
179    }
180
181    #[test]
182    fn test_convert_optional() {
183        let texpr = TypeExpr::Optional(Box::new(TypeExpr::Named("int".into())));
184        assert_eq!(convert_type_expr(&texpr), Type::Option(Box::new(Type::Int)));
185    }
186
187    #[test]
188    fn test_convert_generic_list() {
189        let texpr = TypeExpr::Generic {
190            name: "list".into(),
191            args: vec![TypeExpr::Named("int".into())],
192        };
193        assert_eq!(convert_type_expr(&texpr), Type::List(Box::new(Type::Int)));
194    }
195
196    #[test]
197    fn test_convert_result() {
198        let texpr = TypeExpr::Generic {
199            name: "result".into(),
200            args: vec![
201                TypeExpr::Named("int".into()),
202                TypeExpr::Named("string".into()),
203            ],
204        };
205        assert_eq!(
206            convert_type_expr(&texpr),
207            Type::Result(Box::new(Type::Int), Box::new(Type::String))
208        );
209    }
210
211    #[test]
212    fn test_convert_function_type() {
213        let texpr = TypeExpr::Function {
214            params: vec![TypeExpr::Named("int".into())],
215            return_type: Box::new(TypeExpr::Named("string".into())),
216        };
217        assert_eq!(
218            convert_type_expr(&texpr),
219            Type::Function {
220                params: vec![Type::Int],
221                ret: Box::new(Type::String),
222            }
223        );
224    }
225}