1use harn_parser::{BindingPattern, Node, SNode};
2
3#[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 pub const fn has_runtime_value(self) -> bool {
24 !matches!(self, Self::Type | Self::Interface | Self::Parameter)
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct PublicDeclaration {
35 pub name: String,
36 pub kind: DefKind,
37}
38
39pub fn public_declarations(snode: &SNode) -> Vec<PublicDeclaration> {
45 match &snode.node {
46 Node::AttributedDecl { attributes, inner } => {
47 if attributes
48 .iter()
49 .any(|attribute| attribute.name == "sibling")
50 {
51 Vec::new()
52 } else {
53 public_declarations(inner)
54 }
55 }
56 Node::FnDecl {
57 name, is_pub: true, ..
58 } => declaration(name, DefKind::Function),
59 Node::Pipeline {
60 name, is_pub: true, ..
61 } => declaration(name, DefKind::Pipeline),
62 Node::ToolDecl {
63 name, is_pub: true, ..
64 } => declaration(name, DefKind::Tool),
65 Node::SkillDecl {
66 name, is_pub: true, ..
67 } => declaration(name, DefKind::Skill),
68 Node::EvalPackDecl {
69 binding_name,
70 is_pub: true,
71 ..
72 } => declaration(binding_name, DefKind::EvalPack),
73 Node::StructDecl {
74 name, is_pub: true, ..
75 } => declaration(name, DefKind::Struct),
76 Node::EnumDecl {
77 name, is_pub: true, ..
78 } => declaration(name, DefKind::Enum),
79 Node::InterfaceDecl { name, .. } => declaration(name, DefKind::Interface),
80 Node::TypeDecl {
81 name, is_pub: true, ..
82 } => declaration(name, DefKind::Type),
83 Node::LetBinding {
84 pattern,
85 is_pub: true,
86 ..
87 }
88 | Node::ConstBinding {
89 pattern,
90 is_pub: true,
91 ..
92 } => pattern_names(pattern)
93 .into_iter()
94 .map(|name| PublicDeclaration {
95 name,
96 kind: DefKind::Variable,
97 })
98 .collect(),
99 _ => Vec::new(),
100 }
101}
102
103pub fn sibling_declarations(snode: &SNode) -> Vec<PublicDeclaration> {
107 let Node::AttributedDecl { attributes, inner } = &snode.node else {
108 return Vec::new();
109 };
110 if !attributes
111 .iter()
112 .any(|attribute| attribute.name == "sibling")
113 {
114 return Vec::new();
115 }
116 match &inner.node {
117 Node::FnDecl { name, .. } => declaration(name, DefKind::Function),
118 _ => Vec::new(),
119 }
120}
121
122fn declaration(name: &str, kind: DefKind) -> Vec<PublicDeclaration> {
123 vec![PublicDeclaration {
124 name: name.to_string(),
125 kind,
126 }]
127}
128
129pub(crate) fn pattern_names(pattern: &BindingPattern) -> Vec<String> {
130 match pattern {
131 BindingPattern::Identifier(name) => vec![name.clone()],
132 BindingPattern::Dict(fields) => fields
133 .iter()
134 .filter_map(|field| field.alias.as_ref().or(Some(&field.key)).cloned())
135 .collect(),
136 BindingPattern::List(elements) => elements
137 .iter()
138 .map(|element| element.name.clone())
139 .collect(),
140 BindingPattern::Pair(a, b) => vec![a.clone(), b.clone()],
141 }
142}
143
144use std::path::Path;
154
155use harn_lexer::Span;
156
157use crate::{import_recording, DefSite, ModuleInfo, PackageSnapshot};
158
159pub(crate) fn collect_module_info(
160 file: &Path,
161 snode: &SNode,
162 module: &mut ModuleInfo,
163 package_snapshots: &[PackageSnapshot],
164) {
165 if let Node::AttributedDecl { inner, .. } = &snode.node {
166 for sibling in sibling_declarations(snode) {
167 module.sibling_exports.insert(sibling.name);
168 }
169 collect_module_info(file, inner, module, package_snapshots);
170 return;
171 }
172
173 for public in public_declarations(snode) {
174 module.own_exports.insert(public.name);
175 }
176
177 match &snode.node {
178 Node::FnDecl { name, params, .. } => {
179 module.declarations.insert(
180 name.clone(),
181 decl_site(file, snode.span, name, DefKind::Function),
182 );
183 for param_name in params.iter().map(|param| param.name.clone()) {
184 module.declarations.insert(
185 param_name.clone(),
186 decl_site(file, snode.span, ¶m_name, DefKind::Parameter),
187 );
188 }
189 }
190 Node::Pipeline { name, .. } => {
191 module.declarations.insert(
192 name.clone(),
193 decl_site(file, snode.span, name, DefKind::Pipeline),
194 );
195 }
196 Node::ToolDecl { name, .. } => {
197 module.declarations.insert(
198 name.clone(),
199 decl_site(file, snode.span, name, DefKind::Tool),
200 );
201 }
202 Node::SkillDecl { name, .. } => {
203 module.declarations.insert(
204 name.clone(),
205 decl_site(file, snode.span, name, DefKind::Skill),
206 );
207 }
208 Node::EvalPackDecl { binding_name, .. } => {
209 module.declarations.insert(
210 binding_name.clone(),
211 decl_site(file, snode.span, binding_name, DefKind::EvalPack),
212 );
213 }
214 Node::StructDecl { name, .. } => {
215 module.declarations.insert(
216 name.clone(),
217 decl_site(file, snode.span, name, DefKind::Struct),
218 );
219 }
220 Node::EnumDecl { name, .. } => {
221 module.declarations.insert(
222 name.clone(),
223 decl_site(file, snode.span, name, DefKind::Enum),
224 );
225 }
226 Node::InterfaceDecl { name, .. } => {
227 module.declarations.insert(
228 name.clone(),
229 decl_site(file, snode.span, name, DefKind::Interface),
230 );
231 }
232 Node::TypeDecl { name, .. } => {
233 module.declarations.insert(
234 name.clone(),
235 decl_site(file, snode.span, name, DefKind::Type),
236 );
237 }
238 Node::LetBinding { pattern, .. } | Node::ConstBinding { pattern, .. } => {
239 for name in pattern_names(pattern) {
240 module.declarations.insert(
241 name.clone(),
242 decl_site(file, snode.span, &name, DefKind::Variable),
243 );
244 }
245 }
246 _ if import_recording::record_import_node(module, file, snode, package_snapshots) => {}
247 _ => {}
248 }
249}
250
251pub(crate) fn collect_type_declarations(snode: &SNode, decls: &mut Vec<SNode>) {
252 match &snode.node {
253 Node::TypeDecl { .. }
254 | Node::StructDecl { .. }
255 | Node::EnumDecl { .. }
256 | Node::InterfaceDecl { .. } => decls.push(snode.clone()),
257 Node::AttributedDecl { inner, .. } => collect_type_declarations(inner, decls),
258 _ => {}
259 }
260}
261
262pub(crate) fn collect_callable_declarations(snode: &SNode, decls: &mut Vec<SNode>) {
263 match &snode.node {
264 Node::FnDecl { .. } | Node::Pipeline { .. } | Node::ToolDecl { .. } => {
265 let mut signature = snode.clone();
270 match &mut signature.node {
271 Node::FnDecl { body, .. }
272 | Node::Pipeline { body, .. }
273 | Node::ToolDecl { body, .. } => body.clear(),
274 _ => unreachable!("matched callable declaration"),
275 }
276 decls.push(signature);
277 }
278 Node::AttributedDecl { inner, .. } => collect_callable_declarations(inner, decls),
279 _ => {}
280 }
281}
282
283pub(crate) fn type_decl_name(snode: &SNode) -> Option<&str> {
284 match &snode.node {
285 Node::TypeDecl { name, .. }
286 | Node::StructDecl { name, .. }
287 | Node::EnumDecl { name, .. }
288 | Node::InterfaceDecl { name, .. } => Some(name.as_str()),
289 _ => None,
290 }
291}
292
293pub(crate) fn callable_decl_name(snode: &SNode) -> Option<&str> {
294 match &snode.node {
295 Node::FnDecl { name, .. } | Node::Pipeline { name, .. } | Node::ToolDecl { name, .. } => {
296 Some(name.as_str())
297 }
298 Node::AttributedDecl { inner, .. } => callable_decl_name(inner),
299 _ => None,
300 }
301}
302
303pub(crate) fn decl_site(file: &Path, span: Span, name: &str, kind: DefKind) -> DefSite {
304 DefSite {
305 name: name.to_string(),
306 file: file.to_path_buf(),
307 kind,
308 span,
309 }
310}