Skip to content

Instantly share code, notes, and snippets.

@jparishy
Last active December 21, 2015 04:09
Show Gist options
  • Save jparishy/6247445 to your computer and use it in GitHub Desktop.
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.
//
// 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