forked from migueldeicaza/SwiftGodot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add macros from Indexing-Your-Heart/game
Port over the macros meant to go upstream. As the sole author of the macro library, I give permission to have these licensed under the same license as the SwiftGodot project.
- Loading branch information
1 parent
844cc7c
commit 4a80a61
Showing
13 changed files
with
801 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
Sources/SwiftGodotMacroLibrary/InitSwiftExtensionMacro.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// | ||
// InitSwiftExtensionMacro.swift | ||
// SwiftGodot | ||
// | ||
// Created by Marquis Kurt on 5/27/23. | ||
// | ||
|
||
import Foundation | ||
import SwiftCompilerPlugin | ||
import SwiftDiagnostics | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct InitSwiftExtensionMacro: DeclarationMacro { | ||
public static func expansion(of node: some FreestandingMacroExpansionSyntax, | ||
in context: some MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] { | ||
|
||
guard let cDecl = node.argumentList.first?.expression else { | ||
fatalError("compiler bug: the macro does not have any arguments") | ||
} | ||
guard let types = node.argumentList.last?.expression else { | ||
fatalError("compiler bug: the macro does not have any arguments") | ||
} | ||
|
||
let initModule: DeclSyntax = """ | ||
@_cdecl(\(raw: cDecl.description)) public func enterExtension(interface: OpaquePointer?, library: OpaquePointer?, extension: OpaquePointer?) -> UInt8 { | ||
guard let library, let interface, let `extension` else { | ||
print("Error: Not all parameters were initialized.") | ||
return 0 | ||
} | ||
let deinitHook: (GDExtension.InitializationLevel) -> Void = { _ in } | ||
initializeSwiftModule(interface, library, `extension`, initHook: setupExtension, deInitHook: deinitHook) | ||
return 1 | ||
} | ||
""" | ||
|
||
let setupModule: DeclSyntax = """ | ||
func setupExtension(level: GDExtension.InitializationLevel) { | ||
let types = \(types) | ||
switch level { | ||
case .scene: | ||
types.forEach(register) | ||
default: | ||
break | ||
} | ||
} | ||
""" | ||
return [initModule, setupModule] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
Sources/SwiftGodotMacroLibrary/NativeHandleDiscardingMacro.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// NativeHandleDiscardingMacro.swift | ||
// SwiftGodot | ||
// | ||
// Created by Marquis Kurt on 5/27/23. | ||
// | ||
|
||
import Foundation | ||
import SwiftCompilerPlugin | ||
import SwiftDiagnostics | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct NativeHandleDiscardingMacro: MemberMacro { | ||
enum ProviderDiagnostic: String, DiagnosticMessage { | ||
case notAClass | ||
case missingNode | ||
var severity: DiagnosticSeverity { | ||
switch self { | ||
case .notAClass: return .error | ||
case .missingNode: return .error | ||
} | ||
} | ||
|
||
var message: String { | ||
switch self { | ||
case .notAClass: | ||
return "@NativeHandleDiscarding can only be applied to a 'class'" | ||
case .missingNode: | ||
return "@NativeHandleDiscarding requires inheritance to 'Node' or a subclass" | ||
} | ||
} | ||
|
||
var diagnosticID: MessageID { | ||
MessageID(domain: "SwiftGodotMacros", id: rawValue) | ||
} | ||
} | ||
|
||
public static func expansion(of node: AttributeSyntax, | ||
providingMembersOf declaration: some DeclGroupSyntax, | ||
in context: some MacroExpansionContext) throws -> [DeclSyntax] { | ||
guard declaration.as(ClassDeclSyntax.self) != nil else { | ||
let classError = Diagnostic(node: declaration.root, message: ProviderDiagnostic.notAClass) | ||
context.diagnose(classError) | ||
return [] | ||
} | ||
|
||
let initSyntax = try InitializerDeclSyntax("required init(nativeHandle _: UnsafeRawPointer)") { | ||
StmtSyntax("fatalError(\"init(nativeHandle:) has not been implemented\")") | ||
} | ||
|
||
return [DeclSyntax(initSyntax)] | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
Sources/SwiftGodotMacroLibrary/PickerNameProviderMacro.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// | ||
// PickerNameProviderMacro.swift | ||
// SwiftGodot | ||
// | ||
// Created by Marquis Kurt on 6/9/23. | ||
// | ||
|
||
import Foundation | ||
import SwiftCompilerPlugin | ||
import SwiftDiagnostics | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct PickerNameProviderMacro: ExtensionMacro { | ||
enum ProviderDiagnostic: String, DiagnosticMessage { | ||
case notAnEnum | ||
case missingInt | ||
var severity: DiagnosticSeverity { | ||
switch self { | ||
case .notAnEnum: return .error | ||
case .missingInt: return .error | ||
} | ||
} | ||
|
||
var message: String { | ||
switch self { | ||
case .notAnEnum: | ||
return "@PickerNameProvider can only be applied to an 'enum'" | ||
case .missingInt: | ||
return "@PickerNameProvider requires an Int backing" | ||
} | ||
} | ||
|
||
var diagnosticID: MessageID { | ||
MessageID(domain: "SwiftGodotMacros", id: rawValue) | ||
} | ||
} | ||
|
||
public static func expansion(of node: AttributeSyntax, | ||
attachedTo declaration: some DeclGroupSyntax, | ||
providingExtensionsOf type: some TypeSyntaxProtocol, | ||
conformingTo protocols: [TypeSyntax], | ||
in context: some MacroExpansionContext) throws -> [ExtensionDeclSyntax] { | ||
|
||
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { | ||
let enumError = Diagnostic(node: declaration.root, message: ProviderDiagnostic.notAnEnum) | ||
context.diagnose(enumError) | ||
return [] | ||
} | ||
|
||
guard let inheritors = enumDecl.inheritanceClause?.inheritedTypes else { | ||
let missingInt = Diagnostic(node: declaration.root, message: ProviderDiagnostic.missingInt) | ||
context.diagnose(missingInt) | ||
return [] | ||
} | ||
|
||
let types = inheritors.map { $0.type.as(IdentifierTypeSyntax.self) } | ||
let names = types.map { $0?.name.text } | ||
|
||
guard names.contains("Int") else { | ||
let missingInt = Diagnostic(node: declaration.root, message: ProviderDiagnostic.missingInt) | ||
context.diagnose(missingInt) | ||
return [] | ||
} | ||
|
||
let members = enumDecl.memberBlock.members | ||
let cases = members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self) } | ||
let elements = cases.flatMap { $0.elements } | ||
|
||
let nameDeclBase = try VariableDeclSyntax("var name: String") { | ||
try SwitchExprSyntax("switch self") { | ||
for element in elements { | ||
SwitchCaseSyntax( | ||
""" | ||
case .\(element.name): | ||
return \(literal: element.name.text.capitalized) | ||
""" | ||
) | ||
} | ||
} | ||
} | ||
|
||
var nameDecl = nameDeclBase | ||
for modifier in enumDecl.modifiers { | ||
nameDecl.modifiers.append(modifier) | ||
} | ||
|
||
let caseIterableExtensionDecl: DeclSyntax = | ||
""" | ||
extension \(type.trimmed): CaseIterable {} | ||
""" | ||
|
||
guard let caseIterableExtension = caseIterableExtensionDecl.as(ExtensionDeclSyntax.self) else { | ||
return [] | ||
} | ||
|
||
let nameableExtension = try ExtensionDeclSyntax("extension \(type.trimmed): Nameable") { | ||
DeclSyntax(nameDecl) | ||
} | ||
|
||
return [caseIterableExtension, nameableExtension] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// | ||
// SceneTreeMacro.swift | ||
// SwiftGodot | ||
// | ||
// Created by Marquis Kurt on 6/22/23. | ||
// | ||
|
||
import Foundation | ||
import SwiftCompilerPlugin | ||
import SwiftDiagnostics | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct SceneTreeMacro: AccessorMacro { | ||
enum ProviderDiagnostic: String, DiagnosticMessage { | ||
case missingPathArgument | ||
case invalidDeclaration | ||
case missingTypeAnnotation | ||
case nonOptionalTypeAnnotation | ||
|
||
var severity: DiagnosticSeverity { .error } | ||
|
||
var message: String { | ||
switch self { | ||
case .missingPathArgument: | ||
"Missing argument 'path'" | ||
case .invalidDeclaration: | ||
"SceneTree can only be applied to stored properties" | ||
case .missingTypeAnnotation: | ||
"SceneTree requires an explicit type declaration" | ||
case .nonOptionalTypeAnnotation: | ||
"Stored properties with SceneTree must be marked as Optional" | ||
} | ||
} | ||
|
||
var diagnosticID: MessageID { | ||
MessageID(domain: "SwiftGodotMacros", id: rawValue) | ||
} | ||
} | ||
|
||
struct MarkOptionalMessage: FixItMessage { | ||
var message: String { | ||
"Mark as Optional" | ||
} | ||
|
||
var fixItID: SwiftDiagnostics.MessageID { | ||
ProviderDiagnostic.nonOptionalTypeAnnotation.diagnosticID | ||
} | ||
|
||
} | ||
|
||
public static func expansion(of node: AttributeSyntax, | ||
providingAccessorsOf declaration: some DeclSyntaxProtocol, | ||
in context: some MacroExpansionContext) throws -> [AccessorDeclSyntax] { | ||
guard let argument = node.argument?.as(TupleExprElementListSyntax.self)?.first?.expression else { | ||
let missingArgErr = Diagnostic(node: node.root, message: ProviderDiagnostic.missingPathArgument) | ||
context.diagnose(missingArgErr) | ||
return [ | ||
""" | ||
get { getNodeOrNull(path: NodePath(stringLiteral: "")) as? Node } | ||
""" | ||
] | ||
} | ||
guard let varDecl = declaration.as(VariableDeclSyntax.self) else { | ||
let invalidUsageErr = Diagnostic(node: node.root, message: ProviderDiagnostic.invalidDeclaration) | ||
context.diagnose(invalidUsageErr) | ||
return [] | ||
} | ||
guard let nodeType = varDecl.bindings.first?.typeAnnotation?.type else { | ||
let missingAnnotationErr = Diagnostic(node: node.root, message: ProviderDiagnostic.missingTypeAnnotation) | ||
context.diagnose(missingAnnotationErr) | ||
return [] | ||
} | ||
|
||
guard let optional = nodeType.as(OptionalTypeSyntax.self) else { | ||
let newOptional = OptionalTypeSyntax(wrappedType: nodeType) | ||
let addOptionalFix = FixIt(message: MarkOptionalMessage(), | ||
changes: [.replace(oldNode: Syntax(nodeType), newNode: Syntax(newOptional))]) | ||
let nonOptional = Diagnostic(node: nodeType.root, | ||
message: ProviderDiagnostic.nonOptionalTypeAnnotation, | ||
fixIts: [addOptionalFix]) | ||
context.diagnose(nonOptional) | ||
return [ | ||
""" | ||
get { getNodeOrNull(path: NodePath(stringLiteral: \(argument))) as? \(nodeType) } | ||
""" | ||
] | ||
} | ||
|
||
return [ | ||
""" | ||
get { getNodeOrNull(path: NodePath(stringLiteral: \(argument))) as? \(optional.wrappedType) } | ||
""" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// | ||
// TextureLiteralMacro.swift | ||
// SwiftGodot | ||
// | ||
// Created by Marquis Kurt on 6/11/23. | ||
// | ||
|
||
import Foundation | ||
import SwiftCompilerPlugin | ||
import SwiftDiagnostics | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import SwiftSyntaxMacros | ||
|
||
public struct Texture2DLiteralMacro: ExpressionMacro { | ||
enum ProviderDiagnostic: String, DiagnosticMessage { | ||
case missingArguments | ||
var severity: DiagnosticSeverity { | ||
switch self { | ||
case .missingArguments: return .error | ||
} | ||
} | ||
|
||
var message: String { | ||
switch self { | ||
case .missingArguments: | ||
return "Argument 'path' is missing." | ||
} | ||
} | ||
|
||
var diagnosticID: MessageID { | ||
MessageID(domain: "SwiftGodotMacros", id: rawValue) | ||
} | ||
} | ||
|
||
public static func expansion(of node: some FreestandingMacroExpansionSyntax, | ||
in context: some MacroExpansionContext) throws -> ExprSyntax { | ||
guard let argument = node.argumentList.first?.expression else { | ||
let argumentError = Diagnostic(node: node.root, message: ProviderDiagnostic.missingArguments) | ||
context.diagnose(argumentError) | ||
return "\"\"" | ||
} | ||
let location: AbstractSourceLocation = context.location(of: node)! | ||
return """ | ||
{ | ||
guard let texture: Texture2D = GD.load(path: \(argument)) else { | ||
preconditionFailure( | ||
"Texture could not be loaded.", | ||
file: \(raw: location.file), | ||
line: \(raw: location.line)) | ||
} | ||
return texture | ||
}() | ||
""" | ||
} | ||
} |
Oops, something went wrong.