DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / objc / step6_file.m
CommitLineData
7cae6e6f
JM
1#import <Foundation/Foundation.h>
2
3#import "mal_readline.h"
4#import "types.h"
5#import "reader.h"
6#import "printer.h"
7#import "env.h"
8#import "malfunc.h"
9#import "core.h"
10
11// read
12NSObject *READ(NSString *str) {
13 return read_str(str);
14}
15
16// eval
17NSObject *eval_ast(NSObject *ast, Env *env) {
18 if ([ast isMemberOfClass:[MalSymbol class]]) {
19 return [env get:(MalSymbol *)ast];
20 } else if ([ast isKindOfClass:[NSArray class]]) {
21 NSMutableArray *newLst = [NSMutableArray array];
22 for (NSObject * x in (NSArray *)ast) {
23 [newLst addObject:EVAL(x, env)];
24 }
25 if ([ast isKindOfClass:[MalVector class]]) {
26 return [MalVector fromArray:newLst];
27 } else {
28 return newLst;
29 }
30 } else if ([ast isKindOfClass:[NSDictionary class]]) {
31 NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
32 for (NSString * k in (NSDictionary *)ast) {
33 newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
34 }
35 return newDict;
36 } else {
37 return ast;
38 }
39}
40
41NSObject *EVAL(NSObject *ast, Env *env) {
42 while (true) {
43 //NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
44 if (!list_Q(ast)) {
45 return eval_ast(ast, env);
46 }
47
203e9599
JM
48 // apply list
49 if ([(NSArray *)ast count] == 0) {
50 return ast;
51 }
7cae6e6f
JM
52 NSArray * alst = (NSArray *)ast;
53 id a0 = alst[0];
54 NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
55 : @"__<*fn*>__";
56
57 if ([a0sym isEqualTo:@"def!"]) {
58 return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
59 } else if ([(NSString *)a0 isEqualTo:@"let*"]) {
60 Env *let_env = [Env fromOuter:env];
61 NSArray * binds = (NSArray *)alst[1];
62 for (int i=0; i < [binds count]; i+=2) {
63 [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
64 }
65 env = let_env;
66 ast = alst[2]; // TCO
67 } else if ([a0sym isEqualTo:@"do"]) {
68 NSRange r = NSMakeRange(1, [alst count] - 2);
69 eval_ast([alst subarrayWithRange:r], env);
70 ast = [alst lastObject]; // TCO
71 } else if ([a0sym isEqualTo:@"if"]) {
72 NSObject * cond = EVAL(alst[1], env);
73 if ([cond isKindOfClass:[NSNull class]] ||
74 [cond isKindOfClass:[MalFalse class]]) {
75 if ([alst count] > 3) {
76 ast = alst[3]; // TCO
77 } else {
78 return [NSNull alloc];
79 }
80 } else {
81 ast = alst[2]; // TCO
82 }
83 } else if ([a0sym isEqualTo:@"fn*"]) {
84 return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
85 } else {
86 NSArray * el = (NSArray *) eval_ast(ast, env);
87 NSArray * args = @[];
88 if ([el count] > 1) {
89 args = _rest(el);
90 }
91 if ([el[0] isKindOfClass:[MalFunc class]]) {
92 MalFunc * mf = el[0];
93 env = [Env fromBindings:[mf env] binds:[mf params] exprs:args];
94 ast = [mf ast]; // TCO
95 } else {
96 NSObject * (^ f)(NSArray *) = el[0];
97 return f(args);
98 }
99 }
100 }
101}
102
103// print
104NSString *PRINT(NSObject *exp) {
105 return _pr_str(exp, true);
106}
107
108// REPL
109NSString *REP(NSString *line, Env *env) {
110 return PRINT(EVAL(READ(line), env));
111}
112
113int main () {
114 // Outside of pool to prevent "Block_release called upon
115 // a stack..." message on exit
116 Env * repl_env = [[Env alloc] init];
117 NSArray *args = [[NSProcessInfo processInfo] arguments];
118
119 // Create an autorelease pool to manage the memory into the program
120 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
121 // If using automatic reference counting (ARC), use @autoreleasepool instead:
122// @autoreleasepool {
123
124 // core.m: defined using Objective-C
125 NSDictionary * core_ns = [Core ns];
126 for (NSString* key in core_ns) {
127 [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
128 }
129 [repl_env set:(MalSymbol *)@"eval" val:^(NSArray *args) {
130 return EVAL(args[0], repl_env);
131 }];
132 NSArray *argv = @[];
133 if ([args count] > 2) {
134 argv = [args subarrayWithRange:NSMakeRange(2, [args count] - 2)];
135 }
136 [repl_env set:(MalSymbol *)@"*ARGV*" val:argv];
137
138 // core.mal: defined using the language itself
139 REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
e6d41de4 140 REP(@"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
7cae6e6f
JM
141
142 if ([args count] > 1) {
143 @try {
144 REP([NSString stringWithFormat:@"(load-file \"%@\")", args[1]], repl_env);
145 } @catch(NSString *e) {
146 printf("Error: %s\n", [e UTF8String]);
147 }
148 return 0;
149 }
150
151 while (true) {
152 char *rawline = _readline("user> ");
153 if (!rawline) { break; }
154 NSString *line = [NSString stringWithUTF8String:rawline];
155 if ([line length] == 0) { continue; }
156 @try {
157 printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
158 } @catch(NSString *e) {
159 printf("Error: %s\n", [e UTF8String]);
dd7a4f55
JM
160 } @catch(NSObject *e) {
161 NSObject * exc = e;
162 printf("Exception: %s\n", [_pr_str(exc, true) UTF8String]);
7cae6e6f
JM
163 } @catch(NSException *e) {
164 if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; }
165 printf("Exception: %s\n", [[e reason] UTF8String]);
166 }
167 }
168
169 [pool drain];
170
171// }
172}