Skip to main content

harn_parser/builtin_signatures/
types.rs

1//! Re-exports of builtin signature shape types from `harn-builtin-meta`, plus
2//! parser-local conversion helpers from the const IR (`Ty`) to the runtime
3//! IR (`TypeExpr`).
4//!
5//! The const-constructible types live in `harn-builtin-meta` (a dep-free
6//! crate consumed by both the parser and `harn-vm`). Conversion to the
7//! parser's owned `TypeExpr` requires types that are private to this crate,
8//! so it lives here as an extension trait.
9
10use crate::ast::{ShapeField, TypeExpr};
11
12pub use harn_builtin_meta::{
13    BuiltinMetadata, BuiltinSignature, Param, ShapeFieldDescriptor, Ty, TY_ANY, TY_BOOL, TY_BYTES,
14    TY_BYTES_OR_NIL, TY_CLOSURE, TY_DECIMAL, TY_DICT, TY_DICT_OR_NIL, TY_DURATION, TY_FLOAT,
15    TY_INT, TY_INT_OR_NIL, TY_LIST, TY_NEVER, TY_NIL, TY_NUMBER, TY_STRING, TY_STRING_OR_NIL,
16};
17
18/// Convert a const-IR [`Ty`] into the parser's owned [`TypeExpr`]. Generic
19/// references stay as `Named(name)` so the checker's existing scope-based
20/// generic-param resolution applies.
21pub fn ty_to_type_expr(ty: &Ty) -> TypeExpr {
22    match ty {
23        Ty::Named(name) => TypeExpr::Named((*name).into()),
24        Ty::Generic(name) => TypeExpr::Named((*name).into()),
25        Ty::Any => TypeExpr::Named("any".into()),
26        Ty::Optional(inner) => {
27            TypeExpr::Union(vec![ty_to_type_expr(inner), TypeExpr::Named("nil".into())])
28        }
29        // Keep the metadata IR compact, but project Harn's built-in container
30        // constructors onto their dedicated AST variants. Leaving (for
31        // example) `Ty::Apply("list", ...)` as a generic `Applied` node makes
32        // a generated builtin contract print exactly like `list<T>` while
33        // remaining incompatible with the same type parsed from Harn source.
34        Ty::Apply("list", [inner]) => TypeExpr::List(Box::new(ty_to_type_expr(inner))),
35        Ty::Apply("dict", [key, value]) => TypeExpr::DictType(
36            Box::new(ty_to_type_expr(key)),
37            Box::new(ty_to_type_expr(value)),
38        ),
39        Ty::Apply("iter", [inner]) => TypeExpr::Iter(Box::new(ty_to_type_expr(inner))),
40        Ty::Apply("generator" | "Generator", [inner]) => {
41            TypeExpr::Generator(Box::new(ty_to_type_expr(inner)))
42        }
43        Ty::Apply("stream" | "Stream", [inner]) => {
44            TypeExpr::Stream(Box::new(ty_to_type_expr(inner)))
45        }
46        Ty::Apply("owned", [inner]) => TypeExpr::Owned(Box::new(ty_to_type_expr(inner))),
47        Ty::Apply(name, args) => TypeExpr::Applied {
48            name: (*name).into(),
49            args: args.iter().map(ty_to_type_expr).collect(),
50        },
51        Ty::Union(members) => TypeExpr::Union(members.iter().map(ty_to_type_expr).collect()),
52        Ty::Fn(params, return_type) => TypeExpr::FnType {
53            params: params.iter().map(ty_to_type_expr).collect(),
54            return_type: Box::new(ty_to_type_expr(return_type)),
55        },
56        Ty::Shape(fields) => TypeExpr::Shape(
57            fields
58                .iter()
59                .map(|f| ShapeField::synthetic(f.name, ty_to_type_expr(&f.ty), f.optional))
60                .collect(),
61        ),
62        Ty::OpenShape(fields, rests) => TypeExpr::OpenShape {
63            fields: fields
64                .iter()
65                .map(|f| ShapeField::synthetic(f.name, ty_to_type_expr(&f.ty), f.optional))
66                .collect(),
67            rests: rests.iter().map(ty_to_type_expr).collect(),
68        },
69        Ty::SchemaOf(name) => TypeExpr::Applied {
70            name: "Schema".into(),
71            args: vec![TypeExpr::Named((*name).into())],
72        },
73        Ty::Never => TypeExpr::Never,
74        Ty::LitInt(v) => TypeExpr::LitInt(*v),
75        Ty::LitString(s) => TypeExpr::LitString((*s).into()),
76    }
77}
78
79/// Parser-side extension methods on [`Ty`] and [`BuiltinSignature`] that
80/// depend on the parser's owned AST types (kept out of `harn-builtin-meta`
81/// so that crate stays dep-free).
82pub trait TyExt {
83    /// Materialize as a runtime [`TypeExpr`].
84    fn to_type_expr(&self) -> TypeExpr;
85}
86
87impl TyExt for Ty {
88    fn to_type_expr(&self) -> TypeExpr {
89        ty_to_type_expr(self)
90    }
91}
92
93pub trait BuiltinSignatureExt {
94    /// Materialize per-parameter types as owned [`TypeExpr`]s for the type
95    /// checker's call-site validation.
96    fn param_type_exprs(&self) -> Vec<TypeExpr>;
97    /// Owned [`TypeExpr`] return type.
98    fn return_type_expr(&self) -> TypeExpr;
99}
100
101impl BuiltinSignatureExt for BuiltinSignature {
102    fn param_type_exprs(&self) -> Vec<TypeExpr> {
103        self.params.iter().map(|p| ty_to_type_expr(&p.ty)).collect()
104    }
105
106    fn return_type_expr(&self) -> TypeExpr {
107        ty_to_type_expr(&self.returns)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    const STRING: Ty = Ty::Named("string");
116    const LIST_ARGS: &[Ty] = &[STRING];
117
118    #[test]
119    fn builtin_container_metadata_uses_language_ast_variants() {
120        assert_eq!(
121            ty_to_type_expr(&Ty::Apply("list", LIST_ARGS)),
122            TypeExpr::List(Box::new(TypeExpr::Named("string".into())))
123        );
124    }
125}