Skip to content

Commit e4aad6e

Browse files
committed
feat(swift): upgrade Swift parser to support Swift 6 syntax
- Add new Swift 6 keywords: async, await, actor, distributed, consuming, borrowing, nonisolated, isolated, sending, macro - Support typed throws syntax: throws(ErrorType) - Support async functions - Support actor and distributed actor declarations - Support ownership modifiers (consuming, borrowing) for parameters - Support noncopyable types with ~Copyable syntax - Add macro declaration support (#externalMacro) - Update README with Swift language coverage All 25 tests passing.
1 parent 904c947 commit e4aad6e

File tree

5 files changed

+368
-14
lines changed

5 files changed

+368
-14
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ Chapi (pronounced /tʃɑpi/) can also be read as “XP” in Chinese if you pron
2525
| Feature | Java | Python | Go | Kotlin | TS/JS | C | C# | Scala | C++ | Rust | Swift |
2626
|----------------|------|--------|----|--------|-------|----|----|-------|-----|------|-------|
2727
| HTTP API decl || 🆕 |||| 🆕 ||| 🆕 || |
28-
| Syntax parsing ||||||||||| 🆕 |
29-
| Function calls |||||||| | || |
30-
| Arch/package || |||| 🆕 ||||| |
28+
| Syntax parsing ||||||||||| |
29+
| Function calls |||||||| | || |
30+
| Arch/package || |||| 🆕 ||||| |
3131
| Real-world || | ||| | | | | | |
3232

3333
### IDL stages
@@ -59,6 +59,7 @@ Tested language versions:
5959
- Kotlin
6060
- Rust: v1.60.0
6161
- Python: 2, 3
62+
- Swift: 5, 6 (with typed throws, async/await, actors, ownership modifiers)
6263

6364
Gradle modules (by tier):
6465

chapi-ast-swift/src/main/antlr/Swift5Lexer.g4

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine
33
// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true
44

5+
// Swift 6 Lexer (upgraded from Swift 5)
56
lexer grammar Swift5Lexer;
67
// Insert here @header for C++ lexer.
78

@@ -48,6 +49,8 @@ RETURN : 'return';
4849
THROW : 'throw';
4950
THROWS : 'throws';
5051
RETHROWS : 'rethrows';
52+
ASYNC : 'async';
53+
AWAIT : 'await';
5154
INDIRECT : 'indirect';
5255
INIT : 'init';
5356
DEINIT : 'deinit';
@@ -87,6 +90,26 @@ UNSAFE : 'unsafe';
8790
MUTATING : 'mutating';
8891
NONMUTATING : 'nonmutating';
8992
FILE_PRIVATE : 'fileprivate';
93+
94+
// Swift 6 new keywords
95+
CONSUMING : 'consuming';
96+
BORROWING : 'borrowing';
97+
NONISOLATED : 'nonisolated';
98+
ISOLATED : 'isolated';
99+
SENDING : 'sending';
100+
PACKAGE : 'package';
101+
DISCARD : 'discard';
102+
COPY : 'copy';
103+
CONSUME : 'consume';
104+
105+
// Macro-related (Swift 5.9+)
106+
MACRO : 'macro';
107+
FREESTANDING : 'freestanding';
108+
ATTACHED : 'attached';
109+
110+
// Distributed actor (Swift 5.5+)
111+
DISTRIBUTED : 'distributed';
112+
ACTOR : 'actor';
90113
IS : 'is';
91114
TRY : 'try';
92115
SUPER : 'super';
@@ -148,6 +171,10 @@ HASH_KEYPATH : '#keyPath';
148171
HASH_COLOR_LITERAL : '#colorLiteral';
149172
HASH_FILE_LITERAL : '#fileLiteral';
150173
HASH_IMAGE_LITERAL : '#imageLiteral';
174+
175+
// Swift 6 macro-related hash keywords
176+
HASH_EXTERNAL_MACRO: '#externalMacro';
177+
151178
GETTER : 'getter';
152179
SETTER : 'setter';
153180

chapi-ast-swift/src/main/antlr/Swift5Parser.g4

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false
22
// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging
33

4+
// Swift 6 Parser (upgraded from Swift 5)
5+
// Supports: typed throws, noncopyable types, ownership modifiers, macros, trailing commas
46
parser grammar Swift5Parser;
57

68
// Insert here @header for C++ parser.
@@ -418,13 +420,15 @@ declaration
418420
| enum_declaration
419421
| struct_declaration
420422
| class_declaration
423+
| actor_declaration // Swift 5.5+
421424
| protocol_declaration
422425
| initializer_declaration
423426
| deinitializer_declaration
424427
| extension_declaration
425428
| subscript_declaration
426429
| operator_declaration
427430
| precedence_group_declaration
431+
| macro_declaration // Swift 5.9+
428432
) SEMI?
429433
;
430434

@@ -579,7 +583,18 @@ function_name
579583
;
580584

581585
function_signature
582-
: parameter_clause (THROWS? | RETHROWS) function_result?
586+
: parameter_clause async_clause? throw_clause? function_result?
587+
;
588+
589+
// Swift 6: async clause
590+
async_clause
591+
: ASYNC
592+
;
593+
594+
// Swift 6: typed throws - throws(ErrorType)
595+
throw_clause
596+
: THROWS (LPAREN type RPAREN)?
597+
| RETHROWS
583598
;
584599

585600
function_result
@@ -599,12 +614,19 @@ parameter_list
599614
;
600615

601616
parameter
602-
: attributes? external_parameter_name? local_parameter_name type_annotation (
617+
: attributes? ownership_modifier? external_parameter_name? local_parameter_name type_annotation (
603618
default_argument_clause?
604619
| range_operator
605620
)
606621
;
607622

623+
// Swift 6: ownership modifiers for parameters
624+
ownership_modifier
625+
: CONSUMING
626+
| BORROWING
627+
| INOUT
628+
;
629+
608630
external_parameter_name
609631
: identifier
610632
;
@@ -738,6 +760,28 @@ class_member
738760
| compiler_control_statement
739761
;
740762

763+
// Actor Declaration (Swift 5.5+)
764+
actor_declaration
765+
: attributes? access_level_modifier? DISTRIBUTED? ACTOR actor_name generic_parameter_clause? type_inheritance_clause? generic_where_clause? actor_body
766+
;
767+
768+
actor_name
769+
: identifier
770+
;
771+
772+
actor_body
773+
: LCURLY actor_members RCURLY
774+
;
775+
776+
actor_members
777+
: actor_member*
778+
;
779+
780+
actor_member
781+
: declaration
782+
| compiler_control_statement
783+
;
784+
741785
// Protocol Declaration
742786
protocol_declaration
743787
: attributes? access_level_modifier? PROTOCOL protocol_name (
@@ -926,6 +970,12 @@ declaration_modifier
926970
| WEAK
927971
| access_level_modifier
928972
| mutation_modifier
973+
// Swift 6 new modifiers
974+
| NONISOLATED
975+
| ISOLATED
976+
| CONSUMING
977+
| BORROWING
978+
| DISTRIBUTED
929979
;
930980

931981
declaration_modifiers
@@ -1478,7 +1528,12 @@ type_inheritance_clause
14781528
;
14791529

14801530
type_inheritance_list
1481-
: type_identifier (COMMA type_identifier)*
1531+
: type_inheritance_item (COMMA type_inheritance_item)*
1532+
;
1533+
1534+
// Swift 6: support ~Copyable (noncopyable) in inheritance
1535+
type_inheritance_item
1536+
: TILDE? type_identifier
14821537
;
14831538

14841539
// Identifiers
@@ -1571,6 +1626,16 @@ identifier
15711626
| OPERATOR
15721627
| DO
15731628
| CATCH
1629+
// Swift 6 keywords that can also be identifiers
1630+
| ASYNC
1631+
| AWAIT
1632+
| ACTOR
1633+
| NONISOLATED
1634+
| ISOLATED
1635+
| CONSUMING
1636+
| BORROWING
1637+
| SENDING
1638+
| MACRO
15741639
)
15751640
| Identifier
15761641
| BACKTICK (keyword | Identifier | DOLLAR) BACKTICK
@@ -1818,4 +1883,41 @@ interpolated_string_literal
18181883
Quoted_multi_line_text
18191884
| Interpolation_multi_line (expression | tuple_element COMMA tuple_element_list) RPAREN
18201885
)* Multi_line_string_close
1886+
;
1887+
1888+
// ==================== Swift 5.9+ Macro Declarations ====================
1889+
1890+
// Macro Declaration
1891+
macro_declaration
1892+
: attributes? access_level_modifier? MACRO macro_name generic_parameter_clause? macro_signature generic_where_clause? macro_definition?
1893+
;
1894+
1895+
macro_name
1896+
: identifier
1897+
;
1898+
1899+
macro_signature
1900+
: parameter_clause (arrow_operator type)?
1901+
;
1902+
1903+
macro_definition
1904+
: EQUAL macro_expansion_expression
1905+
;
1906+
1907+
macro_expansion_expression
1908+
: HASH_EXTERNAL_MACRO LPAREN macro_arguments RPAREN
1909+
| HASH identifier LPAREN macro_arguments? RPAREN
1910+
;
1911+
1912+
macro_arguments
1913+
: macro_argument (COMMA macro_argument)*
1914+
;
1915+
1916+
macro_argument
1917+
: (identifier COLON)? expression
1918+
;
1919+
1920+
// Freestanding macro expression (used in expressions)
1921+
freestanding_macro_expression
1922+
: HASH identifier generic_argument_clause? (LPAREN macro_arguments? RPAREN)? trailing_closures?
18211923
;

chapi-ast-swift/src/main/kotlin/chapi/ast/swiftast/SwiftFullIdentListener.kt

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,47 @@ class SwiftFullIdentListener(private val filePath: String) : Swift5ParserBaseLis
182182
finishDataStruct()
183183
}
184184

185+
// ==================== Actor Declaration (Swift 5.5+) ====================
186+
187+
override fun enterActor_declaration(ctx: Swift5Parser.Actor_declarationContext) {
188+
val name = ctx.actor_name()?.text ?: return
189+
190+
// Extract attributes and modifiers
191+
val annotations = buildAnnotationsFromAttributes(ctx.attributes())
192+
val accessModifier = buildAccessLevelModifier(ctx.access_level_modifier())
193+
val modifiers = mutableListOf<String>()
194+
195+
if (accessModifier.isNotEmpty()) {
196+
modifiers.add(accessModifier)
197+
}
198+
199+
// Check for distributed modifier
200+
if (ctx.DISTRIBUTED() != null) {
201+
modifiers.add("distributed")
202+
}
203+
204+
val modifierAnnotations = modifiers.map { CodeAnnotation(Name = it) }
205+
206+
val actorStruct = CodeDataStruct(
207+
NodeName = name,
208+
Type = DataStructType.CLASS, // Actor is similar to class
209+
FilePath = filePath,
210+
Position = ctx.toPosition(),
211+
Annotations = annotations + modifierAnnotations
212+
)
213+
214+
// Parse inheritance
215+
ctx.type_inheritance_clause()?.let { inheritanceCtx ->
216+
actorStruct.Implements = buildInheritanceList(inheritanceCtx)
217+
}
218+
219+
stack.addLast(actorStruct)
220+
}
221+
222+
override fun exitActor_declaration(ctx: Swift5Parser.Actor_declarationContext) {
223+
finishDataStruct()
224+
}
225+
185226
// ==================== Enum Declaration ====================
186227

