Skip to main content

leekscript_analysis/
type_expr.rs

1//! Parse `NodeTypeExpr` (and signature type nodes) into `Type`.
2//!
3//! Used for program type annotations (var decl, param, return, cast) and for
4//! signature files (stdlib function/global types). Both grammars produce
5//! `NodeTypeExpr` with the same shape: `type_optional` ( | `type_optional` )*.
6
7use sipha::red::{SyntaxElement, SyntaxNode};
8use sipha::types::{IntoSyntaxKind, Span};
9
10use leekscript_core::syntax::Kind;
11use leekscript_core::Type;
12
13/// Result of parsing a type expression: either a type or an error span.
14#[derive(Debug)]
15pub enum TypeExprResult {
16    Ok(Type),
17    Err(Span),
18}
19
20/// Parse a `NodeTypeExpr` node into a `Type`. Works for both program and signature grammar.
21#[must_use]
22pub fn parse_type_expr(node: &SyntaxNode) -> TypeExprResult {
23    if node.kind_as::<Kind>() != Some(Kind::NodeTypeExpr) {
24        return TypeExprResult::Err(
25            node.first_token()
26                .map_or_else(|| Span::new(0, 0), |t| t.text_range()),
27        );
28    }
29
30    // Collect elements: non-trivia tokens (ident/keyword or op) and NodeTypeParams, in order.
31    let mut elements: Vec<TypeExprElement> = Vec::new();
32    for child in node.children() {
33        match child {
34            SyntaxElement::Token(t) => {
35                if t.is_trivia() {
36                    continue;
37                }
38                let text = t.text().to_string();
39                if t.kind_as::<Kind>() == Some(Kind::TokOp) {
40                    elements.push(TypeExprElement::Op(text, t.text_range()));
41                } else {
42                    // Ident or keyword (type name)
43                    elements.push(TypeExprElement::Ident(text, t.text_range()));
44                }
45            }
46            SyntaxElement::Node(n) => {
47                if n.kind_as::<Kind>() == Some(Kind::NodeTypeParams) {
48                    elements.push(TypeExprElement::TypeParams(n.clone()));
49                }
50            }
51        }
52    }
53
54    // When type_expr has nested structure (e.g. "integer getX()" or "Array<Cell> cells"), direct
55    // children may be intermediate rule nodes, so elements is empty. Use the node's first token
56    // as the type name and look for a descendant NodeTypeParams so we get e.g. Array<Cell> not Array<any>.
57    if elements.is_empty() {
58        if let Some(first_tok) = node.first_token() {
59            let name = first_tok.text().to_string();
60            let type_params = node
61                .find_all_nodes(Kind::NodeTypeParams.into_syntax_kind())
62                .into_iter()
63                .next();
64            let ty = parse_primary_type(name.as_str(), type_params.as_ref());
65            if let Ok(ty) = ty {
66                return TypeExprResult::Ok(ty);
67            }
68        }
69        return TypeExprResult::Err(
70            node.first_token()
71                .map_or_else(|| Span::new(0, 0), |t| t.text_range()),
72        );
73    }
74
75    // Split by |. Each segment is type_optional: type_primary + optional ?
76    let segments = split_by_pipe(&elements);
77    let mut types = Vec::with_capacity(segments.len());
78    for seg in segments {
79        match parse_type_optional_segment(seg) {
80            Ok(ty) => types.push(ty),
81            Err(span) => return TypeExprResult::Err(span),
82        }
83    }
84
85    if types.is_empty() {
86        return TypeExprResult::Err(
87            node.first_token()
88                .map_or_else(|| Span::new(0, 0), |t| t.text_range()),
89        );
90    }
91    if types.len() == 1 {
92        TypeExprResult::Ok(types.into_iter().next().unwrap())
93    } else {
94        TypeExprResult::Ok(Type::compound(types))
95    }
96}
97
98#[derive(Debug)]
99enum TypeExprElement {
100    Ident(String, Span),
101    Op(String, Span),
102    TypeParams(SyntaxNode),
103}
104
105fn split_by_pipe(elements: &[TypeExprElement]) -> Vec<&[TypeExprElement]> {
106    let mut segments = Vec::new();
107    let mut start = 0;
108    for (i, el) in elements.iter().enumerate() {
109        if let TypeExprElement::Op(ref t, _) = el {
110            if t == "|" {
111                segments.push(&elements[start..i]);
112                start = i + 1;
113            }
114        }
115    }
116    segments.push(&elements[start..]);
117    segments
118}
119
120fn parse_type_optional_segment(seg: &[TypeExprElement]) -> Result<Type, Span> {
121    if seg.is_empty() {
122        return Err(Span::new(0, 0));
123    }
124    let optional = seg.last().and_then(|el| {
125        if let TypeExprElement::Op(t, _) = el {
126            if t == "?" {
127                return Some(());
128            }
129        }
130        None
131    });
132    let seg = if optional.is_some() {
133        &seg[..seg.len().saturating_sub(1)]
134    } else {
135        seg
136    };
137    let ty = parse_type_primary_segment(seg)?;
138    if optional.is_some() {
139        Ok(Type::compound2(ty, Type::null()))
140    } else {
141        Ok(ty)
142    }
143}
144
145fn parse_type_primary_segment(seg: &[TypeExprElement]) -> Result<Type, Span> {
146    let first = seg.first().ok_or_else(|| Span::new(0, 0))?;
147    let (name, _span) = match first {
148        TypeExprElement::Ident(n, s) => (n.as_str(), *s),
149        _ => return Err(first_span(first)),
150    };
151
152    // TypeParams may be at index 1 (sig grammar) or after Op("<") (program grammar: Ident, op_lt, TypeParams).
153    let type_params = seg.iter().find_map(|el| {
154        if let TypeExprElement::TypeParams(n) = el {
155            Some(n)
156        } else {
157            None
158        }
159    });
160
161    parse_primary_type(name, type_params)
162}
163
164fn first_span(el: &TypeExprElement) -> Span {
165    match el {
166        TypeExprElement::Ident(_, s) => *s,
167        TypeExprElement::Op(_, s) => *s,
168        TypeExprElement::TypeParams(n) => n
169            .first_token()
170            .map_or_else(|| Span::new(0, 0), |t| t.text_range()),
171    }
172}
173
174fn parse_primary_type(name: &str, type_params: Option<&SyntaxNode>) -> Result<Type, Span> {
175    let err_span = type_params
176        .and_then(|n| n.first_token().map(|t| t.text_range()))
177        .unwrap_or_else(|| Span::new(0, 0));
178
179    match name {
180        "integer" => Ok(Type::int()),
181        "real" => Ok(Type::real()),
182        "string" => Ok(Type::string()),
183        "boolean" => Ok(Type::bool()),
184        "void" => Ok(Type::void()),
185        "any" => Ok(Type::any()),
186        "null" => Ok(Type::null()),
187        "Object" => Ok(Type::object()),
188        "Class" => {
189            let inner = type_params.and_then(parse_class_type_param);
190            Ok(Type::class(inner))
191        }
192        "Array" => {
193            let inner = type_params
194                .and_then(parse_single_type_param)
195                .unwrap_or(Type::any());
196            Ok(Type::array(inner))
197        }
198        "Map" => {
199            let (k, v) = type_params
200                .and_then(parse_map_type_params)
201                .unwrap_or((Type::any(), Type::any()));
202            Ok(Type::map(k, v))
203        }
204        "Set" => {
205            let inner = type_params
206                .and_then(parse_single_type_param)
207                .unwrap_or(Type::any());
208            Ok(Type::set(inner))
209        }
210        "Interval" => {
211            let inner = type_params
212                .and_then(parse_single_type_param)
213                .unwrap_or(Type::any());
214            Ok(Type::interval(inner))
215        }
216        "Function" => {
217            let (args, ret) = type_params
218                .and_then(parse_function_type_params)
219                .unwrap_or((vec![], Type::any()));
220            Ok(Type::function(args, ret))
221        }
222        "var" => {
223            // "var" is a keyword for untyped declarations, not a type name.
224            Err(err_span)
225        }
226        _ => {
227            // User class in type position (e.g. return type, variable): instance of that class
228            if type_params.is_some() {
229                return Err(err_span);
230            }
231            Ok(Type::instance(name.to_string()))
232        }
233    }
234}
235
236/// `NodeTypeParams` for Class: single `type_expr` child = class name (identifier type).
237fn parse_class_type_param(node: &SyntaxNode) -> Option<String> {
238    let type_expr = node
239        .child_nodes()
240        .find(|n| n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr))?;
241    let res = parse_type_expr(&type_expr);
242    match res {
243        TypeExprResult::Ok(Type::Class(Some(name)) | Type::Instance(name)) => Some(name),
244        _ => None,
245    }
246}
247
248/// `NodeTypeParams` with single `type_expr` (Array<T>, Set<T>, Interval<T>).
249fn parse_single_type_param(node: &SyntaxNode) -> Option<Type> {
250    let type_expr = node
251        .child_nodes()
252        .find(|n| n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr))?;
253    match parse_type_expr(&type_expr) {
254        TypeExprResult::Ok(t) => Some(t),
255        TypeExprResult::Err(_) => None,
256    }
257}
258
259/// `NodeTypeParams` for Map: two `type_exprs` (K, V).
260fn parse_map_type_params(node: &SyntaxNode) -> Option<(Type, Type)> {
261    let type_exprs: Vec<SyntaxNode> = node
262        .child_nodes()
263        .filter(|n| n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr))
264        .collect();
265    if type_exprs.len() >= 2 {
266        let k = match parse_type_expr(&type_exprs[0]) {
267            TypeExprResult::Ok(t) => t,
268            _ => return None,
269        };
270        let v = match parse_type_expr(&type_exprs[1]) {
271            TypeExprResult::Ok(t) => t,
272            _ => return None,
273        };
274        Some((k, v))
275    } else {
276        None
277    }
278}
279
280/// `NodeTypeParams` for Function: either `=> R` (0 args) or `T1, T2, ... => R` or `(T1, T2) => R`.
281fn parse_function_type_params(node: &SyntaxNode) -> Option<(Vec<Type>, Type)> {
282    let type_exprs: Vec<SyntaxNode> = node
283        .child_nodes()
284        .filter(|n| n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr))
285        .collect();
286    if type_exprs.is_empty() {
287        return None;
288    }
289    // Check for arrow form: last type_expr is return type, rest are params (or single (T1,T2)=>R might be one child).
290    // Grammar: => type_expr (zero params) | type_expr => type_expr (one param) | type_expr , type_expr => type_expr | ...
291    if type_exprs.len() == 1 {
292        // Could be => R (zero params) — then the single child is R.
293        // In the grammar, zero-param is "arrow" "type_expr", so we have one type_expr = return type.
294        let ret = match parse_type_expr(&type_exprs[0]) {
295            TypeExprResult::Ok(t) => t,
296            _ => return None,
297        };
298        return Some((vec![], ret));
299    }
300    // Two or more: last is return, rest are params (or we have (T1,T2) as one node? No — type_params contains multiple type_exprs)
301    let ret = match parse_type_expr(type_exprs.last()?) {
302        TypeExprResult::Ok(t) => t,
303        _ => return None,
304    };
305    let args: Result<Vec<Type>, ()> = type_exprs[..type_exprs.len() - 1]
306        .iter()
307        .map(|n| match parse_type_expr(n) {
308            TypeExprResult::Ok(t) => Ok(t),
309            TypeExprResult::Err(_) => Err(()),
310        })
311        .collect();
312    let args = args.ok()?;
313    Some((args, ret))
314}
315
316/// Find a child node of `parent` that is `NodeTypeExpr` (for var decl, param, etc.).
317#[must_use]
318pub fn find_type_expr_child(parent: &SyntaxNode) -> Option<SyntaxNode> {
319    parent
320        .child_nodes()
321        .find(|n| n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr))
322}
323
324/// Parameter types and return type for a `NodeAnonFn` (lambda or anonymous function).
325/// Params without type annotation get `Type::any()`; return type is always `Type::any()` (no syntax for it).
326/// Collects params in source order by sorting by span start (`find_all_nodes` is depth-first and may not match source order).
327#[must_use]
328pub fn anon_fn_types(node: &SyntaxNode) -> (Vec<Type>, Type) {
329    if node.kind_as::<Kind>() != Some(Kind::NodeAnonFn) {
330        return (Vec::new(), Type::any());
331    }
332    let mut param_nodes: Vec<SyntaxNode> = node.find_all_nodes(Kind::NodeParam.into_syntax_kind());
333    param_nodes.sort_by_key(|p| p.text_range().start);
334    let param_types: Vec<Type> = param_nodes
335        .iter()
336        .map(|p| {
337            find_type_expr_child(p)
338                .and_then(|te| match parse_type_expr(&te) {
339                    TypeExprResult::Ok(t) => Some(t),
340                    TypeExprResult::Err(_) => None,
341                })
342                .unwrap_or(Type::any())
343        })
344        .collect();
345    (param_types, Type::any())
346}
347
348/// Extract parameter types and return type from a function-like node (program `NodeFunctionDecl`
349/// or signature `NodeSigFunction` / `NodeSigMethod`). Collects direct children with `param_kind`
350/// (e.g. `Kind::NodeParam` or `Kind::NodeSigParam`), parses each param's type, and uses the last
351/// direct child `NodeTypeExpr` as the return type.
352#[must_use]
353pub fn param_and_return_types(
354    node: &SyntaxNode,
355    param_kind: Kind,
356) -> (Option<Vec<Type>>, Option<Type>) {
357    let param_nodes: Vec<SyntaxNode> = node
358        .child_nodes()
359        .filter(|n| n.kind_as::<Kind>() == Some(param_kind))
360        .collect();
361    let mut param_types = Vec::with_capacity(param_nodes.len());
362    for p in &param_nodes {
363        if let Some(te) = find_type_expr_child(p) {
364            if let TypeExprResult::Ok(ty) = parse_type_expr(&te) {
365                param_types.push(ty);
366            } else {
367                return (None, None);
368            }
369        } else {
370            return (None, None);
371        }
372    }
373    let child_nodes: Vec<SyntaxNode> = node.child_nodes().collect();
374    let return_type_node = child_nodes
375        .iter()
376        .rev()
377        .find(|c| c.kind_as::<Kind>() == Some(Kind::NodeTypeExpr));
378    let return_type = return_type_node.and_then(|te| match parse_type_expr(te) {
379        TypeExprResult::Ok(t) => Some(t),
380        TypeExprResult::Err(_) => None,
381    });
382    (Some(param_types), return_type)
383}
384
385#[cfg(test)]
386mod tests {
387    use super::*;
388    use leekscript_core::parse_signatures;
389    use sipha::red::SyntaxElement;
390    use sipha::types::IntoSyntaxKind;
391
392    fn find_first_type_expr(node: &SyntaxNode) -> Option<SyntaxNode> {
393        if node.kind_as::<Kind>() == Some(Kind::NodeTypeExpr) {
394            return Some(node.clone());
395        }
396        for child in node.children() {
397            if let SyntaxElement::Node(n) = child {
398                if let Some(found) = find_first_type_expr(&n) {
399                    return Some(found);
400                }
401            }
402        }
403        None
404    }
405
406    #[test]
407    fn parse_type_expr_integer_from_sig() {
408        let root = parse_signatures("global integer FOO\n")
409            .unwrap()
410            .expect("parse");
411        let type_expr_node = find_first_type_expr(&root).expect("NodeTypeExpr");
412        let result = parse_type_expr(&type_expr_node);
413        match &result {
414            TypeExprResult::Ok(t) => assert_eq!(*t, Type::int()),
415            TypeExprResult::Err(_) => panic!("expected Ok(integer), got {:?}", result),
416        }
417    }
418
419    #[test]
420    fn parse_type_expr_real_optional_from_sig() {
421        let root = parse_signatures("global real? FOO\n")
422            .unwrap()
423            .expect("parse");
424        let type_expr_node = find_first_type_expr(&root).expect("NodeTypeExpr");
425        let result = parse_type_expr(&type_expr_node);
426        match &result {
427            TypeExprResult::Ok(t) => {
428                assert_eq!(*t, Type::compound2(Type::real(), Type::null()));
429            }
430            TypeExprResult::Err(_) => panic!("expected Ok(real|null), got {:?}", result),
431        }
432    }
433
434    #[test]
435    fn parse_type_expr_union_from_sig() {
436        let root = parse_signatures("function f(real|integer x) -> real\n")
437            .unwrap()
438            .expect("parse");
439        let type_expr_nodes = root.find_all_nodes(Kind::NodeTypeExpr.into_syntax_kind());
440        assert!(!type_expr_nodes.is_empty());
441        let param_type = parse_type_expr(&type_expr_nodes[0]);
442        match &param_type {
443            TypeExprResult::Ok(t) => {
444                assert_eq!(*t, Type::compound2(Type::real(), Type::int()));
445            }
446            TypeExprResult::Err(_) => panic!("expected Ok(real|integer), got {:?}", param_type),
447        }
448    }
449
450    /// Find a NodeTypeExpr whose first token text is `name` (e.g. "Function").
451    fn find_type_expr_with_name(root: &SyntaxNode, name: &str) -> Option<SyntaxNode> {
452        for node in root.find_all_nodes(Kind::NodeTypeExpr.into_syntax_kind()) {
453            let first = node.first_token()?;
454            if first.text() == name {
455                return Some(node);
456            }
457        }
458        None
459    }
460
461    #[test]
462    fn parse_type_expr_function_two_args_from_program() {
463        use leekscript_core::parse;
464
465        let source = "var f = null as Function<integer, integer => void>;";
466        let root = parse(source).unwrap().expect("parse");
467        let type_expr_node =
468            find_type_expr_with_name(&root, "Function").expect("Function type node");
469        let result = parse_type_expr(&type_expr_node);
470        match &result {
471            TypeExprResult::Ok(ty) => {
472                if let Type::Function { args, return_type } = ty {
473                    assert_eq!(args.len(), 2, "expected 2 param types");
474                    assert_eq!(args[0], Type::int());
475                    assert_eq!(args[1], Type::int());
476                    assert_eq!(**return_type, Type::void());
477                } else {
478                    panic!("expected Type::Function, got {:?}", ty);
479                }
480            }
481            TypeExprResult::Err(_) => panic!("expected Ok(Function<...>), got {:?}", result),
482        }
483    }
484
485    #[test]
486    fn parse_type_expr_function_zero_params_from_sig() {
487        let source = "function noArg(Function< => boolean> pred) -> void\n";
488        let root = parse_signatures(source).unwrap().expect("parse");
489        let type_expr_node =
490            find_type_expr_with_name(&root, "Function").expect("Function type node");
491        let result = parse_type_expr(&type_expr_node);
492        match &result {
493            TypeExprResult::Ok(ty) => {
494                if let Type::Function { args, return_type } = ty {
495                    assert!(args.is_empty(), "expected 0 param types");
496                    assert_eq!(**return_type, Type::bool());
497                } else {
498                    panic!("expected Type::Function, got {:?}", ty);
499                }
500            }
501            TypeExprResult::Err(_) => {
502                panic!("expected Ok(Function< => boolean>), got {:?}", result)
503            }
504        }
505    }
506
507    #[test]
508    fn parse_type_expr_function_one_param_from_sig() {
509        let source = "function oneArg(Function<integer => void> fn) -> void\n";
510        let root = parse_signatures(source).unwrap().expect("parse");
511        let type_expr_node =
512            find_type_expr_with_name(&root, "Function").expect("Function type node");
513        let result = parse_type_expr(&type_expr_node);
514        match &result {
515            TypeExprResult::Ok(ty) => {
516                if let Type::Function { args, return_type } = ty {
517                    assert_eq!(args.len(), 1);
518                    assert_eq!(args[0], Type::int());
519                    assert_eq!(**return_type, Type::void());
520                } else {
521                    panic!("expected Type::Function, got {:?}", ty);
522                }
523            }
524            TypeExprResult::Err(_) => {
525                panic!("expected Ok(Function<integer => void>), got {:?}", result)
526            }
527        }
528    }
529}