2016-03-04 10:56:54 +03:00
|
|
|
#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
|
2020-07-21 19:01:48 +03:00
|
|
|
BOOL starts_with(NSObject *ast, NSString *sym) {
|
|
|
|
if (!list_Q(ast))
|
|
|
|
return 0;
|
|
|
|
NSArray *alst = (NSArray *)ast;
|
|
|
|
if (![alst count])
|
|
|
|
return 0;
|
|
|
|
NSObject *a0 = alst[0];
|
|
|
|
return [a0 isKindOfClass:[MalSymbol class]] &&
|
|
|
|
[(NSString *)a0 isEqualTo:sym];
|
2016-03-04 10:56:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NSObject * quasiquote(NSObject *ast) {
|
2020-07-21 19:01:48 +03:00
|
|
|
if ([ast isMemberOfClass:[MalSymbol class]] ||
|
|
|
|
[ast isKindOfClass:[NSDictionary class]])
|
2016-03-04 10:56:54 +03:00
|
|
|
return @[[MalSymbol stringWithString:@"quote"], ast];
|
2020-07-21 19:01:48 +03:00
|
|
|
|
|
|
|
if (![ast isKindOfClass:[NSArray class]])
|
|
|
|
return ast;
|
|
|
|
|
|
|
|
NSArray * alst = (NSArray *)ast;
|
|
|
|
if (starts_with(alst, @"unquote"))
|
|
|
|
return alst[1];
|
|
|
|
|
|
|
|
NSObject *res = @[];
|
|
|
|
for (int i= [alst count] - 1; 0<=i; i--) {
|
|
|
|
NSObject *elt = alst[i];
|
|
|
|
if (starts_with(elt, @"splice-unquote"))
|
|
|
|
res = @[[MalSymbol stringWithString:@"concat"], ((NSArray *)elt)[1], res];
|
|
|
|
else
|
|
|
|
res = @[[MalSymbol stringWithString:@"cons"], quasiquote(elt), res];
|
2016-03-04 10:56:54 +03:00
|
|
|
}
|
2020-07-21 19:01:48 +03:00
|
|
|
if ([ast isKindOfClass:[MalVector class]])
|
|
|
|
res = @[[MalSymbol stringWithString:@"vec"], res];
|
|
|
|
return res;
|
2016-03-04 10:56:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-04-03 01:27:25 +03:00
|
|
|
// apply list
|
|
|
|
if ([(NSArray *)ast count] == 0) {
|
|
|
|
return ast;
|
|
|
|
}
|
2016-03-04 10:56:54 +03:00
|
|
|
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];
|
2020-07-21 19:01:48 +03:00
|
|
|
} else if ([(NSString *)a0 isEqualTo:@"quasiquoteexpand"]) {
|
|
|
|
return quasiquote(alst[1]);
|
2016-03-04 10:56:54 +03:00
|
|
|
} 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);
|
2019-07-16 00:57:02 +03:00
|
|
|
REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
2016-03-04 10:56:54 +03:00
|
|
|
|
|
|
|
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]);
|
Test uncaught throw, catchless try* . Fix 46 impls.
Fixes made to: ada, c, chuck, clojure, coffee, common-lisp, cpp,
crystal, d, dart, elm, erlang, es6, factor, fsharp, gnu-smalltalk,
groovy, guile, haxe, hy, js, livescript, matlab, miniMAL, nasm, nim,
objc, objpascal, ocaml, perl, perl6, php, plsql, ps, python, r,
rpython, ruby, scheme, swift3, tcl, ts, vb, vimscript, wasm, yorick.
Catchless try* test is an optional test. Not all implementations
support catchless try* but a number were fixed so they at least don't
crash on catchless try*.
2018-12-03 22:20:44 +03:00
|
|
|
} @catch(NSObject *e) {
|
|
|
|
NSObject * exc = e;
|
|
|
|
printf("Exception: %s\n", [_pr_str(exc, true) UTF8String]);
|
2016-03-04 10:56:54 +03:00
|
|
|
} @catch(NSException *e) {
|
|
|
|
if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
|
|
|
|
printf("Exception: %s\n", [[e reason] UTF8String]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[pool drain];
|
|
|
|
|
|
|
|
// }
|
|
|
|
}
|