#define MAX_NUMBER_OF_APPLES (3)
#define TRUE (1)
#define FALSE (0)
#define SUCCESSFUL_EXIT_CODE (0)
#define UNSUCCESSFUL_EXIT_CODE (1)
...
int check_apples_count(int count) {
if (count < MAX_NUMBER_OF_APPLES) {
return TRUE;
}
printf("Maximum apples count is: %d", MAX_NUMBER_OF_APPLES);
return FALSE;
}
...
if check_apples_count(2) == TRUE {
exit(SUCCESSFUL_EXIT_CODE);
} else {
exit(UNSUCCESSFUL_EXIT_CODE);
}
int check_apples_count(int count) {
if (count < (3)) {
return (1);
}
printf("Maximum apples count is: %d", (3));
return (0);
}
...
if check_apples_count(2) == (1) {
exit((0));
} else {
exit((1));
}
#define square(x) x * x
...
int value = square(2 + 2);
#define square(x) x * x
...
int value = 2 + 2 * 2 + 2;
#define square(x) ((x) * (x))
...
int value = square(2 + 2); // Расширится в ((2 + 2) * (2 + 2))
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(module: "PumpkinMacrosStorage", type: "AutoInitMacro")
public struct AutoInitMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Тут генерируется код для развертывания
...
}
}
...
@main
struct PumpkinMacrosPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
AutoInitMacro.self
]
}
enum Constants {
static let validUrl = URL(string: "https://cleverpumpkin.ru")!
static let invalidUrl = URL(string: "https://url with mistake")!
}
@freestanding(expression)
public macro URL(_ value: String) -> URL = #externalMacro(module: "PumpkinMacrosStorage", type: "URLMacro")
...
public struct URLMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard
let firstExpression = node.argumentList.first?.expression,
let stringLiteralExprSyntax = firstExpression.as(StringLiteralExprSyntax.self),
case .stringSegment(let literalSegment) = stringLiteralExprSyntax.segments.first
else {
throw URLMacroError.invalidMactoArgument
}
guard let _ = URL(string: literalSegment.content.text) else {
throw URLMacroError.invalidUrl
}
return "URL(string: \(firstExpression))!"
}
}
@freestanding(declaration, names: arbitrary)
public macro vector(_ value: Int) = #externalMacro(module: "PumpkinMacrosStorage", type: "VectorMacro")
...
public struct VectorMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard
let firstExpression = node.argumentList.first?.expression,
let integerLiteral = firstExpression.as(IntegerLiteralExprSyntax.self),
let integerValue = Int(integerLiteral.digits.text),
integerValue > 0
else {
throw VectorMacroError.invalidMactoArgument
}
let header: PartialSyntaxNodeString = "public struct Vector\(integerLiteral): Equatable"
let structDeclSyntax = try StructDeclSyntax(header) {
for index in 0..
struct User: Codable {
let name: String
let registrationDate: Date
let friendsIds: [String]
}
updates = [
"name": "Nikita"
"registrationDate": FieldValue.serverTimestamp(),
"friendsIds": FieldValue.arrayUnion(["1", "2", "3"])
]
struct User: Codable {
var name: String?
var registrationDate: FieldValue?
var friendsIds: FieldValue?
}
@attached(member, names: named(Editable))
public macro FirebaseEditable() = #externalMacro(module: "PumpkinMacrosStorage", type: "FirebaseEditableMacro")
...
public struct FirebaseEditableMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard declaration.is(StructDeclSyntax.self) else {
// Выводим ошибку в XCode средствами Diagnostic
let errorDiagnose = Diagnostic(
node: Syntax(node),
message: MistakeDiagnostic.notAStruct
)
context.diagnose(errorDiagnose)
return []
}
// Получаем список пропертей
let variables = declaration.memberBlock.members.compactMap {
$0.decl.as(VariableDeclSyntax.self)
}
// Создаем определение структуры
let editableStructure = try StructDeclSyntax("struct Editable: Codable") {
// Заполняем ее пропертями
for variable in variables {
variable.byReplacingTypeToFieldValue
}
}
return [
DeclSyntax(editableStructure)
]
}
}
// MARK: - FirebaseEditableMacro
private extension FirebaseEditableMacro {
enum MistakeDiagnostic: String {
case notAStruct
}
}
// MARK: - Diagnostic.DiagnosticMessage
extension FirebaseEditableMacro.MistakeDiagnostic: DiagnosticMessage {
var message: String {
switch self {
case .notAStruct:
return "'@FirebaseEditable' can only be applied to a 'struct'"
}
}
var diagnosticID: MessageID {
.init(domain: "PumpkinMacros", id: rawValue)
}
var severity: DiagnosticSeverity {
switch self {
case .notAStruct:
return .error
}
}
}
// MARK: - VariableDeclSyntax+Convenience
private extension VariableDeclSyntax {
var byReplacingTypeToFieldValue: Self {
var editable = self
let typesToReplace: Set = ["date"]
// Заменяем типы typesToReplace на FieldValue
let updatedBindings = editable.bindings.map { binding -> PatternBindingSyntax in
var binding = binding
guard var typeAnnotation = binding.typeAnnotation else {
return binding
}
if shouldReplace(type: typeAnnotation.type, typesToReplace: typesToReplace) {
typeAnnotation = .init(
type: SimpleTypeIdentifierSyntax(name: "FieldValue")
)
}
if typeAnnotation.type.is(OptionalTypeSyntax.self) == false {
typeAnnotation = .init(
type: OptionalTypeSyntax(
wrappedType: typeAnnotation.type,
questionMark: .postfixQuestionMarkToken()
)
)
}
binding.typeAnnotation = typeAnnotation
return binding
}
editable.bindings = .init(updatedBindings)
editable.bindingKeyword = "var"
return editable
}
// MARK: - Private methods
private func shouldReplace(type: TypeSyntax, typesToReplace: Set) -> Bool {
if let simpleTypeIdentifier = type.as(SimpleTypeIdentifierSyntax.self) {
return typesToReplace.contains(simpleTypeIdentifier.name.text.lowercased())
} else if type.is(ArrayTypeSyntax.self) {
return true
} else if let optionalType = type.as(OptionalTypeSyntax.self) {
return shouldReplace(type: optionalType.wrappedType, typesToReplace: typesToReplace)
} else {
return false
}
}
}