(def add [a b] [:number :number :number]
(+ a b))
(def 2str [obj] [:any :string]
tostring(obj))
(def opt [?a] [:?number :number]
(or ?a 1))
(def len [obj] [[:table :string] :number]
(if (table? table obj)
(length obj)
(string.length obj)
(def add [x ...] [:number :varg :number]
...)
(fn _2str [obj]
"obj: string or table"
(if
(= (type obj) :table) (table.concat obj " or ")
(= (type obj) :string) obj
(assert false "expected string or table")))
(fn def [name args types ...]
"Example:
(def add [a b] [:number :number :number] (+ a b))
(def 2str [obj] [:any :string] ...)
(def opt [?a] [:?number :number] ...)
(def len [obj] [[:table :string] :number] ...)
(def add [x ...] [:number :varg :number] ...)"
(assert-compile (not (= (type name) :string)) "name expects symbol, vector, or list as first arugument" name)
(assert-compile (= (type types) :table) "types expects table as first arugment" types)
`(fn ,name [,(unpack args)]
(let [rest# (λ [lst#] [(unpack lst# 2)])
empty?# (λ [str#] (or (= str# nil) (= str# "")))
type?# (λ [?obj# type#] (if (= type# :any) true (= (type ?obj#) type#)))]
(fn type-eq# [?actual# expect#]
(match (type expect#)
:string (if (let [res# (string.match expect# "^?")]
(or (= res# nil) (= res# "")))
(type?# ?actual# expect#)
(or (type?# ?actual# (string.match expect# "%w+"))
(type?# ?actual# :nil)))
:table (or (= (type ?actual#) (. expect# 1)) (type-eq# ?actual# (rest# expect#)))
_# false))
,(icollect [i# k# (ipairs args)]
(if (< i# (length types))
(if (varg? k#)
(assert-compile (= :varg (. types i#)) "[type mismatch] ... expects :varg" types)
`(assert (type-eq# ,k# ,(. types i#))
(.. "argument " (tostring ,k#) "[type mismatch] must be " ,(_2str (. types i#)) " but " (type ,k#))))
(assert-compile false "too many arguments" args)))
(let [ret# (do ,...)]
(assert (type-eq# ret# ,(. types (length types))) (.. "return value must be " ,(_2str (. types (length types))) " but " (type ret#)))
ret#))))
values
in the def
.
I want to fix it someday.