auto_lsp_codegen/
lib.rs

1/*
2This file is part of auto-lsp.
3Copyright (C) 2025 CLAUZEL Adrien
4
5auto-lsp is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <http://www.gnu.org/licenses/>
17*/
18
19mod ir;
20mod json;
21mod output;
22mod supertypes;
23mod tests;
24mod utils;
25
26use crate::json::{NodeType, TypeInfo};
27use crate::output::{generate_enum, generate_struct};
28use crate::supertypes::{generate_super_type, SuperType};
29use crate::utils::{sanitize_string, sanitize_string_to_pascal};
30use proc_macro2::TokenStream;
31use quote::{format_ident, quote, ToTokens};
32use std::collections::{HashMap, HashSet};
33use std::sync::{LazyLock, Mutex, RwLock};
34
35/// List of all named rules
36pub(crate) static NAMED_RULES: LazyLock<Mutex<Vec<String>>> = LazyLock::new(Default::default);
37
38pub(crate) struct OperatorList {
39    index: usize,
40    operators: Vec<TypeInfo>,
41}
42
43/// List of fields/children that are only composed of operators
44pub(crate) static OPERATORS_RULES: LazyLock<Mutex<HashMap<String, OperatorList>>> =
45    LazyLock::new(Default::default);
46
47/// List of fields/children that are composed of multiple rules
48pub(crate) static INLINE_MULTIPLE_RULES: LazyLock<Mutex<HashMap<String, Vec<TypeInfo>>>> =
49    LazyLock::new(Default::default);
50
51/// List of anonymous rules (usually aliases created on the fly)
52pub(crate) static ANONYMOUS_TYPES: LazyLock<Mutex<HashSet<String>>> =
53    LazyLock::new(Default::default);
54
55/// Map of node kind to  named node id
56pub(crate) static NODE_ID_FOR_NAMED_NODE: LazyLock<Mutex<HashMap<String, u16>>> =
57    LazyLock::new(Default::default);
58
59// / Map of node kind to unnamed node id
60pub(crate) static NODE_ID_FOR_UNNAMED_NODE: LazyLock<Mutex<HashMap<String, u16>>> =
61    LazyLock::new(Default::default);
62
63/// Map of field name to field id
64pub(crate) static FIELD_ID_FOR_NAME: LazyLock<Mutex<HashMap<String, u16>>> =
65    LazyLock::new(Default::default);
66
67/// List of super types
68pub(crate) static SUPER_TYPES: LazyLock<RwLock<HashMap<String, SuperType>>> =
69    LazyLock::new(Default::default);
70
71pub fn generate(source: &str, language: &tree_sitter::Language) -> TokenStream {
72    let nodes: Vec<NodeType> = serde_json::from_str(source).expect("Invalid JSON");
73
74    let mut output = quote! {
75        // Auto-generated file. Do not edit manually.
76        #![allow(clippy::all)]
77        #![allow(unused)]
78        #![allow(dead_code)]
79        #![allow(non_camel_case_types)]
80        #![allow(non_snake_case)]
81
82    };
83    for node in &nodes {
84        if node.named {
85            // Push the node kind to the list of named rules
86            NAMED_RULES
87                .lock()
88                .unwrap()
89                .push(sanitize_string_to_pascal(&node.kind));
90            // Push the node kind to the list of ids for named nodes
91            NODE_ID_FOR_NAMED_NODE.lock().unwrap().insert(
92                node.kind.clone(),
93                language.id_for_node_kind(&node.kind, true),
94            );
95            // If the node has fields, we need to add them to the list of fields
96            if let Some(fields) = &node.fields {
97                fields.iter().for_each(|(field_name, _)| {
98                    let field_id = language.field_id_for_name(field_name);
99                    FIELD_ID_FOR_NAME
100                        .lock()
101                        .unwrap()
102                        .insert(field_name.clone(), field_id.unwrap().get());
103                });
104            }
105        } else {
106            // Push the node kind to the list of ids for named nodes
107            NODE_ID_FOR_UNNAMED_NODE.lock().unwrap().insert(
108                node.kind.clone(),
109                language.id_for_node_kind(&node.kind, false),
110            );
111        }
112        // If node is a supertype, add it to the list of super types
113        if node.is_supertype() {
114            SUPER_TYPES
115                .write()
116                .unwrap()
117                .insert(node.kind.clone(), generate_super_type(node));
118        }
119    }
120
121    // Super types may contains other super types
122    // in this case we need to add the nested super types to the `types` field of the current super type
123    let mut super_types_lock = SUPER_TYPES.write().unwrap();
124    let mut new_super_types = HashMap::new();
125
126    for (super_type_name, super_type) in super_types_lock.iter() {
127        let mut new_super_type = SuperType::default();
128
129        // Iterate over the types of this super type
130        super_type.types.iter().enumerate().for_each(|(i, key)| {
131            if let Some(nested_super_type) = super_types_lock
132    .get(key) {
133                // Some types are super types
134                new_super_type.types.extend(nested_super_type.types.clone());
135            } else {
136                // Otherwise, we just clone the type
137                new_super_type.types.push(key.clone());
138            }
139            new_super_type.variants.push(super_type.variants[i].clone())
140        });
141        new_super_types.insert(super_type_name.clone(), new_super_type);
142    }
143
144    // Now we need to merge the new super types with the existing ones
145    new_super_types.into_iter().for_each(|(name, s)| {
146        super_types_lock
147.insert(name.clone(), s.clone());
148    });
149
150    drop(super_types_lock);
151
152    // Generate the structs and enums for all rules
153    for node in &nodes {
154        output.extend(node.to_token_stream());
155    }
156
157    // Generate the list of operators
158    for operators in (*OPERATORS_RULES.lock().unwrap()).values() {
159        output.extend(generate_enum(
160            &format_ident!("Operators_{}", operators.index),
161            &operators.operators,
162        ));
163    }
164
165    // Generate the list of inline multiple rules
166    for (id, values) in &*INLINE_MULTIPLE_RULES.lock().unwrap() {
167        output.extend(generate_enum(
168            &format_ident!("{}", sanitize_string(id)),
169            values,
170        ));
171    }
172
173    // Generate the list of anonymous types
174    for name in ANONYMOUS_TYPES.lock().unwrap().iter() {
175        output.extend(generate_struct(
176            &format_ident!("{}", &sanitize_string_to_pascal(name)),
177            name,
178            &vec![],
179            &vec![],
180            &vec![],
181            &vec![],
182        ));
183    }
184
185    // Generate the list of super types
186    // We need to clone because generate_enum will also check if some variants are super types
187    for (super_type_name, super_type) in SUPER_TYPES.read().unwrap().iter() {
188        output.extend(generate_enum(
189            &format_ident!("{}", &sanitize_string_to_pascal(super_type_name)),
190            &super_type.variants,
191        ));
192    }
193
194    output
195}