Skip to content

Commit 8f3246d

Browse files
committed
Synthesize syntax nodes for operator and precedence groups
Introduce `synthesizeSyntax()` operations for `Operator` and `PrecedenceGroup` to produce syntax nodes describing the semantics.
1 parent 1552f52 commit 8f3246d

File tree

2 files changed

+106
-3
lines changed

2 files changed

+106
-3
lines changed

Sources/SwiftOperators/SyntaxSynthesis.swift

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ extension Operator {
1919
let modifiers = ModifierListSyntax(
2020
[DeclModifierSyntax(name: .identifier("\(kind)"), detail: nil)]
2121
)
22-
let operatorKeyword = TokenSyntax.operatorKeyword(leadingTrivia: .spaces(1))
22+
let operatorKeyword = TokenSyntax.operatorKeyword(leadingTrivia: .space)
2323
let identifierSyntax =
24-
TokenSyntax.identifier(name, leadingTrivia: .spaces(1))
24+
TokenSyntax.identifier(name, leadingTrivia: .space)
2525
let precedenceGroupSyntax = precedenceGroup.map { groupName in
2626
OperatorPrecedenceAndTypesSyntax(
2727
colon: .colonToken(),
2828
precedenceGroupAndDesignatedTypes: IdentifierListSyntax(
29-
[.identifier(groupName, leadingTrivia: .spaces(1))]
29+
[.identifier(groupName, leadingTrivia: .space)]
3030
)
3131
)
3232
}
@@ -38,3 +38,88 @@ extension Operator {
3838
)
3939
}
4040
}
41+
42+
extension PrecedenceGroup {
43+
/// Synthesize a syntactic representation of this precedence group based on
44+
/// its semantic definition.
45+
public func synthesizedSyntax(
46+
indentation: Int = 4
47+
) -> PrecedenceGroupDeclSyntax {
48+
let precedencegroupKeyword = TokenSyntax.precedencegroupKeyword()
49+
let identifierSyntax =
50+
TokenSyntax.identifier(name, leadingTrivia: .space)
51+
let leftBrace = TokenSyntax.leftBraceToken(leadingTrivia: .space)
52+
53+
var groupAttributes: [Syntax] = []
54+
55+
switch associativity {
56+
case .left, .right:
57+
groupAttributes.append(
58+
Syntax(
59+
PrecedenceGroupAssociativitySyntax(
60+
associativityKeyword:
61+
.identifier(
62+
"associativity",
63+
leadingTrivia: [ .newlines(1), .spaces(indentation) ]
64+
),
65+
colon: .colonToken(),
66+
value: .identifier("\(associativity)", leadingTrivia: .space)
67+
)
68+
)
69+
)
70+
71+
case .none:
72+
// None is the default associativity.
73+
break
74+
}
75+
76+
if assignment {
77+
groupAttributes.append(
78+
Syntax(
79+
PrecedenceGroupAssignmentSyntax(
80+
assignmentKeyword:
81+
.identifier(
82+
"assignment",
83+
leadingTrivia: [ .newlines(1), .spaces(indentation) ]
84+
),
85+
colon: .colonToken(),
86+
flag: .trueKeyword(leadingTrivia: .space)
87+
)
88+
)
89+
)
90+
}
91+
92+
for relation in relations {
93+
groupAttributes.append(
94+
Syntax(
95+
PrecedenceGroupRelationSyntax(
96+
higherThanOrLowerThan: .contextualKeyword(
97+
"\(relation.kind)",
98+
leadingTrivia: [ .newlines(1), .spaces(indentation) ]
99+
),
100+
colon: .colonToken(),
101+
otherNames: PrecedenceGroupNameListSyntax(
102+
[
103+
PrecedenceGroupNameElementSyntax(
104+
name: .identifier(relation.groupName, leadingTrivia: .space),
105+
trailingComma: nil)
106+
]
107+
)
108+
)
109+
)
110+
)
111+
}
112+
113+
let rightBrace = TokenSyntax.rightBraceToken(
114+
leadingTrivia: groupAttributes.isEmpty ? .space : .newline
115+
)
116+
117+
return PrecedenceGroupDeclSyntax(
118+
attributes: nil, modifiers: nil,
119+
precedencegroupKeyword: precedencegroupKeyword,
120+
identifier: identifierSyntax, leftBrace: leftBrace,
121+
groupAttributes: PrecedenceGroupAttributeListSyntax(groupAttributes),
122+
rightBrace: rightBrace
123+
)
124+
}
125+
}

Tests/SwiftOperatorsTest/SyntaxSynthesisTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,22 @@ public class SyntaxSynthesisTests: XCTestCase {
2121
XCTAssertEqual(
2222
plusSyntax.description, "infix operator +: AdditivePrecedence")
2323
}
24+
25+
func testPrecedenceGroup() {
26+
let group = PrecedenceGroup(
27+
name: "MyGroup", associativity: .right, assignment: true,
28+
relations: [ .lowerThan("BetterGroup"), .higherThan("WorseGroup")]
29+
)
30+
let groupSyntax = group.synthesizedSyntax()
31+
XCTAssertEqual(
32+
groupSyntax.description,
33+
"""
34+
precedencegroup MyGroup {
35+
associativity: right
36+
assignment: true
37+
lowerThan: BetterGroup
38+
higherThan: WorseGroup
39+
}
40+
""")
41+
}
2442
}

0 commit comments

Comments
 (0)