Commit | Line | Data |
---|---|---|
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 | |
12 | NSObject *READ(NSString *str) { | |
13 | return read_str(str); | |
14 | } | |
15 | ||
16 | // eval | |
17 | NSObject *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 | ||
41 | NSObject *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 | ||
104 | NSString *PRINT(NSObject *exp) { | |
105 | return _pr_str(exp, true); | |
106 | } | |
107 | ||
108 | // REPL | |
109 | NSString *REP(NSString *line, Env *env) { | |
110 | return PRINT(EVAL(READ(line), env)); | |
111 | } | |
112 | ||
113 | int main () { | |
114 | Env * repl_env = [[Env alloc] init]; | |
115 | ||
116 | // Create an autorelease pool to manage the memory into the program | |
117 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | |
118 | // If using automatic reference counting (ARC), use @autoreleasepool instead: | |
119 | // @autoreleasepool { | |
120 | ||
121 | // core.m: defined using Objective-C | |
122 | NSDictionary * core_ns = [Core ns]; | |
123 | for (NSString* key in core_ns) { | |
124 | [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]]; | |
125 | } | |
126 | ||
127 | // core.mal: defined using the language itself | |
128 | REP(@"(def! not (fn* (a) (if a false true)))", repl_env); | |
129 | ||
130 | while (true) { | |
131 | char *rawline = _readline("user> "); | |
132 | if (!rawline) { break; } | |
133 | NSString *line = [NSString stringWithUTF8String:rawline]; | |
134 | if ([line length] == 0) { continue; } | |
135 | @try { | |
136 | printf("%s\n", [[REP(line, repl_env) description] UTF8String]); | |
137 | } @catch(NSString *e) { | |
138 | printf("Error: %s\n", [e UTF8String]); | |
dd7a4f55 JM |
139 | } @catch(NSObject *e) { |
140 | NSObject * exc = e; | |
141 | printf("Exception: %s\n", [_pr_str(exc, true) UTF8String]); | |
7cae6e6f JM |
142 | } @catch(NSException *e) { |
143 | if ([[e name] isEqualTo:@"ReaderContinue"]) { continue; } | |
144 | printf("Exception: %s\n", [[e reason] UTF8String]); | |
145 | } | |
146 | } | |
147 | ||
148 | [pool drain]; | |
149 | ||
150 | // } | |
151 | } |