-
Notifications
You must be signed in to change notification settings - Fork 0
/
RPNLang.m
218 lines (193 loc) · 6.77 KB
/
RPNLang.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#import "RPNLang.h"
#import <objc/runtime.h>
typedef enum {
INT,
VAR,
ADD,
SUB,
MUL,
DIV,
REM,
NEG,
} Tag;
@interface Command : NSObject
@property(readonly) int value;
@property(readonly) Tag tag;
+ (instancetype)withTag:(Tag)tag;
+ (instancetype)withTag:(Tag)tag value:(int)value;
+ (nullable instancetype)withString:(NSString *)string;
- (int)arity;
- (int)results;
- (NSString *)description;
@end
@implementation Command
+ (instancetype)withTag:(Tag)tag {
return [self withTag:tag value:0];
}
+ (instancetype)withTag:(Tag)tag value:(int)value {
Command *instance = [Command new];
instance->_tag = tag;
instance->_value = value;
return instance;
}
+ (nullable instancetype)withString:(NSString *)string {
int value = 0;
if (string.length == 1) {
// operators and single digits
switch ([string characterAtIndex:0]) {
case '+': return [self withTag:ADD];
case '-': return [self withTag:SUB];
case '*': return [self withTag:MUL];
case '/': return [self withTag:DIV];
case '%': return [self withTag:REM];
case '~': return [self withTag:NEG];
default:
if (![[NSScanner scannerWithString:string] scanInt:&value]) {
return nil;
}
return [self withTag:INT value:value];
}
}
if ([string hasPrefix:@"$"]) {
// variables $#, e.g. $0
if (![[NSScanner scannerWithString:[string substringFromIndex:1]] scanInt:&value]) {
return nil;
}
// negative variable indices are illegal
if (value < 0) {
return nil;
}
return [self withTag:VAR value:value];
} else {
// numbers #, e.g. 0
if (![[NSScanner scannerWithString:string] scanInt:&value]) {
return nil;
}
return [self withTag:INT value:value];
}
}
- (int)arity {
switch (self.tag) {
case VAR:
case INT: return 0;
case NEG: return 1;
default: return 2;
}
}
- (int)results {
return 1;
}
- (NSString *)description {
switch (self.tag) {
case INT: return [NSString stringWithFormat:@"%d", self.value];
case VAR: return [NSString stringWithFormat:@"$%d", self.value];
case ADD: return @"+";
case SUB: return @"-";
case MUL: return @"*";
case DIV: return @"/";
case REM: return @"%";
case NEG: return @"~";
}
}
- (void)operateOnStack:(NSMutableArray<NSNumber *> *)stack {
[self operateOnStack:stack env:@[]];
}
- (void)operateOnStack:(NSMutableArray<NSNumber *> *)stack env:(NSArray<NSNumber *> *)env {
#define BINOP(A, B, X) \
{ \
NSNumber *A = [stack lastObject]; \
[stack removeLastObject]; \
NSNumber *B = [stack lastObject]; \
[stack removeLastObject]; \
[stack addObject:[NSNumber numberWithInt:(X)]]; \
break; \
}
switch (self.tag) {
case INT: [stack addObject:[NSNumber numberWithInteger:self.value]]; break;
case VAR: [stack addObject:[env objectAtIndex:self.value]]; break;
case ADD: BINOP(a, b, a.intValue + b.intValue);
case SUB: BINOP(a, b, a.intValue - b.intValue);
case MUL: BINOP(a, b, a.intValue * b.intValue);
case DIV: BINOP(a, b, a.intValue / b.intValue);
case REM: BINOP(a, b, a.intValue % b.intValue);
case NEG: {
NSNumber *val = [stack lastObject];
[stack removeLastObject];
[stack addObject:[NSNumber numberWithInt:-val.intValue]];
break;
}
}
#undef BINOP
}
@end
@implementation RPNLang
/*
-(void)forwardInvocation:(NSInvocation *)invocation {
const char *name = sel_getName(invocation.selector);
NSString *sName = [NSString stringWithCString:name
encoding:NSUTF8StringEncoding]; NSArray<NSString*> *parts = [sName
componentsSeparatedByCharactersInSet:[NSCharacterSet
whitespaceAndNewlineCharacterSet]]; parts = [parts
filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != ''"]];
sName = [parts componentsJoinedByString:@" "];
NSLog(@"Forwarding invocation %s to %@\n", name, sName);
invocation.selector = sel_registerName([sName
cStringUsingEncoding:NSUTF8StringEncoding]); [invocation invoke];
}
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
const char *name = sel_getName(sel);
NSString *sName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSArray<NSString *> *parts = [sName
componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
parts = [parts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != ''"]];
// sName = [parts componentsJoinedByString:@" "];
NSLog(@"Compiling selector %s\n", name);
// Let's parse the argument list!
int stackSize = 0;
int numArguments = 0;
NSMutableArray<Command *> *code = [NSMutableArray new];
for (NSString *part in parts) {
Command *command = [Command withString:part];
if (command == nil) {
return NO;
}
stackSize -= [command arity];
if (stackSize < 0) {
return NO;
}
stackSize += [command results];
[code addObject:command];
if (command.tag == VAR) {
numArguments = MAX(numArguments, command.value + 1);
}
}
if (stackSize != 1) {
return NO;
}
if (numArguments != 0) {
IMP impl = imp_implementationWithBlock(^int(RPNLang *self, NSArray<NSNumber *> *params) {
NSMutableArray<NSNumber *> *stack = [NSMutableArray new];
for (Command *inst in code) {
[inst operateOnStack:stack env:params];
}
return [stack firstObject].intValue;
});
// FINALLY!
// @TODO: use the normalized method name, redirect from there
// [sName cStringUsingEncoding:NSUTF8StringEncoding]
class_addMethod(self, sel_registerName(name), impl, "i@:@");
return YES;
} else {
IMP impl = imp_implementationWithBlock(^int(RPNLang *self) {
NSMutableArray<NSNumber *> *stack = [NSMutableArray new];
for (Command *inst in code) {
[inst operateOnStack:stack];
}
return [stack firstObject].intValue;
});
class_addMethod(self, sel_registerName(name), impl, "i@:");
return YES;
}
}
@end