release 0.1.0

This commit is contained in:
rus07tam 2026-05-06 12:21:06 +03:00
commit 30d94536a9
90 changed files with 7722 additions and 0 deletions

17
modules/core/assert.owa Normal file
View file

@ -0,0 +1,17 @@
(namespace assert
(defmacro ok! [] (if (bool.and $%&)
:true
(throw! "assertion failed")))
(defmacro not! [] (if (bool.and $%&)
(throw! "assertion failed")
:true))
(defmacro eq! [] (if-eq $(%&)
:true
(throw! "assertion failed")))
(defmacro nq! [] (if-eq $(%&)
(throw! "assertion failed")
:true))
)

10
modules/core/ast.owa Normal file
View file

@ -0,0 +1,10 @@
(namespace ast
(defmacro pack! [v]
(builtins.ast.pack $v))
(defmacro value! [v]
(map.get (ast.pack! $v) :data))
(defmacro type! [v]
(map.get (ast.pack! $v) :type))
)

12
modules/core/branch.owa Normal file
View file

@ -0,0 +1,12 @@
(seq
;; builtins
(def if-eq builtins.cond.if_eq)
(def match builtins.cond.match)
(def if-has builtins.cond.if_has)
(defmacro if [cond then]
(if-eq (:false $cond) (seq $%&) (seq $then)))
(defmacro unless [cond then]
(if-eq (:false $cond) (seq $then) (seq $%&)))
)

25
modules/core/cmp.owa Normal file
View file

@ -0,0 +1,25 @@
(seq
(defmacro eq? <Bool>[]
(if-eq ($%%) :true :false))
(defmacro nq? <Bool>[]
(if-eq ($%%) :false :true))
(fn cmp <Keyword>[left right]
(match (builtins.cmp left right)
(-1 :less)
(0 :equal)
(1 :greater)))
(fn lt? <Bool>[left right]
(eq? (cmp left right) :less))
(fn lte? <Bool>[left right]
(bool.or (lt? left right) (eq? left right)))
(fn gt? <Bool>[left right]
(eq? (cmp left right) :greater))
(fn gte? <Bool>[left right]
(bool.or (gt? left right) (eq? left right)))
)

22
modules/core/error.owa Normal file
View file

@ -0,0 +1,22 @@
(seq
(fn throw! []
(throw.runtime_error! (str.join "" %%)))
(fn panic! [v]
(throw.panic! v))
(fn unreachable! []
(throw! "Reached unreachable code"))
(fn not_implemented! []
(throw! "Not implemented"))
(def try builtins.errors.try)
(defmacro catch [pattern then]
(lambda [msg type] (if-eq (type $pattern)
$then
:null)))
(namespace throw
(def panic! builtins.errors.throw_panic)
(def type_error! builtins.errors.throw_type_error)
(def arity_error! builtins.errors.throw_arity_error)
(def runtime_error! builtins.errors.throw_runtime_error)
))

32
modules/core/ffi.owa Normal file
View file

@ -0,0 +1,32 @@
(namespace ffi
(def unload builtins.ffi.unload)
(def ctypes [
:i8 :i16 :i32 :i64
:u8 :u16 :u32 :u64
:f32 :f64
:ptr
:str
])
(defmacro extern [name libname]
(namespace $name
(builtins.ffi.load $libname)
(fn call [symbol signature args]
(builtins.ffi.call $libname symbol signature args)
)
)
)
(defmacro native [name libname]
(namespace $name
(builtins.ffi.load $libname)
(defmacro fn [name symbol args ret]
(def '$name #(builtins.ffi.call $libname '$symbol ['$args '$ret] %&))
)
$%&
)
)
(extern ucrtbase "ucrtbase.dll")
)

8
modules/core/lambda.owa Normal file
View file

@ -0,0 +1,8 @@
(seq
;; builtins
(def apply builtins.apply)
(def lambda builtins.lambda)
(defmacro fn [name params body]
(def $name (lambda $params $body)))
)

23
modules/core/loop.owa Normal file
View file

@ -0,0 +1,23 @@
(seq
(def break builtins.flow.break)
(def continue builtins.flow.continue)
(def loop builtins.flow.loop)
(defmacro return []
(builtins.flow.return $%% null))
(defmacro while [cond] (loop
(if (eq? $cond :false) (break) :null)
$%&))
(defmacro do-while [cond] (loop
$%&
(if (eq? $cond :false) (break) :null)))
(defmacro for [var iter] (vec.map
(lambda [x] (seq
(def $var x)
$%&))
(vec.from $iter)
))
)

25
modules/core/main.owa Normal file
View file

