1
1
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:
Joel Martin 2016-03-04 01:56:54 -06:00
parent 57350ed73c
commit 7cae6e6f12
19 changed files with 1639 additions and 39 deletions

View File

@ -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)*

View File

@ -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);
},
};
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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];
}

View File

@ -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]);
}

View File

@ -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:

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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];
// }
}

View File

@ -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

View File

@ -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) {