1
1
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:
Joel Martin 2015-02-09 23:20:23 -06:00
parent 7f567f3645
commit 6a572dff25
18 changed files with 677 additions and 291 deletions

47
matlab/+types/HashMap.m Normal file
View 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
View 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
View 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

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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