Skip to content

Commit ae4ccc6

Browse files
committed
unified/swift: Translate protocol properties using context
Avoids more "mutation after creation" via prepend_field. Also adds a test to the corpus for exercising this syntax. Although it's not evident, the test output was unchanged by this refactoring.
1 parent 0d845c2 commit ae4ccc6

2 files changed

Lines changed: 169 additions & 34 deletions

File tree

unified/extractor/src/languages/swift/swift.rs

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,44 @@ use yeast::{manual_rule, rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseK
99
#[derive(Clone, Default)]
1010
struct SwiftContext {
1111
/// Identifier node for the property name. Set by the outer
12-
/// `property_binding` (computed accessors / willSet-didSet) rule
13-
/// before translating accessor children; read by
14-
/// `computed_getter`/`computed_setter`/`computed_modify`/
15-
/// `willset_clause`/`didset_clause`.
12+
/// `property_binding` (computed accessors / willSet-didSet) and
13+
/// `protocol_property_declaration` rules before translating accessor
14+
/// children; read by the accessor inner rules
15+
/// (`computed_getter`/`computed_setter`/`computed_modify`/
16+
/// `willset_clause`/`didset_clause`/`getter_specifier`/
17+
/// `setter_specifier`).
1618
property_name: Option<yeast::Id>,
1719
/// Translated type node for the property type. Set by the outer
18-
/// `property_binding` rule (computed accessors variant) when
19-
/// present; read by `computed_*` rules.
20+
/// `property_binding` rule (computed accessors variant) and
21+
/// `protocol_property_declaration` when present; read by the
22+
/// accessor inner rules.
2023
property_type: Option<yeast::Id>,
2124
/// Default-value expression for the next translated `parameter`. Set
2225
/// by the outer `function_parameter` rule; read by the `parameter`
2326
/// rules.
2427
default_value: Option<yeast::Id>,
28+
/// Translated outer modifiers (e.g. visibility, attributes) to
29+
/// attach to each child of a flattening outer rule. Set by
30+
/// `protocol_property_declaration` (and, later,
31+
/// `property_declaration`/`enum_entry`).
32+
outer_modifiers: Vec<yeast::Id>,
33+
/// True when the current child of a flattening outer rule is not
34+
/// the first one — its inner rule should emit a
35+
/// `chained_declaration` modifier so the original grouping can be
36+
/// recovered downstream.
37+
is_chained: bool,
38+
}
39+
40+
/// Build a freshly-created `chained_declaration` modifier node if
41+
/// `ctx.is_chained`, else `None`. Used by inner declaration rules to
42+
/// emit the chained tag for non-first children of a flattening outer
43+
/// rule. Returns `Option<Id>` so it splices via `{..…}` to 0 or 1 ids.
44+
fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option<yeast::Id> {
45+
if ctx.is_chained {
46+
Some(ctx.literal("modifier", "chained_declaration"))
47+
} else {
48+
None
49+
}
2550
}
2651

2752
fn translation_rules() -> Vec<Rule<SwiftContext>> {
@@ -904,41 +929,61 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
904929
name: (identifier #{name})
905930
bound: {..bound})
906931
),
907-
// Protocol property declaration: translate each accessor requirement to an
908-
// accessor_declaration without a body, carrying the property name and type.
909-
// Subsequent accessors get chained_declaration (same flattening as computed properties).
910-
rule!(
932+
// Protocol property declaration: translate each accessor
933+
// requirement to an `accessor_declaration` carrying the property
934+
// name, type, and outer modifiers. Manual rule: we publish the
935+
// property's name/type/modifiers into `ctx` and translate each
936+
// accessor with `ctx.is_chained` toggled per iteration so the
937+
// inner `getter_specifier`/`setter_specifier` rules emit
938+
// complete nodes from the start (including the
939+
// `chained_declaration` tag for non-first accessors).
940+
manual_rule!(
911941
(protocol_property_declaration
912-
name: @pattern
942+
name: (pattern bound_identifier: @name)
913943
requirements: (protocol_property_requirements accessor: _+ @accessors)
914944
type: _? @ty
915945
(modifiers)* @mods)
916-
=>
917-
{..{
918-
let name_text = ctx.ast.source_text(pattern.into());
919-
let mod_ids: Vec<usize> = mods.iter().map(|&m| m.into()).collect();
920-
let ty_ids: Vec<usize> = ty.iter().map(|&t| t.into()).collect();
921-
let acc_ids: Vec<usize> = accessors.iter().map(|&a| a.into()).collect();
922-
for (i, &acc_id) in acc_ids.iter().enumerate() {
923-
if i > 0 {
924-
let chained = ctx.literal("modifier", "chained_declaration");
925-
ctx.prepend_field(acc_id, "modifier", chained);
926-
}
927-
for &mod_id in mod_ids.iter().rev() {
928-
ctx.prepend_field(acc_id, "modifier", mod_id);
929-
}
930-
for &ty_id in ty_ids.iter().rev() {
931-
ctx.prepend_field(acc_id, "type", ty_id);
932-
}
933-
let ident = ctx.literal("identifier", &name_text);
934-
ctx.prepend_field(acc_id, "name", ident);
946+
{
947+
ctx.property_name = Some(tree!((identifier #{name})));
948+
ctx.property_type = ctx.translate_opt(ty)?;
949+
let mut modifiers = Vec::new();
950+
for m in mods {
951+
modifiers.extend(ctx.translate(m)?);
935952
}
936-
acc_ids
937-
}}
953+
ctx.outer_modifiers = modifiers;
954+
955+
let mut result = Vec::new();
956+
for (i, acc) in accessors.into_iter().enumerate() {
957+
ctx.is_chained = i > 0;
958+
result.extend(ctx.translate(acc)?);
959+
}
960+
Ok(result)
961+
}
938962
),
939963
// getter_specifier / setter_specifier → bodyless accessor_declaration
940-
rule!((getter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "get"))),
941-
rule!((setter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "set"))),
964+
// getter_specifier / setter_specifier → bodyless
965+
// accessor_declaration. Reads property name/type/modifiers from
966+
// `ctx` set by the outer `protocol_property_declaration` rule.
967+
rule!(
968+
(getter_specifier)
969+
=>
970+
(accessor_declaration
971+
name: {ctx.property_name.ok_or("getter_specifier outside protocol_property_declaration context")?}
972+
type: {..ctx.property_type}
973+
accessor_kind: (accessor_kind "get")
974+
modifier: {..ctx.outer_modifiers.clone()}
975+
modifier: {..chained_modifier(&mut ctx)})
976+
),
977+
rule!(
978+
(setter_specifier)
979+
=>
980+
(accessor_declaration
981+
name: {ctx.property_name.ok_or("setter_specifier outside protocol_property_declaration context")?}
982+
type: {..ctx.property_type}
983+
accessor_kind: (accessor_kind "set")
984+
modifier: {..ctx.outer_modifiers.clone()}
985+
modifier: {..chained_modifier(&mut ctx)})
986+
),
942987
// protocol_property_requirements wrapper — should be consumed by above; fallback
943988
rule!((protocol_property_requirements accessor: _* @accs) => {..accs}),
944989
// Computed getter → accessor_declaration (body optional).

unified/extractor/tests/corpus/swift/types.txt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,3 +924,93 @@ top_level
924924
accessor_kind: accessor_kind "set"
925925
modifier: modifier "class"
926926
name: identifier "Box"
927+
928+
===
929+
Protocol with read-only and read-write property requirements
930+
===
931+
932+
protocol P {
933+
var foo: Int { get }
934+
var bar: String { get set }
935+
}
936+
937+
---
938+
939+
source_file
940+
statement:
941+
protocol_declaration
942+
body:
943+
protocol_body
944+
member:
945+
protocol_property_declaration
946+
name:
947+
pattern
948+
binding:
949+
value_binding_pattern
950+
mutability: var
951+
bound_identifier: simple_identifier "foo"
952+
requirements:
953+
protocol_property_requirements
954+
accessor:
955+
getter_specifier
956+
type:
957+
type_annotation
958+
type:
959+
type
960+
name:
961+
user_type
962+
part:
963+
simple_user_type
964+
name: type_identifier "Int"
965+
protocol_property_declaration
966+
name:
967+
pattern
968+
binding:
969+
value_binding_pattern
970+
mutability: var
971+
bound_identifier: simple_identifier "bar"
972+
requirements:
973+
protocol_property_requirements
974+
accessor:
975+
getter_specifier
976+
setter_specifier
977+
type:
978+
type_annotation
979+
type:
980+
type
981+
name:
982+
user_type
983+
part:
984+
simple_user_type
985+
name: type_identifier "String"
986+
name: type_identifier "P"
987+
988+
---
989+
990+
top_level
991+
body:
992+
block
993+
stmt:
994+
class_like_declaration
995+
member:
996+
accessor_declaration
997+
name: identifier "foo"
998+
type:
999+
named_type_expr
1000+
name: identifier "Int"
1001+
accessor_kind: accessor_kind "get"
1002+
accessor_declaration
1003+
name: identifier "bar"
1004+
type:
1005+
named_type_expr
1006+
name: identifier "String"
1007+
accessor_kind: accessor_kind "get"
1008+
accessor_declaration
1009+
modifier: modifier "chained_declaration"
1010+
name: identifier "bar"
1011+
type:
1012+
named_type_expr
1013+
name: identifier "String"
1014+
accessor_kind: accessor_kind "set"
1015+
modifier: modifier "protocol"
1016+
name: identifier "P"

0 commit comments

Comments
 (0)