Skip to main content

harn_parser/builtin_signatures/
mod.rs

1//! Single source of truth for builtin function signatures used by the parser
2//! and runtime VM: identifier resolution, typo suggestions, return-type
3//! inference, static arity & per-arg type checks, runtime arity & type
4//! enforcement, and lint awareness all consult the registry through the
5//! [`lookup`] / [`is_builtin`] helpers.
6//!
7//! ## Architecture
8//!
9//! Historically every builtin lived in two places — its implementation +
10//! runtime registration in `harn-vm/src/stdlib/*.rs`, and a hand-written
11//! `BuiltinSignature` literal under `signatures/*.rs` here in the parser.
12//! Drift between the two was caught at test time but cost a 2-file tax per
13//! new builtin.
14//!
15//! That two-sided system has been replaced by the `#[harn_builtin]`
16//! proc-macro (see `harn-builtin-macros`), which emits both the runtime
17//! handler registration AND the parser `BuiltinSignature` from a single
18//! annotated function. The vm crate aggregates them and installs them here
19//! at driver startup via [`harn_builtin_registry::install_builtin_signatures`].
20//!
21//! During migration the legacy static `signatures::groups()` tables remain
22//! as a fallback so unmigrated builtins still type-check. Lookups always
23//! consult installed entries first and fall through to the static tables;
24//! installed wins on name collisions. As modules port to `#[harn_builtin]`
25//! their entries move out of the static tables into the macro-emitted
26//! `MODULE_BUILTINS` slices. Once all signatures have migrated the static
27//! tables are deleted.
28
29mod lookup;
30mod signatures;
31mod types;
32
33pub use lookup::{
34    builtin_return_type, is_builtin, is_untyped_boundary_source, iter_builtin_metadata,
35    iter_builtin_names, lookup, static_signature_names,
36};
37pub use types::{
38    ty_to_type_expr, BuiltinMetadata, BuiltinSignature, BuiltinSignatureExt, Param,
39    ShapeFieldDescriptor, Ty, TyExt, TY_ANY, TY_BOOL, TY_BYTES, TY_BYTES_OR_NIL, TY_CLOSURE,
40    TY_DICT, TY_DICT_OR_NIL, TY_DURATION, TY_FLOAT, TY_INT, TY_INT_OR_NIL, TY_LIST, TY_NEVER,
41    TY_NIL, TY_NUMBER, TY_STRING, TY_STRING_OR_NIL,
42};
43
44pub use harn_builtin_registry::install_builtin_signatures;
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::ast::TypeExpr;
50    use std::collections::HashSet;
51
52    #[test]
53    fn iter_builtin_names_is_unique() {
54        // Sanity-check that no name is exposed twice across the
55        // installed slice + static groups. Installed entries take
56        // priority on collisions inside `lookup`, but the
57        // `iter_builtin_names` helper already filters static names that
58        // are shadowed, so the output should be deduped.
59        let mut seen = HashSet::new();
60        for name in iter_builtin_names() {
61            assert!(seen.insert(name), "duplicate builtin name in iter: {name}");
62        }
63    }
64
65    #[test]
66    fn lookup_hits_and_misses() {
67        assert!(is_builtin("snake_to_camel"));
68        assert!(is_builtin("log"));
69        assert!(is_builtin("await"));
70        assert!(!is_builtin("definitely_not_a_builtin"));
71        assert!(!is_builtin(""));
72    }
73
74    #[test]
75    fn return_type_named_variant() {
76        assert_eq!(
77            builtin_return_type("snake_to_camel"),
78            Some(TypeExpr::Named("string".into()))
79        );
80        assert_eq!(
81            builtin_return_type("log"),
82            Some(TypeExpr::Named("nil".into()))
83        );
84        assert_eq!(
85            builtin_return_type("file_exists"),
86            Some(TypeExpr::Named("bool".into()))
87        );
88    }
89
90    #[test]
91    fn return_type_union_variant() {
92        assert_eq!(
93            builtin_return_type("env"),
94            Some(TypeExpr::Union(vec![
95                TypeExpr::Named("string".into()),
96                TypeExpr::Named("nil".into()),
97            ]))
98        );
99    }
100
101    #[test]
102    fn return_type_unknown_for_dynamic_builtins() {
103        assert!(is_builtin("json_parse"));
104        assert_eq!(builtin_return_type("json_parse"), None);
105    }
106
107    #[test]
108    fn return_type_none_for_unknown_names() {
109        assert_eq!(builtin_return_type("not_a_real_thing"), None);
110    }
111}