mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
Objective-C: steps 5-A, self-hosting, perf, README
This commit is contained in:
parent
57350ed73c
commit
7cae6e6f12
14
README.md
14
README.md
@ -6,7 +6,7 @@
|
||||
|
||||
Mal is a Clojure inspired Lisp interpreter.
|
||||
|
||||
Mal is implemented in 47 languages:
|
||||
Mal is implemented in 48 languages:
|
||||
|
||||
* GNU awk
|
||||
* Bash shell
|
||||
@ -39,6 +39,7 @@ Mal is implemented in 47 languages:
|
||||
* MATLAB
|
||||
* [miniMAL](https://github.com/kanaka/miniMAL)
|
||||
* Nim
|
||||
* Objective C
|
||||
* OCaml
|
||||
* Perl
|
||||
* PHP
|
||||
@ -441,6 +442,17 @@ nimble build
|
||||
./stepX_YYY
|
||||
```
|
||||
|
||||
### Objective C
|
||||
|
||||
The Objective C implementation of mal has been built and tested on
|
||||
Linux using clang/LLVM 3.6.
|
||||
|
||||
```
|
||||
cd objc
|
||||
make
|
||||
./stepX_YYY
|
||||
```
|
||||
|
||||
### OCaml 4.01.0
|
||||
|
||||
*The OCaml implementation was created by [Chris Houser (chouser)](https://github.com/chouser)*
|
||||
|
241
objc/core.m
241
objc/core.m
@ -1,7 +1,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
NSObject * wrap_tf(BOOL val) {
|
||||
@ -15,6 +18,36 @@ NSObject * wrap_tf(BOOL val) {
|
||||
@"=": ^(NSArray *args){
|
||||
return wrap_tf(equal_Q(args[0], args[1]));
|
||||
},
|
||||
@"throw": ^(NSArray *args){
|
||||
@throw args[0];
|
||||
},
|
||||
|
||||
@"nil?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[NSNull class]]);
|
||||
},
|
||||
@"true?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[MalTrue class]]);
|
||||
},
|
||||
@"false?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[MalFalse class]]);
|
||||
},
|
||||
@"string?": ^(NSArray *args){
|
||||
return wrap_tf(string_Q(args[0]));
|
||||
},
|
||||
@"symbol": ^(NSArray *args){
|
||||
return [MalSymbol stringWithString:args[0]];
|
||||
},
|
||||
@"symbol?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[MalSymbol class]]);
|
||||
},
|
||||
@"keyword": ^(NSArray *args){
|
||||
return [NSString stringWithFormat:@"\u029e%@", args[0]];
|
||||
},
|
||||
@"keyword?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[NSString class]] &&
|
||||
![args[0] isKindOfClass:[MalSymbol class]] &&
|
||||
!string_Q(args[0]));
|
||||
},
|
||||
|
||||
@"pr-str": ^(NSArray *args){
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
@ -40,6 +73,22 @@ NSObject * wrap_tf(BOOL val) {
|
||||
fflush(stdout);
|
||||
return [NSNull alloc];
|
||||
},
|
||||
@"read-string": ^(NSArray *args){
|
||||
return read_str(args[0]);
|
||||
},
|
||||
@"readline": ^(NSArray *args){
|
||||
char * rawline = _readline((char *)[(NSString *)args[0] UTF8String]);
|
||||
if (rawline) {
|
||||
return (NSObject *)[NSString stringWithUTF8String:rawline];
|
||||
} else {
|
||||
return (NSObject *)[NSNull alloc];
|
||||
}
|
||||
},
|
||||
@"slurp": ^(NSArray *args){
|
||||
return [NSString stringWithContentsOfFile:args[0]
|
||||
encoding: NSUTF8StringEncoding
|
||||
error: NULL];
|
||||
},
|
||||
|
||||
@"<": ^(NSArray *args){
|
||||
return wrap_tf([args[0] intValue] < [args[1] intValue]);
|
||||
@ -65,6 +114,10 @@ NSObject * wrap_tf(BOOL val) {
|
||||
@"/": ^(NSArray *args){
|
||||
return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
|
||||
},
|
||||
@"time-ms": ^(NSArray *args){
|
||||
long long ms = [[NSDate date] timeIntervalSince1970] * 1000;
|
||||
return [NSNumber numberWithUnsignedInteger:ms];
|
||||
},
|
||||
|
||||
@"list": ^(NSArray *args){
|
||||
return args;
|
||||
@ -72,7 +125,102 @@ NSObject * wrap_tf(BOOL val) {
|
||||
@"list?": ^(NSArray *args){
|
||||
return wrap_tf(list_Q(args[0]));
|
||||
},
|
||||
@"vector": ^(NSArray *args){
|
||||
return [MalVector fromArray:args];
|
||||
},
|
||||
@"vector?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[MalVector class]]);
|
||||
},
|
||||
@"hash-map": ^(NSArray *args){
|
||||
return hash_map(args);
|
||||
},
|
||||
@"map?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[NSDictionary class]]);
|
||||
},
|
||||
@"assoc": ^(NSArray *args){
|
||||
NSDictionary * dict = args[0];
|
||||
NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
|
||||
initWithDictionary:dict
|
||||
copyItems:NO];
|
||||
return assoc_BANG(new_dict, _rest(args));
|
||||
},
|
||||
@"dissoc": ^(NSArray *args){
|
||||
NSDictionary * dict = args[0];
|
||||
NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
|
||||
initWithDictionary:dict
|
||||
copyItems:NO];
|
||||
for (NSString * key in _rest(args)) {
|
||||
[new_dict removeObjectForKey:key];
|
||||
}
|
||||
return new_dict;
|
||||
},
|
||||
@"get": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return (NSObject *)[NSNull alloc];
|
||||
}
|
||||
NSObject * res = ((NSDictionary *)args[0])[args[1]];
|
||||
return res ? res : [NSNull alloc];
|
||||
},
|
||||
@"contains?": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return wrap_tf(false);
|
||||
}
|
||||
return wrap_tf(((NSDictionary *)args[0])[args[1]] != nil);
|
||||
},
|
||||
@"keys": ^(NSArray *args){
|
||||
return [(NSDictionary *)args[0] allKeys];
|
||||
},
|
||||
@"vals": ^(NSArray *args){
|
||||
return [(NSDictionary *)args[0] allValues];
|
||||
},
|
||||
|
||||
@"sequential?": ^(NSArray *args){
|
||||
return wrap_tf([args[0] isKindOfClass:[NSArray class]]);
|
||||
},
|
||||
@"cons": ^(NSArray *args){
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
[res addObject:args[0]];
|
||||
[res addObjectsFromArray:args[1]];
|
||||
return res;
|
||||
},
|
||||
@"concat": ^(NSArray *args){
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
for (NSArray * arr in args) {
|
||||
[res addObjectsFromArray:arr];
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@"nth": ^(NSArray *args){
|
||||
NSArray * lst = (NSArray *)args[0];
|
||||
int idx = [(NSNumber *)args[1] intValue];
|
||||
if (idx < [lst count]) {
|
||||
return lst[idx];
|
||||
} else {
|
||||
@throw @"nth: index out of range";
|
||||
}
|
||||
},
|
||||
@"first": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return (NSObject *)[NSNull alloc];
|
||||
}
|
||||
NSArray * lst = (NSArray *)args[0];
|
||||
if ([lst count] > 0) {
|
||||
return (NSObject *)lst[0];
|
||||
} else {
|
||||
return (NSObject *)[NSNull alloc];
|
||||
}
|
||||
},
|
||||
@"rest": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return @[];
|
||||
}
|
||||
NSArray * lst = (NSArray *)args[0];
|
||||
if ([lst count] > 1) {
|
||||
return _rest(lst);
|
||||
} else {
|
||||
return @[];
|
||||
}
|
||||
},
|
||||
@"empty?": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return wrap_tf(true);
|
||||
@ -87,7 +235,100 @@ NSObject * wrap_tf(BOOL val) {
|
||||
return [NSNumber numberWithInt:[args[0] count]];
|
||||
}
|
||||
},
|
||||
@"apply": ^(NSArray *args){
|
||||
NSObject * (^ f)(NSArray *) = args[0];
|
||||
NSMutableArray * fargs = [NSMutableArray array];
|
||||
if ([args count] > 1) {
|
||||
NSRange r = NSMakeRange(1, [args count]-2);
|
||||
[fargs addObjectsFromArray:[args subarrayWithRange:r]];
|
||||
}
|
||||
[fargs addObjectsFromArray:(NSArray *)[args lastObject]];
|
||||
return apply(f, fargs);
|
||||
},
|
||||
@"map": ^(NSArray *args){
|
||||
NSObject * (^ f)(NSArray *) = args[0];
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)args[1]) {
|
||||
[res addObject:apply(f, @[x])];
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@"conj": ^(NSArray *args){
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
if ([args[0] isKindOfClass:[MalVector class]]) {
|
||||
[res addObjectsFromArray:args[0]];
|
||||
[res addObjectsFromArray:_rest(args)];
|
||||
return (NSObject *)[MalVector arrayWithArray:res];
|
||||
} else {
|
||||
[res addObjectsFromArray:[[_rest(args) reverseObjectEnumerator]
|
||||
allObjects]];
|
||||
[res addObjectsFromArray:args[0]];
|
||||
return (NSObject *)res;
|
||||
}
|
||||
},
|
||||
@"seq": ^(NSArray *args){
|
||||
if (list_Q(args[0])) {
|
||||
if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
|
||||
return (NSObject *)args[0];
|
||||
} else if ([args[0] isKindOfClass:[MalVector class]]) {
|
||||
if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
|
||||
return (NSObject *)[NSArray arrayWithArray:args[0]];
|
||||
} else if (string_Q(args[0])) {
|
||||
NSString * str = args[0];
|
||||
if ([str length] == 0) { return (NSObject *)[NSNull alloc]; }
|
||||
NSMutableArray * res = [NSMutableArray array];
|
||||
for (int i=0; i < [str length]; i++) {
|
||||
char c = [str characterAtIndex:i];
|
||||
[res addObject:[NSString stringWithFormat:@"%c", c]];
|
||||
}
|
||||
return (NSObject *)res;
|
||||
} else if ([args[0] isKindOfClass:[NSNull class]]) {
|
||||
return (NSObject *)args[0];
|
||||
} else {
|
||||
@throw @"seq: called on non-sequence";
|
||||
}
|
||||
},
|
||||
|
||||
@"meta": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[MalFunc class]]) {
|
||||
return [(MalFunc *)args[0] meta];
|
||||
} else {
|
||||
return (NSObject *)[NSNull alloc];
|
||||
}
|
||||
},
|
||||
@"with-meta": ^(NSArray *args){
|
||||
if ([args[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * cmf = [(MalFunc *)args[0] copy];
|
||||
cmf.meta = args[1];
|
||||
return cmf;
|
||||
} else {
|
||||
@throw @"with-meta: object type not supported";
|
||||
}
|
||||
},
|
||||
@"atom": ^(NSArray *args){
|
||||
return [MalAtom fromObject:args[0]];
|
||||
},
|
||||
@"atom?": ^(NSArray *args){
|
||||
return wrap_tf(atom_Q(args[0]));
|
||||
},
|
||||
@"deref": ^(NSArray *args){
|
||||
return [(MalAtom *)args[0] val];
|
||||
},
|
||||
@"reset!": ^(NSArray *args){
|
||||
MalAtom * atm = (MalAtom *)args[0];
|
||||
return atm.val = args[1];
|
||||
},
|
||||
@"swap!": ^(NSArray *args){
|
||||
MalAtom * atm = (MalAtom *)args[0];
|
||||
NSObject * (^ f)(NSArray *) = args[1];
|
||||
NSMutableArray * fargs = [NSMutableArray array];
|
||||
[fargs addObject:atm.val];
|
||||
if ([args count] > 2) {
|
||||
NSRange r = NSMakeRange(2, [args count]-2);
|
||||
[fargs addObjectsFromArray:[args subarrayWithRange:r]];
|
||||
}
|
||||
return atm.val = apply(f, fargs);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,18 @@
|
||||
// Forward declaration of EVAL function
|
||||
NSObject *EVAL(id ast, id env);
|
||||
|
||||
@interface MalFunc : NSObject
|
||||
@interface MalFunc : NSObject <NSCopying>
|
||||
|
||||
@property (copy) NSArray * ast;
|
||||
@property (copy) Env * env;
|
||||
@property (copy) NSArray * params;
|
||||
@property BOOL isMacro;
|
||||
@property (copy) NSObject * meta;
|
||||
|
||||
- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params;
|
||||
|
||||
- (id)apply:(NSArray *)args;
|
||||
|
||||
@end
|
||||
|
||||
NSObject * apply(id f, NSArray *args);
|
||||
|
@ -7,6 +7,8 @@
|
||||
@synthesize ast = _ast;
|
||||
@synthesize env = _env;
|
||||
@synthesize params = _params;
|
||||
@synthesize isMacro = _isMacro;
|
||||
@synthesize meta = _meta;
|
||||
|
||||
- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params {
|
||||
self = [super init];
|
||||
@ -14,6 +16,8 @@
|
||||
_ast = ast;
|
||||
_env = env;
|
||||
_params = params;
|
||||
_isMacro = false;
|
||||
_meta = [NSNull alloc];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -22,4 +26,24 @@
|
||||
return EVAL(_ast, [Env fromBindings:_env binds:_params exprs:args]);
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
MalFunc * copy = [[[self class] alloc] init:_ast env:_env params:_params];
|
||||
if (copy) {
|
||||
copy.isMacro = _isMacro;
|
||||
copy.meta = _meta;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
NSObject * apply(id f, NSArray *args) {
|
||||
if ([f isKindOfClass:[MalFunc class]]) {
|
||||
return [f apply:args];
|
||||
} else {
|
||||
NSObject * (^ fn)(NSArray *) = f;
|
||||
return fn(args);
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ NSString * _pr_str(NSObject * obj, BOOL print_readably) {
|
||||
[elems componentsJoinedByString:@" "]];
|
||||
} else if (block_Q(obj)) {
|
||||
return @"#<native function>";
|
||||
} else if (atom_Q(obj)) {
|
||||
return [NSString stringWithFormat:@"(atom %@)",
|
||||
_pr_str([(MalAtom *)obj val], print_readably)];
|
||||
} else {
|
||||
return [obj description];
|
||||
}
|
||||
|
@ -61,7 +61,9 @@ NSArray * tokenize(NSString *str) {
|
||||
|
||||
NSMutableArray * tokens = [NSMutableArray array];
|
||||
for (NSTextCheckingResult *match in matches) {
|
||||
[tokens addObject:[str substringWithRange:[match rangeAtIndex:1]]];
|
||||
NSString * mstr = [str substringWithRange:[match rangeAtIndex:1]];
|
||||
if ([mstr characterAtIndex:0] == ';') { continue; }
|
||||
[tokens addObject:mstr];
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
@ -181,5 +183,9 @@ NSObject * read_form(Reader * rdr) {
|
||||
|
||||
NSObject * read_str(NSString *str) {
|
||||
NSArray * tokens = tokenize(str);
|
||||
if ([tokens count] == 0) { @throw [NSException exceptionWithName:@"ReaderContinue"
|
||||
reason:@"empty token"
|
||||
userInfo:nil]; }
|
||||
//if ([tokens count] == 0) { @throw [[MalContinue alloc] init]; }
|
||||
return read_form([[Reader alloc] initWithTokens:tokens]);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ NSString *REP(NSString *line) {
|
||||
return PRINT(EVAL(READ(line), @""));
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
int main () {
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
|
@ -1,6 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
|
||||
@ -20,7 +21,7 @@ NSString *REP(NSString *line) {
|
||||
return PRINT(EVAL(READ(line), @""));
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
int main () {
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
@ -35,6 +36,9 @@ int main (int argc, const char * argv[]) {
|
||||
printf("%s\n", [[REP(line) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ NSObject *EVAL(NSObject *ast, NSDictionary *env) {
|
||||
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
NSArray * args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
|
||||
NSArray * args = _rest(el);
|
||||
return f(args);
|
||||
}
|
||||
|
||||
@ -65,12 +65,7 @@ NSString *REP(NSString *line, NSDictionary *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
int main () {
|
||||
NSDictionary * repl_env = @{
|
||||
@"+": ^(NSArray *args){
|
||||
return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
|
||||
@ -86,6 +81,11 @@ int main (int argc, const char * argv[]) {
|
||||
},
|
||||
};
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
@ -95,6 +95,9 @@ int main (int argc, const char * argv[]) {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
NSArray * args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
|
||||
NSArray * args = _rest(el);
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
@ -78,13 +78,14 @@ NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
int main () {
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
[repl_env set:(MalSymbol *)@"+" val:^(NSArray *args){
|
||||
return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
|
||||
}];
|
||||
@ -107,6 +108,9 @@ int main (int argc, const char * argv[]) {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,6 @@ NSObject *READ(NSString *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
|
||||
// forward declaration
|
||||
NSObject *EVAL(NSObject *ast, Env *env);
|
||||
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
@ -63,8 +59,7 @@ NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
}
|
||||
return EVAL(alst[2], let_env);
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 1);
|
||||
NSArray * el = (NSArray *)eval_ast([alst subarrayWithRange:r], env);
|
||||
NSArray * el = (NSArray *)eval_ast(_rest(alst), env);
|
||||
return [el lastObject];
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
@ -84,8 +79,10 @@ NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
|
||||
args = _rest(el);
|
||||
}
|
||||
return apply(el[0], args);
|
||||
/*
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
return [mf apply:args];
|
||||
@ -93,6 +90,7 @@ NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,14 +104,15 @@ NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
int main () {
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
@ -131,6 +130,9 @@ int main (int argc, const char * argv[]) {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
|
144
objc/step5_tco.m
Normal file
144
objc/step5_tco.m
Normal file
@ -0,0 +1,144 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
165
objc/step6_file.m
Normal file
165
objc/step6_file.m
Normal file
@ -0,0 +1,165 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
// Outside of pool to prevent "Block_release called upon
|
||||
// a stack..." message on exit
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
|
||||
return EVAL(args[0], repl_env);
|
||||
}];
|
||||
NSArray *argv = @[];
|
||||
if ([args count] > 2) {
|
||||
argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
|
||||
|
||||
if ([args count] > 1) {
|
||||
@try {
|
||||
REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
199
objc/step7_quote.m
Normal file
199
objc/step7_quote.m
Normal file
@ -0,0 +1,199 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
BOOL is_pair(NSObject *obj) {
|
||||
return [obj isKindOfClass:[NSArray class]] &&
|
||||
[(NSArray *)obj count] > 0;
|
||||
}
|
||||
|
||||
NSObject * quasiquote(NSObject *ast) {
|
||||
if (!is_pair(ast)) {
|
||||
return @[[MalSymbol stringWithString:@"quote"], ast];
|
||||
} else {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
if ([a0 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a0 isEqualTo:@"unquote"]) {
|
||||
return alst[1];
|
||||
} else if (is_pair(a0)) {
|
||||
id a0lst = (NSArray *)a0;
|
||||
id a00 = a0lst[0];
|
||||
if ([a00 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a00 isEqualTo:@"splice-unquote"]) {
|
||||
return @[[MalSymbol stringWithString:@"concat"],
|
||||
a0lst[1],
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
return @[[MalSymbol stringWithString:@"cons"],
|
||||
quasiquote(a0),
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quote"]) {
|
||||
return alst[1];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quasiquote"]) {
|
||||
ast = quasiquote(alst[1]); // TCO
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
// Outside of pool to prevent "Block_release called upon
|
||||
// a stack..." message on exit
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
|
||||
return EVAL(args[0], repl_env);
|
||||
}];
|
||||
NSArray *argv = @[];
|
||||
if ([args count] > 2) {
|
||||
argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
|
||||
|
||||
if ([args count] > 1) {
|
||||
@try {
|
||||
REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
236
objc/step8_macros.m
Normal file
236
objc/step8_macros.m
Normal file
@ -0,0 +1,236 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
BOOL is_pair(NSObject *obj) {
|
||||
return [obj isKindOfClass:[NSArray class]] &&
|
||||
[(NSArray *)obj count] > 0;
|
||||
}
|
||||
|
||||
NSObject * quasiquote(NSObject *ast) {
|
||||
if (!is_pair(ast)) {
|
||||
return @[[MalSymbol stringWithString:@"quote"], ast];
|
||||
} else {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
if ([a0 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a0 isEqualTo:@"unquote"]) {
|
||||
return alst[1];
|
||||
} else if (is_pair(a0)) {
|
||||
id a0lst = (NSArray *)a0;
|
||||
id a00 = a0lst[0];
|
||||
if ([a00 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a00 isEqualTo:@"splice-unquote"]) {
|
||||
return @[[MalSymbol stringWithString:@"concat"],
|
||||
a0lst[1],
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
return @[[MalSymbol stringWithString:@"cons"],
|
||||
quasiquote(a0),
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_macro_call(NSObject *ast, Env *env) {
|
||||
if (list_Q(ast)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
if ([alst[0] isKindOfClass:[MalSymbol class]] && [env find:alst[0]]) {
|
||||
id mf = [env get:alst[0]];
|
||||
if ([mf isKindOfClass:[MalFunc class]]) {
|
||||
return [(MalFunc *)mf isMacro];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NSObject *macroexpand(NSObject *ast, Env *env) {
|
||||
while(is_macro_call(ast, env)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
MalFunc * mf = (MalFunc *)[env get:alst[0]];
|
||||
ast = [mf apply:_rest(alst)];
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
// apply list
|
||||
ast = macroexpand(ast, env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quote"]) {
|
||||
return alst[1];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quasiquote"]) {
|
||||
ast = quasiquote(alst[1]); // TCO
|
||||
} else if ([a0sym isEqualTo:@"defmacro!"]) {
|
||||
MalFunc * f = (MalFunc *)EVAL(alst[2], env);
|
||||
f.isMacro = true;
|
||||
return [env set:alst[1] val:f];
|
||||
} else if ([a0sym isEqualTo:@"macroexpand"]) {
|
||||
return macroexpand(alst[1], env);
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
// Outside of pool to prevent "Block_release called upon
|
||||
// a stack..." message on exit
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
|
||||
return EVAL(args[0], repl_env);
|
||||
}];
|
||||
NSArray *argv = @[];
|
||||
if ([args count] > 2) {
|
||||
argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
|
||||
REP(@"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
REP(@"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env);
|
||||
|
||||
|
||||
if ([args count] > 1) {
|
||||
@try {
|
||||
REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
255
objc/step9_try.m
Normal file
255
objc/step9_try.m
Normal file
@ -0,0 +1,255 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
BOOL is_pair(NSObject *obj) {
|
||||
return [obj isKindOfClass:[NSArray class]] &&
|
||||
[(NSArray *)obj count] > 0;
|
||||
}
|
||||
|
||||
NSObject * quasiquote(NSObject *ast) {
|
||||
if (!is_pair(ast)) {
|
||||
return @[[MalSymbol stringWithString:@"quote"], ast];
|
||||
} else {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
if ([a0 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a0 isEqualTo:@"unquote"]) {
|
||||
return alst[1];
|
||||
} else if (is_pair(a0)) {
|
||||
id a0lst = (NSArray *)a0;
|
||||
id a00 = a0lst[0];
|
||||
if ([a00 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a00 isEqualTo:@"splice-unquote"]) {
|
||||
return @[[MalSymbol stringWithString:@"concat"],
|
||||
a0lst[1],
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
return @[[MalSymbol stringWithString:@"cons"],
|
||||
quasiquote(a0),
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_macro_call(NSObject *ast, Env *env) {
|
||||
if (list_Q(ast)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
if ([alst[0] isKindOfClass:[MalSymbol class]] && [env find:alst[0]]) {
|
||||
id mf = [env get:alst[0]];
|
||||
if ([mf isKindOfClass:[MalFunc class]]) {
|
||||
return [(MalFunc *)mf isMacro];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NSObject *macroexpand(NSObject *ast, Env *env) {
|
||||
while(is_macro_call(ast, env)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
MalFunc * mf = (MalFunc *)[env get:alst[0]];
|
||||
ast = [mf apply:_rest(alst)];
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
// apply list
|
||||
ast = macroexpand(ast, env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quote"]) {
|
||||
return alst[1];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quasiquote"]) {
|
||||
ast = quasiquote(alst[1]); // TCO
|
||||
} else if ([a0sym isEqualTo:@"defmacro!"]) {
|
||||
MalFunc * f = (MalFunc *)EVAL(alst[2], env);
|
||||
f.isMacro = true;
|
||||
return [env set:alst[1] val:f];
|
||||
} else if ([a0sym isEqualTo:@"macroexpand"]) {
|
||||
return macroexpand(alst[1], env);
|
||||
} else if ([a0sym isEqualTo:@"try*"]) {
|
||||
@try {
|
||||
return EVAL(alst[1], env);
|
||||
} @catch(NSObject *e) {
|
||||
if ([alst count] > 2 && [alst[2] isKindOfClass:[NSArray class]]) {
|
||||
NSArray * a2lst = alst[2];
|
||||
if ([a2lst[0] isKindOfClass:[MalSymbol class]] &&
|
||||
[(MalSymbol *)a2lst[0] isEqualTo:@"catch*"]) {
|
||||
NSObject * exc = e;
|
||||
if ([e isKindOfClass:[NSException class]]) {
|
||||
exc = [e description];
|
||||
}
|
||||
return EVAL(a2lst[2], [Env fromBindings:env
|
||||
binds:@[a2lst[1]]
|
||||
exprs:@[exc]]);
|
||||
}
|
||||
}
|
||||
@throw e;
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
// Outside of pool to prevent "Block_release called upon
|
||||
// a stack..." message on exit
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
|
||||
return EVAL(args[0], repl_env);
|
||||
}];
|
||||
NSArray *argv = @[];
|
||||
if ([args count] > 2) {
|
||||
argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
|
||||
REP(@"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
REP(@"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env);
|
||||
|
||||
|
||||
if ([args count] > 1) {
|
||||
@try {
|
||||
REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
258
objc/stepA_mal.m
Normal file
258
objc/stepA_mal.m
Normal file
@ -0,0 +1,258 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "mal_readline.h"
|
||||
#import "types.h"
|
||||
#import "reader.h"
|
||||
#import "printer.h"
|
||||
#import "env.h"
|
||||
#import "malfunc.h"
|
||||
#import "core.h"
|
||||
|
||||
// read
|
||||
NSObject *READ(NSString *str) {
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
// eval
|
||||
BOOL is_pair(NSObject *obj) {
|
||||
return [obj isKindOfClass:[NSArray class]] &&
|
||||
[(NSArray *)obj count] > 0;
|
||||
}
|
||||
|
||||
NSObject * quasiquote(NSObject *ast) {
|
||||
if (!is_pair(ast)) {
|
||||
return @[[MalSymbol stringWithString:@"quote"], ast];
|
||||
} else {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
if ([a0 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a0 isEqualTo:@"unquote"]) {
|
||||
return alst[1];
|
||||
} else if (is_pair(a0)) {
|
||||
id a0lst = (NSArray *)a0;
|
||||
id a00 = a0lst[0];
|
||||
if ([a00 isKindOfClass:[MalSymbol class]] &&
|
||||
[(NSString *)a00 isEqualTo:@"splice-unquote"]) {
|
||||
return @[[MalSymbol stringWithString:@"concat"],
|
||||
a0lst[1],
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
return @[[MalSymbol stringWithString:@"cons"],
|
||||
quasiquote(a0),
|
||||
quasiquote(_rest(alst))];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_macro_call(NSObject *ast, Env *env) {
|
||||
if (list_Q(ast)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
if ([alst[0] isKindOfClass:[MalSymbol class]] && [env find:alst[0]]) {
|
||||
id mf = [env get:alst[0]];
|
||||
if ([mf isKindOfClass:[MalFunc class]]) {
|
||||
return [(MalFunc *)mf isMacro];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NSObject *macroexpand(NSObject *ast, Env *env) {
|
||||
while(is_macro_call(ast, env)) {
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
MalFunc * mf = (MalFunc *)[env get:alst[0]];
|
||||
ast = [mf apply:_rest(alst)];
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
NSObject *eval_ast(NSObject *ast, Env *env) {
|
||||
if ([ast isMemberOfClass:[MalSymbol class]]) {
|
||||
return [env get:(MalSymbol *)ast];
|
||||
} else if ([ast isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newLst = [NSMutableArray array];
|
||||
for (NSObject * x in (NSArray *)ast) {
|
||||
[newLst addObject:EVAL(x, env)];
|
||||
}
|
||||
if ([ast isKindOfClass:[MalVector class]]) {
|
||||
return [MalVector fromArray:newLst];
|
||||
} else {
|
||||
return newLst;
|
||||
}
|
||||
} else if ([ast isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
|
||||
for (NSString * k in (NSDictionary *)ast) {
|
||||
newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
|
||||
}
|
||||
return newDict;
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
NSObject *EVAL(NSObject *ast, Env *env) {
|
||||
while (true) {
|
||||
//NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
// apply list
|
||||
ast = macroexpand(ast, env);
|
||||
if (!list_Q(ast)) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
|
||||
NSArray * alst = (NSArray *)ast;
|
||||
id a0 = alst[0];
|
||||
NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
|
||||
: @"__<*fn*>__";
|
||||
|
||||
if ([a0sym isEqualTo:@"def!"]) {
|
||||
return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"let*"]) {
|
||||
Env *let_env = [Env fromOuter:env];
|
||||
NSArray * binds = (NSArray *)alst[1];
|
||||
for (int i=0; i < [binds count]; i+=2) {
|
||||
[let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
|
||||
}
|
||||
env = let_env;
|
||||
ast = alst[2]; // TCO
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quote"]) {
|
||||
return alst[1];
|
||||
} else if ([(NSString *)a0 isEqualTo:@"quasiquote"]) {
|
||||
ast = quasiquote(alst[1]); // TCO
|
||||
} else if ([a0sym isEqualTo:@"defmacro!"]) {
|
||||
MalFunc * f = (MalFunc *)EVAL(alst[2], env);
|
||||
f.isMacro = true;
|
||||
return [env set:alst[1] val:f];
|
||||
} else if ([a0sym isEqualTo:@"macroexpand"]) {
|
||||
return macroexpand(alst[1], env);
|
||||
} else if ([a0sym isEqualTo:@"try*"]) {
|
||||
@try {
|
||||
return EVAL(alst[1], env);
|
||||
} @catch(NSObject *e) {
|
||||
if ([alst count] > 2 && [alst[2] isKindOfClass:[NSArray class]]) {
|
||||
NSArray * a2lst = alst[2];
|
||||
if ([a2lst[0] isKindOfClass:[MalSymbol class]] &&
|
||||
[(MalSymbol *)a2lst[0] isEqualTo:@"catch*"]) {
|
||||
NSObject * exc = e;
|
||||
if ([e isKindOfClass:[NSException class]]) {
|
||||
exc = [e description];
|
||||
}
|
||||
return EVAL(a2lst[2], [Env fromBindings:env
|
||||
binds:@[a2lst[1]]
|
||||
exprs:@[exc]]);
|
||||
}
|
||||
}
|
||||
@throw e;
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"do"]) {
|
||||
NSRange r = NSMakeRange(1, [alst count] - 2);
|
||||
eval_ast([alst subarrayWithRange:r], env);
|
||||
ast = [alst lastObject]; // TCO
|
||||
} else if ([a0sym isEqualTo:@"if"]) {
|
||||
NSObject * cond = EVAL(alst[1], env);
|
||||
if ([cond isKindOfClass:[NSNull class]] ||
|
||||
[cond isKindOfClass:[MalFalse class]]) {
|
||||
if ([alst count] > 3) {
|
||||
ast = alst[3]; // TCO
|
||||
} else {
|
||||
return [NSNull alloc];
|
||||
}
|
||||
} else {
|
||||
ast = alst[2]; // TCO
|
||||
}
|
||||
} else if ([a0sym isEqualTo:@"fn*"]) {
|
||||
return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
|
||||
} else {
|
||||
NSArray * el = (NSArray *) eval_ast(ast, env);
|
||||
NSArray * args = @[];
|
||||
if ([el count] > 1) {
|
||||
args = _rest(el);
|
||||
}
|
||||
if ([el[0] isKindOfClass:[MalFunc class]]) {
|
||||
MalFunc * mf = el[0];
|
||||
env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
|
||||
ast = [mf ast]; // TCO
|
||||
} else {
|
||||
NSObject * (^ f)(NSArray *) = el[0];
|
||||
return f(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
NSString *PRINT(NSObject *exp) {
|
||||
return _pr_str(exp, true);
|
||||
}
|
||||
|
||||
// REPL
|
||||
NSString *REP(NSString *line, Env *env) {
|
||||
return PRINT(EVAL(READ(line), env));
|
||||
}
|
||||
|
||||
int main () {
|
||||
// Outside of pool to prevent "Block_release called upon
|
||||
// a stack..." message on exit
|
||||
Env * repl_env = [[Env alloc] init];
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
|
||||
// Create an autorelease pool to manage the memory into the program
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
// If using automatic reference counting (ARC), use @autoreleasepool instead:
|
||||
// @autoreleasepool {
|
||||
|
||||
// core.m: defined using Objective-C
|
||||
NSDictionary * core_ns = [Core ns];
|
||||
for (NSString* key in core_ns) {
|
||||
[repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
|
||||
return EVAL(args[0], repl_env);
|
||||
}];
|
||||
NSArray *argv = @[];
|
||||
if ([args count] > 2) {
|
||||
argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
|
||||
}
|
||||
[repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
REP(@"(def! *host-language* \"Objective-C\")", repl_env);
|
||||
REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
|
||||
REP(@"(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
REP(@"(def! *gensym-counter* (atom 0))", repl_env);
|
||||
REP(@"(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))", repl_env);
|
||||
REP(@"(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))", repl_env);
|
||||
|
||||
|
||||
if ([args count] > 1) {
|
||||
@try {
|
||||
REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *rawline = _readline("user> ");
|
||||
if (!rawline) { break; }
|
||||
NSString *line = [NSString stringWithUTF8String:rawline];
|
||||
if ([line length] == 0) { continue; }
|
||||
@try {
|
||||
printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
|
||||
} @catch(NSString *e) {
|
||||
printf("Error: %s\n", [e UTF8String]);
|
||||
} @catch(NSException *e) {
|
||||
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
||||
printf("Exception: %s\n", [[e reason] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
[pool drain];
|
||||
|
||||
// }
|
||||
}
|
19
objc/types.h
19
objc/types.h
@ -28,7 +28,6 @@
|
||||
// Mal Types
|
||||
//
|
||||
|
||||
|
||||
@interface MalTrue : NSObject
|
||||
@end
|
||||
|
||||
@ -38,11 +37,14 @@
|
||||
@interface MalSymbol: NSString
|
||||
@end
|
||||
|
||||
BOOL string_Q(NSObject * obj);
|
||||
|
||||
// Lists
|
||||
|
||||
BOOL list_Q(id obj);
|
||||
|
||||
NSArray * _rest(NSArray * obj);
|
||||
|
||||
|
||||
// Vectors
|
||||
|
||||
@ -63,6 +65,7 @@ BOOL list_Q(id obj);
|
||||
|
||||
// Hash Maps
|
||||
|
||||
NSDictionary * assoc_BANG(NSMutableDictionary * d, NSArray * kvs);
|
||||
NSDictionary * hash_map(NSArray *kvs);
|
||||
|
||||
|
||||
@ -71,6 +74,20 @@ NSDictionary * hash_map(NSArray *kvs);
|
||||
BOOL block_Q(id obj);
|
||||
|
||||
|
||||
// Atoms
|
||||
|
||||
@interface MalAtom : NSObject
|
||||
|
||||
@property (copy) NSObject * val;
|
||||
|
||||
- (id)init:(NSObject *)val;
|
||||
|
||||
+ (id)fromObject:(NSObject *)val;
|
||||
|
||||
@end
|
||||
|
||||
BOOL atom_Q(id obj);
|
||||
|
||||
|
||||
// General functions
|
||||
|
||||
|
51
objc/types.m
51
objc/types.m
@ -37,27 +37,27 @@
|
||||
@end
|
||||
|
||||
|
||||
// Lists
|
||||
BOOL string_Q(id obj) {
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
NSString * s = obj;
|
||||
if (![s isKindOfClass:[MalSymbol class]]) {
|
||||
return ![s hasPrefix:@"\u029e"];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Add map to NSArray
|
||||
//@implementation NSArray (CMMap)
|
||||
//- (NSArray *) map:(NSObject *(^)(NSObject * obj))block {
|
||||
// NSMutableArray *res = [NSMutableArray array];
|
||||
// for (NSObject * x in self) {
|
||||
// [res addObject: block(x)];
|
||||
// }
|
||||
// return res;
|
||||
//}
|
||||
//@end
|
||||
//
|
||||
// E.g.:
|
||||
// return [(NSArray *)ast map:^(NSObject * x) { return EVAL(x, env); }];
|
||||
// Lists
|
||||
|
||||
BOOL list_Q(id obj) {
|
||||
return ([obj isKindOfClass:[NSArray class]] &&
|
||||
![obj isKindOfClass:[MalVector class]]);
|
||||
}
|
||||
|
||||
NSArray * _rest(NSArray * obj) {
|
||||
return [obj subarrayWithRange:NSMakeRange(1, [obj count]-1)];
|
||||
}
|
||||
|
||||
// Vectors
|
||||
|
||||
@implementation MalVector
|
||||
@ -115,6 +115,29 @@ BOOL block_Q(id obj) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation MalAtom
|
||||
|
||||
@synthesize val = _val;
|
||||
|
||||
- (id)init:(NSObject *)val {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_val = val;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id)fromObject:(NSObject *)val {
|
||||
return [[MalAtom alloc] init:val];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BOOL atom_Q(id obj) {
|
||||
return [obj isKindOfClass:[MalAtom class]];
|
||||
}
|
||||
|
||||
// General functions
|
||||
|
||||
BOOL sequential_Q(NSObject * obj) {
|
||||
|
Loading…
Reference in New Issue
Block a user