@ -0,0 +1,25 @@
(builtins.seq
(builtins.def include builtins.include)
(include "scope")
(include "branch")
(include "lambda")
(def exec builtins.exec)
(def trace builtins.trace)
(def typeof builtins.typeof)
(fn identity [x] x)
(include "types")
(include "assert")
(include "ast")
(include "cmp")
(include "error")
(include "ffi")
(include "loop")
(include "math")
(include "test")
(test.space "core" (include "tests/main"))
)

14
modules/core/math.owa Normal file
View file

@ -0,0 +1,14 @@
(seq
(def + builtins.math.add)
(def - builtins.math.sub)
(def * builtins.math.mul)
(def / builtins.math.div)
(def % builtins.math.mod)
(def ** builtins.math.pow)
(fn ++ <Number>[<Number>x]
(+ x 1))
(fn -- <Number>[<Number>x]
(- x 1))
)

22
modules/core/scope.owa Normal file
View file

@ -0,0 +1,22 @@
(builtins.seq
;; builtins
(builtins.def def builtins.def)
(def lookup builtins.lookup)
(def set! builtins.set!)
(def scope builtins.scope)
;; macro
(def macro builtins.macro)
(def defmacro (macro [name params body]
(def $name (macro $params $body))))
;; flow
(defmacro seq []
(builtins.seq :null $%&))
(defmacro namespace [name] (def $name (scope $%&)))
;; other
(defmacro mut! [v fn] (seq
(set! (ast.value! $v) ($fn $v $%&)))
)
)

16
modules/core/test.owa Normal file
View file

