1 #import
<Foundation
/Foundation.h
>
3 #import "mal_readline.h"
12 NSObject
*READ
(NSString
*str
) {
17 BOOL is_pair
(NSObject
*obj
) {
18 return [obj isKindOfClass
:[NSArray
class]] &&
19 [(NSArray
*)obj count
] > 0;
22 NSObject
* quasiquote
(NSObject
*ast
) {
24 return @
[[MalSymbol stringWithString
:@"quote"
], ast
];
26 NSArray
* alst
= (NSArray
*)ast
;
28 if ([a0 isKindOfClass
:[MalSymbol
class]] &&
29 [(NSString
*)a0 isEqualTo
:@"unquote"
]) {
31 } else if (is_pair
(a0
)) {
32 id a0lst
= (NSArray
*)a0
;
34 if ([a00 isKindOfClass
:[MalSymbol
class]] &&
35 [(NSString
*)a00 isEqualTo
:@"splice
-unquote"
]) {
36 return @
[[MalSymbol stringWithString
:@"concat"
],
38 quasiquote
(_rest
(alst
))];
41 return @
[[MalSymbol stringWithString
:@"cons"
],
43 quasiquote
(_rest
(alst
))];
47 BOOL is_macro_call
(NSObject
*ast
, Env
*env
) {
49 NSArray
* alst
= (NSArray
*)ast
;
50 if ([alst
[0] isKindOfClass
:[MalSymbol
class]] && [env
find:alst
[0]]) {
51 id mf
= [env
get:alst
[0]];
52 if ([mf isKindOfClass
:[MalFunc
class]]) {
53 return [(MalFunc
*)mf isMacro
];
60 NSObject
*macroexpand
(NSObject
*ast
, Env
*env
) {
61 while(is_macro_call
(ast
, env
)) {
62 NSArray
* alst
= (NSArray
*)ast
;
63 MalFunc
* mf
= (MalFunc
*)[env
get:alst
[0]];
64 ast
= [mf apply
:_rest
(alst
)];
69 NSObject
*eval_ast
(NSObject
*ast
, Env
*env
) {
70 if ([ast isMemberOfClass
:[MalSymbol
class]]) {
71 return [env
get:(MalSymbol
*)ast
];
72 } else if ([ast isKindOfClass
:[NSArray
class]]) {
73 NSMutableArray
*newLst
= [NSMutableArray array
];
74 for (NSObject
* x in
(NSArray
*)ast
) {
75 [newLst addObject
:EVAL(x
, env
)];
77 if ([ast isKindOfClass
:[MalVector
class]]) {
78 return [MalVector fromArray
:newLst
];
82 } else if ([ast isKindOfClass
:[NSDictionary
class]]) {
83 NSMutableDictionary
*newDict
= [NSMutableDictionary dictionary
];
84 for (NSString
* k in
(NSDictionary
*)ast
) {
85 newDict
[k
] = EVAL(((NSDictionary
*)ast
)[k
], env
);
93 NSObject
*EVAL(NSObject
*ast
, Env
*env
) {
95 //NSLog
(@"
EVAL: %@ (%@)", _pr_str(ast, true), env);
97 return eval_ast
(ast
, env
);
101 ast
= macroexpand
(ast
, env
);
103 return eval_ast
(ast
, env
);
106 NSArray
* alst
= (NSArray
*)ast
;
108 NSString
* a0sym
= [a0 isKindOfClass
:[MalSymbol
class]] ?
(NSString
*)a0
111 if ([a0sym isEqualTo
:@"def
!"
]) {
112 return [env
set:((MalSymbol
*)alst
[1]) val
:EVAL(alst
[2], env
)];
113 } else if ([(NSString
*)a0 isEqualTo
:@"let
*"
]) {
114 Env
*let_env
= [Env fromOuter
:env
];
115 NSArray
* binds
= (NSArray
*)alst
[1];
116 for (int i
=0; i
< [binds count
]; i
+=2) {
117 [let_env
set:binds
[i
] val
:EVAL(binds
[i
+1], let_env
)];
120 ast
= alst
[2]; // TCO
121 } else if ([(NSString
*)a0 isEqualTo
:@"quote"
]) {
123 } else if ([(NSString
*)a0 isEqualTo
:@"quasiquote"
]) {
124 ast
= quasiquote
(alst
[1]); // TCO
125 } else if ([a0sym isEqualTo
:@"defmacro
!"
]) {
126 MalFunc
* f
= (MalFunc
*)EVAL(alst
[2], env
);
128 return [env
set:alst
[1] val
:f
];
129 } else if ([a0sym isEqualTo
:@"macroexpand"
]) {
130 return macroexpand
(alst
[1], env
);
131 } else if ([a0sym isEqualTo
:@"
try*"
]) {
133 return EVAL(alst
[1], env
);
134 } @
catch(NSObject
*e
) {
135 if ([alst count
] > 2 && [alst
[2] isKindOfClass
:[NSArray
class]]) {
136 NSArray
* a2lst
= alst
[2];
137 if ([a2lst
[0] isKindOfClass
:[MalSymbol
class]] &&
138 [(MalSymbol
*)a2lst
[0] isEqualTo
:@"
catch*"
]) {
140 if ([e isKindOfClass
:[NSException
class]]) {
141 exc
= [e description
];
143 return EVAL(a2lst
[2], [Env fromBindings
:env
150 } else if ([a0sym isEqualTo
:@"do"
]) {
151 NSRange r
= NSMakeRange
(1, [alst count
] - 2);
152 eval_ast
([alst subarrayWithRange
:r
], env
);
153 ast
= [alst lastObject
]; // TCO
154 } else if ([a0sym isEqualTo
:@"
if"
]) {
155 NSObject
* cond = EVAL(alst
[1], env
);
156 if ([cond isKindOfClass
:[NSNull
class]] ||
157 [cond isKindOfClass
:[MalFalse
class]]) {
158 if ([alst count
] > 3) {
159 ast
= alst
[3]; // TCO
161 return [NSNull alloc
];
164 ast
= alst
[2]; // TCO
166 } else if ([a0sym isEqualTo
:@"fn
*"
]) {
167 return [[MalFunc alloc
] init
:alst
[2] env
:env params
:alst
[1]];
169 NSArray
* el
= (NSArray
*) eval_ast
(ast
, env
);
170 NSArray
* args
= @
[];
171 if ([el count
] > 1) {
174 if ([el
[0] isKindOfClass
:[MalFunc
class]]) {
175 MalFunc
* mf
= el
[0];
176 env
= [Env fromBindings
:[mf env
] binds
:[mf params
] exprs
:args
];
177 ast
= [mf ast
]; // TCO
179 NSObject
* (^ f
)(NSArray
*) = el
[0];
187 NSString
*PRINT(NSObject
*exp) {
188 return _pr_str
(exp, true
);
192 NSString
*REP
(NSString
*line, Env
*env
) {
193 return PRINT(EVAL(READ
(line), env
));
197 // Outside of pool to prevent "Block_release called upon
198 // a stack..." message on exit
199 Env
* repl_env
= [[Env alloc
] init
];
200 NSArray
*args
= [[NSProcessInfo processInfo
] arguments
];
202 // Create an autorelease pool to manage the
memory into the program
203 NSAutoreleasePool
* pool
= [[NSAutoreleasePool alloc
] init
];
204 // If using automatic reference counting
(ARC
), use @autoreleasepool instead
:
205 // @autoreleasepool
{
207 // core.m
: defined using Objective
-C
208 NSDictionary
* core_ns
= [Core ns
];
209 for (NSString
* key in core_ns
) {
210 [repl_env
set:(MalSymbol
*)key val
:[core_ns objectForKey
:key
]];
212 [repl_env
set:(MalSymbol
*)@"
eval" val
:^
(NSArray
*args
) {
213 return EVAL(args
[0], repl_env
);
216 if ([args count
] > 2) {
217 argv
= [args subarrayWithRange
:NSMakeRange
(2, [args count
] - 2)];
219 [repl_env
set:(MalSymbol
*)@"
*ARGV
*" val
:argv
];
221 // core.mal
: defined using the language itself
222 REP
(@"
(def
! *host
-language
* \"Objective
-C
\")"
, repl_env
);
223 REP
(@"
(def
! not
(fn
* (a
) (if a false true
)))"
, repl_env
);
224 REP
(@"
(def
! load-file
(fn
* (f
) (eval (read
-string
(str
\"(do
\" (slurp f
) \")\")))))"
, repl_env
);
225 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
);
226 REP
(@"
(def
! *gensym
-counter
* (atom
0))"
, repl_env
);
227 REP
(@"
(def
! gensym
(fn
* [] (symbol
(str
\"G__
\" (swap
! *gensym
-counter
* (fn
* [x
] (+ 1 x
)))))))"
, repl_env
);
228 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
);
231 if ([args count
] > 1) {
233 REP
([NSString stringWithFormat
:@"
(load-file
\"%@\")", args[1]], repl_env);
234 } @
catch(NSString
*e
) {
235 printf
("
Error: %s\n", [e UTF8String]);
241 char *rawline
= _readline
("user
> "
);
242 if (!rawline
) { break; }
243 NSString
*line = [NSString stringWithUTF8String
:rawline
];
244 if ([line length] == 0) { continue
; }
246 printf
("
%s\n", [[REP(line, repl_env) description] UTF8String]);
247 } @
catch(NSString
*e
) {
248 printf
("
Error: %s\n", [e UTF8String]);
249 } @
catch(NSException
*e
) {
250 if ([[e name
] isEqualTo
:@"ReaderContinue"
]) { continue
; }
251 printf
("Exception
: %s\n", [[e reason] UTF8String]);