Last active
December 21, 2015 04:09
-
-
Save jparishy/6247445 to your computer and use it in GitHub Desktop.
Playing with Lisp interpretation in a C/Objective-C hybrid. Mostly implemented in C with use of the Objective-C runtime.
This file contains hidden or 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
// | |
// main.m | |
// MLisp | |
// | |
// Created by Julius Parishy on 8/13/13. | |
// Copyright (c) 2013 Lonely Ether. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface Atom : NSObject | |
@property (nonatomic, copy) NSString *name; | |
- (id)initWithName:(NSString *)name; | |
@end | |
@implementation Atom | |
- (id)initWithName:(NSString *)name | |
{ | |
if((self = [super init])) | |
_name = name; | |
return self; | |
} | |
@end | |
@interface Context : NSObject | |
@property (nonatomic, weak) Context *parent; | |
@property (nonatomic, strong) NSMutableDictionary *vars; | |
- (Context *)find:(id)obj; | |
@end | |
@implementation Context | |
- (id)init | |
{ | |
if((self = [super init])) | |
{ | |
self.vars = [NSMutableDictionary dictionary]; | |
} | |
return self; | |
} | |
- (Context *)find:(id)obj | |
{ | |
id inSelf = [self objectForAtom:obj]; | |
if(inSelf) | |
return self; | |
id inParent = [self.parent find:obj]; | |
if(inParent) | |
return inParent; | |
return nil; | |
} | |
- (id)objectForAtom:(Atom *)atom | |
{ | |
return self.vars[atom.name]; | |
} | |
- (void)setObject:(id)object forAtom:(Atom *)atom | |
{ | |
self.vars[atom.name] = object; | |
} | |
@end | |
Atom *a(NSString *name) | |
{ | |
Atom *atom = [[Atom alloc] initWithName:name]; | |
return atom; | |
} | |
BOOL is_list(id obj) | |
{ | |
return [obj isKindOfClass:[NSArray class]]; | |
} | |
BOOL is_num(id obj) | |
{ | |
return [obj isKindOfClass:[NSNumber class]]; | |
} | |
BOOL is_str(id obj) | |
{ | |
return [obj isKindOfClass:[NSString class]]; | |
} | |
BOOL is_atom(id obj) | |
{ | |
return [obj isKindOfClass:[Atom class]]; | |
} | |
id eval(id obj, Context *context) | |
{ | |
if(is_atom(obj)) | |
{ | |
id ret = [[context find:obj] objectForAtom:obj]; | |
if(!ret) | |
{ | |
if([[obj name] hasPrefix:@"@"]) | |
{ | |
NSString *func = [[obj name] substringFromIndex:1]; | |
return [^id(NSArray *args, Context *context) { | |
id target = eval(args[0], context); | |
NSArray *params = [args subarrayWithRange:NSMakeRange(1, args.count - 1)]; | |
SEL selector = NSSelectorFromString(func); | |
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector]; | |
if(!methodSignature) | |
{ | |
NSLog(@"Unrecognized selector %@ on object of class %@", func, NSStringFromClass([target class])); | |
exit(1); | |
} | |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; | |
invocation.selector = selector; | |
invocation.target = target; | |
[params enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | |
NSInteger index = idx + 2; | |
id arg = eval(obj, context); | |
void *ptr = nil; | |
const char *type = [methodSignature getArgumentTypeAtIndex:index]; | |
if(strcmp(type, @encode(int)) == 0) | |
{ | |
int val = [obj intValue]; | |
ptr = (void *)&val; | |
} | |
else if(strcmp(type, @encode(NSInteger)) == 0) | |
{ | |
NSInteger val = [obj integerValue]; | |
ptr = (void *)&val; | |
} | |
else if(strcmp(type, @encode(NSUInteger)) == 0) | |
{ | |
NSUInteger val = [obj unsignedIntegerValue]; | |
ptr = (void *)&val; | |
} | |
else | |
{ | |
ptr = (void *)&arg; | |
} | |
[invocation setArgument:ptr atIndex:index]; | |
}]; | |
[invocation invoke]; | |
id returnValue = nil; | |
[invocation getReturnValue:&returnValue]; | |
return returnValue; | |
} copy]; | |
} | |
else | |
{ | |
NSLog(@"Unbound variable: %@", [obj name]); | |
exit(1); | |
} | |
} | |
else | |
{ | |
return ret; | |
} | |
} | |
else if(!is_list(obj)) | |
{ | |
return obj; | |
} | |
else if(is_str(obj[0]) && [obj[0] isEqualToString:@"lambda"]) | |
{ | |
return [^(NSArray *args, Context *newContext) { | |
Context *ac = [[Context alloc] init]; | |
ac.parent = newContext; | |
NSInteger i = 0; | |
for(id an in obj[1]) | |
{ | |
[ac setObject:args[i++] forAtom:an]; | |
} | |
return eval(obj[2], ac); | |
} copy]; | |
} | |
else | |
{ | |
NSMutableArray *exprs = [NSMutableArray array]; | |
for(id expr in obj) | |
{ | |
id val = eval(expr, context); | |
if(!val) | |
{ | |
if(is_atom(expr)) | |
{ | |
NSLog(@"Unbound variable: %@", [expr name]); | |
exit(1); | |
} | |
} | |
[exprs addObject:val]; | |
} | |
id(^fun)(id, Context *) = exprs[0]; | |
return fun([exprs subarrayWithRange:NSMakeRange(1, exprs.count - 1)], context); | |
} | |
} | |
int main(int argc, const char * argv[]) | |
{ | |
@autoreleasepool { | |
Context *c = [[Context alloc] init]; | |
[c setObject:[^id(NSArray *args, Context *context) { | |
NSLog(@"%@", args[0]); | |
return nil; | |
} copy] forAtom:a(@"print-obj")]; | |
[c setObject:[^id(NSArray *args, Context *context) { | |
return args[0]; | |
} copy] forAtom:a(@"ident")]; | |
// (print-obj (@substringToIndex: (ident "Julius Parishy") 6) | |
// "Julius" | |
NSArray *program = @[ a(@"print-obj"), @[ a(@"@substringToIndex:"), @[ a(@"ident"), @"Julius Parishy" ], @6 ] ]; | |
eval(program, c); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment