mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 11:23:59 +03:00
matlab: add keyword, vector, hash-map support.
Switch List to full object like vector and hash-map.
This commit is contained in:
parent
7f567f3645
commit
6a572dff25
47
matlab/+types/HashMap.m
Normal file
47
matlab/+types/HashMap.m
Normal file
@ -0,0 +1,47 @@
|
||||
classdef HashMap < handle
|
||||
properties
|
||||
data = containers.Map();
|
||||
meta = types.nil;
|
||||
end
|
||||
methods
|
||||
function obj = HashMap(varargin)
|
||||
if nargin == 0
|
||||
obj.data = containers.Map();
|
||||
else
|
||||
obj.data = containers.Map(varargin(1:2:end), ...
|
||||
varargin(2:2:end));
|
||||
end
|
||||
end
|
||||
|
||||
function len = length(obj)
|
||||
len = length(obj.data);
|
||||
end
|
||||
|
||||
function ret = get(obj, key)
|
||||
ret = obj.data(key);
|
||||
end
|
||||
|
||||
function ret = set(obj, key, val)
|
||||
obj.data(key) = val;
|
||||
ret = val;
|
||||
end
|
||||
|
||||
function ret = keys(obj)
|
||||
ret = obj.data.keys();
|
||||
end
|
||||
|
||||
function ret = values(obj)
|
||||
ret = obj.data.values();
|
||||
end
|
||||
|
||||
function ret = clone(obj)
|
||||
ret = types.HashMap();
|
||||
if length(obj) > 0
|
||||
ret.data = containers.Map(obj.data.keys(), obj.data.values());
|
||||
else
|
||||
ret.data = containers.Map();
|
||||
end
|
||||
ret.meta = obj.meta;
|
||||
end
|
||||
end
|
||||
end
|
60
matlab/+types/List.m
Normal file
60
matlab/+types/List.m
Normal file
@ -0,0 +1,60 @@
|
||||
classdef List < handle
|
||||
properties
|
||||
data = {}
|
||||
meta = types.nil;
|
||||
end
|
||||
methods
|
||||
function obj = List(varargin)
|
||||
obj.data = varargin;
|
||||
end
|
||||
|
||||
function len = length(obj)
|
||||
len = length(obj.data);
|
||||
end
|
||||
|
||||
function ret = get(obj, idx)
|
||||
ret = obj.data{idx};
|
||||
end
|
||||
|
||||
function ret = set(obj, key, val)
|
||||
obj.data{key} = val;
|
||||
ret = val;
|
||||
end
|
||||
|
||||
function ret = append(obj, val)
|
||||
obj.data{end+1} = val;
|
||||
ret = val;
|
||||
end
|
||||
|
||||
function ret = slice(obj, start, last)
|
||||
if nargin < 3
|
||||
last = length(obj.data);
|
||||
end
|
||||
ret = types.List(obj.data{start:end});
|
||||
end
|
||||
|
||||
% function varargout = subsref(vec, S)
|
||||
% % This doesn't work for ranges
|
||||
% [varargout{1:nargout}] = builtin('subsref', vec.data, S);
|
||||
%
|
||||
% varargout = cell(1,max(1,nargout));
|
||||
% [varargout{:}] = builtin('subsref',vec.data,S);
|
||||
%
|
||||
%% switch S.type
|
||||
%% case '()'
|
||||
%% varargout = cell(1,numel(vec));
|
||||
%% varargout{1} = builtin('subsref', vec.data, S);
|
||||
%% case '{}'
|
||||
%% varargout = cell(1,numel(vec));
|
||||
%% varargout{1} = builtin('subsref', vec.data, S);
|
||||
%% case '.'
|
||||
%% error('Vector property access not yet implemented');
|
||||
%% end
|
||||
% end
|
||||
|
||||
% %function n = numel(varargin)
|
||||
% % n = 1;
|
||||
% %end
|
||||
|
||||
end
|
||||
end
|
15
matlab/+types/Vector.m
Normal file
15
matlab/+types/Vector.m
Normal file
@ -0,0 +1,15 @@
|
||||
classdef Vector < types.List
|
||||
methods
|
||||
function obj = Vector(varargin)
|
||||
obj.data = varargin;
|
||||
end
|
||||
|
||||
function ret = slice(obj, start, last)
|
||||
if nargin < 3
|
||||
last = length(obj.data);
|
||||
end
|
||||
ret = types.Vector(obj.data{2:end});
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -11,12 +11,12 @@ classdef Env < handle
|
||||
if nargin > 1
|
||||
env = Env(outer);
|
||||
for i=1:length(binds)
|
||||
k = binds{i}.name;
|
||||
k = binds.get(i).name;
|
||||
if strcmp(k, '&')
|
||||
env.data(binds{i+1}.name) = exprs(i:end);
|
||||
env.data(binds.get(i+1).name) = exprs.slice(i);
|
||||
break;
|
||||
else
|
||||
env.data(k) = exprs{i};
|
||||
env.data(k) = exprs.get(i);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
SOURCES_BASE = Reader.m types/Symbol.m reader.m printer.m
|
||||
#SOURCES_LISP = env.m core.m stepA_interop.m
|
||||
SOURCES_LISP = step1_read_print.m
|
||||
SOURCES_LISP = stepA_interop.m
|
||||
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
|
||||
|
||||
|
||||
|
@ -33,28 +33,76 @@ classdef core
|
||||
ret = floor(secs.*repmat(24*3600.0*1000,size(now)));
|
||||
end
|
||||
|
||||
function new_hm = assoc(hm, varargin)
|
||||
new_hm = clone(hm);
|
||||
for i=1:2:length(varargin)
|
||||
new_hm.set(varargin{i}, varargin{i+1});
|
||||
end
|
||||
end
|
||||
|
||||
function new_hm = dissoc(hm, varargin)
|
||||
new_hm = clone(hm);
|
||||
ks = intersect(hm.keys(),varargin);
|
||||
remove(new_hm.data, ks);
|
||||
end
|
||||
|
||||
function ret = get(hm, key)
|
||||
if hm == types.nil
|
||||
ret = types.nil;
|
||||
else
|
||||
if hm.data.isKey(key)
|
||||
ret = hm.data(key);
|
||||
else
|
||||
ret = types.nil;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ret = keys(hm)
|
||||
ks = hm.keys();
|
||||
ret = types.List(ks{:});
|
||||
end
|
||||
|
||||
function ret = vals(hm)
|
||||
vs = hm.values();
|
||||
ret = types.List(vs{:});
|
||||
end
|
||||
|
||||
function ret = cons(a, seq)
|
||||
cella = [{a}, seq.data];
|
||||
ret = types.List(cella{:});
|
||||
end
|
||||
|
||||
function ret = concat(varargin)
|
||||
if nargin == 0
|
||||
ret = {};
|
||||
cella = {};
|
||||
else
|
||||
ret = cat(2,varargin{:});
|
||||
cells = cellfun(@(x) x.data, varargin, ...
|
||||
'UniformOutput', false);
|
||||
cella = cat(2,cells{:});
|
||||
end
|
||||
ret = types.List(cella{:});
|
||||
end
|
||||
|
||||
function ret = first(seq)
|
||||
if length(seq) < 1
|
||||
ret = types.nil;
|
||||
else
|
||||
ret = seq{1};
|
||||
ret = seq.get(1);
|
||||
end
|
||||
end
|
||||
|
||||
function ret = rest(seq)
|
||||
cella = seq.data(2:end);
|
||||
ret = types.List(cella{:});
|
||||
end
|
||||
|
||||
function ret = nth(seq, idx)
|
||||
if idx+1 > length(seq)
|
||||
throw(MException('Range:nth', ...
|
||||
'nth: index out of range'))
|
||||
end
|
||||
ret = seq{idx+1};
|
||||
ret = seq.get(idx+1);
|
||||
end
|
||||
|
||||
function ret = apply(varargin)
|
||||
@ -63,7 +111,7 @@ classdef core
|
||||
f = f.fn;
|
||||
end
|
||||
first_args = varargin(2:end-1);
|
||||
rest_args = varargin{end};
|
||||
rest_args = varargin{end}.data;
|
||||
args = [first_args rest_args];
|
||||
ret = f(args{:});
|
||||
end
|
||||
@ -72,7 +120,8 @@ classdef core
|
||||
if isa(f, 'types.Function')
|
||||
f = f.fn;
|
||||
end
|
||||
ret = cellfun(@(x) f(x), lst, 'UniformOutput', false);
|
||||
cells = cellfun(@(x) f(x), lst.data, 'UniformOutput', false);
|
||||
ret = types.List(cells{:});
|
||||
end
|
||||
|
||||
function n = ns()
|
||||
@ -84,6 +133,8 @@ classdef core
|
||||
n('false?') = @(a) isa(a, 'logical') && a == false;
|
||||
n('symbol') = @(a) types.Symbol(a);
|
||||
n('symbol?') = @(a) isa(a, 'types.Symbol');
|
||||
n('keyword') = @types.keyword;
|
||||
n('keyword?') = @types.keyword_Q;
|
||||
|
||||
n('pr-str') = @core.pr_str;
|
||||
n('str') = @core.do_str;
|
||||
@ -103,14 +154,25 @@ classdef core
|
||||
n('/') = @(a,b) floor(a/b);
|
||||
n('time-ms') = @core.time_ms;
|
||||
|
||||
n('list') = @(varargin) varargin;
|
||||
n('list?') = @iscell;
|
||||
n('list') = @(varargin) types.List(varargin{:});
|
||||
n('list?') = @types.list_Q;
|
||||
n('vector') = @(varargin) types.Vector(varargin{:});
|
||||
n('vector?') = @types.vector_Q;
|
||||
n('hash-map') = @(varargin) types.HashMap(varargin{:});
|
||||
n('map?') = @types.hash_map_Q;
|
||||
n('assoc') = @core.assoc;
|
||||
n('dissoc') = @core.dissoc;
|
||||
n('get') = @core.get;
|
||||
n('contains?') = @(a,b) a.data.isKey(b);
|
||||
n('keys') = @core.keys;
|
||||
n('vals') = @core.vals;
|
||||
|
||||
n('cons') = @(a,b) [{a}, b];
|
||||
n('sequential?') = @types.sequential_Q;
|
||||
n('cons') = @core.cons;
|
||||
n('concat') = @core.concat;
|
||||
n('nth') = @core.nth;
|
||||
n('first') = @core.first;
|
||||
n('rest') = @(a) a(2:end);
|
||||
n('rest') = @core.rest;
|
||||
n('empty?') = @(a) length(a) == 0;
|
||||
n('count') = @(a) length(a);
|
||||
n('apply') = @core.apply;
|
||||
|
@ -8,18 +8,35 @@ classdef printer
|
||||
case 'double'
|
||||
str = num2str(obj);
|
||||
case 'char'
|
||||
if print_readably
|
||||
str = strrep(obj, '\', '\\');
|
||||
str = strrep(str, '"', '\"');
|
||||
str = strrep(str, char(10), '\n');
|
||||
str = strcat('"', str, '"');
|
||||
if types.keyword_Q(obj)
|
||||
str = strcat(':', obj(2:end));
|
||||
else
|
||||
str = obj;
|
||||
if print_readably
|
||||
str = strrep(obj, '\', '\\');
|
||||
str = strrep(str, '"', '\"');
|
||||
str = strrep(str, char(10), '\n');
|
||||
str = strcat('"', str, '"');
|
||||
else
|
||||
str = obj;
|
||||
end
|
||||
end
|
||||
case 'cell'
|
||||
case 'types.List'
|
||||
strs = cellfun(@(x) printer.pr_str(x, print_readably), ...
|
||||
obj, 'UniformOutput', false);
|
||||
obj.data, 'UniformOutput', false);
|
||||
str = strcat('(', strjoin(strs, ' '), ')');
|
||||
case 'types.Vector'
|
||||
strs = cellfun(@(x) printer.pr_str(x, print_readably), ...
|
||||
obj.data, 'UniformOutput', false);
|
||||
str = strcat('[', strjoin(strs, ' '), ']');
|
||||
case 'types.HashMap'
|
||||
strs = {};
|
||||
ks = obj.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
strs{end+1} = printer.pr_str(k, print_readably);
|
||||
strs{end+1} = printer.pr_str(obj.get(k), print_readably);
|
||||
end
|
||||
str = strcat('{', strjoin(strs, ' '), '}');
|
||||
case 'types.Nil'
|
||||
str = 'nil';
|
||||
case 'logical'
|
||||
|
@ -18,6 +18,8 @@ classdef reader
|
||||
atm = token(2:length(token)-1);
|
||||
atm = strrep(atm, '\"', '"');
|
||||
atm = strrep(atm, '\n', char(10));
|
||||
elseif strcmp(token(1), ':')
|
||||
atm = types.keyword(token);
|
||||
elseif strcmp(token, 'nil')
|
||||
atm = types.nil;
|
||||
elseif strcmp(token, 'true')
|
||||
@ -29,45 +31,72 @@ classdef reader
|
||||
end
|
||||
end
|
||||
|
||||
function lst = read_list(rdr)
|
||||
%fprintf('in read_list\n');
|
||||
lst = {};
|
||||
function seq = read_seq(rdr, start, last)
|
||||
%fprintf('in read_seq\n');
|
||||
seq = {};
|
||||
token = rdr.next();
|
||||
if not(strcmp(token, '('))
|
||||
error('expected ''(''');
|
||||
if not(strcmp(token, start))
|
||||
error(strcat('expected ''', start, ''''));
|
||||
end
|
||||
token = rdr.peek();
|
||||
while true
|
||||
if eq(token, false)
|
||||
error('expected '')''');
|
||||
error(strcat('expected ''', last, ''''));
|
||||
end
|
||||
if strcmp(token, ')'), break, end
|
||||
lst{length(lst)+1} = reader.read_form(rdr);
|
||||
if strcmp(token, last), break, end
|
||||
seq{end+1} = reader.read_form(rdr);
|
||||
token = rdr.peek();
|
||||
end
|
||||
rdr.next();
|
||||
end
|
||||
|
||||
function lst = read_list(rdr)
|
||||
seq = reader.read_seq(rdr, '(', ')');
|
||||
lst = types.List(seq{:});
|
||||
end
|
||||
|
||||
function vec = read_vector(rdr)
|
||||
seq = reader.read_seq(rdr, '[', ']');
|
||||
vec = types.Vector(seq{:});
|
||||
end
|
||||
|
||||
function map = read_hash_map(rdr)
|
||||
seq = reader.read_seq(rdr, '{', '}');
|
||||
map = types.HashMap(seq{:});
|
||||
end
|
||||
|
||||
function ast = read_form(rdr)
|
||||
%fprintf('in read_form\n');
|
||||
token = rdr.peek();
|
||||
switch token
|
||||
case ''''
|
||||
rdr.next();
|
||||
ast = {types.Symbol('quote'), reader.read_form(rdr)};
|
||||
ast = types.List(types.Symbol('quote'), ...
|
||||
reader.read_form(rdr));
|
||||
case '`'
|
||||
rdr.next();
|
||||
ast = {types.Symbol('quasiquote'), reader.read_form(rdr)};
|
||||
ast = types.List(types.Symbol('quasiquote'), ...
|
||||
reader.read_form(rdr));
|
||||
case '~'
|
||||
rdr.next();
|
||||
ast = {types.Symbol('unquote'), reader.read_form(rdr)};
|
||||
ast = types.List(types.Symbol('unquote'), ...
|
||||
reader.read_form(rdr));
|
||||
case '~@'
|
||||
rdr.next();
|
||||
ast = {types.Symbol('splice-unquote'), reader.read_form(rdr)};
|
||||
ast = types.List(types.Symbol('splice-unquote'), ...
|
||||
reader.read_form(rdr));
|
||||
case ')'
|
||||
error('unexpected '')''');
|
||||
case '('
|
||||
ast = reader.read_list(rdr);
|
||||
case ']'
|
||||
error('unexpected '']''');
|
||||
case '['
|
||||
ast = reader.read_vector(rdr);
|
||||
case '}'
|
||||
error('unexpected ''}''');
|
||||
case '{'
|
||||
ast = reader.read_hash_map(rdr);
|
||||
otherwise
|
||||
ast = reader.read_atom(rdr);
|
||||
end
|
||||
|
@ -10,10 +10,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env(ast.name);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -21,15 +33,15 @@ function ret = eval_ast(ast, env)
|
||||
end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.data(2:end);
|
||||
ret = f(args{:});
|
||||
end
|
||||
|
||||
|
@ -10,10 +10,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -21,30 +33,30 @@ function ret = eval_ast(ast, env)
|
||||
end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(3), env));
|
||||
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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
ret = EVAL(ast{3}, let_env);
|
||||
ret = EVAL(ast.get(3), let_env);
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.data(2:end);
|
||||
ret = f(args{:});
|
||||
end
|
||||
end
|
||||
|
@ -10,10 +10,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -21,47 +33,48 @@ function ret = eval_ast(ast, env)
|
||||
end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(3), env));
|
||||
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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
ret = EVAL(ast{3}, let_env);
|
||||
ret = EVAL(ast.get(3), let_env);
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end), env);
|
||||
ret = el{end};
|
||||
el = eval_ast(ast.slice(2), env);
|
||||
ret = el.get(length(el));
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ret = EVAL(ast{4}, env);
|
||||
ret = EVAL(ast.get(4), env);
|
||||
else
|
||||
ret = types.nil;
|
||||
ret = types.nil;
|
||||
end
|
||||
else
|
||||
ret = EVAL(ast{3}, env);
|
||||
ret = EVAL(ast.get(3), env);
|
||||
end
|
||||
case 'fn*'
|
||||
ret = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.data(2:end);
|
||||
ret = f(args{:});
|
||||
end
|
||||
end
|
||||
|
@ -10,10 +10,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -22,57 +34,58 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -10,10 +10,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -22,57 +34,58 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -99,7 +112,8 @@ function main(args)
|
||||
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));
|
||||
rest_args = args(2:end);
|
||||
repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
|
||||
|
||||
% core.mal: defined using the langauge itself
|
||||
rep('(def! not (fn* (a) (if a false true)))', repl_env);
|
||||
|
@ -7,24 +7,25 @@ end
|
||||
|
||||
% eval
|
||||
function ret = is_pair(ast)
|
||||
ret = iscell(ast) && length(ast) > 0;
|
||||
ret = types.sequential_Q(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))};
|
||||
ret = types.List(types.Symbol('quote'), ast);
|
||||
elseif isa(ast.get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).name, 'unquote')
|
||||
ret = ast.get(2);
|
||||
elseif is_pair(ast.get(1)) && ...
|
||||
isa(ast.get(1).get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).get(1).name, 'splice-unquote')
|
||||
ret = types.List(types.Symbol('concat'), ...
|
||||
ast.get(1).get(2), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
else
|
||||
ret = {types.Symbol('cons'), ...
|
||||
quasiquote(ast{1}), ...
|
||||
quasiquote(ast(2:end))};
|
||||
ret = types.List(types.Symbol('cons'), ...
|
||||
quasiquote(ast.get(1)), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
end
|
||||
end
|
||||
|
||||
@ -32,10 +33,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -44,62 +57,63 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'quote'
|
||||
ret = ast{2};
|
||||
ret = ast.get(2);
|
||||
return;
|
||||
case 'quasiquote'
|
||||
ast = quasiquote(ast{2}); % TCO
|
||||
ast = quasiquote(ast.get(2)); % TCO
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -126,7 +140,8 @@ function main(args)
|
||||
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));
|
||||
rest_args = args(2:end);
|
||||
repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
|
||||
|
||||
% core.mal: defined using the langauge itself
|
||||
rep('(def! not (fn* (a) (if a false true)))', repl_env);
|
||||
|
@ -7,31 +7,32 @@ end
|
||||
|
||||
% eval
|
||||
function ret = is_pair(ast)
|
||||
ret = iscell(ast) && length(ast) > 0;
|
||||
ret = types.sequential_Q(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))};
|
||||
ret = types.List(types.Symbol('quote'), ast);
|
||||
elseif isa(ast.get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).name, 'unquote')
|
||||
ret = ast.get(2);
|
||||
elseif is_pair(ast.get(1)) && ...
|
||||
isa(ast.get(1).get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).get(1).name, 'splice-unquote')
|
||||
ret = types.List(types.Symbol('concat'), ...
|
||||
ast.get(1).get(2), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
else
|
||||
ret = {types.Symbol('cons'), ...
|
||||
quasiquote(ast{1}), ...
|
||||
quasiquote(ast(2:end))};
|
||||
ret = types.List(types.Symbol('cons'), ...
|
||||
quasiquote(ast.get(1)), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
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});
|
||||
if types.list_Q(ast) && isa(ast.get(1), 'types.Symbol') && ...
|
||||
~islogical(env.find(ast.get(1)))
|
||||
f = env.get(ast.get(1));
|
||||
ret = isa(f,'types.Function') && f.is_macro;
|
||||
else
|
||||
ret = false;
|
||||
@ -40,8 +41,9 @@ end
|
||||
|
||||
function ret = macroexpand(ast, env)
|
||||
while is_macro_call(ast, env)
|
||||
mac = env.get(ast{1});
|
||||
ast = mac.fn(ast{2:end});
|
||||
mac = env.get(ast.get(1));
|
||||
args = ast.slice(2);
|
||||
ast = mac.fn(args.data{:});
|
||||
end
|
||||
ret = ast;
|
||||
end
|
||||
@ -50,10 +52,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -62,75 +76,76 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
ast = macroexpand(ast, env);
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = ast;
|
||||
return;
|
||||
end
|
||||
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'quote'
|
||||
ret = ast{2};
|
||||
ret = ast.get(2);
|
||||
return;
|
||||
case 'quasiquote'
|
||||
ast = quasiquote(ast{2}); % TCO
|
||||
ast = quasiquote(ast.get(2)); % TCO
|
||||
case 'defmacro!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(3), env));
|
||||
ret.is_macro = true;
|
||||
return;
|
||||
case 'macroexpand'
|
||||
ret = macroexpand(ast{2}, env);
|
||||
ret = macroexpand(ast.get(2), env);
|
||||
return;
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -157,7 +172,8 @@ function main(args)
|
||||
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));
|
||||
rest_args = args(2:end);
|
||||
repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
|
||||
|
||||
% core.mal: defined using the langauge itself
|
||||
rep('(def! not (fn* (a) (if a false true)))', repl_env);
|
||||
|
@ -7,31 +7,32 @@ end
|
||||
|
||||
% eval
|
||||
function ret = is_pair(ast)
|
||||
ret = iscell(ast) && length(ast) > 0;
|
||||
ret = types.sequential_Q(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))};
|
||||
ret = types.List(types.Symbol('quote'), ast);
|
||||
elseif isa(ast.get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).name, 'unquote')
|
||||
ret = ast.get(2);
|
||||
elseif is_pair(ast.get(1)) && ...
|
||||
isa(ast.get(1).get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).get(1).name, 'splice-unquote')
|
||||
ret = types.List(types.Symbol('concat'), ...
|
||||
ast.get(1).get(2), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
else
|
||||
ret = {types.Symbol('cons'), ...
|
||||
quasiquote(ast{1}), ...
|
||||
quasiquote(ast(2:end))};
|
||||
ret = types.List(types.Symbol('cons'), ...
|
||||
quasiquote(ast.get(1)), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
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});
|
||||
if types.list_Q(ast) && isa(ast.get(1), 'types.Symbol') && ...
|
||||
~islogical(env.find(ast.get(1)))
|
||||
f = env.get(ast.get(1));
|
||||
ret = isa(f,'types.Function') && f.is_macro;
|
||||
else
|
||||
ret = false;
|
||||
@ -40,8 +41,9 @@ end
|
||||
|
||||
function ret = macroexpand(ast, env)
|
||||
while is_macro_call(ast, env)
|
||||
mac = env.get(ast{1});
|
||||
ast = mac.fn(ast{2:end});
|
||||
mac = env.get(ast.get(1));
|
||||
args = ast.slice(2);
|
||||
ast = mac.fn(args.data{:});
|
||||
end
|
||||
ret = ast;
|
||||
end
|
||||
@ -50,10 +52,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -62,92 +76,95 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
ast = macroexpand(ast, env);
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = ast;
|
||||
return;
|
||||
end
|
||||
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'quote'
|
||||
ret = ast{2};
|
||||
ret = ast.get(2);
|
||||
return;
|
||||
case 'quasiquote'
|
||||
ast = quasiquote(ast{2}); % TCO
|
||||
ast = quasiquote(ast.get(2)); % TCO
|
||||
case 'defmacro!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(3), env));
|
||||
ret.is_macro = true;
|
||||
return;
|
||||
case 'macroexpand'
|
||||
ret = macroexpand(ast{2}, env);
|
||||
ret = macroexpand(ast.get(2), env);
|
||||
return;
|
||||
case 'try*'
|
||||
try
|
||||
ret = EVAL(ast{2}, env);
|
||||
ret = EVAL(ast.get(2), env);
|
||||
return;
|
||||
catch e
|
||||
if length(ast) > 2 && strcmp(ast{3}{1}.name, 'catch*')
|
||||
if length(ast) > 2 && strcmp(ast.get(3).get(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}));
|
||||
catch_env = Env(env, types.List(ast.get(3).get(2)), ...
|
||||
types.List(exc));
|
||||
ret = EVAL(ast.get(3).get(3), catch_env);
|
||||
return;
|
||||
else
|
||||
throw(e);
|
||||
end
|
||||
end
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -174,7 +191,8 @@ function main(args)
|
||||
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));
|
||||
rest_args = args(2:end);
|
||||
repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
|
||||
|
||||
% core.mal: defined using the langauge itself
|
||||
rep('(def! not (fn* (a) (if a false true)))', repl_env);
|
||||
|
@ -7,31 +7,32 @@ end
|
||||
|
||||
% eval
|
||||
function ret = is_pair(ast)
|
||||
ret = iscell(ast) && length(ast) > 0;
|
||||
ret = types.sequential_Q(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))};
|
||||
ret = types.List(types.Symbol('quote'), ast);
|
||||
elseif isa(ast.get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).name, 'unquote')
|
||||
ret = ast.get(2);
|
||||
elseif is_pair(ast.get(1)) && ...
|
||||
isa(ast.get(1).get(1),'types.Symbol') && ...
|
||||
strcmp(ast.get(1).get(1).name, 'splice-unquote')
|
||||
ret = types.List(types.Symbol('concat'), ...
|
||||
ast.get(1).get(2), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
else
|
||||
ret = {types.Symbol('cons'), ...
|
||||
quasiquote(ast{1}), ...
|
||||
quasiquote(ast(2:end))};
|
||||
ret = types.List(types.Symbol('cons'), ...
|
||||
quasiquote(ast.get(1)), ...
|
||||
quasiquote(ast.slice(2)));
|
||||
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});
|
||||
if types.list_Q(ast) && isa(ast.get(1), 'types.Symbol') && ...
|
||||
~islogical(env.find(ast.get(1)))
|
||||
f = env.get(ast.get(1));
|
||||
ret = isa(f,'types.Function') && f.is_macro;
|
||||
else
|
||||
ret = false;
|
||||
@ -40,8 +41,9 @@ end
|
||||
|
||||
function ret = macroexpand(ast, env)
|
||||
while is_macro_call(ast, env)
|
||||
mac = env.get(ast{1});
|
||||
ast = mac.fn(ast{2:end});
|
||||
mac = env.get(ast.get(1));
|
||||
args = ast.slice(2);
|
||||
ast = mac.fn(args.data{:});
|
||||
end
|
||||
ret = ast;
|
||||
end
|
||||
@ -50,10 +52,22 @@ function ret = eval_ast(ast, env)
|
||||
switch class(ast)
|
||||
case 'types.Symbol'
|
||||
ret = env.get(ast);
|
||||
case 'cell'
|
||||
ret = {};
|
||||
case 'types.List'
|
||||
ret = types.List();
|
||||
for i=1:length(ast)
|
||||
ret{end+1} = EVAL(ast{i}, env);
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.Vector'
|
||||
ret = types.Vector();
|
||||
for i=1:length(ast)
|
||||
ret.append(EVAL(ast.get(i), env));
|
||||
end
|
||||
case 'types.HashMap'
|
||||
ret = types.HashMap();
|
||||
ks = ast.keys();
|
||||
for i=1:length(ks)
|
||||
k = ks{i};
|
||||
ret.set(EVAL(k, env), EVAL(ast.get(k), env));
|
||||
end
|
||||
otherwise
|
||||
ret = ast;
|
||||
@ -62,92 +76,95 @@ end
|
||||
|
||||
function ret = EVAL(ast, env)
|
||||
while true
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = eval_ast(ast, env);
|
||||
return;
|
||||
end
|
||||
|
||||
% apply
|
||||
ast = macroexpand(ast, env);
|
||||
if ~iscell(ast)
|
||||
if ~types.list_Q(ast)
|
||||
ret = ast;
|
||||
return;
|
||||
end
|
||||
|
||||
if isa(ast{1},'types.Symbol')
|
||||
a1sym = ast{1}.name;
|
||||
if isa(ast.get(1),'types.Symbol')
|
||||
a1sym = ast.get(1).name;
|
||||
else
|
||||
a1sym = '_@$fn$@_';
|
||||
end
|
||||
switch (a1sym)
|
||||
case 'def!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(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));
|
||||
for i=1:2:length(ast.get(2))
|
||||
let_env.set(ast.get(2).get(i), EVAL(ast.get(2).get(i+1), let_env));
|
||||
end
|
||||
env = let_env;
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
case 'quote'
|
||||
ret = ast{2};
|
||||
ret = ast.get(2);
|
||||
return;
|
||||
case 'quasiquote'
|
||||
ast = quasiquote(ast{2}); % TCO
|
||||
ast = quasiquote(ast.get(2)); % TCO
|
||||
case 'defmacro!'
|
||||
ret = env.set(ast{2}, EVAL(ast{3}, env));
|
||||
ret = env.set(ast.get(2), EVAL(ast.get(3), env));
|
||||
ret.is_macro = true;
|
||||
return;
|
||||
case 'macroexpand'
|
||||
ret = macroexpand(ast{2}, env);
|
||||
ret = macroexpand(ast.get(2), env);
|
||||
return;
|
||||
case 'try*'
|
||||
try
|
||||
ret = EVAL(ast{2}, env);
|
||||
ret = EVAL(ast.get(2), env);
|
||||
return;
|
||||
catch e
|
||||
if length(ast) > 2 && strcmp(ast{3}{1}.name, 'catch*')
|
||||
if length(ast) > 2 && strcmp(ast.get(3).get(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}));
|
||||
catch_env = Env(env, types.List(ast.get(3).get(2)), ...
|
||||
types.List(exc));
|
||||
ret = EVAL(ast.get(3).get(3), catch_env);
|
||||
return;
|
||||
else
|
||||
throw(e);
|
||||
end
|
||||
end
|
||||
case 'do'
|
||||
el = eval_ast(ast(2:end-1), env);
|
||||
ast = ast{end}; % TCO
|
||||
el = eval_ast(ast.slice(2,length(ast)-1), env);
|
||||
ast = ast.get(length(ast)); % TCO
|
||||
case 'if'
|
||||
cond = EVAL(ast{2}, env);
|
||||
cond = EVAL(ast.get(2), env);
|
||||
if strcmp(class(cond), 'types.Nil') || ...
|
||||
(islogical(cond) && cond == false)
|
||||
if length(ast) > 3
|
||||
ast = ast{4}; % TCO
|
||||
ast = ast.get(4); % TCO
|
||||
else
|
||||
ret = types.nil;
|
||||
return;
|
||||
end
|
||||
else
|
||||
ast = ast{3}; % TCO
|
||||
ast = ast.get(3); % TCO
|
||||
end
|
||||
case 'fn*'
|
||||
fn = @(varargin) EVAL(ast{3}, Env(env, ast{2}, varargin));
|
||||
ret = types.Function(fn, ast{3}, env, ast{2});
|
||||
fn = @(varargin) EVAL(ast.get(3), Env(env, ast.get(2), ...
|
||||
types.List(varargin{:})));
|
||||
ret = types.Function(fn, ast.get(3), env, ast.get(2));
|
||||
return;
|
||||
otherwise
|
||||
el = eval_ast(ast, env);
|
||||
f = el{1};
|
||||
args = el(2:end);
|
||||
f = el.get(1);
|
||||
args = el.slice(2);
|
||||
if isa(f, 'types.Function')
|
||||
env = Env(f.env, f.params, args);
|
||||
ast = f.ast; % TCO
|
||||
else
|
||||
ret = f(args{:});
|
||||
ret = f(args.data{:});
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -174,7 +191,8 @@ function main(args)
|
||||
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));
|
||||
rest_args = args(2:end);
|
||||
repl_env.set(types.Symbol('*ARGV*'), types.List(rest_args{:}));
|
||||
|
||||
% core.mal: defined using the langauge itself
|
||||
rep('(def! *host-language* "matlab")', repl_env);
|
||||
|
@ -7,17 +7,18 @@ classdef types
|
||||
function ret = equal(a,b)
|
||||
ret = false;
|
||||
ota = class(a); otb = class(b);
|
||||
if ~(strcmp(ota,otb) || (iscell(a) && iscell(b)))
|
||||
if ~(strcmp(ota,otb) || ...
|
||||
(types.sequential_Q(a) && types.sequential_Q(b)))
|
||||
return;
|
||||
end
|
||||
switch (ota)
|
||||
case 'cell'
|
||||
case {'types.List', 'types.Vector'}
|
||||
if ~(length(a) == length(b))
|
||||
return
|
||||
return;
|
||||
end
|
||||
for i=1:length(a)
|
||||
if ~(types.equal(a{i}, b{i}))
|
||||
return
|
||||
if ~(types.equal(a.get(i), b.get(i)))
|
||||
return;
|
||||
end
|
||||
end
|
||||
ret = true;
|
||||
@ -27,6 +28,30 @@ classdef types
|
||||
ret = a == b;
|
||||
end
|
||||
end
|
||||
|
||||
function ret = sequential_Q(obj)
|
||||
ret = strcmp(class(obj), 'types.List') || ...
|
||||
strcmp(class(obj), 'types.Vector');
|
||||
end
|
||||
|
||||
function ret = list_Q(obj)
|
||||
ret = strcmp(class(obj), 'types.List');
|
||||
end
|
||||
function ret = vector_Q(obj)
|
||||
ret = strcmp(class(obj), 'types.Vector');
|
||||
end
|
||||
function ret = hash_map_Q(obj)
|
||||
ret = strcmp(class(obj), 'types.HashMap');
|
||||
end
|
||||
|
||||
function ret = keyword(str)
|
||||
ret = strcat(native2unicode(hex2dec('029e'),'UTF-8'), ...
|
||||
str(2:end));
|
||||
end
|
||||
function ret = keyword_Q(obj)
|
||||
ret = length(obj) > 1 && ...
|
||||
strcmp(obj(1), native2unicode(hex2dec('029e'),'UTF-8'));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user