Skip to main content

harn_modules/
declarations.rs

1use harn_parser::{BindingPattern, Node, SNode};
2
3/// Kind of symbol that can be exported by a module.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
5pub enum DefKind {
6    Function,
7    Pipeline,
8    Tool,
9    Skill,
10    EvalPack,
11    Struct,
12    Enum,
13    Interface,
14    Type,
15    Variable,
16    Parameter,
17}
18
19impl DefKind {
20    /// Whether an exported declaration has a runtime binding that can be
21    /// projected into an importing module. Type and interface declarations
22    /// remain valid imports, but carry only static information.
23    pub const fn has_runtime_value(self) -> bool {
24        !matches!(self, Self::Type | Self::Interface | Self::Parameter)
25    }
26}
27
28/// One public name introduced by a single top-level declaration.
29///
30/// This is the language-level export contract shared by the module graph and
31/// the VM artifact compiler. Consumers must not re-derive declaration kinds
32/// with independent AST matches.
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct PublicDeclaration {
35    pub name: String,
36    pub kind: DefKind,
37}
38
39/// Return every public name introduced by one declaration.
40///
41/// Interfaces are public by language design: unlike the other declaration
42/// forms, the grammar does not accept a `pub` modifier for them. Attributes
43/// preserve the visibility of their wrapped declaration.
44pub fn public_declarations(snode: &SNode) -> Vec<PublicDeclaration> {
45    match &snode.node {
46        Node::AttributedDecl { inner, .. } => public_declarations(inner),
47        Node::FnDecl {
48            name, is_pub: true, ..
49        } => declaration(name, DefKind::Function),
50        Node::Pipeline {
51            name, is_pub: true, ..
52        } => declaration(name, DefKind::Pipeline),
53        Node::ToolDecl {
54            name, is_pub: true, ..
55        } => declaration(name, DefKind::Tool),
56        Node::SkillDecl {
57            name, is_pub: true, ..
58        } => declaration(name, DefKind::Skill),
59        Node::EvalPackDecl {
60            binding_name,
61            is_pub: true,
62            ..
63        } => declaration(binding_name, DefKind::EvalPack),
64        Node::StructDecl {
65            name, is_pub: true, ..
66        } => declaration(name, DefKind::Struct),
67        Node::EnumDecl {
68            name, is_pub: true, ..
69        } => declaration(name, DefKind::Enum),
70        Node::InterfaceDecl { name, .. } => declaration(name, DefKind::Interface),
71        Node::TypeDecl {
72            name, is_pub: true, ..
73        } => declaration(name, DefKind::Type),
74        Node::LetBinding {
75            pattern,
76            is_pub: true,
77            ..
78        }
79        | Node::ConstBinding {
80            pattern,
81            is_pub: true,
82            ..
83        } => pattern_names(pattern)
84            .into_iter()
85            .map(|name| PublicDeclaration {
86                name,
87                kind: DefKind::Variable,
88            })
89            .collect(),
90        _ => Vec::new(),
91    }
92}
93
94fn declaration(name: &str, kind: DefKind) -> Vec<PublicDeclaration> {
95    vec![PublicDeclaration {
96        name: name.to_string(),
97        kind,
98    }]
99}
100
101pub(crate) fn pattern_names(pattern: &BindingPattern) -> Vec<String> {
102    match pattern {
103        BindingPattern::Identifier(name) => vec![name.clone()],
104        BindingPattern::Dict(fields) => fields
105            .iter()
106            .filter_map(|field| field.alias.as_ref().or(Some(&field.key)).cloned())
107            .collect(),
108        BindingPattern::List(elements) => elements
109            .iter()
110            .map(|element| element.name.clone())
111            .collect(),
112        BindingPattern::Pair(a, b) => vec![a.clone(), b.clone()],
113    }
114}