@ -0,0 +1,16 @@
(namespace test
(def target (lookup __test__ null))
(defmacro if-target [then]
(if-eq (test.target null) :null (seq $%%)))
(defmacro case [name] (test.if-target
(builtins.errors.try
(scope $%& (trace "Running test \"" $name "\": OK ✅"))
#(trace "Running test \"" $name "\": FAILED ❌ (" %2 ": " %1 ")"))))
(defmacro space [name] (if
(bool.or (eq? test.target $name) (eq? test.target "."))
(namespace $name (seq $%&))
))
)

View file

@ -0,0 +1,38 @@
(namespace tests.bool
(test.case "bool.bool?"
(assert.ok! (bool? true))
(assert.ok! (bool? false))
(assert.not! (bool? 1))
)
(test.case "bool.not"
(assert.eq! (bool.not true) false)
(assert.eq! (bool.not false) true)
)
(test.case "bool.and"
(assert.ok! (bool.and true true))
(assert.not! (bool.and true false))
(assert.not! (bool.and false true))
(assert.not! (bool.and false false))
)
(test.case "bool.or"
(assert.ok! (bool.or true true))
(assert.ok! (bool.or true false))
(assert.ok! (bool.or false true))
(assert.not! (bool.or false false))
)
(test.case "bool.nand"
(assert.not! (bool.nand true true))
(assert.ok! (bool.nand true false))
(assert.ok! (bool.nand false true))
(assert.ok! (bool.nand false false))
)
(test.case "bool.new"
(assert.eq! (bool.new true) true)
(assert.eq! (bool.new false) false)
)
)

View file

@ -0,0 +1,41 @@
(namespace tests.cmp
(test.case "cmp.eq?"
(assert.ok! (eq? 1 1))
(assert.not! (eq? 1 2))
)
(test.case "cmp.nq?"
(assert.ok! (nq? 1 2))
(assert.not! (nq? 1 1))
)
(test.case "cmp.cmp"
(assert.eq! (cmp 1 2) :less)
(assert.eq! (cmp 2 1) :greater)
(assert.eq! (cmp 1 1) :equal)
)
(test.case "cmp.lt?"
(assert.ok! (lt? 1 2))
(assert.not! (lt? 2 1))
(assert.not! (lt? 1 1))
)
(test.case "cmp.lte?"
(assert.ok! (lte? 1 2))
(assert.ok! (lte? 1 1))
(assert.not! (lte? 2 1))
)
(test.case "cmp.gt?"
(assert.ok! (gt? 2 1))
(assert.not! (gt? 1 2))
(assert.not! (gt? 1 1))
)
(test.case "cmp.gte?"
(assert.ok! (gte? 2 1))
(assert.ok! (gte? 1 1))
(assert.not! (gte? 1 2))
)
)

View file

@ -0,0 +1,42 @@
(namespace tests.loop
(test.case "while.basic-true"
(def counter 0)
(while (eq? counter 0)
(set! counter 1))
(assert.eq! counter 1)
)
(test.case "while.condition-false"
(def counter 5)
(while (lt? counter 0)
(set! counter (+ counter 1)))
(assert.eq! counter 5)
)
(test.case "while.counting-loop"
(def n 0)
(while (lt? n 10)
(set! n (+ n 1)))
(assert.eq! n 10)
)
(test.case "while.loop-condition-reached"
(def i 0)
(while (lt? i 5)
(set! i (+ i 1)))
(assert.eq! i 5)
)
(test.case "while.nested"
(def outer 0)
(def inner 0)
(def total 0)
(while (lt? outer 3)
(set! inner 0)
(while (lt? inner 2)
(set! total (+ total 1))
(set! inner (+ inner 1)))
(set! outer (+ outer 1)))
(assert.eq! total 6)
)
)

View file

@ -0,0 +1,14 @@
(seq
(include "vec")
(include "str")
(include "set")
(include "map")
(include "bool")
(include "option")
(include "scope")
(include "null")
(include "cmp")
(include "math")
(include "types")
(include "loop")
)

View file

@ -0,0 +1,83 @@
(namespace tests.map
(test.case "map.len"
(assert.eq! (map.len (map.new)) 0)
)
(test.case "map.empty?"
(assert.ok! (map.empty? (map.new)))
)
(test.case "map.is-empty?"
(assert.ok! (map.is-empty? (map.new)))
)
(test.case "map.get"
(seq
(def m (map.new :a 1 :b 2))
(assert.eq! (map.get m :a) 1)
(assert.eq! (map.get m :b) 2)
(assert.eq! (map.get m :c) null)
)
)
(test.case "map.put"
(seq
(def m (map.new :a 1))
(def m2 (map.put m :b 2))
(assert.eq! (map.len m2) 2)
(assert.eq! (map.get m2 :b) 2)
)
)
(test.case "map.has-key?"
(seq
(def m (map.new :a 1 :b 2))
(assert.ok! (map.has-key? m :a))
(assert.not! (map.has-key? m :c))
)
)
(test.case "map.has-value?"
(seq
(def m (map.new :a 1 :b 2))
(assert.ok! (map.has-value? m 1))
(assert.not! (map.has-value? m 5))
)
)
(test.case "map.keys"
(seq
(def m (map.new :a 1 :b 2))
(def ks (map.keys m))
(assert.eq! (vec.len ks) 2)
)
)
(test.case "map.values"
(seq
(def m (map.new :a 1 :b 2))
(def vs (map.values m))
(assert.eq! (vec.len vs) 2)
)
)
(test.case "map.merge"
(seq
(def m1 (map.new :a 1 :b 2))
(def m2 (map.new :b 20 :c 3))
(def m3 (map.merge m1 m2))
(assert.eq! (map.get m3 :a) 1)
(assert.eq! (map.get m3 :b) 20)
(assert.eq! (map.get m3 :c) 3)
)
)
(test.case "map.dissoc"
(seq
(def m (map.new :a 1 :b 2 :c 3))
(def m2 (map.dissoc m :b))
(assert.eq! (map.len m2) 2)
(assert.not! (map.has-key? m2 :b))
)
)
)

View file

@ -0,0 +1,40 @@
(namespace tests.math
(test.case "math.add"
(assert.eq! (+ 1 2) 3)
(assert.eq! (+ 0 0) 0)
)
(test.case "math.sub"
(assert.eq! (- 5 3) 2)
(assert.eq! (- 1 1) 0)
)
(test.case "math.mul"
(assert.eq! (* 3 4) 12)
(assert.eq! (* 0 5) 0)
)
(test.case "math.div"
(assert.eq! (/ 10 2) 5)
)
(test.case "math.mod"
(assert.eq! (% 10 3) 1)
(assert.eq! (% 5 5) 0)
)
(test.case "math.pow"
(assert.eq! (** 2 3) 8)
(assert.eq! (** 5 2) 25)
)
(test.case "math.++"
(assert.eq! (++ 5) 6)
(assert.eq! (++ 0) 1)
)
(test.case "math.--"
(assert.eq! (-- 5) 4)
(assert.eq! (-- 0) -1)
)
)

View file

@ -0,0 +1,8 @@
(namespace tests.null
(test.case "null.null?"
(assert.ok! (null? null))
(assert.ok! (null? :nil))
(assert.not! (null? 0))
(assert.not! (null? false))
)
)

View file

@ -0,0 +1,33 @@
(namespace tests.option
(test.case "option.ok"
(seq
(def opt (option.ok 42))
(assert.ok! (option.ok? opt))
(assert.eq! (vec.get opt 1) 42)
)
)
(test.case "option.err"
(seq
(def opt (option.err "error"))
(assert.ok! (option.err? opt))
(assert.eq! (vec.get opt 1) "error")
)
)
(test.case "option.ok?"
(assert.ok! (option.ok? (option.ok 1)))
(assert.not! (option.ok? (option.err "x")))
)
(test.case "option.err?"
(assert.ok! (option.err? (option.err "x")))
(assert.not! (option.err? (option.ok 1)))
)
(test.case "option.option?"
(assert.ok! (option? (option.ok 1)))
(assert.ok! (option? (option.err "x")))
(assert.not! (option? [1 2]))
)
)

View file

@ -0,0 +1,78 @@
(namespace tests.scopes
(test.case "def"
(def x 1)
(assert.eq! x 1)
(def [y z] [1 2])
(assert.eq! y 1)
(assert.eq! z 2)
)
(test.case "def bounded"
(def x 1)
(try
(def x 2)
(catch :Panic (return))
)
(unreachable!)
)
(test.case "set!"
(def [x y] [1 1])
(set! x 2)
(assert.eq! x 2)
(set! [x y] [3 4])
(assert.eq! x 3)
(assert.eq! y 4)
)
(test.case "set! unbounded"
(try
(set! x 2)
(catch :UnboundVariable (return))
)
(unreachable!)
)
(test.case "lookup"
(def x 1)
(assert.eq! (lookup x null) 1)
(assert.eq! (lookup y null) null)
)
(test.case "inheritance"
(scope
(def x 1)
(scope
(assert.eq! x 1)
(set! x 2)
)
(assert.eq! x 2)
)
)
(test.case "shadowing"
(scope
(def x 100)
(scope
(def x 1)
)
(assert.eq! x 100)
)
(assert.eq! (lookup x null) null)
)
(test.case "leakage"
(scope (def x 1))
(assert.eq! (lookup x null) null)
(scope (def y 2))
(assert.eq! (lookup y null) null)
(scope
(scope (def z 1))
(assert.eq! (lookup z null) null)
)
)
)

View file

@ -0,0 +1,44 @@
(namespace tests.set
(test.case "set.empty?"
(assert.ok! (set.empty? (set.new)))
(assert.not! (set.empty? (set.new 1)))
)
(test.case "set.len"
(assert.eq! (set.len (set.new)) 0)
(assert.eq! (set.len (set.new 1)) 1)
(assert.eq! (set.len (set.new 1 2 3)) 3)
)
(test.case "set.has"
(assert.ok! (set.has (set.new 1 2 3) 2))
(assert.not! (set.has (set.new 1 2 3) 5))
)
(test.case "set.add"
(assert.ok! (set.has (set.add (set.new 1 2) 3) 3))
(assert.eq! (set.len (set.add (set.new 1 2) 3)) 3)
)
(test.case "set.remove"
(assert.not! (set.has (set.remove (set.new 1 2 3) 2) 2))
(assert.eq! (set.len (set.remove (set.new 1 2 3) 2)) 2)
)
(test.case "set.union"
(assert.eq! (set.len (set.union (set.new 1 2) (set.new 2 3))) 3)
(assert.ok! (set.has (set.union (set.new 1 2) (set.new 2 3)) 1))
(assert.ok! (set.has (set.union (set.new 1 2) (set.new 2 3)) 3))
)
(test.case "set.intersection"
(assert.eq! (set.len (set.intersection (set.new 1 2 3) (set.new 2 3 4))) 2)
(assert.ok! (set.has (set.intersection (set.new 1 2 3) (set.new 2 3 4)) 2))
(assert.ok! (set.has (set.intersection (set.new 1 2 3) (set.new 2 3 4)) 3))
)
(test.case "set.difference"
(assert.eq! (set.len (set.difference (set.new 1 2 3) (set.new 2 3))) 1)
(assert.ok! (set.has (set.difference (set.new 1 2 3) (set.new 2 3)) 1))
)
)

View file

@ -0,0 +1,67 @@
(namespace tests.str
(test.case "str.len"
(assert.eq! (str.len "") 0)
(assert.eq! (str.len "hello") 5)
)
(test.case "str.empty?"
(assert.ok! (str.empty? ""))
(assert.not! (str.empty? "a"))
)
(test.case "str.is-empty?"
(assert.ok! (str.is-empty? ""))
(assert.not! (str.is-empty? "a"))
)
(test.case "str.first"
(assert.eq! (str.first "hello") "h")
)
(test.case "str.last"
(assert.eq! (str.last "hello") "o")
)
(test.case "str.concat"
(assert.eq! (str.concat "hello" " " "world") "hello world")
)
(test.case "str.substring"
(assert.eq! (str.substring "hello" 0 3) "hel")
(assert.eq! (str.substring "hello" 1 4) "ell")
)
(test.case "str.take"
(assert.eq! (str.take "hello" 3) "hel")
)
(test.case "str.drop"
(assert.eq! (str.drop "hello" 2) "llo")
)
(test.case "str.reverse"
(assert.eq! (str.reverse "hello") "olleh")
)
(test.case "str.starts-with?"
(assert.ok! (str.starts-with? "hello" "hel"))
(assert.not! (str.starts-with? "hello" "xyz"))
)
(test.case "str.ends-with?"
(assert.ok! (str.ends-with? "hello" "lo"))
(assert.not! (str.ends-with? "hello" "xyz"))
)
(test.case "str.split"
(assert.eq! (str.split "hello world" " ") ["hello" "world"])
(assert.eq! (str.split "hello world" "w") ["hello " "orld"])
(assert.eq! (str.split "hello world" "x") ["hello world"])
)
(test.case "str.join"
(assert.eq! (str.join " " ["hello" "world"]) "hello world")
(assert.eq! (str.join "w" ["hello " "orld"]) "hello world")
(assert.eq! (str.join "x" ["hello world"]) "hello world")
)
)

View file

@ -0,0 +1,53 @@
(namespace tests.types
(test.case "types.is?"
(assert.ok! (is? 42 :int))
(assert.ok! (is? "hello" :str))
(assert.ok! (is? [] :vec))
(assert.not! (is? 42 :str))
)
(test.case "types.guard"
(seq
(def int-guard (guard :int))
(assert.ok! (int-guard 42))
(assert.not! (int-guard "42"))
)
)
(test.case "types.kw?"
(assert.ok! (kw? :keyword))
(assert.not! (kw? "not-keyword"))
)
(test.case "types.type?"
(assert.ok! (type? :int))
(assert.ok! (type? :str))
(assert.not! (type? :unknown))
)
(test.case "types.str?"
(assert.ok! (str? "hello"))
(assert.not! (str? 42))
)
(test.case "types.vec?"
(assert.ok! (vec? []))
(assert.ok! (vec? [1 2 3]))
(assert.not! (vec? 42))
)
(test.case "types.bool?"
(assert.ok! (bool? true))
(assert.ok! (bool? false))
(assert.not! (bool? 1))
)
(test.case "types.null?"
(assert.ok! (null? null))
(assert.not! (null? 0))
)
(test.case "types.map.is?"
(assert.ok! (map.is? (map.new)))
)
)

View file

@ -0,0 +1,91 @@
(namespace tests.vec
(test.case "vec.empty?"
(assert.ok! (vec.empty? []))
(assert.not! (vec.empty? [1]))
)
(test.case "vec.len"
(assert.eq! (vec.len []) 0)
(assert.eq! (vec.len [1]) 1)
(assert.eq! (vec.len [1 2 3]) 3)
)
(test.case "vec.first"
(assert.eq! (vec.first [1 2 3]) 1)
(assert.eq! (vec.first ["a"]) "a")
)
(test.case "vec.last"
(assert.eq! (vec.last [1 2 3]) 3)
(assert.eq! (vec.last ["x"]) "x")
)
(test.case "vec.get"
(assert.eq! (vec.get [10 20 30] 0) 10)
(assert.eq! (vec.get [10 20 30] 1) 20)
(assert.eq! (vec.get [10 20 30] 2) 30)
)
(test.case "vec.map"
(assert.eq!
(vec.map (lambda [x _] (+ x 1)) [1 2 3])
[2 3 4])
)
(test.case "vec.filter"
(assert.eq!
(vec.filter (lambda [x _] (gt? x 1)) [1 2 3])
[2 3])
)
(test.case "vec.has"
(assert.ok! (vec.has [1 2 3] 2))
(assert.not! (vec.has [1 2 3] 5))
)
(test.case "vec.includes?"
(assert.ok! (vec.includes? [1 2 3] 2))
(assert.not! (vec.includes? [1 2 3] 5))
)
(test.case "vec.find"
(assert.eq!
(vec.find (lambda [x _] (gt? x 2)) [1 2 3 4])
3)
)
(test.case "vec.reverse"
(assert.eq! (vec.reverse [1 2 3]) [3 2 1])
)
(test.case "vec.take"
(assert.eq! (vec.take [1 2 3 4 5] 3) [1 2 3])
)
(test.case "vec.drop"
(assert.eq! (vec.drop [1 2 3 4 5] 2) [3 4 5])
)
(test.case "vec.uniq"
(assert.eq! (vec.uniq [1 2 2 3 3 3]) [1 2 3])
)
(test.case "vec.every?"
(assert.ok! (vec.every? (lambda [x _] (gt? x 0)) [1 2 3]))
(assert.not! (vec.every? (lambda [x _] (gt? x 1)) [1 2 3]))
)
(test.case "vec.some?"
(assert.ok! (vec.some? (lambda [x _] (gt? x 2)) [1 2 3]))
(assert.not! (vec.some? (lambda [x _] (gt? x 5)) [1 2 3]))
)
(test.case "vec.count"
(assert.eq! (vec.count (lambda [x _] (gt? x 1)) [1 2 3]) 2)
)
(test.case "vec.sum"
(assert.eq! (vec.sum [1 2 3]) 6)
(assert.eq! (vec.sum []) 0)
)
)

View file

@ -0,0 +1,23 @@
(seq
(def true :true)
(def false :false)
(fn bool? [x]
(bool.or (eq? x true) (eq? x false)))
(namespace bool
(fn new [x]
(eq? x true))
(def __call__ new)
(defmacro or []
(if-eq (false $%%) false true))
(defmacro and []
(if-has false ($%%) false true))
(defmacro nand []
(if-has false ($%%) true false))
(fn not [x]
(if-eq (x true) false true))
))

View file

@ -0,0 +1,39 @@
(seq
(def types [
:symbol
:keyword
:str
:float
:int
:vec
:call
:set
:map
:quote
:lambda
:macro
])
(fn is? [x type]
(eq? (typeof x) type))
(fn guard [type]
(lambda [x] (is? x type)))
(def kw? (guard :keyword))
(def kw builtins.str.kw)
(include "bool")
(include "map")
(include "null")
(include "option")
(include "set")
(include "str")
(include "struct")
(include "vec")
(def vec? (guard :vec))
(def map? (guard :map))
(fn type? [x]
(vec.has types x))
)

View file

@ -0,0 +1,94 @@
(namespace map
(def type :map)
(def is? (guard type))
(def new builtins.map.new)
(def __call__ new)
(fn keys [m]
(vec.map #(vec.first %1) (vec.from m)))
(fn values [m]
(vec.map #(vec.last %1) (vec.from m)))
(fn len [m]
(vec.len (vec.from m)))
(fn empty? [m]
(eq? (len m) 0))
(fn is-empty? [m]
(empty? m))
(fn get [m key]
(vec.fold
(lambda [result pair _]
(if (eq? (vec.get pair 0) key)
(vec.get pair 1)
result))
null
(vec.from m)))
(fn put [m key value]
(apply apply new (vec.from m) [[key value]]))
(fn assoc [m key value]
(put m key value))
(fn dissoc [m key]
(apply apply new (vec.filter
(lambda [pair _] (nq?
(vec.get pair 0)
key
))
(vec.from m))
))
(fn has-key? [m key]
(if (null? (get m key))
false
true))
(fn has-value? [m value]
(vec.fold
(lambda [result pair _]
(seq
(def k (vec.get pair 0))
(def v (vec.get pair 1))
(bool.or result (eq? v value))))
false
(vec.from m)))
(fn map [callback m]
(apply new (vec.apply-flat (vec.map
(lambda [pair _]
(seq
(def k (vec.get pair 0))
(def v (vec.get pair 1))
(def new-v (callback v k))
[k new-v]))
(vec.from m)))))
(fn filter [callback m]
(apply new (vec.apply-flat (vec.filter
(lambda [pair _]
(seq
(def k (vec.get pair 0))
(def v (vec.get pair 1))
(callback v k)))
(vec.from m)))))
(fn merge [m1 m2]
(apply apply new (vec.from m1) (vec.from m2)))
(fn merge-all []
(vec.fold
(lambda [acc m _]
(merge acc m))
(new)
%%))
(fn conj [m other]
(if (map.is? other)
(merge m other)
m))
)

View file

@ -0,0 +1,5 @@
(seq
(def null :null)
(fn null? [x]
(bool.or (eq? x null) (eq? x :nil)))
)

View file

@ -0,0 +1,26 @@
(seq
(namespace option
(fn ok <Option>[value]
(vec :ok value))
(fn ok? <Bool>[x]
(bool.and
(vec? x)
(eq? (vec.first x) :ok)))
(fn err <Option>[value]
(vec :err value))
(fn err? <Bool>[x]
(bool.and
(vec? x)
(eq? (vec.first x) :err)))
)
(fn option? [x]
(bool.and
(vec? x)
(bool.or
(eq? (vec.first x) :err)
(eq? (vec.first x) :ok))))
)

View file

@ -0,0 +1,60 @@
(seq
(namespace set
(def type :set)
(def is? (guard type))
(fn new [] (apply from %%))
(def __call__ new)
(def fold builtins.set.fold)
(def from builtins.set.from)
(def empty #{})
(fn empty? <Bool>[s]
(eq? s empty))
(fn len <Int>[<Set>s]
(fold (lambda [acc _] (+ acc 1)) 0 s))
(fn has <Bool>[<Set>s item]
(fold
(lambda [acc elem]
(bool.or acc (eq? elem item)))
false
s))
(fn add <Set>[<Set>s item]
(from s [item]))
(fn remove <Set>[<Set>s item]
(fold
(lambda [acc elem]
(if (eq? elem item)
acc
(from acc elem)))
#{}
s))
(fn union <Set>[]
(apply from %%))
(fn intersection <Set>[<Set>s1 <Set>s2]
(fold
(lambda [acc elem]
(if (has s2 elem)
(add acc elem)
acc))
#{}
s1))
(fn difference <Set>[<Set>s1 <Set>s2]
(fold
(lambda [acc elem]
(if (has s2 elem)
acc
(add acc elem)))
#{}
s1))
)
(def set? (guard set.type))
)

View file

@ -0,0 +1,76 @@
(seq
(namespace str
(def type :str)
(def new builtins.str.new)
(def __call__ new)
(fn len <Int>[<Str>s]
(vec.len (vec.from s)))
(fn concat <Str>[]
(apply new %%))
(fn empty? <Bool>[<Str>s]
(eq? s ""))
(fn is-empty? <Bool>[<Str>s]
(empty? s))
(fn chars-at <Str>[<Str>s <Int>idx]
(vec.get (vec.from s) idx))
(fn first <Str>[<Str>s]
(chars-at s 0))
(fn last <Str>[<Str>s]
(chars-at s (-- (len s))))
(fn substring <Str>[<Str>s <Int>start <Int>end]
(apply concat (vec.slice (vec.from s) start end)))
(fn substr <Str>[<Str>s <Int>start <Int>len-sub]
(substring s start (+ start len-sub)))
(fn take <Str>[<Str>s <Int>n]
(apply concat (vec.take (vec.from s) n)))
(fn drop <Str>[<Str>s <Int>n]
(apply concat (vec.drop (vec.from s) n)))
(fn reverse <Str>[<Str>s]
(apply concat (vec.reverse (vec.from s))))
(fn starts-with? <Bool>[<Str>s <Str>prefix]
(eq? (substring s 0 (len prefix)) prefix))
(fn ends-with? <Bool>[<Str>s <Str>suffix]
(eq? (substring s (- (len s) (len suffix)) (len s)) suffix))
(fn split <Vec>[<Str>s <Str>sep] (seq
(vec.fold
(lambda [acc elem _]
(if-eq (elem sep)
(vec.conj acc "")
(vec.conj (vec.take acc (- (len acc) 1))
(str.concat (vec.last acc) elem)
)
))
[""]
(vec.from s)
)))
(fn join <Str>[<Str>sep <Vec>strs]
(if (vec.empty? strs)
""
(vec.fold
(lambda [acc elem _]
(new acc sep elem))
(vec.first strs)
(vec.tail strs))))
(fn map <Str>[callback self]
(join "" (vec.map callback (vec.from self))))
)
(def str? (guard str.type))
)

View file

@ -0,0 +1,22 @@
(seq
(defmacro struct [name fields] (seq
(for field $fields
(if (bool.not (kw? %1))
(throw! "Struct fields must be keywords"))
)
(def $name {
:__struct_type__ true
:new (lambda [data] (vec.map
#(if (map.has-key? data %1)
map.get data %1
(throw! "Missing field: " %1)
)
$fields))
})
))
(fn struct-type? [v]
(if (bool.and (map? v) (map.get v :__struct_type__))
true
false))
)

301
modules/core/types/vec.owa Normal file
View file

@ -0,0 +1,301 @@
(namespace vec
(def type :vec)
(def is? (guard type))
(def new builtins.vec.new)
(def __call__ new)
(def fold builtins.vec.fold)
(def from builtins.vec.from)
;; Empty vector
(def empty [])
;; Guard to check if a value is a empty vector
(fn empty? [<Vec>v]
(eq? v empty))
;; Get the count of items in a vector
(fn len [<Vec>v]
(fold (lambda [acc _] (+ acc 1)) 0 v))
;; Append an item to the end of a vector
(fn conj [<Vec>v item]
(apply new v [item]))
;; Check if a vector contains an item
(fn has [<Vec>v item]
(fold
(lambda [acc elem _]
(bool.or acc (eq? elem item)))
false
v))
;; Get an item from a vector by index
(fn get [<Vec>v idx]
(fold
(lambda [result elem index]
(if (eq? index idx)
elem
result))
null
v))
;; Update an item in a vector
;; If the index is out of bounds, the original vector is returned
(fn assoc [<Vec>v <Int>index item]
(fold
(lambda [acc elem idx]
(if (eq? idx index)
(conj acc item)
(conj acc elem)))
[]
v))
;; Remove an item from a vector by index
;; If the index is out of bounds, the original vector is returned
(fn remove [<Vec>v <Int>index]
(filter
(lambda [elem idx]
(nq? idx index))
v))
;; Reverse a vector
(fn reverse [<Vec>v]
(fold
(lambda [acc elem _]
(apply merge elem acc))
[]
v))
;; Flatten a vector of vectors into a single vector
(fn flat [<Vec>v]
(fold
(lambda [acc elem _]
(apply conj [acc elem]))
[]
v))
;; Get the first item in a vector
(fn first [<Vec>v]
(get v 0))
;; Get the last item in a vector
(fn last [<Vec>v]
(get v (-- (len v))))
;; Get all items except the first
(fn tail [<Vec>v]
(filter
(lambda [elem idx]
(nq? idx 0))
v))
;; Get the first n items from a vector
(fn take [<Vec>v <Int>n]
(filter
(lambda [elem idx]
(lt? idx n))
v))
;; Get all items except the first n
(fn drop [<Vec>v n]
(filter
(lambda [elem idx]
(gte? idx n))
v))
;; Get all items except the last
(fn drop-last [<Vec>v]
(filter
(lambda [elem idx]
(nq? idx (-- (len v))))
v))
(fn enumerate [<Vec>v]
(map
(lambda [elem idx]
(new [idx elem]))
v))
(fn zip [v1 v2]
(map
(lambda [elem idx]
(new elem (get v2 idx)))
v1))
(fn slice [<Vec>v start end]
(filter
(lambda [elem idx]
(bool.and (gte? idx start) (lt? idx end)))
v))
(fn includes? [<Vec>v item]
(has v item))
(fn index-of [<Vec>v item]
(fold
(lambda [result elem idx]
(if (null? result)
(if (eq? elem item)
idx
result)
result))
null
v))
(fn join-str [sep v]
(if (empty? v)
""
(fold
(lambda [acc elem _]
(str.new acc sep elem))
(first v)
(tail v))))
;; Map a callback to each item in a vector
(fn map [callback <Vec>v]
(fold
(lambda [acc elem index]
(apply conj [acc (callback elem index)]))
[]
v))
;; Filter a vector by a callback
(fn filter [callback <Vec>v]
(fold
(lambda [acc elem index]
(if (callback elem index)
(apply conj [acc elem])
acc))
[]
v))
;; Find the first item that matches the callback
(fn find [callback <Vec>v]
(fold
(lambda [result elem index]
(if (null? result)
(if (callback elem index)
elem
result)
result))
null
v))
;; Check if every item in a vector matches the callback
;; TODO: Break at first non-match for better performance
(fn every? <Bool>[callback <Vec>v]
(fold
(lambda [result elem idx]
(bool.and result (callback elem idx)))
true
v))
;; Check if any item in a vector matches the callback
;; TODO: Break at first match for better performance
(fn some? <Bool>[callback <Vec>v]
(fold
(lambda [result elem idx]
(bool.or result (callback elem idx)))
false
v))
;; Reduce a vector into a single value
(fn reduce <Bool>[callback initial <Vec>v]
(fold callback initial v))
;; Count the number of items that match the callback
(fn count [callback v]
(fold
(lambda [count elem idx]
(if (callback elem idx)
(+ count 1)
count))
0
v))
;; Merge all vectors into a single vector
(fn merge []
(fold
(lambda [acc elem _]
(apply conj [acc elem]))
[]
%%))
(fn max-by [comparator v]
(fold
(lambda [current elem _]
(if (null? current)
elem
(if (eq? (comparator elem current) :greater)
elem
current)))
null
v))
(fn min-by [comparator v]
(fold
(lambda [current elem _]
(if (null? current)
elem
(if (eq? (comparator elem current) :less)
elem
current)))
null
v))
(fn uniq [<Vec>v]
(fold
(lambda [result elem _]
(if (has result elem)
result
(conj result elem)))
[]
v))
(fn partition [callback v]
(fold
(lambda [result elem idx]
(seq
(def group (if (callback elem idx) 0 1))
(def current-part (get result group))
(assoc result group (conj current-part elem))))
[[] []]
v))
(fn group-by [key-fn v]
(fold
(lambda [result elem idx]
(seq
(def key (key-fn elem idx))
(def group (map.get result key))
(def new-group (if (null? group) [] group))
(map.put result key (conj new-group elem))))
(map.new)
v))
(fn flatten [<Vec>v]
(fold
(lambda [result elem _]
(if (vec.is? elem)
(conj result (flatten elem))
(conj result elem)))
[]
v))
(fn distinct [<Vec>v]
(uniq v))
(fn compact [<Vec>v]
(filter
(lambda [elem _]
(not (null? elem)))
v))
(fn sum [<Vec>v]
(fold
(lambda [acc elem _]
(+ acc elem))
0
v))
)