Skip to content

Commit 7a573c4

Browse files
committed
yeast: Order AST dump fields by schema-declared order
The AST dump previously emitted named fields in field-id order, which made it dependant on registration order and so it could differ between front-ends. We now emit them in the order declared in the node-types YAML instead, so that the order is kept stable.
1 parent d1fed84 commit 7a573c4

73 files changed

Lines changed: 536 additions & 450 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

shared/yeast-schema/src/node_types_yaml.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,41 @@ pub fn extend_schema_from_yaml(
252252
let yaml: YamlNodeTypes =
253253
serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?;
254254
apply_yaml_to_schema(&yaml, schema);
255+
// The typed `YamlNodeTypes` stores each node's fields in a `BTreeMap`
256+
// (alphabetical), losing the authored order. Re-parse as an ordered value to
257+
// record the declared field order for presentation (see the AST dump).
258+
record_field_order(schema, yaml_input)?;
259+
Ok(())
260+
}
261+
262+
/// Record each node kind's declared (named) field order from the source YAML,
263+
/// which `serde`'s `BTreeMap`-based deserialization does not preserve.
264+
/// `serde_yaml::Value` mappings keep insertion (source) order.
265+
fn record_field_order(schema: &mut crate::schema::Schema, yaml_input: &str) -> Result<(), String> {
266+
let value: serde_yaml::Value = serde_yaml::from_str(yaml_input)
267+
.map_err(|e| format!("Failed to parse YAML for field order: {e}"))?;
268+
let Some(named) = value.get("named").and_then(|v| v.as_mapping()) else {
269+
return Ok(());
270+
};
271+
for (node_name, fields) in named {
272+
let Some(node_name) = node_name.as_str() else {
273+
continue;
274+
};
275+
let Some(fields) = fields.as_mapping() else {
276+
continue; // node with no fields (null)
277+
};
278+
let mut order = Vec::new();
279+
for (raw_field_name, _) in fields {
280+
let Some(raw) = raw_field_name.as_str() else {
281+
continue;
282+
};
283+
// Skip the unnamed/`child` slot; the dump handles it separately.
284+
if let Some(name) = parse_field_name(raw).name {
285+
order.push(schema.register_field(&name));
286+
}
287+
}
288+
schema.set_field_order(node_name, order);
289+
}
255290
Ok(())
256291
}
257292

shared/yeast-schema/src/schema.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ pub struct Schema {
4444
field_types: BTreeMap<(String, FieldId), Vec<NodeType>>,
4545
field_cardinalities: BTreeMap<(String, FieldId), FieldCardinality>,
4646
supertypes: BTreeMap<String, Vec<NodeType>>,
47+
/// Per-node-kind declared field order (named fields only), as written in
48+
/// the source node-types YAML. Field ids are not a stable ordering key
49+
/// across front-ends, so this preserves the authored order for
50+
/// presentation (see the AST dump).
51+
field_order: BTreeMap<String, Vec<FieldId>>,
4752
}
4853

4954
impl Default for Schema {
@@ -65,6 +70,7 @@ impl Schema {
6570
field_types: BTreeMap::new(),
6671
field_cardinalities: BTreeMap::new(),
6772
supertypes: BTreeMap::new(),
73+
field_order: BTreeMap::new(),
6874
}
6975
}
7076

@@ -269,6 +275,17 @@ impl Schema {
269275
.get(&(parent_kind.to_string(), field_id))
270276
}
271277

