matlab: add keyword, vector, hash-map support.
authorJoel Martin <github@martintribe.org>
Tue, 10 Feb 2015 05:20:23 +0000 (23:20 -0600)
committerJoel Martin <github@martintribe.org>
Tue, 10 Feb 2015 07:46:17 +0000 (01:46 -0600)
Switch List to full object like vector and hash-map.

18 files changed:
matlab/+types/HashMap.m [new file with mode: 0644]
matlab/+types/List.m [new file with mode: 0644]
matlab/+types/Vector.m [new file with mode: 0644]
matlab/Env.m
matlab/Makefile
matlab/core.m
matlab/printer.m
matlab/reader.m
matlab/step2_eval.m
matlab/step3_env.m
matlab/step4_if_fn_do.m
matlab/step5_tco.m
matlab/step6_file.m
matlab/step7_quote.m
matlab/step8_macros.m
matlab/step9_try.m
matlab/stepA_interop.m
matlab/types.m

diff --git a/matlab/+types/HashMap.m b/matlab/+types/HashMap.m
new file mode 100644 (file)
index 0000000..f48ba81
--- /dev/null
@@ -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
diff --git a/matlab/+types/List.m b/matlab/+types/List.m
new file mode 100644 (file)
index 0000000..fd27cb4
--- /dev/null
@@ -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
diff --git a/matlab/+types/Vector.m b/matlab/+types/Vector.m
new file mode 100644 (file)
index 0000000..3f854fb
--- /dev/null
@@ -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
index 1a7b3fc..0a09db8 100644 (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
index db69428..445512a 100644 (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)
 
 
index ee86ad9..5947c2d 100644 (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;
index 11e22a8..fa55e06 100644 (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'
index 0a985e8..7265ab3 100644 (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
index a388145..34559c0 100644 (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
 
index f5b953b..801ad6f 100644 (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
index dc9f28b..0e24b28 100644 (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
index 8ffb261..a4385e4 100644 (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
index 8e50633..cfca152 100644 (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);
index 524d547..cb572a6 100644 (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);
index a4b45c2..8757c7a 100644 (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);
index 4880aac..a9bf92f 100644 (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);
index 4972095..f9e3364 100644 (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);
index 7263fb2..91f052d 100644 (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