Skip to main content

weavatrix_parse/braced/
mod.rs

1//! Structural extraction for the brace-scoped languages.
2//!
3//! Rust, Go, Java, C#, C, C++ and Solidity differ in which keyword introduces
4//! a declaration and how a module is named, and agree on everything else:
5//! braces open bodies, a name followed by a parameter list is callable, and a
6//! call is an identifier followed by `(`. Those differences are tables, so one
7//! walk serves all seven instead of seven near-identical scanners - and adding
8//! the next such language costs a table, not a scanner.
9
10use crate::facts::{
11    Declaration, DeclarationKind, Facts, Import, ImportBinding, Reference, ReferenceKind, Span,
12};
13use crate::syntax::Language;
14use crate::token::{Mode, Token, TokenKind, Tokenizer};
15
16/// Extracts structural facts from one brace-scoped source file.
17#[must_use]
18pub fn extract(source: &str, language: Language) -> Facts {
19    let tokens = Tokenizer::new(source, language)
20        .mode(Mode::Lite)
21        .collect::<Vec<_>>();
22    let mut state = Extractor {
23        source,
24        tokens: &tokens,
25        language,
26        rules: Rules::of(language),
27        facts: Facts::default(),
28        scopes: Vec::new(),
29        depth: 0,
30    };
31    state.run();
32    state.facts
33}
34
35/// Keywords one language uses, as data.
36struct Rules {
37    /// Keyword to the kind it declares.
38    declarations: &'static [(&'static str, DeclarationKind)],
39    /// Keywords that introduce a module dependency.
40    imports: &'static [&'static str],
41    /// Modifiers to step over before the declaring keyword.
42    modifiers: &'static [&'static str],
43    /// Whether a bare `name(` at type-body depth declares a method.
44    braced_members: bool,
45    /// Whether `const (...)` and `var (...)` contain one declaration spec per
46    /// top-level line. This is Go syntax, not a generic braced-language rule.
47    grouped_declarations: bool,
48    /// Whether a function is declared by a return type rather than a keyword,
49    /// as C and C++ do: `int add(int a, int b) { }`.
50    typed_functions: bool,
51    /// Whether a declaration is public by keyword rather than by convention.
52    exported_keyword: Option<&'static str>,
53    /// Keywords that open a named scope without declaring anything: Rust's
54    /// `impl Type` and Swift's `extension Type` say what the members belong
55    /// to, and declare no new name.
56    scope_keywords: &'static [&'static str],
57}
58
59impl Rules {
60    const fn of(language: Language) -> Self {
61        match language {
62            Language::Rust => Self::rust(),
63            Language::Swift => Self::swift(),
64            Language::Go => Self::go(),
65            Language::Java | Language::CSharp => Self::managed(),
66            Language::Solidity => Self::solidity(),
67            _ => Self::c_family(),
68        }
69    }
70
71    const fn rust() -> Self {
72        Self {
73            declarations: &[
74                ("fn", DeclarationKind::Function),
75                ("struct", DeclarationKind::Struct),
76                ("enum", DeclarationKind::Enum),
77                ("trait", DeclarationKind::Trait),
78                ("type", DeclarationKind::TypeAlias),
79                ("const", DeclarationKind::Constant),
80                ("static", DeclarationKind::Constant),
81                ("mod", DeclarationKind::Module),
82            ],
83            imports: &["use", "mod"],
84            modifiers: &["pub", "async", "unsafe", "extern", "default"],
85            braced_members: false,
86            grouped_declarations: false,
87            typed_functions: false,
88            exported_keyword: Some("pub"),
89            scope_keywords: &["impl"],
90        }
91    }
92
93    const fn swift() -> Self {
94        Self {
95            declarations: &[
96                ("func", DeclarationKind::Function),
97                ("class", DeclarationKind::Class),
98                ("struct", DeclarationKind::Struct),
99                ("actor", DeclarationKind::Class),
100                ("enum", DeclarationKind::Enum),
101                ("protocol", DeclarationKind::Interface),
102                ("typealias", DeclarationKind::TypeAlias),
103                ("associatedtype", DeclarationKind::TypeAlias),
104                ("let", DeclarationKind::Constant),
105                ("var", DeclarationKind::Variable),
106                ("init", DeclarationKind::Method),
107                ("subscript", DeclarationKind::Method),
108            ],
109            imports: &["import"],
110            modifiers: &[
111                "public",
112                "private",
113                "internal",
114                "fileprivate",
115                "open",
116                "static",
117                "final",
118                "override",
119                "mutating",
120                "nonmutating",
121                "lazy",
122                "weak",
123                "unowned",
124                "required",
125                "convenience",
126                "indirect",
127                "dynamic",
128                "optional",
129                "async",
130                "throws",
131            ],
132            braced_members: false,
133            grouped_declarations: false,
134            typed_functions: false,
135            // `open` is wider than `public`, but both leave the module.
136            exported_keyword: Some("public"),
137            scope_keywords: &["extension"],
138        }
139    }
140
141    const fn go() -> Self {
142        Self {
143            declarations: &[
144                ("func", DeclarationKind::Function),
145                ("type", DeclarationKind::Struct),
146                ("const", DeclarationKind::Constant),
147                ("var", DeclarationKind::Variable),
148            ],
149            imports: &["import"],
150            modifiers: &[],
151            braced_members: false,
152            grouped_declarations: true,
153            typed_functions: false,
154            exported_keyword: None,
155            scope_keywords: &[],
156        }
157    }
158
159    const fn managed() -> Self {
160        Self {
161            declarations: &[
162                ("class", DeclarationKind::Class),
163                ("interface", DeclarationKind::Interface),
164                ("enum", DeclarationKind::Enum),
165                ("record", DeclarationKind::Struct),
166                ("struct", DeclarationKind::Struct),
167            ],
168            imports: &["import", "using"],
169            modifiers: &[
170                "public",
171                "private",
172                "protected",
173                "static",
174                "final",
175                "abstract",
176                "sealed",
177                "internal",
178                "override",
179                "async",
180                "virtual",
181                "readonly",
182            ],
183            braced_members: true,
184            grouped_declarations: false,
185            typed_functions: false,
186            exported_keyword: Some("public"),
187            scope_keywords: &[],
188        }
189    }
190
191    const fn solidity() -> Self {
192        Self {
193            declarations: &[
194                ("contract", DeclarationKind::Class),
195                ("library", DeclarationKind::Class),
196                ("interface", DeclarationKind::Interface),
197                ("struct", DeclarationKind::Struct),
198                ("enum", DeclarationKind::Enum),
199                ("function", DeclarationKind::Function),
200                ("constructor", DeclarationKind::Method),
201                ("modifier", DeclarationKind::Function),
202                ("event", DeclarationKind::Field),
203                ("error", DeclarationKind::Struct),
204            ],
205            imports: &["import"],
206            modifiers: &[
207                "abstract", "virtual", "override", "public", "private", "internal", "external",
208                "pure", "view", "payable",
209            ],
210            braced_members: false,
211            grouped_declarations: false,
212            typed_functions: false,
213            // Anything not internal or private is reachable from another contract.
214            exported_keyword: Some("public"),
215            scope_keywords: &[],
216        }
217    }
218
219    const fn c_family() -> Self {
220        Self {
221            declarations: &[
222                ("struct", DeclarationKind::Struct),
223                ("class", DeclarationKind::Class),
224                ("enum", DeclarationKind::Enum),
225                ("namespace", DeclarationKind::Module),
226            ],
227            imports: &["#include", "include"],
228            modifiers: &["static", "inline", "extern", "const", "virtual"],
229            braced_members: true,
230            grouped_declarations: false,
231            typed_functions: true,
232            exported_keyword: None,
233            scope_keywords: &[],
234        }
235    }
236}
237
238struct Scope {
239    name: String,
240    depth: Option<i32>,
241    type_body: bool,
242    test_only: bool,
243}
244
245struct Extractor<'source, 'tokens> {
246    source: &'source str,
247    tokens: &'tokens [Token],
248    language: Language,
249    rules: Rules,
250    facts: Facts,
251    scopes: Vec<Scope>,
252    depth: i32,
253}
254
255mod calls;
256mod declarations;
257mod fields;
258mod imports;
259mod scopes;
260mod traversal;
261
262#[cfg(test)]
263mod tests;