187228
override fun enterEnum_declaration(ctx: Swift5Parser.Enum_declarationContext) {
@@ -332,7 +373,7 @@ class SwiftFullIdentListener(private val filePath: String) : Swift5ParserBaseLis
332373
// Extract attributes and modifiers from function_head
333374
val functionHead = ctx.function_head()
334375
val annotations = buildAnnotationsFromAttributes(functionHead?.attributes())
335-
val modifiers = buildModifiersFromDeclarationModifiers(functionHead?.declaration_modifiers())
376+
val modifiers = buildModifiersFromDeclarationModifiers(functionHead?.declaration_modifiers()).toMutableList()
336377

337378
val function = CodeFunction(
338379
Name = name,
@@ -348,13 +389,23 @@ class SwiftFullIdentListener(private val filePath: String) : Swift5ParserBaseLis
348389
function.Parameters += buildParameter(paramCtx)
349390
}
350391

351-
// Check for throws/rethrows
352-
ctx.function_signature()?.let { sig ->
353-
if (sig.THROWS() != null) {
354-
function.Modifiers = function.Modifiers + "throws"
355-
}
356-
if (sig.RETHROWS() != null) {
392+
// Check for async (Swift 5.5+)
393+
ctx.function_signature()?.async_clause()?.let {
394+
function.Modifiers = function.Modifiers + "async"
395+
}
396+
397+
// Check for throws/rethrows with typed throws support (Swift 6)
398+
ctx.function_signature()?.throw_clause()?.let { throwClause ->
399+
if (throwClause.RETHROWS() != null) {
357400
function.Modifiers = function.Modifiers + "rethrows"
401+
} else if (throwClause.THROWS() != null) {
402+
// Check for typed throws: throws(ErrorType)
403+
val errorType = throwClause.type()?.text
404+
if (errorType != null) {
405+
function.Modifiers = function.Modifiers + "throws($errorType)"
406+
} else {
407+
function.Modifiers = function.Modifiers + "throws"
408+
}
358409
}
359410
}
360411

@@ -621,6 +672,17 @@ class SwiftFullIdentListener(private val filePath: String) : Swift5ParserBaseLis
621672
// Build annotations from parameter attributes
622673
val annotations = ctx.attributes()?.attribute()?.map { buildAnnotation(it) } ?: listOf()
623674

675+
// Check for ownership modifier (Swift 6: consuming, borrowing, inout)
676+
val modifiers = mutableListOf<String>()
677+
ctx.ownership_modifier()?.let { ownershipCtx ->
678+
when {
679+
ownershipCtx.CONSUMING() != null -> modifiers.add("consuming")
680+
ownershipCtx.BORROWING() != null -> modifiers.add("borrowing")
681+
ownershipCtx.INOUT() != null -> modifiers.add("inout")
682+
else -> { /* no modifier */ }
683+
}
684+
}
685+
624686
return CodeProperty(
625687
TypeValue = if (externalName.isNotEmpty() && externalName != localName) {
626688
"$externalName $localName"
@@ -630,12 +692,22 @@ class SwiftFullIdentListener(private val filePath: String) : Swift5ParserBaseLis
630692
TypeType = typeAnnotation,
631693
DefaultValue = defaultValue,
632694
Annotations = annotations,
695+
Modifiers = modifiers,
633696
TypeRef = if (typeAnnotation.isNotEmpty()) SwiftTypeRefBuilder.build(typeAnnotation) else null
634697
)
635698
}
636699

637700
private fun buildInheritanceList(ctx: Swift5Parser.Type_inheritance_clauseContext): List<String> {
638-
return ctx.type_inheritance_list()?.type_identifier()?.mapNotNull { it.text } ?: listOf()
701+
// Swift 6: support ~Copyable syntax in inheritance
702+
return ctx.type_inheritance_list()?.type_inheritance_item()?.mapNotNull { item ->
703+
val typeText = item.type_identifier()?.text ?: return@mapNotNull null
704+
// Check if it's a noncopyable type (~Copyable)
705+
if (item.TILDE() != null) {
706+
"~$typeText"
707+
} else {
708+
typeText
709+
}
710+
} ?: listOf()
639711
}
640712

641713
private fun addFieldToCurrentScope(field: CodeField) {

0 commit comments

Comments
 (0)