Change quasiquote algorithm
[jackhill/mal.git] / impls / matlab / step7_quote.m
CommitLineData
c3023f26
JM
1function step7_quote(varargin), main(varargin), end
2
3% read
4function ret = READ(str)
5 ret = reader.read_str(str);
6end
7
8% eval
fbfe6784
NB
9function ret = starts_with(ast, sym)
10 ret = length(ast);
11 if ret
12 first = ast.get(1);
13 ret = isa(first,'types.Symbol') && strcmp(first.name, sym);
14 end
15end
16
17function ret = quasiquote_loop(ast)
18 ret = types.List();
19 for i=length(ast):-1:1
20 elt = ast.get(i)
21 if isa(elt, 'types.List') && starts_with(elt, 'splice-unquote')
22 ret = types.List(types.Symbol('concat'), elt.get(2), ret);
23 else
24 ret = types.List(types.Symbol('cons'), quasiquote(elt), ret);
25 end
26 end
c3023f26
JM
27end
28
29function ret = quasiquote(ast)
fbfe6784
NB
30 switch class(ast)
31 case 'types.List'
32 if starts_with(ast, 'unquote')
33 ret = ast.get(2);
34 else
35 ret = quasiquote_loop(ast);
36 end
37 case 'types.Vector'
38 ret = types.List(types.Symbol('vec'), quasiquote_loop(ast));
39 case {'types.Symbol', 'types.HashMap'}
6a572dff 40 ret = types.List(types.Symbol('quote'), ast);
fbfe6784
NB
41 otherwise
42 ret = ast;
c3023f26
JM
43 end
44end
45
46function ret = eval_ast(ast, env)
47 switch class(ast)
48 case 'types.Symbol'
49 ret = env.get(ast);
6a572dff
JM
50 case 'types.List'
51 ret = types.List();
c3023f26 52 for i=1:length(ast)
6a572dff
JM
53 ret.append(EVAL(ast.get(i), env));
54 end
55 case 'types.Vector'
56 ret = types.Vector();
57 for i=1:length(ast)
58 ret.append(EVAL(ast.get(i), env));
59 end
60 case 'types.HashMap'
61 ret = types.HashMap();
62 ks = ast.keys();
63 for i=1:length(ks)
64 k = ks{i};
65 ret.set(EVAL(k, env), EVAL(ast.get(k), env));
c3023f26
JM
66 end
67 otherwise
68 ret = ast;
69 end
70end
71
72function ret = EVAL(ast, env)
73 while true
0b234e13 74 %fprintf('EVAL: %s\n', printer.pr_str(ast, true));
47699629 75 if ~type_utils.list_Q(ast)
c3023f26
JM
76 ret = eval_ast(ast, env);
77 return;
78 end
79
80 % apply
9870acb6
JM
81 if length(ast) == 0
82 ret = ast;
83 return;
84 end
6a572dff
JM
85 if isa(ast.get(1),'types.Symbol')
86 a1sym = ast.get(1).name;
c3023f26
JM
87 else
88 a1sym = '_@$fn$@_';
89 end
90 switch (a1sym)
91 case 'def!'
6a572dff 92 ret = env.set(ast.get(2), EVAL(ast.get(3), env));
c3023f26
JM
93 return;
94 case 'let*'
47699629 95 let_env = Env({env});
6a572dff
JM
96 for i=1:2:length(ast.get(2))
97 let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
c3023f26
JM
98 end
99 env = let_env;
6a572dff 100 ast = ast.get(3); % TCO
c3023f26 101 case 'quote'
6a572dff 102 ret = ast.get(2);
c3023f26 103 return;
fbfe6784
NB
104 case 'quasiquoteexpand'
105 ret = quasiquote(ast.get(2));
106 return;
c3023f26 107 case 'quasiquote'
6a572dff 108 ast = quasiquote(ast.get(2)); % TCO
c3023f26 109 case 'do'
6a572dff
JM
110 el = eval_ast(ast.slice(2,length(ast)-1), env);
111 ast = ast.get(length(ast)); % TCO
c3023f26 112 case 'if'
6a572dff 113 cond = EVAL(ast.get(2), env);
c3023f26
JM
114 if strcmp(class(cond), 'types.Nil') || ...
115 (islogical(cond) && cond == false)
116 if length(ast) > 3
6a572dff 117 ast = ast.get(4); % TCO
c3023f26 118 else
9d54117c 119 ret = type_utils.nil;
c3023f26
JM
120 return;
121 end
122 else
6a572dff 123 ast = ast.get(3); % TCO
c3023f26
JM
124 end
125 case 'fn*'
9d54117c 126 fn = @(varargin) EVAL(ast.get(3), Env({env}, ast.get(2), ...
6a572dff
JM
127 types.List(varargin{:})));
128 ret = types.Function(fn, ast.get(3), env, ast.get(2));
c3023f26
JM
129 return;
130 otherwise
131 el = eval_ast(ast, env);
6a572dff
JM
132 f = el.get(1);
133 args = el.slice(2);
b550d8b7 134 if isa(f, 'types.Function')
47699629 135 env = Env({f.env}, f.params, args);
c3023f26
JM
136 ast = f.ast; % TCO
137 else
6a572dff 138 ret = f(args.data{:});
c3023f26
JM
139 return
140 end
141 end
142 end
143end
144
145% print
146function ret = PRINT(ast)
147 ret = printer.pr_str(ast, true);
148end
149
150% REPL
151function ret = rep(str, env)
152 ret = PRINT(EVAL(READ(str), env));
153end
154
155function main(args)
47699629 156 repl_env = Env();
c3023f26
JM
157
158 % core.m: defined using matlab
159 ns = core.ns(); ks = ns.keys();
160 for i=1:length(ks)
161 k = ks{i};
162 repl_env.set(types.Symbol(k), ns(k));
163 end
164 repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env));
6a572dff
JM
165 rest_args = args(2:end);
166 repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
c3023f26
JM
167
168 % core.mal: defined using the langauge itself
169 rep('(def! not (fn* (a) (if a false true)))', repl_env);
e6d41de4 170 rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))"', repl_env);
c3023f26
JM
171
172 if ~isempty(args)
0b234e13 173 rep(sprintf('(load-file "%s")', args{1}), repl_env);
c3023f26
JM
174 quit;
175 end
176
177 %cleanObj = onCleanup(@() disp('*** here1 ***'));
178 while (true)
47699629
JM
179 try
180 line = input('user> ', 's');
181 catch err
182 return
183 end
c3023f26
JM
184 if strcmp(strtrim(line),''), continue, end
185 try
186 fprintf('%s\n', rep(line, repl_env));
187 catch err
188 fprintf('Error: %s\n', err.message);
47699629 189 type_utils.print_stack(err);
c3023f26
JM
190 end
191 end
192end