diff options
| author | Joel Martin <github@martintribe.org> | 2015-02-08 23:50:12 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-02-08 23:51:53 -0600 |
| commit | b550d8b72a16aa83de1de9e76ac66ab000a0ef2f (patch) | |
| tree | 14e4d1748ba53a59065baf5a44c230f4fbf93bc7 | |
| parent | c4033aab513035999bb322b2063b6fbf57bedc97 (diff) | |
| download | mal-b550d8b72a16aa83de1de9e76ac66ab000a0ef2f.tar.gz mal-b550d8b72a16aa83de1de9e76ac66ab000a0ef2f.zip | |
matlab: Add step9.
| -rw-r--r-- | matlab/+types/Function.m (renamed from matlab/Function.m) | 0 | ||||
| -rw-r--r-- | matlab/+types/MalException.m | 11 | ||||
| -rw-r--r-- | matlab/core.m | 32 | ||||
| -rw-r--r-- | matlab/step5_tco.m | 4 | ||||
| -rw-r--r-- | matlab/step6_file.m | 4 | ||||
| -rw-r--r-- | matlab/step7_quote.m | 4 | ||||
| -rw-r--r-- | matlab/step8_macros.m | 4 | ||||
| -rw-r--r-- | matlab/step9_try.m | 205 |
8 files changed, 256 insertions, 8 deletions
diff --git a/matlab/Function.m b/matlab/+types/Function.m index 2f28368..2f28368 100644 --- a/matlab/Function.m +++ b/matlab/+types/Function.m diff --git a/matlab/+types/MalException.m b/matlab/+types/MalException.m new file mode 100644 index 0000000..1269a1d --- /dev/null +++ b/matlab/+types/MalException.m @@ -0,0 +1,11 @@ +classdef MalException < MException + properties + obj + end + methods + function exc = MalException(obj) + exc@MException('MalException:object', 'MalException'); + exc.obj = obj; + end + end +end diff --git a/matlab/core.m b/matlab/core.m index ef0b019..578ec4d 100644 --- a/matlab/core.m +++ b/matlab/core.m @@ -1,5 +1,10 @@ classdef core methods(Static) + function ret = throw(obj) + ret = types.nil; + throw(types.MalException(obj)); + end + function str = pr_str(varargin) strs = cellfun(@(s) printer.pr_str(s,true), varargin, ... 'UniformOutput', false); @@ -47,15 +52,40 @@ classdef core ret = seq{idx+1}; end + function ret = apply(varargin) + f = varargin{1}; + if isa(f, 'types.Function') + f = f.fn; + end + first_args = varargin(2:end-1); + rest_args = varargin{end}; + args = [first_args rest_args]; + ret = f(args{:}); + end + + function ret = map(f, lst) + if isa(f, 'types.Function') + f = f.fn; + end + ret = cellfun(@(x) f(x), lst, 'UniformOutput', false); + end + function n = ns() n = containers.Map(); n('=') = @types.equal; + n('throw') = @core.throw; + n('nil?') = @(a) isa(a, 'types.Nil'); + n('true?') = @(a) isa(a, 'logical') && a == true; + n('false?') = @(a) isa(a, 'logical') && a == false; + n('symbol') = @(a) types.Symbol(a); + n('symbol?') = @(a) isa(a, 'types.Symbol'); n('pr-str') = @core.pr_str; n('str') = @core.do_str; n('prn') = @core.prn; n('println') = @core.println; n('read-string') = @reader.read_str; + n('readline') = @(p) input(p, 's'); n('slurp') = @fileread; n('<') = @(a,b) a<b; @@ -77,6 +107,8 @@ classdef core n('rest') = @(a) a(2:end); n('empty?') = @(a) length(a) == 0; n('count') = @(a) length(a); + n('apply') = @core.apply; + n('map') = @core.map; end end end diff --git a/matlab/step5_tco.m b/matlab/step5_tco.m index 7f6ecd7..8ffb261 100644 --- a/matlab/step5_tco.m +++ b/matlab/step5_tco.m @@ -62,13 +62,13 @@ function ret = EVAL(ast, env) end case 'fn*' fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin)); - ret = Function(fn, ast{3}, env, ast{2}); + ret = types.Function(fn, ast{3}, env, ast{2}); return; otherwise el = eval_ast(ast, env); f = el{1}; args = el(2:end); - if isa(f, 'Function') + if isa(f, 'types.Function') env = Env(f.env, f.params, args); ast = f.ast; % TCO else diff --git a/matlab/step6_file.m b/matlab/step6_file.m index 674a53a..8e50633 100644 --- a/matlab/step6_file.m +++ b/matlab/step6_file.m @@ -62,13 +62,13 @@ function ret = EVAL(ast, env) end case 'fn*' fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin)); - ret = Function(fn, ast{3}, env, ast{2}); + ret = types.Function(fn, ast{3}, env, ast{2}); return; otherwise el = eval_ast(ast, env); f = el{1}; args = el(2:end); - if isa(f, 'Function') + if isa(f, 'types.Function') env = Env(f.env, f.params, args); ast = f.ast; % TCO else diff --git a/matlab/step7_quote.m b/matlab/step7_quote.m index 266751a..524d547 100644 --- a/matlab/step7_quote.m +++ b/matlab/step7_quote.m @@ -89,13 +89,13 @@ function ret = EVAL(ast, env) end case 'fn*' fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin)); - ret = Function(fn, ast{3}, env, ast{2}); + ret = types.Function(fn, ast{3}, env, ast{2}); return; otherwise el = eval_ast(ast, env); f = el{1}; args = el(2:end); - if isa(f, 'Function') + if isa(f, 'types.Function') env = Env(f.env, f.params, args); ast = f.ast; % TCO else diff --git a/matlab/step8_macros.m b/matlab/step8_macros.m index 61a86f4..8910946 100644 --- a/matlab/step8_macros.m +++ b/matlab/step8_macros.m @@ -120,13 +120,13 @@ function ret = EVAL(ast, env) end case 'fn*' fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin)); - ret = Function(fn, ast{3}, env, ast{2}); + ret = types.Function(fn, ast{3}, env, ast{2}); return; otherwise el = eval_ast(ast, env); f = el{1}; args = el(2:end); - if isa(f, 'Function') + if isa(f, 'types.Function') env = Env(f.env, f.params, args); ast = f.ast; % TCO else diff --git a/matlab/step9_try.m b/matlab/step9_try.m new file mode 100644 index 0000000..4ffaf41 --- /dev/null +++ b/matlab/step9_try.m @@ -0,0 +1,205 @@ +function step9_try(varargin), main(varargin), end + +% read +function ret = READ(str) + ret = reader.read_str(str); +end + +% eval +function ret = is_pair(ast) + ret = iscell(ast) && length(ast) > 0; +end + +function ret = quasiquote(ast) + if ~is_pair(ast) + ret = {types.Symbol('quote'), ast}; + elseif isa(ast{1},'types.Symbol') && ... + strcmp(ast{1}.name, 'unquote') + ret = ast{2}; + elseif is_pair(ast{1}) && isa(ast{1}{1},'types.Symbol') && ... + strcmp(ast{1}{1}.name, 'splice-unquote') + ret = {types.Symbol('concat'), ... + ast{1}{2}, ... + quasiquote(ast(2:end))}; + else + ret = {types.Symbol('cons'), ... + quasiquote(ast{1}), ... + quasiquote(ast(2:end))}; + end +end + +function ret = is_macro_call(ast, env) + if iscell(ast) && isa(ast{1}, 'types.Symbol') && ... + ~islogical(env.find(ast{1})) + f = env.get(ast{1}); + ret = isa(f,'Function') && f.is_macro; + else + ret = false; + end +end + +function ret = macroexpand(ast, env) + while is_macro_call(ast, env) + mac = env.get(ast{1}); + ast = mac.fn(ast{2:end}); + end + ret = ast; +end + +function ret = eval_ast(ast, env) + switch class(ast) + case 'types.Symbol' + ret = env.get(ast); + case 'cell' + ret = {}; + for i=1:length(ast) + ret{end+1} = EVAL(ast{i}, env); + end + otherwise + ret = ast; + end +end + +function ret = EVAL(ast, env) + while true + if ~iscell(ast) + ret = eval_ast(ast, env); + return; + end + + % apply + ast = macroexpand(ast, env); + if ~iscell(ast) + ret = ast; + return; + end + + if isa(ast{1},'types.Symbol') + a1sym = ast{1}.name; + else + a1sym = '_@$fn$@_'; + end + switch (a1sym) + case 'def!' + ret = env.set(ast{2}, EVAL(ast{3}, env)); + return; + case 'let*' + let_env = Env(env); + for i=1:2:length(ast{2}) + let_env.set(ast{2}{i}, EVAL(ast{2}{i+1}, let_env)); + end + env = let_env; + ast = ast{3}; % TCO + case 'quote' + ret = ast{2}; + return; + case 'quasiquote' + ast = quasiquote(ast{2}); % TCO + case 'defmacro!' + ret = env.set(ast{2}, EVAL(ast{3}, env)); + ret.is_macro = true; + return; + case 'macroexpand' + ret = macroexpand(ast{2}, env); + return; + case 'try*' + try + ret = EVAL(ast{2}, env); + return; + catch e + if length(ast) > 2 && strcmp(ast{3}{1}.name, 'catch*') + if isa(e, 'types.MalException') + exc = e.obj; + else + exc = e.message; + end + ret = EVAL(ast{3}{3}, Env(env, {ast{3}{2}}, {exc})); + return; + else + throw(e); + end + end + case 'do' + el = eval_ast(ast(2:end-1), env); + ast = ast{end}; % TCO + case 'if' + cond = EVAL(ast{2}, env); + if strcmp(class(cond), 'types.Nil') || ... + (islogical(cond) && cond == false) + if length(ast) > 3 + ast = ast{4}; % TCO + else + ret = types.nil; + return; + end + else + ast = ast{3}; % TCO + end + case 'fn*' + fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin)); + ret = types.Function(fn, ast{3}, env, ast{2}); + return; + otherwise + el = eval_ast(ast, env); + f = el{1}; + args = el(2:end); + if isa(f, 'types.Function') + env = Env(f.env, f.params, args); + ast = f.ast; % TCO + else + ret = f(args{:}); + return + end + end + end +end + +% print +function ret = PRINT(ast) + ret = printer.pr_str(ast, true); +end + +% REPL +function ret = rep(str, env) + ret = PRINT(EVAL(READ(str), env)); +end + +function main(args) + repl_env = Env(false); + + % core.m: defined using matlab + ns = core.ns(); ks = ns.keys(); + for i=1:length(ks) + k = ks{i}; + repl_env.set(types.Symbol(k), ns(k)); + end + repl_env.set(types.Symbol('eval'), @(a) EVAL(a, repl_env)); + repl_env.set(types.Symbol('*ARGV*'), args(2:end)); + + % core.mal: defined using the langauge itself + rep('(def! not (fn* (a) (if a false true)))', repl_env); + rep('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))"', repl_env); + 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); + rep('(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))', repl_env); + + if ~isempty(args) + rep(strcat('(load-file "', args{1}, '")'), repl_env); + quit; + end + + %cleanObj = onCleanup(@() disp('*** here1 ***')); + while (true) + line = input('user> ', 's'); + if strcmp(strtrim(line),''), continue, end + try + fprintf('%s\n', rep(line, repl_env)); + catch err + if isa(err, 'types.MalException') + fprintf('Error: %s\n', printer.pr_str(err.obj, true)); + else + fprintf('Error: %s\n', err.message); + end + fprintf('%s\n', getReport(err, 'extended')); + end + end +end |
