crystal: all tests for stepA pass
[jackhill/mal.git] / crystal / types.cr
index 4ecc742..af61cb7 100644 (file)
@@ -1,8 +1,14 @@
+require "./printer"
+
 module Mal
   class Symbol
     property :str
     def initialize(@str)
     end
+
+    def ==(other : Symbol)
+      @str == other.str
+    end
   end
 
   class List < Array(Type)
@@ -14,20 +20,95 @@ module Mal
   class HashMap < Hash(String, Type)
   end
 
+  class Atom
+    property :val
+    def initialize(@val)
+    end
+
+    def ==(rhs : Atom)
+      @val == rhs.val
+    end
+  end
+
+  class Closure
+    property :ast, :params, :env, :fn
+    def initialize(@ast, @params, @env, @fn)
+    end
+  end
+
   class Type
-    alias ValueType = Nil | Bool | Int32 | String | Symbol | List | Vector | HashMap | ((Array(Type) -> Type))
+    alias Func = (Array(Type) -> Type)
+    alias ValueType = Nil | Bool | Int32 | String | Symbol | List | Vector | HashMap | Func | Closure | Atom
+
+    is_macro :: Bool
+    meta :: Type
+
+    property :is_macro, :meta
 
     def initialize(@val : ValueType)
+      @is_macro = false
+      @meta = nil
     end
 
     def initialize(other : Type)
-      @val = other.val
+      @val = other.unwrap
+      @is_macro = other.is_macro
+      @meta = other.meta
     end
 
     def unwrap
       @val
     end
+
+    def macro?
+      @is_macro
+    end
+
+    def to_s
+      pr_str(self)
+    end
+
+    def dup
+      Type.new(@val).tap do |t|
+        t.is_macro = @is_macro
+        t.meta = @meta
+      end
+    end
+
+    def ==(other : Type)
+      @val == other.unwrap
+    end
+
+    macro rel_op(*ops)
+      {% for op in ops %}
+        def {{op.id}}(other : Mal::Type)
+          l, r = @val, other.unwrap
+            {% for t in [Int32, String] %}
+              if l.is_a?({{t}}) && r.is_a?({{t}})
+                return (l) {{op.id}} (r)
+              end
+            {% end %}
+            if l.is_a?(Symbol) && r.is_a?(Symbol)
+              return l.str {{op.id}} r.str
+            end
+          false
+        end
+      {% end %}
+    end
+
+    rel_op :<, :>, :<=, :>=
   end
 
-  alias Func = Array(Type) -> Type
+  alias Func = Type::Func
+end
+
+macro gen_type(t, *args)
+  Mal::Type.new {{t.id}}.new({{*args}})
 end
+
+class Array
+  def to_mal(t = Mal::List)
+    each_with_object(t.new){|e, l| l << e}
+  end
+end
+