Skip to content

Instantly share code, notes, and snippets.

@Frityet
Created January 21, 2025 10:21
Show Gist options
  • Save Frityet/884bfcb3b9be5e96a9f43616530527a2 to your computer and use it in GitHub Desktop.
Save Frityet/884bfcb3b9be5e96a9f43616530527a2 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface HTMLNode : NSObject
@property (nonatomic, copy) NSString *tag;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *attributes;
@property (nonatomic, strong) NSMutableArray *children;
+ (instancetype)element: (NSString *)tag;
- (NSString *)description;
@end
@implementation HTMLNode
+ (instancetype)element:(NSString *)tag {
HTMLNode *node = [[HTMLNode alloc] init];
node.tag = tag;
node.attributes = [NSMutableDictionary dictionary];
node.children = [NSMutableArray array];
return node;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSString *selString = NSStringFromSelector(sel);
NSUInteger colonCount = 0;
for (NSUInteger i = 0; i < selString.length; i++) {
if ([selString characterAtIndex:i] == ':') colonCount++;
}
NSMutableString *types = [NSMutableString stringWithString:@"@@:"];
for (NSUInteger i = 0; i < colonCount; i++) {
[types appendString:@"@"];
}
return [NSMethodSignature signatureWithObjCTypes:types.UTF8String];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
NSString *selString = NSStringFromSelector(invocation.selector);
if ([selString hasSuffix:@":"]) {
selString = [selString substringToIndex:selString.length - 1];
}
NSArray *parts = [selString componentsSeparatedByString:@":"];
for (NSUInteger i = 0; i < parts.count; i++) {
__unsafe_unretained id value;
[invocation getArgument:&value atIndex:(i + 2)];
NSString *key = parts[i];
if ([key isEqualToString:@"children"]) {
if ([value isKindOfClass:[NSArray class]]) {
[self.children addObjectsFromArray:value];
} else {
[self.children addObject:value];
}
} else {
self.attributes[key] = value;
}
}
HTMLNode *result = self;
[invocation setReturnValue:&result];
}
- (NSString *)description {
NSMutableString *html = [NSMutableString stringWithFormat:@"<%@", self.tag];
for (NSString *key in self.attributes) {
[html appendFormat:@" %@=\"%@\"", key, self.attributes[key]];
}
[html appendString:@">"];
for (id child in self.children) {
[html appendString:[child description]];
}
[html appendFormat:@"</%@>", self.tag];
return html;
}
@end
#define $node(...) [HTMLNode element: @#__VA_ARGS__]
#define html $node(html)
#define head $node(head)
#define body $node(body)
#define h1 $node(h1)
#define p $node(p)
int main()
{
#pragma clang diagnostic ignored "-Wobjc-method-access"
HTMLNode *doc = [html children: @[
[body children: @[
[h1 class: @"header" children: @"Hello!"],
[p class: @"para" id: @"main" children: @"this is a paragraph"]
]]
]];
NSLog(@"%@", doc);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment