             [epicea.utils.defmultiple :refer [defmultiple]]
             [epicea.utils.access :as access] :reload-all))
 
-(spec/def ::double (spec/or :type (partial = :double)
+(spec/def ::unspecified nil?)
+(spec/def ::bool boolean?)
+(spec/def ::float float?)
+(spec/def ::double double?)
+(spec/def ::long #(instance? java.lang.Long %))
+(spec/def ::int #(instance? java.lang.Integer %))
+(spec/def ::primitive (spec/or
+                       :bool (partial = :bool)
+                       :float (partial = :float)
+                       :long (partial = :long)
+                       :double (partial = :double)
+                       :int (partial = :int)))
+(spec/def ::dynamic (partial = :dynamic))
+
+(spec/def ::number (spec/or :type #(or (= % :number)
+                                       (= % :double))
                             :literal number?))
-(spec/def ::unspecified nil?)
 (spec/def ::count number?)
 (spec/def ::key keyword?)
 


(spec/def ::vecdata (spec/cat
                     :type (partial = :vecdata)
                     :values (spec/* ::sized-type)))

;; Compile time tagging
(spec/def ::tagged (spec/cat

(def size-mul *)
(def size-max max)


;;;; Compute the size of a ::sized-type
(defmultiple compute-size get-pair-tag
  (:vec [x] (let [value (get-pair-value x)]
              (size-mul (:count value) (compute-size (:type value)))))
  (:number [x] 1)
  (:tagged [x] (compute-size (:data (get-pair-value x))))
  (:union [x] (reduce size-max (map compute-size (-> x get-pair-value :alts))))
  (:record [x] (reduce size-add (map (comp compute-size :value) 
                                    (-> x get-pair-value :fields))))
  (:unspecified [x] nil)
  (:vecdata [x] (reduce size-add (map compute-size (-> x get-pair-value :data)))))

  
(assert (= 1 (compute-size (spec/conform ::sized-type [:tag 119 :number]))))
(assert (= 1 (compute-size (spec/conform ::sized-type 3))))
(assert (= 3 (compute-size (spec/conform ::sized-type [:vec :number 3]))))
(assert (= 2 (compute-size (spec/conform ::sized-type [:union :number [:vec :number 2]]))))
(assert (= 2 (compute-size (spec/conform ::sized-type [:record :a :number :b :number]))))
(assert (= 3 (compute-size (spec/conform 
                            ::sized-type 
                            [:record :a :number :b :number :c :number]))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Grammar
(spec/def ::arg-name symbol?)

(spec/def ::arg-decl (spec/cat
                    :type ::type
                    :name ::arg-name))

(spec/def ::arg-list (spec/* ::arg-decl))

(spec/def ::statement (constantly true))

(spec/def ::typed-fun (spec/cat
                       :return-type ::type
                       :arg-list (spec/spec ::arg-list)
                       :body (spec/* ::statement)))
(assert (not= 
         ::spec/invalid 
         (spec/conform ::typed-fun [nil [:double 'a :double 'b] :a :b :c])))
