rbs 4.0.1.dev.1 → 4.0.1.dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rbs/version.rb +1 -1
- data/rbs.gemspec +2 -2
- metadata +1 -17
- data/rust/.gitignore +0 -1
- data/rust/Cargo.lock +0 -378
- data/rust/Cargo.toml +0 -7
- data/rust/ruby-rbs/Cargo.toml +0 -22
- data/rust/ruby-rbs/build.rs +0 -764
- data/rust/ruby-rbs/examples/locations.rs +0 -60
- data/rust/ruby-rbs/src/lib.rs +0 -1
- data/rust/ruby-rbs/src/node/mod.rs +0 -742
- data/rust/ruby-rbs/tests/sanity.rs +0 -47
- data/rust/ruby-rbs/vendor/rbs/config.yml +0 -1
- data/rust/ruby-rbs-sys/Cargo.toml +0 -23
- data/rust/ruby-rbs-sys/build.rs +0 -204
- data/rust/ruby-rbs-sys/src/lib.rs +0 -50
- data/rust/ruby-rbs-sys/vendor/rbs/include +0 -1
- data/rust/ruby-rbs-sys/vendor/rbs/src +0 -1
- data/rust/ruby-rbs-sys/wrapper.h +0 -1
data/rust/ruby-rbs/build.rs
DELETED
|
@@ -1,764 +0,0 @@
|
|
|
1
|
-
use serde::Deserialize;
|
|
2
|
-
use std::{env, error::Error, fs::File, io::Write, path::Path};
|
|
3
|
-
|
|
4
|
-
// This config-driven code generation approach is inspired by Prism's ruby-prism crate.
|
|
5
|
-
// See: https://github.com/ruby/prism/blob/main/rust/ruby-prism/build.rs
|
|
6
|
-
|
|
7
|
-
#[derive(Debug, Deserialize)]
|
|
8
|
-
struct Config {
|
|
9
|
-
nodes: Vec<Node>,
|
|
10
|
-
#[serde(default)]
|
|
11
|
-
enums: std::collections::HashMap<String, EnumDef>,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
#[derive(Debug, Deserialize)]
|
|
15
|
-
struct EnumDef {
|
|
16
|
-
#[serde(default)]
|
|
17
|
-
#[allow(dead_code)]
|
|
18
|
-
optional: bool,
|
|
19
|
-
symbols: Vec<String>,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
#[derive(Debug, Deserialize)]
|
|
23
|
-
struct NodeField {
|
|
24
|
-
name: String,
|
|
25
|
-
c_type: String,
|
|
26
|
-
c_name: Option<String>,
|
|
27
|
-
#[serde(default)]
|
|
28
|
-
optional: bool,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
impl NodeField {
|
|
32
|
-
fn c_name(&self) -> &str {
|
|
33
|
-
let name = self.c_name.as_ref().unwrap_or(&self.name);
|
|
34
|
-
if name == "type" { "type_" } else { name }
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
#[derive(Debug, Deserialize)]
|
|
39
|
-
struct LocationField {
|
|
40
|
-
#[serde(default)]
|
|
41
|
-
required: Option<String>,
|
|
42
|
-
#[serde(default)]
|
|
43
|
-
optional: Option<String>,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
impl LocationField {
|
|
47
|
-
fn name(&self) -> &str {
|
|
48
|
-
self.required.as_ref().or(self.optional.as_ref()).unwrap()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
fn is_required(&self) -> bool {
|
|
52
|
-
self.required.is_some()
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
#[derive(Debug, Deserialize)]
|
|
57
|
-
struct Node {
|
|
58
|
-
name: String,
|
|
59
|
-
rust_name: String,
|
|
60
|
-
fields: Option<Vec<NodeField>>,
|
|
61
|
-
locations: Option<Vec<LocationField>>,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
impl Node {
|
|
65
|
-
fn variant_name(&self) -> &str {
|
|
66
|
-
self.rust_name
|
|
67
|
-
.strip_suffix("Node")
|
|
68
|
-
.unwrap_or(&self.rust_name)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
fn main() -> Result<(), Box<dyn Error>> {
|
|
73
|
-
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
74
|
-
let config_path = manifest_dir.join("vendor/rbs/config.yml");
|
|
75
|
-
|
|
76
|
-
let config_path = config_path
|
|
77
|
-
.canonicalize()
|
|
78
|
-
.map_err(|e| format!("Failed to find config.yml at {:?}: {}", config_path, e))?;
|
|
79
|
-
|
|
80
|
-
println!("cargo:rerun-if-changed={}", config_path.display());
|
|
81
|
-
|
|
82
|
-
let config_file = File::open(&config_path)?;
|
|
83
|
-
let mut config: Config = serde_yaml::from_reader(config_file)?;
|
|
84
|
-
|
|
85
|
-
// Symbol represents identifiers (interned strings), not traditional AST nodes.
|
|
86
|
-
// However, the C parser defines it in `rbs_node_type` (RBS_AST_SYMBOL) and
|
|
87
|
-
// treats it as a node (rbs_node_t*) in many contexts (lists, hashes).
|
|
88
|
-
// We inject it into the config so it is generated as a struct matching the Node pattern,
|
|
89
|
-
// allowing it to be wrapped in the Node enum and handled uniformly in Rust.
|
|
90
|
-
config.nodes.push(Node {
|
|
91
|
-
name: "RBS::AST::Symbol".to_string(),
|
|
92
|
-
rust_name: "SymbolNode".to_string(),
|
|
93
|
-
fields: None,
|
|
94
|
-
locations: None,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
config.nodes.sort_by(|a, b| a.name.cmp(&b.name));
|
|
98
|
-
generate(&config)?;
|
|
99
|
-
|
|
100
|
-
Ok(())
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
enum CIdentifier {
|
|
104
|
-
Type, // foo_bar_t
|
|
105
|
-
Constant, // FOO_BAR
|
|
106
|
-
Method, // visit_foo_bar
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
fn convert_name(name: &str, identifier: CIdentifier) -> String {
|
|
110
|
-
let type_name = name.replace("::", "_");
|
|
111
|
-
let lowercase = matches!(identifier, CIdentifier::Type | CIdentifier::Method);
|
|
112
|
-
let mut out = String::new();
|
|
113
|
-
let mut prev_is_lower = false;
|
|
114
|
-
|
|
115
|
-
for ch in type_name.chars() {
|
|
116
|
-
if ch.is_ascii_uppercase() {
|
|
117
|
-
if prev_is_lower {
|
|
118
|
-
out.push('_');
|
|
119
|
-
}
|
|
120
|
-
out.push(if lowercase {
|
|
121
|
-
ch.to_ascii_lowercase()
|
|
122
|
-
} else {
|
|
123
|
-
ch
|
|
124
|
-
});
|
|
125
|
-
prev_is_lower = false;
|
|
126
|
-
} else if ch == '_' {
|
|
127
|
-
out.push(ch);
|
|
128
|
-
prev_is_lower = false;
|
|
129
|
-
} else {
|
|
130
|
-
out.push(if lowercase {
|
|
131
|
-
ch
|
|
132
|
-
} else {
|
|
133
|
-
ch.to_ascii_uppercase()
|
|
134
|
-
});
|
|
135
|
-
prev_is_lower = ch.is_ascii_lowercase() || ch.is_ascii_digit();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if matches!(identifier, CIdentifier::Type) {
|
|
140
|
-
out.push_str("_t");
|
|
141
|
-
}
|
|
142
|
-
out
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/// Converts snake_case to PascalCase for Rust enum names
|
|
146
|
-
fn snake_to_pascal(s: &str) -> String {
|
|
147
|
-
s.split('_')
|
|
148
|
-
.map(|word| {
|
|
149
|
-
let mut chars = word.chars();
|
|
150
|
-
match chars.next() {
|
|
151
|
-
None => String::new(),
|
|
152
|
-
Some(first) => first.to_uppercase().chain(chars).collect(),
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
.collect()
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/// Generates Rust enum module constant name from snake_case (e.g., "unspecified" -> "RBS_ATTRIBUTE_VISIBILITY_UNSPECIFIED")
|
|
159
|
-
fn enum_constant_name(enum_name: &str, variant: &str) -> String {
|
|
160
|
-
format!(
|
|
161
|
-
"RBS_{}",
|
|
162
|
-
format!("{}_{}", enum_name, variant).to_uppercase()
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
fn write_enum_field_accessor(
|
|
167
|
-
file: &mut File,
|
|
168
|
-
field: &NodeField,
|
|
169
|
-
enum_name: &str,
|
|
170
|
-
) -> std::io::Result<()> {
|
|
171
|
-
let rust_enum_name = snake_to_pascal(enum_name);
|
|
172
|
-
writeln!(file, " #[must_use]")?;
|
|
173
|
-
writeln!(
|
|
174
|
-
file,
|
|
175
|
-
" pub fn {}(&self) -> {} {{",
|
|
176
|
-
field.name, rust_enum_name
|
|
177
|
-
)?;
|
|
178
|
-
writeln!(
|
|
179
|
-
file,
|
|
180
|
-
" {}::from_raw(unsafe {{ (*self.pointer).{} }})",
|
|
181
|
-
rust_enum_name,
|
|
182
|
-
field.c_name()
|
|
183
|
-
)?;
|
|
184
|
-
writeln!(file, " }}")?;
|
|
185
|
-
writeln!(file)?;
|
|
186
|
-
Ok(())
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
fn write_node_field_accessor(
|
|
190
|
-
file: &mut File,
|
|
191
|
-
field: &NodeField,
|
|
192
|
-
rust_type: &str,
|
|
193
|
-
) -> std::io::Result<()> {
|
|
194
|
-
if field.optional {
|
|
195
|
-
writeln!(file, " #[must_use]")?;
|
|
196
|
-
writeln!(
|
|
197
|
-
file,
|
|
198
|
-
" pub fn {}(&self) -> Option<{rust_type}<'a>> {{",
|
|
199
|
-
field.name,
|
|
200
|
-
)?;
|
|
201
|
-
writeln!(
|
|
202
|
-
file,
|
|
203
|
-
" let ptr = unsafe {{ (*self.pointer).{} }};",
|
|
204
|
-
field.c_name()
|
|
205
|
-
)?;
|
|
206
|
-
writeln!(file, " if ptr.is_null() {{")?;
|
|
207
|
-
writeln!(file, " None")?;
|
|
208
|
-
writeln!(file, " }} else {{")?;
|
|
209
|
-
writeln!(
|
|
210
|
-
file,
|
|
211
|
-
" Some({rust_type} {{ parser: self.parser, pointer: ptr, marker: PhantomData }})"
|
|
212
|
-
)?;
|
|
213
|
-
writeln!(file, " }}")?;
|
|
214
|
-
} else {
|
|
215
|
-
writeln!(file, " #[must_use]")?;
|
|
216
|
-
writeln!(
|
|
217
|
-
file,
|
|
218
|
-
" pub fn {}(&self) -> {rust_type}<'a> {{",
|
|
219
|
-
field.name
|
|
220
|
-
)?;
|
|
221
|
-
writeln!(
|
|
222
|
-
file,
|
|
223
|
-
" {rust_type} {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }}, marker: PhantomData }}",
|
|
224
|
-
field.c_name()
|
|
225
|
-
)?;
|
|
226
|
-
}
|
|
227
|
-
writeln!(file, " }}")?;
|
|
228
|
-
writeln!(file)?;
|
|
229
|
-
Ok(())
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
fn write_visit_trait(file: &mut File, config: &Config) -> Result<(), Box<dyn std::error::Error>> {
|
|
233
|
-
writeln!(file, "/// A trait for traversing the AST using a visitor")?;
|
|
234
|
-
writeln!(file, "pub trait Visit {{")?;
|
|
235
|
-
writeln!(
|
|
236
|
-
file,
|
|
237
|
-
" /// Visit any node of the AST. Generally used to continue traversal"
|
|
238
|
-
)?;
|
|
239
|
-
writeln!(file, " fn visit(&mut self, node: &Node) {{")?;
|
|
240
|
-
writeln!(file, " match node {{")?;
|
|
241
|
-
|
|
242
|
-
for node in &config.nodes {
|
|
243
|
-
let node_variant_name = node.variant_name();
|
|
244
|
-
let method_name = convert_name(node_variant_name, CIdentifier::Method);
|
|
245
|
-
|
|
246
|
-
writeln!(file, " Node::{node_variant_name}(it) => {{")?;
|
|
247
|
-
writeln!(file, " self.visit_{method_name}_node(it);")?;
|
|
248
|
-
writeln!(file, " }}")?;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
writeln!(file, " }}")?;
|
|
252
|
-
writeln!(file, " }}")?;
|
|
253
|
-
|
|
254
|
-
for node in &config.nodes {
|
|
255
|
-
let node_variant_name = node.variant_name();
|
|
256
|
-
let method_name = convert_name(node_variant_name, CIdentifier::Method);
|
|
257
|
-
|
|
258
|
-
writeln!(file)?;
|
|
259
|
-
writeln!(
|
|
260
|
-
file,
|
|
261
|
-
" fn visit_{method_name}_node(&mut self, node: &{node_variant_name}Node) {{"
|
|
262
|
-
)?;
|
|
263
|
-
writeln!(file, " visit_{method_name}_node(self, node);")?;
|
|
264
|
-
writeln!(file, " }}")?;
|
|
265
|
-
}
|
|
266
|
-
writeln!(file, "}}")?;
|
|
267
|
-
writeln!(file)?;
|
|
268
|
-
|
|
269
|
-
// Map C field types (e.g. `rbs_type_name`) to the corresponding
|
|
270
|
-
// visitor method name (e.g. `type_name` -> `visit_type_name_node`).
|
|
271
|
-
let visitor_method_names: std::collections::HashMap<String, String> = config
|
|
272
|
-
.nodes
|
|
273
|
-
.iter()
|
|
274
|
-
.map(|node| {
|
|
275
|
-
let c_type = convert_name(&node.name, CIdentifier::Type);
|
|
276
|
-
let c_type = c_type.strip_suffix("_t").unwrap_or(&c_type).to_string();
|
|
277
|
-
let method = convert_name(node.variant_name(), CIdentifier::Method);
|
|
278
|
-
(c_type, method)
|
|
279
|
-
})
|
|
280
|
-
.collect();
|
|
281
|
-
|
|
282
|
-
let is_visitable = |c_type: &str| -> bool {
|
|
283
|
-
matches!(c_type, "rbs_node" | "rbs_node_list" | "rbs_hash")
|
|
284
|
-
|| visitor_method_names.contains_key(c_type)
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
for node in &config.nodes {
|
|
288
|
-
let node_variant_name = node.variant_name();
|
|
289
|
-
let method_name = convert_name(node_variant_name, CIdentifier::Method);
|
|
290
|
-
|
|
291
|
-
let has_visitable_fields = node
|
|
292
|
-
.fields
|
|
293
|
-
.iter()
|
|
294
|
-
.flatten()
|
|
295
|
-
.any(|field| is_visitable(&field.c_type));
|
|
296
|
-
|
|
297
|
-
if !has_visitable_fields {
|
|
298
|
-
// If there's nothing to visit in this node, write the empty method with
|
|
299
|
-
// underscored parameters, and skip to the next iteration
|
|
300
|
-
writeln!(
|
|
301
|
-
file,
|
|
302
|
-
"pub fn visit_{method_name}_node<V: Visit + ?Sized>(_visitor: &mut V, _node: &{node_variant_name}Node) {{}}"
|
|
303
|
-
)?;
|
|
304
|
-
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
writeln!(
|
|
309
|
-
file,
|
|
310
|
-
"pub fn visit_{method_name}_node<V: Visit + ?Sized>(visitor: &mut V, node: &{node_variant_name}Node) {{"
|
|
311
|
-
)?;
|
|
312
|
-
|
|
313
|
-
if let Some(fields) = &node.fields {
|
|
314
|
-
for field in fields {
|
|
315
|
-
let field_method_name = if field.name == "type" {
|
|
316
|
-
"type_"
|
|
317
|
-
} else {
|
|
318
|
-
field.name.as_str()
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
match field.c_type.as_str() {
|
|
322
|
-
"rbs_node" => {
|
|
323
|
-
if field.optional {
|
|
324
|
-
writeln!(
|
|
325
|
-
file,
|
|
326
|
-
" if let Some(item) = node.{field_method_name}() {{"
|
|
327
|
-
)?;
|
|
328
|
-
writeln!(file, " visitor.visit(&item);")?;
|
|
329
|
-
writeln!(file, " }}")?;
|
|
330
|
-
} else {
|
|
331
|
-
writeln!(file, " visitor.visit(&node.{field_method_name}());")?;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
"rbs_node_list" => {
|
|
336
|
-
if field.optional {
|
|
337
|
-
writeln!(
|
|
338
|
-
file,
|
|
339
|
-
" if let Some(list) = node.{field_method_name}() {{"
|
|
340
|
-
)?;
|
|
341
|
-
writeln!(file, " for item in list.iter() {{")?;
|
|
342
|
-
writeln!(file, " visitor.visit(&item);")?;
|
|
343
|
-
writeln!(file, " }}")?;
|
|
344
|
-
writeln!(file, " }}")?;
|
|
345
|
-
} else {
|
|
346
|
-
writeln!(file, " for item in node.{field_method_name}().iter() {{")?;
|
|
347
|
-
writeln!(file, " visitor.visit(&item);")?;
|
|
348
|
-
writeln!(file, " }}")?;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
"rbs_hash" => {
|
|
353
|
-
if field.optional {
|
|
354
|
-
writeln!(
|
|
355
|
-
file,
|
|
356
|
-
" if let Some(hash) = node.{field_method_name}() {{"
|
|
357
|
-
)?;
|
|
358
|
-
writeln!(file, " for (key, value) in hash.iter() {{")?;
|
|
359
|
-
writeln!(file, " visitor.visit(&key);")?;
|
|
360
|
-
writeln!(file, " visitor.visit(&value);")?;
|
|
361
|
-
writeln!(file, " }}")?;
|
|
362
|
-
writeln!(file, " }}")?;
|
|
363
|
-
} else {
|
|
364
|
-
writeln!(
|
|
365
|
-
file,
|
|
366
|
-
" for (key, value) in node.{field_method_name}().iter() {{"
|
|
367
|
-
)?;
|
|
368
|
-
writeln!(file, " visitor.visit(&key);")?;
|
|
369
|
-
writeln!(file, " visitor.visit(&value);")?;
|
|
370
|
-
writeln!(file, " }}")?;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
_ => {
|
|
375
|
-
if let Some(visit_method_name) = visitor_method_names.get(&field.c_type) {
|
|
376
|
-
if field.optional {
|
|
377
|
-
writeln!(
|
|
378
|
-
file,
|
|
379
|
-
" if let Some(item) = node.{field_method_name}() {{"
|
|
380
|
-
)?;
|
|
381
|
-
writeln!(
|
|
382
|
-
file,
|
|
383
|
-
" visitor.visit_{visit_method_name}_node(&item);"
|
|
384
|
-
)?;
|
|
385
|
-
writeln!(file, " }}")?;
|
|
386
|
-
} else {
|
|
387
|
-
writeln!(
|
|
388
|
-
file,
|
|
389
|
-
" visitor.visit_{visit_method_name}_node(&node.{field_method_name}());"
|
|
390
|
-
)?;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
writeln!(file, "}}")?;
|
|
398
|
-
writeln!(file)?;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
Ok(())
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
fn write_enum_types(
|
|
405
|
-
file: &mut File,
|
|
406
|
-
enums: &std::collections::HashMap<String, EnumDef>,
|
|
407
|
-
) -> Result<(), Box<dyn Error>> {
|
|
408
|
-
for (enum_name, enum_def) in enums {
|
|
409
|
-
let rust_enum_name = snake_to_pascal(enum_name);
|
|
410
|
-
let c_enum_module = format!("rbs_{}", enum_name);
|
|
411
|
-
|
|
412
|
-
// Write enum definition
|
|
413
|
-
writeln!(file, "/// Generated from config.yml enums.{}", enum_name)?;
|
|
414
|
-
writeln!(file, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]")?;
|
|
415
|
-
writeln!(file, "pub enum {} {{", rust_enum_name)?;
|
|
416
|
-
|
|
417
|
-
for symbol in &enum_def.symbols {
|
|
418
|
-
let variant_name = snake_to_pascal(symbol);
|
|
419
|
-
writeln!(file, " /// {} (:{}) ", variant_name, symbol)?;
|
|
420
|
-
writeln!(file, " {},", variant_name)?;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
writeln!(file, "}}")?;
|
|
424
|
-
writeln!(file)?;
|
|
425
|
-
|
|
426
|
-
// Write impl block with from_raw
|
|
427
|
-
writeln!(file, "impl {} {{", rust_enum_name)?;
|
|
428
|
-
writeln!(
|
|
429
|
-
file,
|
|
430
|
-
" /// Converts the raw C enum value to the Rust enum."
|
|
431
|
-
)?;
|
|
432
|
-
writeln!(file, " #[must_use]")?;
|
|
433
|
-
writeln!(
|
|
434
|
-
file,
|
|
435
|
-
" pub fn from_raw(raw: {}::Type) -> Self {{",
|
|
436
|
-
c_enum_module
|
|
437
|
-
)?;
|
|
438
|
-
writeln!(file, " match raw {{")?;
|
|
439
|
-
|
|
440
|
-
for symbol in &enum_def.symbols {
|
|
441
|
-
let variant_name = snake_to_pascal(symbol);
|
|
442
|
-
let constant_name = enum_constant_name(enum_name, symbol);
|
|
443
|
-
writeln!(
|
|
444
|
-
file,
|
|
445
|
-
" {}::{} => Self::{},",
|
|
446
|
-
c_enum_module, constant_name, variant_name
|
|
447
|
-
)?;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
writeln!(
|
|
451
|
-
file,
|
|
452
|
-
" _ => panic!(\"Unknown {}: {{}}\", raw),",
|
|
453
|
-
c_enum_module
|
|
454
|
-
)?;
|
|
455
|
-
writeln!(file, " }}")?;
|
|
456
|
-
writeln!(file, " }}")?;
|
|
457
|
-
writeln!(file, "}}")?;
|
|
458
|
-
writeln!(file)?;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
Ok(())
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
fn generate(config: &Config) -> Result<(), Box<dyn Error>> {
|
|
465
|
-
let out_dir = env::var("OUT_DIR")?;
|
|
466
|
-
let dest_path = Path::new(&out_dir).join("bindings.rs");
|
|
467
|
-
|
|
468
|
-
let mut file = File::create(&dest_path)?;
|
|
469
|
-
|
|
470
|
-
writeln!(file, "// Generated by build.rs from config.yml")?;
|
|
471
|
-
writeln!(file)?;
|
|
472
|
-
|
|
473
|
-
// Generate enum types from config
|
|
474
|
-
write_enum_types(&mut file, &config.enums)?;
|
|
475
|
-
|
|
476
|
-
for node in &config.nodes {
|
|
477
|
-
writeln!(file, "#[derive(Debug)]")?;
|
|
478
|
-
writeln!(file, "pub struct {}<'a> {{", node.rust_name)?;
|
|
479
|
-
writeln!(file, " #[allow(dead_code)]")?;
|
|
480
|
-
writeln!(file, " parser: NonNull<rbs_parser_t>,")?;
|
|
481
|
-
writeln!(
|
|
482
|
-
file,
|
|
483
|
-
" pointer: *mut {},",
|
|
484
|
-
convert_name(&node.name, CIdentifier::Type)
|
|
485
|
-
)?;
|
|
486
|
-
writeln!(
|
|
487
|
-
file,
|
|
488
|
-
" marker: PhantomData<&'a mut {}>",
|
|
489
|
-
convert_name(&node.name, CIdentifier::Type)
|
|
490
|
-
)?;
|
|
491
|
-
writeln!(file, "}}\n")?;
|
|
492
|
-
|
|
493
|
-
writeln!(file, "impl<'a> {}<'a> {{", node.rust_name)?;
|
|
494
|
-
writeln!(file, " /// Converts this node to a generic node.")?;
|
|
495
|
-
writeln!(file, " #[must_use]")?;
|
|
496
|
-
writeln!(file, " pub fn as_node(self) -> Node<'a> {{")?;
|
|
497
|
-
writeln!(file, " Node::{}(self)", node.variant_name())?;
|
|
498
|
-
writeln!(file, " }}")?;
|
|
499
|
-
writeln!(file)?;
|
|
500
|
-
writeln!(file, " /// Returns the location of this node.")?;
|
|
501
|
-
writeln!(file, " #[must_use]")?;
|
|
502
|
-
writeln!(file, " pub fn location(&self) -> RBSLocationRange {{")?;
|
|
503
|
-
writeln!(
|
|
504
|
-
file,
|
|
505
|
-
" RBSLocationRange::new(unsafe {{ (*self.pointer).base.location }})"
|
|
506
|
-
)?;
|
|
507
|
-
writeln!(file, " }}")?;
|
|
508
|
-
writeln!(file)?;
|
|
509
|
-
|
|
510
|
-
// Generate location accessor methods
|
|
511
|
-
if let Some(locations) = &node.locations {
|
|
512
|
-
for location in locations {
|
|
513
|
-
let location_name = location.name();
|
|
514
|
-
let method_name = format!("{}_location", location_name);
|
|
515
|
-
let field_name = format!("{}_range", location_name);
|
|
516
|
-
|
|
517
|
-
if location.is_required() {
|
|
518
|
-
writeln!(
|
|
519
|
-
file,
|
|
520
|
-
" /// Returns the `{}` sub-location of this node.",
|
|
521
|
-
location_name
|
|
522
|
-
)?;
|
|
523
|
-
writeln!(file, " #[must_use]")?;
|
|
524
|
-
writeln!(
|
|
525
|
-
file,
|
|
526
|
-
" pub fn {}(&self) -> RBSLocationRange {{",
|
|
527
|
-
method_name
|
|
528
|
-
)?;
|
|
529
|
-
writeln!(
|
|
530
|
-
file,
|
|
531
|
-
" RBSLocationRange::new(unsafe {{ (*self.pointer).{} }})",
|
|
532
|
-
field_name
|
|
533
|
-
)?;
|
|
534
|
-
writeln!(file, " }}")?;
|
|
535
|
-
} else {
|
|
536
|
-
writeln!(
|
|
537
|
-
file,
|
|
538
|
-
" /// Returns the `{}` sub-location of this node if present.",
|
|
539
|
-
location_name
|
|
540
|
-
)?;
|
|
541
|
-
writeln!(file, " #[must_use]")?;
|
|
542
|
-
writeln!(
|
|
543
|
-
file,
|
|
544
|
-
" pub fn {}(&self) -> Option<RBSLocationRange> {{",
|
|
545
|
-
method_name
|
|
546
|
-
)?;
|
|
547
|
-
writeln!(
|
|
548
|
-
file,
|
|
549
|
-
" let range = unsafe {{ (*self.pointer).{} }};",
|
|
550
|
-
field_name
|
|
551
|
-
)?;
|
|
552
|
-
writeln!(file, " if range.start_char == -1 {{")?;
|
|
553
|
-
writeln!(file, " None")?;
|
|
554
|
-
writeln!(file, " }} else {{")?;
|
|
555
|
-
writeln!(file, " Some(RBSLocationRange::new(range))")?;
|
|
556
|
-
writeln!(file, " }}")?;
|
|
557
|
-
writeln!(file, " }}")?;
|
|
558
|
-
}
|
|
559
|
-
writeln!(file)?;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if let Some(fields) = &node.fields {
|
|
564
|
-
for field in fields {
|
|
565
|
-
match field.c_type.as_str() {
|
|
566
|
-
"rbs_string" => {
|
|
567
|
-
writeln!(file, " #[must_use]")?;
|
|
568
|
-
writeln!(file, " pub fn {}(&self) -> RBSString {{", field.name)?;
|
|
569
|
-
writeln!(
|
|
570
|
-
file,
|
|
571
|
-
" RBSString::new(unsafe {{ &(*self.pointer).{} }})",
|
|
572
|
-
field.c_name()
|
|
573
|
-
)?;
|
|
574
|
-
writeln!(file, " }}")?;
|
|
575
|
-
writeln!(file)?;
|
|
576
|
-
}
|
|
577
|
-
"bool" => {
|
|
578
|
-
writeln!(file, " #[must_use]")?;
|
|
579
|
-
writeln!(file, " pub fn {}(&self) -> bool {{", field.name)?;
|
|
580
|
-
writeln!(file, " unsafe {{ (*self.pointer).{} }}", field.name)?;
|
|
581
|
-
writeln!(file, " }}")?;
|
|
582
|
-
writeln!(file)?;
|
|
583
|
-
}
|
|
584
|
-
"rbs_ast_comment" => {
|
|
585
|
-
write_node_field_accessor(&mut file, field, "CommentNode")?
|
|
586
|
-
}
|
|
587
|
-
"rbs_ast_declarations_class_super" => {
|
|
588
|
-
write_node_field_accessor(&mut file, field, "ClassSuperNode")?
|
|
589
|
-
}
|
|
590
|
-
"rbs_ast_symbol" => write_node_field_accessor(&mut file, field, "SymbolNode")?,
|
|
591
|
-
"rbs_hash" => {
|
|
592
|
-
write_node_field_accessor(&mut file, field, "RBSHash")?;
|
|
593
|
-
}
|
|
594
|
-
"rbs_namespace" => {
|
|
595
|
-
write_node_field_accessor(&mut file, field, "NamespaceNode")?;
|
|
596
|
-
}
|
|
597
|
-
"rbs_node" => {
|
|
598
|
-
let name = if field.name == "type" {
|
|
599
|
-
"type_"
|
|
600
|
-
} else {
|
|
601
|
-
field.name.as_str()
|
|
602
|
-
};
|
|
603
|
-
if field.optional {
|
|
604
|
-
writeln!(file, " #[must_use]")?;
|
|
605
|
-
writeln!(file, " pub fn {name}(&self) -> Option<Node<'a>> {{")?;
|
|
606
|
-
writeln!(
|
|
607
|
-
file,
|
|
608
|
-
" let ptr = unsafe {{ (*self.pointer).{} }};",
|
|
609
|
-
field.c_name()
|
|
610
|
-
)?;
|
|
611
|
-
writeln!(
|
|
612
|
-
file,
|
|
613
|
-
" if ptr.is_null() {{ None }} else {{ Some(Node::new(self.parser, ptr)) }}"
|
|
614
|
-
)?;
|
|
615
|
-
} else {
|
|
616
|
-
writeln!(file, " #[must_use]")?;
|
|
617
|
-
writeln!(file, " pub fn {name}(&self) -> Node<'a> {{")?;
|
|
618
|
-
writeln!(
|
|
619
|
-
file,
|
|
620
|
-
" unsafe {{ Node::new(self.parser, (*self.pointer).{}) }}",
|
|
621
|
-
field.c_name()
|
|
622
|
-
)?;
|
|
623
|
-
}
|
|
624
|
-
writeln!(file, " }}")?;
|
|
625
|
-
writeln!(file)?;
|
|
626
|
-
}
|
|
627
|
-
"rbs_node_list" => {
|
|
628
|
-
write_node_field_accessor(&mut file, field, "NodeList")?;
|
|
629
|
-
}
|
|
630
|
-
"rbs_type_name" => {
|
|
631
|
-
write_node_field_accessor(&mut file, field, "TypeNameNode")?;
|
|
632
|
-
}
|
|
633
|
-
"rbs_types_block" => {
|
|
634
|
-
write_node_field_accessor(&mut file, field, "BlockTypeNode")?
|
|
635
|
-
}
|
|
636
|
-
c_type if config.enums.contains_key(c_type) => {
|
|
637
|
-
write_enum_field_accessor(&mut file, field, c_type)?;
|
|
638
|
-
}
|
|
639
|
-
"rbs_attr_ivar_name" => {
|
|
640
|
-
writeln!(file, " #[must_use]")?;
|
|
641
|
-
writeln!(file, " pub fn {}(&self) -> AttrIvarName {{", field.name)?;
|
|
642
|
-
writeln!(
|
|
643
|
-
file,
|
|
644
|
-
" AttrIvarName::from_raw(unsafe {{ (*self.pointer).{} }})",
|
|
645
|
-
field.c_name()
|
|
646
|
-
)?;
|
|
647
|
-
writeln!(file, " }}")?;
|
|
648
|
-
writeln!(file)?;
|
|
649
|
-
}
|
|
650
|
-
"rbs_location_range" => {
|
|
651
|
-
if field.optional {
|
|
652
|
-
writeln!(file, " #[must_use]")?;
|
|
653
|
-
writeln!(
|
|
654
|
-
file,
|
|
655
|
-
" pub fn {}(&self) -> Option<RBSLocationRange> {{",
|
|
656
|
-
field.name
|
|
657
|
-
)?;
|
|
658
|
-
writeln!(
|
|
659
|
-
file,
|
|
660
|
-
" let range = unsafe {{ (*self.pointer).{} }};",
|
|
661
|
-
field.c_name()
|
|
662
|
-
)?;
|
|
663
|
-
writeln!(file, " if range.start_char == -1 {{")?;
|
|
664
|
-
writeln!(file, " None")?;
|
|
665
|
-
writeln!(file, " }} else {{")?;
|
|
666
|
-
writeln!(file, " Some(RBSLocationRange::new(range))")?;
|
|
667
|
-
writeln!(file, " }}")?;
|
|
668
|
-
writeln!(file, " }}")?;
|
|
669
|
-
} else {
|
|
670
|
-
writeln!(file, " #[must_use]")?;
|
|
671
|
-
writeln!(
|
|
672
|
-
file,
|
|
673
|
-
" pub fn {}(&self) -> RBSLocationRange {{",
|
|
674
|
-
field.name
|
|
675
|
-
)?;
|
|
676
|
-
writeln!(
|
|
677
|
-
file,
|
|
678
|
-
" RBSLocationRange::new(unsafe {{ (*self.pointer).{} }})",
|
|
679
|
-
field.c_name()
|
|
680
|
-
)?;
|
|
681
|
-
writeln!(file, " }}")?;
|
|
682
|
-
}
|
|
683
|
-
writeln!(file)?;
|
|
684
|
-
}
|
|
685
|
-
"rbs_location_range_list" => {
|
|
686
|
-
writeln!(file, " #[must_use]")?;
|
|
687
|
-
writeln!(
|
|
688
|
-
file,
|
|
689
|
-
" pub fn {}(&self) -> RBSLocationRangeList<'a> {{",
|
|
690
|
-
field.name
|
|
691
|
-
)?;
|
|
692
|
-
writeln!(
|
|
693
|
-
file,
|
|
694
|
-
" RBSLocationRangeList {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }}, marker: PhantomData }}",
|
|
695
|
-
field.c_name()
|
|
696
|
-
)?;
|
|
697
|
-
writeln!(file, " }}")?;
|
|
698
|
-
writeln!(file)?;
|
|
699
|
-
}
|
|
700
|
-
_ => panic!("Unknown field type: {}", field.c_type),
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
writeln!(file, "}}\n")?;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Generate the Node enum to wrap all of the structs
|
|
708
|
-
writeln!(file, "#[derive(Debug)]")?;
|
|
709
|
-
writeln!(file, "pub enum Node<'a> {{")?;
|
|
710
|
-
for node in &config.nodes {
|
|
711
|
-
let variant_name = node
|
|
712
|
-
.rust_name
|
|
713
|
-
.strip_suffix("Node")
|
|
714
|
-
.unwrap_or(&node.rust_name);
|
|
715
|
-
|
|
716
|
-
writeln!(file, " {variant_name}({}<'a>),", node.rust_name)?;
|
|
717
|
-
}
|
|
718
|
-
writeln!(file, "}}")?;
|
|
719
|
-
|
|
720
|
-
writeln!(file, "impl Node<'_> {{")?;
|
|
721
|
-
writeln!(file, " #[allow(clippy::missing_safety_doc)]")?;
|
|
722
|
-
writeln!(
|
|
723
|
-
file,
|
|
724
|
-
" fn new(parser: NonNull<rbs_parser_t>, node: *mut rbs_node_t) -> Self {{"
|
|
725
|
-
)?;
|
|
726
|
-
writeln!(file, " match unsafe {{ (*node).type_ }} {{")?;
|
|
727
|
-
for node in &config.nodes {
|
|
728
|
-
let enum_name = convert_name(&node.name, CIdentifier::Constant);
|
|
729
|
-
let c_type = convert_name(&node.name, CIdentifier::Type);
|
|
730
|
-
|
|
731
|
-
writeln!(
|
|
732
|
-
file,
|
|
733
|
-
" rbs_node_type::{enum_name} => Self::{}({} {{ parser, pointer: node.cast::<{c_type}>(), marker: PhantomData }}),",
|
|
734
|
-
node.variant_name(),
|
|
735
|
-
node.rust_name,
|
|
736
|
-
)?;
|
|
737
|
-
}
|
|
738
|
-
writeln!(
|
|
739
|
-
file,
|
|
740
|
-
" _ => panic!(\"Unknown node type: {{}}\", unsafe {{ (*node).type_ }})"
|
|
741
|
-
)?;
|
|
742
|
-
writeln!(file, " }}")?;
|
|
743
|
-
writeln!(file, " }}")?;
|
|
744
|
-
writeln!(file)?;
|
|
745
|
-
writeln!(file, " /// Returns the location of the entire node.")?;
|
|
746
|
-
writeln!(file, " #[must_use]")?;
|
|
747
|
-
writeln!(file, " pub fn location(&self) -> RBSLocationRange {{")?;
|
|
748
|
-
writeln!(file, " match self {{")?;
|
|
749
|
-
for node in &config.nodes {
|
|
750
|
-
writeln!(
|
|
751
|
-
file,
|
|
752
|
-
" Node::{}(node) => node.location(),",
|
|
753
|
-
node.variant_name()
|
|
754
|
-
)?;
|
|
755
|
-
}
|
|
756
|
-
writeln!(file, " }}")?;
|
|
757
|
-
writeln!(file, " }}")?;
|
|
758
|
-
writeln!(file, "}}")?;
|
|
759
|
-
writeln!(file)?;
|
|
760
|
-
|
|
761
|
-
write_visit_trait(&mut file, config)?;
|
|
762
|
-
|
|
763
|
-
Ok(())
|
|
764
|
-
}
|