278+
/// Record the declared (named) field order for a node kind, as authored in
279+
/// the source node-types YAML.
280+
pub fn set_field_order(&mut self, kind: &str, field_ids: Vec<FieldId>) {
281+
self.field_order.insert(kind.to_string(), field_ids);
282+
}
283+
284+
/// The declared (named) field order for a node kind, if known.
285+
pub fn field_order(&self, kind: &str) -> Option<&Vec<FieldId>> {
286+
self.field_order.get(kind)
287+
}
288+
272289
pub fn set_field_cardinality(
273290
&mut self,
274291
parent_kind: &str,

shared/yeast/src/dump.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,45 @@ fn dump_node(
223223

224224
writeln!(out).unwrap();
225225

226-
// Named fields first
227-
for (&field_id, children) in &node.fields {
228-
if field_id == CHILD_FIELD {
229-
continue; // Handle unnamed children last
226+
// Named fields first, in the schema's declared order when available
227+
// (front-end-independent), else in field-id order. Any present fields not
228+
// covered by the declared order are appended in field-id order.
229+
//
230+
// The declared order lives in the validation schema, keyed by *its* field
231+
// ids; the AST being dumped may key the same field names under different
232+
// ids. So map the declared order through field NAMES into this AST's own id
233+
// space, keeping the two schemas independent (they share names, not ids).
234+
let named_field_ids: Vec<u16> = {
235+
let present: Vec<u16> = node
236+
.fields
237+
.keys()
238+
.copied()
239+
.filter(|&f| f != CHILD_FIELD)
240+
.collect();
241+
match type_check.and_then(|(schema, _, _)| {
242+
schema
243+
.field_order(node.kind_name())
244+
.map(|order| (schema, order))
245+
}) {
246+
Some((schema, order)) => {
247+
let mut result: Vec<u16> = order
248+
.iter()
249+
.filter_map(|&f| schema.field_name_for_id(f))
250+
.filter_map(|name| ast.field_id_for_name(name))
251+
.filter(|&f| f != CHILD_FIELD && node.fields.contains_key(&f))
252+
.collect();
253+
for &f in &present {
254+
if !result.contains(&f) {
255+
result.push(f);
256+
}
257+
}
258+
result
259+
}
260+
None => present,
230261
}
262+
};
263+
for field_id in named_field_ids {
264+
let children = &node.fields[&field_id];
231265
let field_name = ast.field_name_for_id(field_id).unwrap_or("?");
232266
let child_type_check = type_check.map(|(schema, _, _)| {
233267
let expected =

unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.output

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ top_level
5151
identifier: identifier "f"
5252
value:
5353
function_expr
54+
capture_declaration:
55+
variable_declaration
56+
modifier: modifier "weak"
57+
pattern:
58+
name_pattern
59+
identifier: identifier "self"
5460
body:
5561
block
5662
stmt:
@@ -61,9 +67,3 @@ top_level
6167
name_expr
6268
identifier: identifier "self"
6369
member: identifier "doThing"
64-
capture_declaration:
65-
variable_declaration
66-
modifier: modifier "weak"
67-
pattern:
68-
name_pattern
69-
identifier: identifier "self"

unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.output

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,23 @@ top_level
5555
identifier: identifier "f"
5656
value:
5757
function_expr
58-
body:
59-
block
60-
stmt:
61-
binary_expr
62-
operator: infix_operator "*"
63-
left:
64-
name_expr
65-
identifier: identifier "x"
66-
right: int_literal "2"
6758
parameter:
6859
parameter
69-
pattern:
70-
name_pattern
71-
identifier: identifier "x"
7260
type:
7361
named_type_expr
7462
name: identifier "Int"
63+
pattern:
64+
name_pattern
65+
identifier: identifier "x"
7566
return_type:
7667
named_type_expr
7768
name: identifier "Int"
69+
body:
70+
block
71+
stmt:
72+
binary_expr
73+
left:
74+
name_expr
75+
identifier: identifier "x"
76+
operator: infix_operator "*"
77+
right: int_literal "2"

unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.output

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ top_level
3838
block
3939
stmt:
4040
binary_expr
41-
operator: infix_operator "+"
4241
left:
4342
name_expr
4443
identifier: identifier "$0"
44+
operator: infix_operator "+"
4545
right:
4646
name_expr
4747
identifier: identifier "$1"

unified/extractor/tests/corpus/swift/closures/multi-statement-closure.output

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ top_level
7575
identifier: identifier "f"
7676
value:
7777
function_expr
78+
parameter:
79+
parameter
80+
type:
81+
named_type_expr
82+
name: identifier "Int"
83+
pattern:
84+
name_pattern
85+
identifier: identifier "x"
86+
return_type:
87+
named_type_expr
88+
name: identifier "Int"
7889
body:
7990
block
8091
stmt:
@@ -85,27 +96,16 @@ top_level
8596
identifier: identifier "y"
8697
value:
8798
binary_expr
88-
operator: infix_operator "+"
8999
left:
90100
name_expr
91101
identifier: identifier "x"
102+
operator: infix_operator "+"
92103
right: int_literal "1"
93104
return_expr
94105
value:
95106
binary_expr
96-
operator: infix_operator "*"
97107
left:
98108
name_expr
99109
identifier: identifier "y"
110+
operator: infix_operator "*"
100111
right: int_literal "2"
101-
parameter:
102-
parameter
103-
pattern:
104-
name_pattern
105-
identifier: identifier "x"
106-
type:
107-
named_type_expr
108-
name: identifier "Int"
109-
return_type:
110-
named_type_expr
111-
name: identifier "Int"

unified/extractor/tests/corpus/swift/closures/trailing-closure.output

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ top_level
2828
block
2929
stmt:
3030
call_expr
31+
callee:
32+
member_access_expr
33+
base:
34+
name_expr
35+
identifier: identifier "xs"
36+
member: identifier "map"
3137
argument:
3238
argument
3339
value:
@@ -36,14 +42,8 @@ top_level
3642
block
3743
stmt:
3844
binary_expr
39-
operator: infix_operator "*"
4045
left:
4146
name_expr
4247
identifier: identifier "$0"
48+
operator: infix_operator "*"
4349
right: int_literal "2"
44-
callee:
45-
member_access_expr
46-
base:
47-
name_expr
48-
identifier: identifier "xs"
49-
member: identifier "map"

unified/extractor/tests/corpus/swift/collections/dictionary-subscript.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ top_level
4343
identifier: identifier "v"
4444
value:
4545
call_expr
46-
argument:
47-
argument
48-
value: string_literal "\"key\""
4946
callee:
5047
name_expr
5148
identifier: identifier "d"
49+
argument:
50+
argument
51+
value: string_literal "\"key\""

unified/extractor/tests/corpus/swift/collections/subscript-access.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ top_level
4343
identifier: identifier "first"
4444
value:
4545
call_expr
46-
argument:
47-
argument
48-
value: int_literal "0"
4946
callee:
5047
name_expr
5148
identifier: identifier "xs"
49+
argument:
50+
argument
51+
value: int_literal "0"

0 commit comments

Comments
 (0)