Skip to main content

semantic/parser/
parser_core.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Core parsing implementation.
3
4use std::sync::{Arc, OnceLock};
5
6use objects::object::ContentHash;
7use tree_sitter::{Node, Tree as TSTree};
8
9use super::{
10    parser_language::Language,
11    parser_pool::parse_fresh,
12    parser_types::{FunctionDef, Import},
13    syntax_index::{FunctionRef, ImportRef, SyntaxIndex},
14};
15
16/// A parsed file with its tree-sitter AST and Heddle-owned syntax index.
17#[derive(Debug)]
18pub struct ParsedFile {
19    pub language: Language,
20    pub source: Arc<str>,
21    content_hash: ContentHash,
22    tree: TSTree,
23    index: OnceLock<SyntaxIndex>,
24}
25
26impl ParsedFile {
27    /// Parse a file's contents.
28    pub fn parse(source: impl AsRef<str>, language: Language) -> Option<Self> {
29        let source = Arc::<str>::from(source.as_ref());
30        let content_hash = ContentHash::compute(source.as_bytes());
31        Self::parse_with_hash(source, language, content_hash)
32    }
33
34    /// Parse already-owned contents without copying the source string.
35    pub fn parse_owned(source: String, language: Language) -> Option<Self> {
36        let content_hash = ContentHash::compute(source.as_bytes());
37        Self::parse_with_hash(Arc::<str>::from(source), language, content_hash)
38    }
39
40    pub(crate) fn parse_with_hash(
41        source: Arc<str>,
42        language: Language,
43        content_hash: ContentHash,
44    ) -> Option<Self> {
45        let tree = parse_fresh(source.as_bytes(), language)?;
46        if tree.root_node().has_error() {
47            return None;
48        }
49
50        Some(Self {
51            language,
52            source,
53            content_hash,
54            tree,
55            index: OnceLock::new(),
56        })
57    }
58
59    /// Stable content identity for caches and sidecars.
60    pub fn content_hash(&self) -> ContentHash {
61        self.content_hash
62    }
63
64    /// Borrow the source text without cloning.
65    pub fn source(&self) -> &str {
66        &self.source
67    }
68
69    /// Get the root node of the AST.
70    pub fn root_node(&self) -> Node<'_> {
71        self.tree.root_node()
72    }
73
74    /// Compact Heddle-owned syntax data derived from the AST.
75    pub fn syntax_index(&self) -> &SyntaxIndex {
76        self.index.get_or_init(|| {
77            SyntaxIndex::build(self.language, self.source.as_ref(), self.root_node())
78        })
79    }
80
81    /// Borrow indexed function definitions.
82    pub fn functions(&self) -> impl Iterator<Item = FunctionRef<'_>> + '_ {
83        self.syntax_index().functions(self.source.as_ref())
84    }
85
86    /// Borrow indexed imports.
87    pub fn imports(&self) -> impl Iterator<Item = ImportRef<'_>> + '_ {
88        self.syntax_index().imports(self.source.as_ref())
89    }
90
91    /// Extract function definitions from the file.
92    pub fn extract_functions(&self) -> Vec<FunctionDef> {
93        self.functions().map(FunctionRef::to_owned).collect()
94    }
95
96    /// Extract imports from the file.
97    pub fn extract_imports(&self) -> Vec<Import> {
98        self.imports().map(ImportRef::to_owned).collect()
99    }
100
101    /// Check if a node kind string represents a function definition in the given language.
102    pub fn is_function_kind(kind: &str, language: Language) -> bool {
103        super::syntax_index::is_function_kind(kind, language)
104    }
105}