aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJJ2024-05-10 01:00:01 +0000
committerJJ2024-05-10 02:41:52 +0000
commit073c902a31936c2b53d89245662fb272c9670169 (patch)
treea8789ed4561dc4c3dde84489a600272cbd5f806b
parent51c8b349a77a8d8b1b34ce8e03518dad6e3cba00 (diff)
std: sweeping changes.
- replace interfaces with classes - replace : with then and do - solidify memory management as rust-style ownership - replace unsafe blocks with safe/unsafe attributes - add magic and copy attributes - switch Coerce impl from from to to
-rw-r--r--std/ast.pk75
-rw-r--r--std/async.pk6
-rw-r--r--std/hashes.pk2
-rw-r--r--std/prelude/arrays.pk24
-rw-r--r--std/prelude/booleans.pk39
-rw-r--r--std/prelude/clone.pk30
-rw-r--r--std/prelude/compare.pk36
-rw-r--r--std/prelude/convert.pk28
-rw-r--r--std/prelude/debug.pk28
-rw-r--r--std/prelude/format.pk32
-rw-r--r--std/prelude/io.pk2
-rw-r--r--std/prelude/iterators.pk91
-rw-r--r--std/prelude/lists.pk128
-rw-r--r--std/prelude/mem.pk39
-rw-r--r--std/prelude/numbers.pk53
-rw-r--r--std/prelude/options.pk97
-rw-r--r--std/prelude/ranges.pk47
-rw-r--r--std/prelude/results.pk112
-rw-r--r--std/prelude/strings.pk104
-rw-r--r--std/threads.pk2
20 files changed, 509 insertions, 466 deletions
diff --git a/std/ast.pk b/std/ast.pk
index abc66ba..b069aa9 100644
--- a/std/ast.pk
+++ b/std/ast.pk
@@ -1,9 +1,13 @@
## std.ast: Exposes the AST for building and operating on with macros.
pub type Expr = union
- Ident(string), Number(int), Float(float), Char(chr), String(str)
- Struct(list[tuple[field: str, value: Expr]])
- Tuple(list[tuple[field: string?, value: Expr]])
+ Ident(string)
+ Number(int)
+ Float(float)
+ Char(chr)
+ String(str)
+ Struct(list[(field: str, value: Expr)])
+ Tuple(list[(field: str?, value: Expr)])
List(list[Expr])
Let(id: Pattern, kind: Type?, value: ref Expr)
Var(id: Pattern, kind: Type?, value: ref Expr?)
@@ -13,51 +17,41 @@ pub type Expr = union
generics: list[GenericParams],
params: list[FunctionParams],
kind: Type,
- body: list[Expr]
- )
+ body: list[Expr])
TypeDecl(id: str, generics: list[str], alias: Type)
Module(id: str, body: list[Expr])
Use(path: str)
Pub(Expr) # can easily generate incoherent statements. still desired?
Call(id: str, params: list[Expr])
- Cond(branches: list[CondBranch], else_body: list[Expr]?)
+ Cond(
+ branches: list[(cond: Expr, body: list[Expr])],
+ else_body: list[Expr]?)
Try(
try_body: list[Expr],
- catches: list[TryBranch]
- finally_body: list[Expr]?
- )
+ catches: list[(exceptions: list[str], body: list[Expr])],
+ finally_body: list[Expr]?)
Match(
item: ref Expr,
- branches: list[MatchBranch]
- )
+ branches: list[(pattern: Pattern, guard: Expr?, body: list[Expr])])
Block(id: str?, body: list[Expr])
ConstBlock(body: list[Expr]),
For(binding: Pattern, range: ref Expr, body: list[Expr])
While(cond: ref Expr, body: list[Expr])
Loop(body: list[Expr]),
-pub type CondBranch = tuple
- cond: Expr
- body: list[Expr]
-
-pub type TryBranch = tuple
- exceptions: list[str]
- body: list[Expr]
-
-pub type MatchBranch = tuple
- pattern: Pattern
- guard: Option[Expr]
- body: list[Expr]
-
pub type Type = ref union
- Void, Never,
- Integer, Float, String,
- Func(from: Type, to: Type) # todo: multiple parameters
- Struct(list[tuple[id: str, kind: Type]])
- Tuple(list[tuple[id: str?, kind: Type]])
- Union(list[tuple[id: str, kind: Type]])
- Interface( # todo: generics
- funcs: list[Signature]
+ Void
+ Never
+ Int(size: uint)
+ Dec(size: uint)
+ Float(size: uint)
+ String
+ Func(from: list[Type], to: Type)
+ Struct(list[(id: str, kind: Type)])
+ Tuple(list[(id: str?, kind: Type)])
+ Union(list[(id: str, kind: Type)])
+ Class( # todo: generics
+ funcs: list[(id: str, from: list[Type], to: Type?)]
for_type: Type?
)
Array(size: uint, kind: Type)
@@ -65,17 +59,18 @@ pub type Type = ref union
Slice(Type) # todo: plus ownership
Alias(str) # todo: params?? huh?
Static(Type)
- Mutable(Type)
- Reference(Type)
- Pointer(Type)
-pub type Signature = struct # todo: generics
- id: str
- effect: Option[str]
- params: list[Type]
- kind: Option[Type]
+ Lent(Type)
+ Mut(Type)
+ Ref(Type)
+ Refc(Type)
+ Ptr(Type)
+
pub type Pattern = union
Ident(str)
Number(int), Float(float), Char(chr), String(str)
Struct(name: str, params: list[Pattern])
Tuple(list[Pattern])
List(list[Pattern])
+
+@[magic]
+pub func quote(body): Expr
diff --git a/std/async.pk b/std/async.pk
index 7c76076..f2ae3e0 100644
--- a/std/async.pk
+++ b/std/async.pk
@@ -1,5 +1,6 @@
## std.async: Zig-style colourless async/await.
## Future types, the async macro, and the await function.
+# reference: https://kristoff.it/blog/zig-colorblind-async-await/
## A standard Future type. Opaque.
pub type Future[T] = struct
@@ -7,6 +8,9 @@ pub type Future[T] = struct
## The `async` macro transforms any function call into
## an asynchronous call returning a Future of some type.
+##
+## This does not "just work". Compiler magic is necessary to package up
+## *any* block into something independently callable.
pub macro async[T](call: T): Future[T] =
...
@@ -14,6 +18,6 @@ pub macro async[T](call: T): Future[T] =
pub func await[T](self: Future[T]): T =
...
-examples:
+examples
std.net.connect()
print 1.async.await
diff --git a/std/hashes.pk b/std/hashes.pk
index 911c9d2..6211a2e 100644
--- a/std/hashes.pk
+++ b/std/hashes.pk
@@ -1,4 +1,4 @@
## std.hashes: Hash functions on types.
-pub type Hash[H] = interface
+pub type Hash[H] = class
hash[H](Self): H
diff --git a/std/prelude/arrays.pk b/std/prelude/arrays.pk
index 2e4a4d4..6f57865 100644
--- a/std/prelude/arrays.pk
+++ b/std/prelude/arrays.pk
@@ -1,20 +1,30 @@
-## std.arrays: The array[T, S] primitive and associated functions.
+## std.arrays: The array[T, size] primitive and associated functions.
## A stub module for documentation. Mostly compiler magic.
## Primitive fixed-size arrays. Their size is statically known at compile-time.
-pub type array[T, S: static[uint]]
+@[magic]
+pub type array[T, size: static uint]
## Array access. Returns None if i is out of range.
-pub func get[T, S: static[uint]](self: array[T, S], i: uint): T?
+@[magic]
+pub func get[T, size: static uint](self: lent array[T, size], i: uint): lent T?
+## Array access. Returns None if i is out of range.
+@[magic]
+pub func get[T, size: static uint](self: mut array[T, size], i: uint): mut T?
+
## Array mutation.
# todo: how do we detect range errors?
-pub func set[T, S: static[uint]](self: mut array[T, S], i: uint, val: T): T?
+@[magic]
+pub func set[T, size: static uint](self: mut array[T, size], i: uint, val: T): Success[IndexOutOfBounds]
+
## A helper function to get the length of an array.
## Known to the compiler, and computed at compile-time.
-pub func len[T, S: static[uint]](self: array[T, S], i: uint): T?
+@[inline]
+pub func len[T, size: static uint](self: lent array[T, size]): uint = size
-type ArrayIter[T, S: static[uint]] = struct
+type ArrayIter[T, size: static uint] = struct
+ ...
+pub func iter[T, size: static uint](self: array[T, size]): ArrayIter[T, S] =
...
-pub func iter[T, S: static[uint]](self: array[T, S]): ArrayIter[T, S]
# todo: Eq, PartialEq, Ord, PartialOrd
diff --git a/std/prelude/booleans.pk b/std/prelude/booleans.pk
index bd81925..e92eac8 100644
--- a/std/prelude/booleans.pk
+++ b/std/prelude/booleans.pk
@@ -4,39 +4,26 @@
pub type unit = union[sole]
pub type bool = union[false, true]
-# note: puck could resolve kleene.false vs bool.false...
-# this is probably not worth it compared to improved type inference
-# pub type Kleene = union[False, Maybe, True]
-
## Boolean equality
-pub func ==(a: bool, b: bool): bool =
- match a
- of (true, true), (false, false): true
- of (false, true), (true, false): false
+@[magic]
+pub func ==(a: bool, b: bool): bool
-pub func !=(a: bool, b: bool): bool =
- not a == b
+## Boolean inequality
+@[magic]
+pub func !=(a: bool, b: bool): bool
## Boolean negation
-pub func not(a: bool): bool =
- match a
- of true: false
- of false: true
+@[magic]
+pub func not(a: bool): bool
## Boolean conjunction
-pub func and(a: bool, b: bool): bool =
- match (a, b)
- of (true, true): true
- of _: false
+@[magic]
+pub func and(a: bool, b: bool): bool
## Boolean disjunction
-pub func or(a: bool, b: bool): bool =
- match (a, b)
- of (false, false): false
- of _: true
+@[magic]
+pub func or(a: bool, b: bool): bool
## Boolean exclusive disjunction
-pub func xor(a: bool, b: bool): bool =
- match (a, b)
- of (true, true), (false, false): false
- of (true, false), (false, true): true
+@[magic]
+pub func xor(a: bool, b: bool): bool
diff --git a/std/prelude/clone.pk b/std/prelude/clone.pk
index 5b16b8b..5d11cdb 100644
--- a/std/prelude/clone.pk
+++ b/std/prelude/clone.pk
@@ -1,28 +1,36 @@
-## std.clone: Interfaces for explicitly Clonable types.
+## std.clone: Classes for explicitly Clonable types.
## This module is imported by default.
-## The Clone interface. Any type implementing Clone is cloneable.
+## The Clone class. Any type implementing Clone is cloneable.
## Data structures built with pointers and references are pretty much
## the only things that are *not* implicitly clonable.
-pub type Clone = interface
- clone(lent Self): Self # todo
+pub type Clone = class
+ clone(lent Self): Self
## Generic implementation of `clone` for structs.
-pub func clone[T: struct](self: T): T =
+## fixme: this relies on no component of a struct being a ref or ptr
+## cloning an rc is fine??
+pub func clone[T: Clone](self: struct[T]): struct[T] =
...
## Generic implementation of `clone` for tuples.
-pub func clone[T: tuple](self: T): T =
+pub func clone[T: Clone](self: tuple[T]): tuple[T] =
...
## Implementation of `clone` for arrays of any size.
-pub func clone[T, S: static[uint]](self: array[T, S]): array[T, S] =
- ...
+pub func clone[T: Clone, size: static[uint]](self: array[T, size]): array[T, size] =
+ var res: array[T, size]
+ for i in 0 .. size do
+ res.push(self.i.clone)
## Implementation of `clone` for lists.
-pub func clone[T](self: list[T]): list[T] =
- ...
+pub func clone[T: Clone](self: list[T]): list[T] =
+ var res: list[T]
+ for elem in self do
+ res.push(elem.clone) # the explicit dependency on Clone gives us this
## Implementation of `clone` for strings.
pub func clone(self: str): str =
- ...
+ var res: str
+ for char in self do
+ res.push(char)
diff --git a/std/prelude/compare.pk b/std/prelude/compare.pk
index b506761..5f3ae74 100644
--- a/std/prelude/compare.pk
+++ b/std/prelude/compare.pk
@@ -1,30 +1,36 @@
-## std.compare: Interfaces for comparable types.
+## std.compare: Classes for comparable types.
## reference: https://doc.rust-lang.org/std/cmp
-## The Eq interface
-pub type Eq = interface
+## The Eq class. For types with some notion of equivalence.
+pub type Eq = class
==(Self, Self): bool
-## A possible Equivalence interface.
-# equality, equivalence, isomorphism, homomorphism, congruence
-# pub type Equiv = interface
-# ...
+## A blanket implementation of a corresponding not-equal function.
+pub !=[T: Eq](a: T, b: T): bool =
+ not(a == b)
-pub type PartialOrd = interface
+## The Compare class. For a type comparable with itself.
+pub type Compare = class
<(a: Self, b: Self): bool
-pub type Ord = interface
+## A blanket implementation of a corresponding greater-than function.
+## Note to self: do NOT inline!
+pub func >[T: Compare](a: T, b: T): bool =
+ b < a
+
+## The Ord class. For types with some notion of equivalence and comparision.
+##
+## Note: This is *not* a mathematical notion of an order!
+## No invariants on `<` nor `==` are guaranteed to hold, as classes
+## are implicitly implementable.
+pub type Ord = class
<(a: Self, b: Self): bool
==(a: Self, b: Self): bool
-pub !=[T: Eq](a: T, b: T): bool =
- not a == b
-
-pub func >[T: PartialOrd](a: T, b: T): bool =
- b < a
-
+## A blanket implementation of a corresponding less-than-or-equal function.
pub func <=[T: Ord](a: T, b: T): bool =
a < b or a == b
+## A blanket implementation of a corresponding greater-than-or-equal function.
pub func >=[T: Ord](a: T, b: T): bool =
a > b or a == b
diff --git a/std/prelude/convert.pk b/std/prelude/convert.pk
index 6abf543..ec1a4a7 100644
--- a/std/prelude/convert.pk
+++ b/std/prelude/convert.pk
@@ -1,23 +1,19 @@
-## std.convert: Interfaces for type coersion and conversion.
+## std.convert: Classes for type coersion and conversion.
## This module is imported by default.
-## The Coerce interface is used for type conversion that will not fail.
+## The Coerce class is used for type conversion that will not fail.
## Its associated methods, `from` and `into`, are used internally
## by the compiler for implicit type conversion (coersion).
-pub type Coerce[T] = interface
- from(T): Self
- # into(Self): T
+pub type Coerce[T] = class
+ to(Self): T
+ # from(T): Self
-## The `into` function is automatically implemented for all types that
-## implement `from`: that is, all types U that are convertable to T.
-pub func into[T, U: Coerce[T]](self: T): U =
- from(self)
+## The `from` function is automatically implemented for all types that
+## implement `to`: that is, all types T that are convertable to U.
+pub func from[T: Coerce[U], U](self: U): T =
+ to(self)
-## The Convert interface is used for type conversion that may fail.
+## The Convert class is used for type conversion that may fail.
# We'll see what this breaks.
-pub type Convert[T] = interface
- from(T): Self?!
-
-## A blanket implementation of a corresponding `into` function.
-pub func into[T, U: Convert[T]](self: T): U =
- from(self)
+pub type Convert[T, E] = class
+ to(Self): Result[T, E]
diff --git a/std/prelude/debug.pk b/std/prelude/debug.pk
index 3af1def..92d31dd 100644
--- a/std/prelude/debug.pk
+++ b/std/prelude/debug.pk
@@ -1,27 +1,22 @@
## std.debug: Useful functions for debugging.
## This module is imported by default.
-## The special ... syntax is used to mark unimplemented parts of code.
-## Such code will compile, but panic upon being called at runtime.
-## It is usable almost anywhere, including in type declarations, thanks to compiler magic.
-pub func ...: never
-
## The `assert` macro checks that a provided assertation is true,
## and panics and dumps information if it is not.
-## Asserts remain in release builds. If not desired, see `debug_assert`
+## Asserts remain in release builds. If not desired, see `dbg_assert`
pub macro assert(cond: bool) =
- quote:
- if not `cond`:
+ quote
+ if not `cond` then
panic "assertation failed!\n {}".fmt(dbg(`cond`))
-## The `debug_assert` function provides an assert that is compiled out in release builds.
+## The `dbg_assert` function provides an assert that is compiled out in release builds.
## This is useful for debugging performance-critical code.
-pub macro debug_assert(cond: bool)
- quote:
- when debug: # fixme: where is this coming from?
+pub macro dbg_assert(cond: bool) =
+ quote
+ when debug then # fixme: where is this constant coming from?
assert `cond`
-## The `discard` function consumes any type.
+## The `discard` function consumes an object of any type.
## Useful for throwing away the result of a computation.
pub func discard[T](self: T) =
return
@@ -30,3 +25,10 @@ pub func discard[T](self: T) =
pub func panic(message: str): never =
stderr.write(message, "\n")
std.os.exit(1)
+
+## The special ... syntax is used to mark unimplemented parts of code.
+## Such code will compile, but panic upon being called at runtime.
+## It is usable almost anywhere, including in type declarations, thanks to compiler magic.
+@[magic]
+pub func ...: never =
+ panic("unimplemented")
diff --git a/std/prelude/format.pk b/std/prelude/format.pk
index 7cc1d81..6fc7c99 100644
--- a/std/prelude/format.pk
+++ b/std/prelude/format.pk
@@ -1,15 +1,15 @@
## std.format: Niceties around printing and debugging.
## This module is imported by default.
-## The Display interface. Any type implementing `str` is printable.
+## The Display class. Any type implementing `str` is printable.
## Any type that is Display must necessarily also implement Debug.
-pub type Display = interface
+pub type Display = class
str(Self): str
dbg(Self): str
-## The Debug interface. Broadly implemented for every type with compiler magic.
+## The Debug class. Broadly implemented for every type with compiler magic.
## Types can (and should) override the generic implementations.
-pub type Debug = interface
+pub type Debug = class
dbg(Self): str
## Prints all of its arguments to the command line.
@@ -17,38 +17,42 @@ pub func print(params: varargs[Display]) =
stdout.write(params.map(x => x.str).join(" "), "\n")
## Prints all of its arguments to the command line, in Debug form.
-## Note: this function is special! does not count as a side effect
+##
+## Note: this function is special! It does not count as a side effect.
+## This breaks effect tracking, of course: but `dbg` is for debugging.
+## It will produce a warning in code compiled for release.
+@[no_side_effect]
pub func dbg(params: varargs[Debug]) =
stdout.write(params.map(x => x.dbg).join(" "), "\n")
-## A dummy implementation of the Display interface for strings.
+## A dummy implementation of the Display class for strings.
pub func str(self: str): str = self
-## An implementation of the Debug interface for strings.
+## An implementation of the Debug class for strings.
pub func dbg(self: str): str = "\"" & self & "\""
-## An implementation of the Debug interface for all structs.
+## An implementation of the Debug class for all structs.
## Uses the special `struct` typeclass.
-pub func dbg(self: struct): str =
+pub func dbg[T: Debug](self: struct[T]): str =
"{{}}".fmt(self.fields.map((key, val) => key & ":" & val.dbg))
-## An implementation of the Debug interface for all tuples.
+## An implementation of the Debug class for all tuples.
## Uses the special `tuple` typeclass.
-pub func dbg(self: tuple): str =
+pub func dbg[T: Debug](self: tuple[T]): str =
"({})".fmt(self.fields.map((key, val) =>
key.map(x => x & ":").get_or("") & val.dbg).join(", "))
-## An implementation of the Debug interface for all arrays and lists.
+## An implementation of the Debug class for all arrays and lists.
pub func dbg[T: Debug](self: Iter[T]): str =
"[{}]".fmt(self.map(x => x.dbg).join(", "))
## The fmt macro. Builds a formatted string from its arguments.
pub macro fmt(self: static[str], args: varargs[Display]): str =
let parts = self.split("{}")
- if parts.len != args.len + 1:
+ if parts.len != args.len + 1 then
macro_error("wrong number of arguments")
use std.ast
var res = parts.get(0)!
- for i, arg in args:
+ for i, arg in args do
res &= quote(`parts` & str(`arg`) &) # fixme
res &= parts.last()!
res
diff --git a/std/prelude/io.pk b/std/prelude/io.pk
index 002cc31..026d12a 100644
--- a/std/prelude/io.pk
+++ b/std/prelude/io.pk
@@ -4,7 +4,7 @@
# reference: https://doc.rust-lang.org/stable/std/io/
pub type File = ...
-pub type FileMode = union[Read, Write, ReadWrite, Append]
+pub type FileMode = enum[Read, Write, ReadWrite, Append]
# todo: file destructors call `close`
diff --git a/std/prelude/iterators.pk b/std/prelude/iterators.pk
index 82137ee..1403b55 100644
--- a/std/prelude/iterators.pk
+++ b/std/prelude/iterators.pk
@@ -1,28 +1,28 @@
-## std.iterators: The Iter interface and associated functions.
+## std.iterators: The Iter class and associated functions.
## This module is imported by default.
# reference: https://doc.rust-lang.org/std/iter/
# reference: https://docs.rs/itertools/latest/itertools/
use std.queue.ring
-## The Iter interface. Any type implementing `next()` is iterable.
-pub type Iter[T] = interface
+## The Iter class. Any type implementing `next()` is iterable.
+pub type Iter[T] = class
next(mut Self): T? # should T be lent?
-## The IntoIter interface.
+## The IntoIter class.
## Any type implementing `iter()` may be converted to an iterable type.
-pub type IntoIter[T] = interface
+pub type IntoIter[T] = class
iter(Self): Iter[T]
-## The Peek interface.
+## The Peek class.
## Any type implementing `Iter`, `peek`, and `peek_nth` is peekable.
-pub type Peek[T] = interface
+pub type Peek[T] = class
next(mut Self): T?
peek(mut Self): T?
peek_nth(mut Self, int): T?
## A concrete Peekable structure formed from a generic Iter type.
-## Implements the Peek interface. Can be formed with `.peekable()`
+## Implements the Peek class. Can be formed with `.peekable()`
type PeekImpl = struct
iter: ref Iter[T] # this is probably bad... heap allocated...
buf: Queue[T]
@@ -34,26 +34,26 @@ pub func peekable[T](iter: owned Iter[T]): PeekImpl[T] =
pub func next[T](self: mut PeekImpl[T]): T? =
match self.buf.pop()
- of None:
+ of None then
self.iter.next()
- of Some(val):
+ of Some(val) then
val
pub func peek[T](self: mut PeekImpl[T]): T? =
match self.buf.peek()
- of None:
+ of None then
self.buf.push(self.iter.next())
self.buf.peek()
- of Some(val):
+ of Some(val) then
val
pub func peek_nth[T](self: mut PeekImpl[T], i: uint): T? =
- if self.buf.len > i:
+ if self.buf.len > i then
self.buf.get(i)!
- else:
- for _ in self.buf.len .. i: # fixme
+ else
+ for _ in self.buf.len .. i do # fixme
match self.buf.next()
- of None:
+ of None then
return None
- of Some(val):
+ of Some(val) then
self.buf.push(val)
self.buf.peek()
@@ -61,26 +61,23 @@ pub func peek_nth[T](self: mut PeekImpl[T], i: uint): T? =
# https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods
func advance_by[T](self: Iter[T], n: uint) =
- for i in 0 .. n:
- if self.next().is_none():
- return
+ for i in 0 .. n do
+ if self.next().is_none() then return
-pub func get[T](self: Iter[T], at: uint): Option[T]
+pub func get[T](self: Iter[T], at: uint): T?
self.advance_by(at-1).ok? # fixme
self.next()
## Returns true if every element in an Iter fulfills the conditional.
pub func all[T](self: Iter[T], cond: T -> bool): bool =
- for elem in self:
- if not cond(elem):
- return false
+ for elem in self do
+ if not cond(elem) then return false
true
## Returns true if any element in an Iter fulfills the conditional.
pub func any[T](self: Iter[T], cond: T -> bool): bool =
- for elem in self:
- if cond(elem):
- return true
+ for elem in self do
+ if cond(elem) then return true
false
## Returns true if an Iter contains the given element.
@@ -89,52 +86,48 @@ pub func contains[T](self: Iter[T], val: T): bool =
## Returns the first element matching a condition, if it exists.
pub func find[T](self: Iter[T], cond: T -> bool): T? =
- for elem in self:
- if cond(elem):
- return Some(elem)
+ for elem in self do
+ if cond(elem) then return Some(elem)
None
## Returns the position of the first element matching a condition, if it exists.
pub func position[T](self: Iter[T], cond: T -> bool): T? =
var i = 0
- for elem in self:
- if cond(elem):
- return Some(i)
+ for elem in self do
+ if cond(elem) then return Some(i)
i += 1
None
+@[inline]
pub func fold[T, U](self: Iter[T], init: U, op: (U, T) -> U) =
var res = init
- for elem in self:
+ for elem in self do
res = res.op(elem)
res
pub func reduce[T, U](self: Iter[T], op: (U, T) -> U): U? =
match self.next()
- of None:
- None
- of Some(val):
+ of None then None
+ of Some(val) then
var res = val
- for elem in self:
- res = op(res, elem)
+ for elem in self do res = op(res, elem)
Some(res)
pub func count[T](self: Iter[T], element: T): uint =
- self.fold(0, (elem, acc) => if elem == element: acc + 1 else: acc)
+ self.fold(0, (elem, acc) => if elem == element then acc + 1 else acc)
pub func count[T](self: Iter[T]): uint =
self.fold(0, (elem, acc) => acc + 1)
pub func last[T](self: Iter[T]): T =
var res = None
- for elem in self:
- res = elem
+ for elem in self do res = elem
res
pub func ==[T](first: Iter[T], second: Iter[T]): bool =
- for (a, b) in :
- if a != b:
- return false
+ if first.len != second.len then return false
+ for (a, b) in zip(first, second) do
+ if a != b then return false
true
type LazyIter = struct
@@ -165,16 +158,16 @@ pub func zip[T, U](first: Iter[T], second: Iter[U]): Iter[(T, U)]
pub func unzip[T, U](self: Iter[(T, U)]): (Iter[T], Iter[U])
# equivalent of collect. useful for type conversion
-pub func from[T](self: LazyIter[T]): list[T]
-pub func from(self: LazyIter[chr]): str
+pub func to[T](self: LazyIter[T]): list[T]
+pub func to(self: LazyIter[chr]): str
-pub type BoundIter[T] = interface
+pub type BoundIter[T] = class
next(mut Self): T?
pop(mut Self): T?
pub func rev[T](self: BoundIter[T]): BoundIter[T]
-pub type SizedIter[T] = interface
+pub type SizedIter[T] = class
next(mut Self): T?
pop(mut Self): T?
len(Self): uint
diff --git a/std/prelude/lists.pk b/std/prelude/lists.pk
index 1e8dee5..57de91b 100644
--- a/std/prelude/lists.pk
+++ b/std/prelude/lists.pk
@@ -2,105 +2,113 @@
## This module is imported by default.
# reference: https://doc.rust-lang.org/nomicon/vec/vec.html
-use std.pointers
-
-## The list type.
-type list[T] = struct
- data: unique[T] # hrm
+## The fundamental list type. Heap-allocated.
+## Equivalent to Vec<T> in other languages.
+@[unsafe] # unsafe on a struct tells us raw field access breaks invariants.
+pub type list[T] = struct
+ data: ptr T
capacity: int
length: int
+## A transparent, common alias for a list of bytes.
+pub type bytes = list[byte]
+
## Initialize and return an empty list with inner type T.
-pub func init[T](): list[T] =
- ... # malloc? idk
- let (data, )
- { data: , capacity: 0, length: 0 }
+pub func init[T]: list[T] =
+ { data = nil, capacity = 0, length = 0 } # fixme: nil!!!!!
## Gets the length of a list.
-pub func len[T](self: list[T]): uint =
+@[inline] # idk what to do with attributes
+pub func len[T](self: lent list[T]): uint =
self.length
-pub func empty[T](self: list[T]): bool =
+pub func empty[T](self: lent list[T]): bool =
self.length == 0
## Gets the internal capacity of a list.
-func cap[T](self: list[T]): uint =
+func cap[T](self: lent list[T]): uint =
self.capacity
## Expands the capacity of a list.
+@[safe]
func grow[T](self: mut list[T]) =
- ...
+ self.capacity = max(self.length + 1, self.capacity * 2)
+ self.data = self.data.realloc(self.capacity * sizeof(T))
## Pushes a new element to the end of a list.
-pub func push[T](self: mut list[T], val: owned T) =
- if self.length == self.capacity:
- self.grow()
- unsafe: # todo: do we want unsafe blocks, as they are in rust?
- self.data.raw_set(self.length, val) # fixme
- # unsafe { ptr::write(self.ptr().add(self.length), val); }
- self.length += 1
+@[safe]
+pub func push[T](self: mut list[T], val: T) =
+ if self.capacity == self.length then self.grow()
+ self.data.set(val, offset = self.length)
+ self.length += 1
## Takes ownership of and pushes all the values of a list into another list.
-pub func push[T](self: mut list[T], val: owned list[T]) =
- ...
+pub func push[T](self: mut list[T], values: list[T]) =
+ for val in values do
+ self.push(val)
## Removes & returns an element from the end of a list, if it exists.
+@[safe]
pub func pop[T](self: mut list[T]): T? =
- if self.length == 0:
+ if self.length == 0 then
None
- unsafe:
+ else
self.length -= 1
- Some(self.data.raw_get[T](self.length)) # fixme
- # unsafe { Some(ptr::read(self.ptr().add(self.length))) }
+ Some(self.data.get(offset = self.length))
## Returns a reference to an element of a list, if in range.
-pub func get[T](self: list[T], i: uint): lent T? =
- ...
+@[safe]
+pub func get[T](self: lent list[T], i: uint): lent T? =
+ if i > self.length then
+ None
+ else # fixme: interior mutability
+ Some(lent self.data.get(offset = i))
+## Returns a mutable reference to an element of a list, if in range.
+@[safe]
+pub func get[T](self: mut list[T], i: uint): mut T? =
+ if i > self.length then
+ None
+ else # fixme: interior mutability
+ Some(mut self.data.get(offset = i))
-## Sets the element of a list to a value. todo: when is `val` owned?
-## todo: how do we deal with having the wrong offset?
-pub func set[T](self: mut list[T], i: uint, val: owned T) =
- ...
+## Sets the element of a list to a value.
+@[safe]
+pub func set[T](self: mut list[T], i: uint, val: T) =
+ assert i <= self.length, "index out of bounds"
+ Okay(self.data.set(offset = i, val))
## Inserts a value at a location and shifts elements of the list accordingly.
+@[safe]
pub func insert[T](self: mut list[T], i: uint, val: T) =
assert i <= self.length, "index out of bounds"
- if self.cap == self.len:
- self.grow()
- ...
- # unsafe {
- # ptr::copy(
- # self.ptr().add(index),
- # self.ptr().add(index + 1),
- # self.len - index,
- # );
- # ptr::write(self.ptr().add(index), elem);
- # self.len += 1;
- # }
+ if self.capacity == self.length then self.grow()
+ self.data.offset(i).copy(self.data.offset(i + 1), self.length - i)
+ self.data.set(i, val)
+ self.length += 1
+## Inserts a list of values at a location and shifts elements of the list accordingly.
+pub func insert[T](self: mut list[T], i: uint, vals: list[T]) =
+ for val in vals.rev: # inserting backwards avoids counting
+ self.insert(val, i)
## Removes a value at a location and shifts elements of the list accordingly.
-pub func remove[T](self: mut list[T], i: uint): T =
- assert index < self.length, "index out of bounds"
- unsafe:
+@[safe]
+pub func remove[T](self: mut list[T], i: uint): T? =
+ if index < self.length then None
+ else
self.length -= 1
- result = ...
- # unsafe {
- # self.len -= 1;
- # let result = ptr::read(self.ptr().add(index));
- # ptr::copy(
- # self.ptr().add(index + 1),
- # self.ptr().add(index),
- # self.len - index,
- # );
- # result
- # }
+ let res = self.data.get(i)
+ self.data.offset(i + 1).copy(self.data.offset(i), self.length - i)
+ res
## Gets the last element of a list, if it exists.
-pub func last[T](self: list[T]): lent T? =
+pub func last[T](self: lent list[T]): lent T? =
+ self.get(self.len - 1)
+## Gets the last element of a list mutably, if it exists.
+pub func last[T](self: mut list[T]): mut T? =
self.get(self.len - 1)
# todo: many questions. owned? how does it not free?
-pub func iter[T](self: owned list[T]): ListIter[T] =
+pub func iter[T](self: list[T]): ListIter[T] =
...
# todo: iteration...
diff --git a/std/prelude/mem.pk b/std/prelude/mem.pk
index 79c524b..5ad3c7a 100644
--- a/std/prelude/mem.pk
+++ b/std/prelude/mem.pk
@@ -1,35 +1,58 @@
## std.mem: Unsafe functions for working with raw memory.
+## This module is imported by default.
## Raw pointers. Extremely unsafe.
## While they are removed from the memory model by default (and hence can
-## cause undefined behavior), pointers must know what *type* they point at.
+## cause memory errors), pointers must know what *type* they point at.
## This cuts down on errors somewhat - not much, but somewhat.
+@[magic]
pub type ptr[T] =
addr: uint
## Heap-allocated, memory-safe references.
+@[magic]
pub type ref[T]
-pub type unique[T]
+## Heap-allocated, reference-counted references.
+## These are a special type - rather than anything handled by the type system
+## alone - because there are significant optimizations the compiler can make.
+@[magic]
+pub type refc[T]
+
+## Tells the compiler to interpret data of one type directly as data of another.
+## Extremely unsafe. Take caution to memory representations when using.
+## Compiler magic. The type signature is left here for documentation purposes.
+@[unsafe, magic]
+pub func cast[T, U](self: T): U
## Sets the sizeof(T) bytes following data + offset * sizeof(T) to T.
## Extremely unsafe.
-pub func raw_set[T](data: ptr[T], val: T, offset: uint = 0) =
+@[unsafe]
+pub func set[T](data: ptr T, val: T, offset: uint = 0) =
...
## Gets the value at data + offset * sizeof(T), and interprets it as type T.
## Extremely unsafe.
-pub func raw_get[T](data: ptr[T], offset: uint = 0) =
+@[unsafe]
+pub func get[T](data: ptr T, offset: uint = 0) =
...
## Returns a pointer offset by offset * sizeof(T).
## Extremely unsafe.
-pub func raw_offset[T](data: ptr[T], offset: uint): ptr[T] =
+@[unsafe]
+pub func offset[T](data: ptr T, offset: uint): ptr T =
+ ...
+
+@[unsafe]
+pub func malloc[T](size: uint = sizeof(T)): ptr T =
...
-pub func malloc[T](size: uint = sizeof(T)): ptr[T] =
+@[unsafe]
+pub func calloc[T](size: uint = sizeof(T)): ptr T =
...
-pub func calloc[T](size: uint = sizeof(T)): ptr[T] =
+@[unsafe]
+pub func realloc[T](data: ptr T): ptr T =
...
-pub func realloc[T](data: ptr[T]): ptr[T] =
+@[unsafe]
+pub func copy[T](from: ptr T, to: ptr T, length: uint) =
...
diff --git a/std/prelude/numbers.pk b/std/prelude/numbers.pk
index 7857d34..801591a 100644
--- a/std/prelude/numbers.pk
+++ b/std/prelude/numbers.pk
@@ -3,53 +3,73 @@
# reference: https://en.wikipedia.org/wiki/IEEE_754
## The default integer type. Size depends on architecture.
+@[copy, magic]
pub type int
## A signed 8-bit integer.
+@[copy, magic]
pub type i8
## A signed 16-bit integer.
+@[copy, magic]
pub type i16
## A signed 32-bit integer.
+@[copy, magic]
pub type i32
## A signed 64-bit integer.
+@[copy, magic]
pub type i64
## A signed 128-bit integer.
+@[copy, magic]
pub type i128
## The default unsigned integer type. Size depends on architecture.
+@[copy, magic]
pub type uint
## An unsigned 8-bit integer.
+@[copy, magic]
pub type u8
## An unsigned 16-bit integer.
+@[copy, magic]
pub type u16
## An unsigned 32-bit integer.
+@[copy, magic]
pub type u32
## An unsigned 64-bit integer.
+@[copy, magic]
pub type u64
## An unsigned 128-bit integer.
+@[copy, magic]
pub type u128
## A floating-point type. Takes up 64 bits by default.
pub type float = f64
## A 32-bit floating point type. Single precision.
+@[copy, magic]
pub type f32
## A 64-bit floating point type. Double precision.
+@[copy, magic]
pub type f64
## A 128-bit floating point type. Quadruple precision.
+@[copy, magic]
pub type f128
## A decimal type, according to IEEE 754. Takes up 64 bits by default.
pub type decimal = dec64
## A 64-bit decimal type.
+@[copy, magic]
pub type dec64
## A 128-bit decimal type.
+@[copy, magic]
pub type dec128
-## 8-bit bytes.
+## A primitive byte type. 8 bits wide.
pub type byte = u8
-## 4-byte chars. These represent distinct Unicode characters.
-pub type char = distinct u32
-## An alias to `char`, to match with `str`.
-pub type chr = char
+## A primitive char type. 4 bytes wide.
+## These represent distinct Unicode characters, and are useful with `str`.
+pub type chr = u32
+
+## Get the underlying length of a char, in bytes. 1-4.
+pub func len(self: chr): uint =
+ ...
## The IEEE floating point value of *Not a Number*.
pub const NaN: f64 = 0x7FF7FFFFFFFFFFFF
@@ -60,18 +80,31 @@ pub const NegInf: f64 = 0xFFF0000000000000
# todo: type conversions
+# fixme: types are all wrong
+@[magic]
pub func +[T](a: T, b: T): T
+@[magic]
pub func -[T](a: T, b: T): T
+@[magic]
pub func *[T](a: T, b: T): T
+@[magic]
pub func /[T](a: T, b: T): float
-pub func ^[T](a: T, b: T): T # todo: should be exp?
+@[magic]
+pub func exp[T](a: T, b: T = eulers_number): T
+@[magic]
+pub func log[T](a: T, b: T = eulers_number): T
+@[magic]
+pub func abs[T](a: T): T
+
+@[magic]
pub func div[T](a: T, b: T): T
-pub func mod[T](a: T, b: T): T # fixme
+@[magic]
+pub func mod[T](a: T, b: T): T
+@[magic]
pub func rem[T](a: T, b: T): T
+@[magic]
pub func shl[T](a: T): T
+@[magic]
pub func shr[T](a: T): T
-
-pub func abs[T](a: T): T
-pub func neg[T](a: T): bool
diff --git a/std/prelude/options.pk b/std/prelude/options.pk
index 4f6cef2..d5cfb9d 100644
--- a/std/prelude/options.pk
+++ b/std/prelude/options.pk
@@ -9,91 +9,84 @@ pub type Option[T] = union
Some(T)
None
+## Syntactic sugar for optional type declarations.
+pub macro ?(T: type) =
+ quote Option[`T`]
+
+## Directly accesses the inner value. Throws an exception if None.
+pub func ![T](self: T?): T =
+ if self of Some(x) then x
+ else raise "empty"
+
+## Indirect access. Propagates `None`.
+pub macro ?[T](self: T?) =
+ quote
+ match `self`
+ of Some(x) then x
+ of None then return None
+
## Checks if a type is present within an `Option` type.
-pub func is_some[T](self: Option[T]): bool =
+pub func is_some[T](self: T?): bool =
self of Some(_)
## Checks if a type is not present within an `Option` type.
-pub func is_none[T](self: Option[T]): bool =
+pub func is_none[T](self: T?): bool =
self of None
## Converts an `Option[T]` to a `Result[T, E]` given a user-provided error.
-pub func err[T, E](self: Option[T], error: E): Result[T, E] =
- if self of Some(x):
+pub func err[T, E](self: T?, error: E): Result[T, E] =
+ if self of Some(x) then
Okay(x)
- else:
+ else
Error(error)
## Applies a function to `T`, if it exists.
-pub func map[T, U](self: Option[T], fn: T -> U): Option[U] =
- if self of Some(x):
+pub func map[T, U](self: T?, fn: T -> U): U? =
+ if self of Some(x) then
Some(fn(x))
- else:
+ else
None
## Converts `T` to a `None`, if `fn` returns false and it exists.
-pub func filter[T](self: Option[T], fn: T -> bool): Option[T] =
- if self of Some(x) and fn(x):
+pub func filter[T](self: T?, fn: T -> bool): T? =
+ if self of Some(x) and fn(x) then
Some(x)
- else:
+ else
None
## Applies a function to T, if it exists. Equivalent to `self.map(fn).flatten`.
-pub func flatmap[T, U](self: Option[T], fn: T -> Option[U]): Option[U] =
- if self of Some(x):
+pub func flatmap[T, U](self: T?, fn: T -> U?): U? =
+ if self of Some(x) then
fn(x)
- else:
+ else
None
## Converts from Option[Option[T]] to Option[T].
-pub func flatten[T](self: Option[Option[T]]): Option[T] =
- match self
- of Some(Some(x)):
+pub func flatten[T](self: T??): T? =
+ if self of Some(Some(x)) then
Some(x)
- of _:
+ else
None
## Returns the inner value or a default.
-pub func get_or[T](self: Option[T], default: T): T =
- if self of Some(x): x
- else: default
-
-## A dummy type signalling the empty value
-type EmptyType = distinct unit
-
-## Directly accesses the inner value. Throws an exception if None.
-pub func ![T](self: Option[T]): T =
- if self of Some(x): x
- else: raise EmptyType
-
-## Indirect access. Propagates `None`.
-## Parsed by compiler magic.
-pub macro ?[T](self: Option[T]) =
- quote:
- match `self`
- of Some(x): x
- of None: return None
-
-## Syntactic sugar for optional type declarations.
-pub macro ?(T: type) =
- quote:
- Option[`T`]
+pub func get_or[T](self: T?, default: T): T =
+ if self of Some(x) then x
+ else default
## Overloads the `==` operation for use on Options.
-pub func ==[T](a, b: Option[T]): bool =
- match (a, b)
- of (Some(x), Some(y)):
+pub func ==[T](a, b: T?): bool =
+ if (a, b) of (Some(x), Some(y)) then
x == y
- of _:
+ else
false
## Overloads the `str()` function for use on Options.
-pub func str[T](self: Option[T]): str =
- if self of Some(x):
- fmt("Some({})", x.str)
- else:
+pub func str[T: Display](self: T?): str =
+ if self of Some(x) then
+ "Some({})".fmt(x.str)
+ else
"None"
-examples:
+examples
let x = Some(42)
- if x of Some(y):
+ if x of Some(y) then
assert x! == y
# references:
diff --git a/std/prelude/ranges.pk b/std/prelude/ranges.pk
index e3af8a4..8410c5e 100644
--- a/std/prelude/ranges.pk
+++ b/std/prelude/ranges.pk
@@ -5,7 +5,7 @@ type Range[T] = struct
start: T
end: T
-type RangeInclusive[T] = struct
+type RangeIncl[T] = struct
start: T
end: T
done: bool
@@ -17,49 +17,48 @@ pub func ..(from: int, to: int): Range[int] =
## Inclusive ranges. Useful for ranges.
## Includes `from` and `to`.
-pub func ..=(from: int, to: int): RangeInclusive[int] =
+pub func ..=(from: int, to: int): RangeIncl[int] =
{ from, to, done: false }
# todo: implement for all types that can increment or smth idk
pub func next[T: int](self: mut Range[T]): T? =
- if self.start < self.end:
- result = Some(self.start)
+ if self.start < self.end then
self.start += 1
- else:
- result = None
+ Some(self.start - 1)
+ else
+ None
# todo: We don't need a mutable Range here to peek.
-# How does this interact with interfaces?
+# How does this interact with classes?
pub func peek[T: int](self: mut Range[T]): T? =
self.peek_nth(0)
pub func peek_nth[T: int](self: mut Range[T], i: uint): T? =
let res = self.start + i
- if res < self.end:
+ if res < self.end then
return Some(res)
- else:
+ else
return None
-pub func next[T: int](self: mut RangeInclusive[T]): T? =
- if self.done:
- return None
- elif self.start < self.end:
+pub func next[T: int](self: mut RangeIncl[T]): T? =
+ if self.done then
+ None
+ elif self.start < self.end then
let res = self.start
self.start += 1
- return Some(res)
- elif self.start == self.end:
+ Some(res)
+ elif self.start == self.end then
self.done = true
- return Some(self.start)
- else:
+ Some(self.start)
+ else
self.done = true
- return None
+ None
-pub func peek[T: int](self: mut RangeInclusive[T]): T? =
+pub func peek[T: int](self: mut RangeIncl[T]): T? =
self.peek_nth(0)
-pub func peek_nth[T: int](self: mut RangeInclusive[T], i: uint): T? =
+pub func peek_nth[T: int](self: mut RangeIncl[T], i: uint): T? =
let res = self.start + i
- if res <= self.end:
- return Some(res)
- else:
- return None
+ if res <= self.end
+ then Some(res)
+ else None
diff --git a/std/prelude/results.pk b/std/prelude/results.pk
index 81bccf0..61b0c8a 100644
--- a/std/prelude/results.pk
+++ b/std/prelude/results.pk
@@ -8,8 +8,8 @@ pub type Result[T, E] = union
Okay(T)
Error(E)
-## The Err interface. Useful for dynamically dispatching errors.
-pub type Err = interface
+## The Err class. Useful for dynamically dispatching errors.
+pub type Err = class
str(Self): str
dbg(Self): str
@@ -18,7 +18,21 @@ pub type Err = interface
pub type Result[T] = Result[T, ref Err]
## A `Result` type that only checks for success.
## Does not contain a value.
-pub type Success[E] = Result[Unit, E]
+# pub type Success[E] = Result[void, E]
+## A `Result` type that only checks for success.
+## Does not contain a value. Dynamically dispatched.
+# pub type Success = Result[void]
+
+## Syntactic sugar for dynamic result type declarations.
+pub macro !(T: type) =
+ quote Result[`T`]
+
+## Indirect access. Propagates `Error`.
+pub macro ?[T, E](self: Result[T, E]) =
+ quote
+ match `self`
+ of Okay(x) then x
+ of Error(e) then return Error(e)
## Checks if a `Result` type was successful.
pub func is_ok[T, E](self: Result[T, E]): bool =
@@ -28,110 +42,94 @@ pub func is_err[T, E](self: Result[T, E]): bool =
self of Error(_)
## Converts from a `Result[T, E]` to an `Option[T]`.
-pub func ok[T, E](self: Result[T, E]): Option[T] =
- if self of Okay(x):
+pub func ok[T, E](self: Result[T, E]): T? =
+ if self of Okay(x) then
Some(x)
- else:
- None()
+ else
+ None
## Converts from a `Result[T, E]` to an `Option[E]`.
-pub func err[T, E](self: Result[T, E]): Option[E] =
- if self of Error(x):
+pub func err[T, E](self: Result[T, E]): E? =
+ if self of Error(x) then
Some(x)
- else:
- None()
+ else
+ None
## Applies a function to `T`, if self is `Okay`.
pub func map[T, E, U](self: Result[T, E], fn: T -> U): Result[U, E] =
match self
- of Okay(x):
+ of Okay(x) then
Okay(fn(x))
- of Error(e):
+ of Error(e) then
Error(e)
## Applies a function to `E`, if self is `Error`.
pub func map_err[T, E, F](self: Result[T, E], fn: E -> F): Result[T, F] =
match self
- of Error(e):
+ of Error(e) then
Error(fn(e))
- of Okay(x):
+ of Okay(x) then
Okay(x)
## Applies a function to `T`, if it exists. Equivalent to `self.map(fn).flatten`.
pub func flatmap[T, E, U](self: Result[T, E], fn: T -> Result[U, E]): Result[U, E] =
match self
- of Okay(x):
+ of Okay(x) then
fn(x)
- of Error(e):
+ of Error(e) then
Error(e)
## Converts from a `Result[Result[T, E], E]` to a `Result[T, E]`.
pub func flatten[T, E](self: Result[Result[T, E], E]): Result[T, E] =
match self
- of Okay(Okay(x)):
+ of Okay(Okay(x)) then
Okay(x)
- of Okay(Error(e)), Error(e):
+ of Okay(Error(e)), Error(e) then
Error(e)
## Transposes a `Result[Option[T], E]` to an `Option[Result[T, E]]`.
-pub func transpose[T, E](self: Result[Option[T], E]): Option[Result[T, E]] =
+pub func transpose[T, E](self: Result[T?, E]): Result[T, E]? =
match self
- of Okay(Some(x)):
+ of Okay(Some(x)) then
Some(Okay(x))
- of Okay(None()), Error(_):
- None()
+ of Okay(None), Error(_) then
+ None
## Transposes an `Option[Result[T, E]]` to a `Result[Option[T], E]`. Takes a default error.
-pub func transpose[T, E](self: Option[Result[T, E]], error: E): Result[Option[T], E] =
+pub func transpose[T, E](self: Result[T, E]?, error: E): Result[T?, E] =
match self
- of Some(Okay(x)):
- Okay(Some(x))
- of Some(Error(e)):
- Error(e)
- of None():
- Error(error)
+ of Some(Okay(x)) then Okay(Some(x))
+ of Some(Error(e)) then Error(e)
+ of None then Error(error)
## Returns the inner value or a default.
pub func get_or[T, E](self: Result[T, E], default: T): T =
- if self of Okay(x): x
- else: default
+ if self of Okay(x) then x
+ else default
## Directly accesses the inner value. Throws an exception if `Error`.
pub func ![T, E](self: Result[T, E]): T =
match self
- of Okay(x): x
- of Error(e): raise e
+ of Okay(x) then x
+ of Error(e) then raise e
## Directly accesses the inner error. Throws an exception of type T if `Okay`.
pub func get_err[T, E](self: Result[T, E]): E =
match self
- of Error(e): e
- of Okay(x): raise x
-
-## Indirect access. Propagates `Error`.
-pub macro ?[T, E](self: Result[T, E]) =
- quote:
- match `self`
- of Okay(x): x
- of Error(e): return Error(e)
-
-## Syntactic sugar for dynamic `Result` type declarations.
-pub macro ?!(T: type) =
- quote:
- Result[`T`]
+ of Error(e) then e
+ of Okay(x) then raise x
## Overloads the `==` operation for use on Results.
pub func ==[T, E, F](a: Result[T, E], b: Result[T, F]): bool =
- match (a, b)
- of (Okay(x), Okay(y)):
+ if (a, b) of (Okay(x), Okay(y)) then
x == y
- of _:
+ else
false
## Overloads the `str()` function for use on Results.
-pub func str[T, E](self: Result[T, E]): str =
+pub func str[T: Display, E: Display](self: Result[T, E]): str =
match self
- of Some(x):
- fmt("Okay({})", x.str)
- of Error(e):
- fmt("Error({})", e.str)
+ of Some(x) then
+ "Okay({})".fmt(x.str)
+ of Error(e) then
+ "Error({})".fmt(e.str)
-examples:
+examples
let x: Error("fuck") = Okay(42)
func idk: Result[int, string]
diff --git a/std/prelude/strings.pk b/std/prelude/strings.pk
index 2f48b67..e86d520 100644
--- a/std/prelude/strings.pk
+++ b/std/prelude/strings.pk
@@ -2,100 +2,82 @@
## This module is imported by default.
# reference: https://doc.rust-lang.org/std/string/struct.String.html
-use std.pointers
-
-# possible approaches: list[byte], distinct list[byte], this
-# should *really* just rely on lists, i don't want to duplicate slicing logic, but... maybe...
-type str = struct
- data: ptr[byte]
- length: uint
- capacity: uint
+## A primitive string type.
+##
+## We do not want methods defined on `list[byte]` to carry over,
+## so we define `str` as a newtype.
+pub type str = struct
+ data: list[byte]
## Initialize and return an empty string.
-pub func init(): str =
- ...
+pub func init: str = { data = [] }
## Gets the length of a string.
-pub func len(self: str): uint =
- self.length
-
-pub func empty(self: str): bool =
- self.length == 0
-
-## Gets the internal capacity of a string.
-func cap(self: str): uint =
- self.capacity
-
-## Expands the capacity of a string.
-func grow(self: mut str) =
- ...
+## This is an O(n) operation, due to UTF-8 encoding.
+pub func len(self: lent str): uint =
+ var res: uint
+ for _ in self do
+ res += 1
+ res
## Pushes a character to the end of a mutable string.
-pub func push(self: mut str, val: chr) = # todo: `add` alias?
- ...
+pub func push(self: mut str, val: chr) =
+ self.data.push(val.byte) # todo: obsolete by from/to conversion??
## Pushes an owned string to the end of a mutable string.
pub func push(self: mut str, val: str) =
- ...
+ self.data.push(val.bytes) # todo: obsolete by from/to conversion??
## Removes and returns the last character of a string, if it exists.
+##
+## SAFETY: We return early upon an empty string.
+## And decrement by one char for a non-empty string.
+@[safe]
pub func pop(self: mut str): chr? =
- ...
+ let char = self.chars.rev.next?
+ self.data.set_len(self.len - char.len) # this is normally unsafe.
+ Some(char)
## Returns the character at the provided index, if it exists.
pub func get(self: str, i: uint): chr? =
...
## Sets the character at the provided index, if it exists.
-## todo: what if it does not exist?
+## As strings are packed, this may call str.grow and reallocate.
+## oh fuck we have to insert + remove anyway
pub func set(self: mut str, i: uint, val: chr) =
...
## Inserts a character at an arbitrary position within a string.
-## Error handling: todo
+## Panics on failure. (todo: can we do better?)
pub func insert(self: mut str, i: uint, val: chr) =
...
## Removes and returns a character at an arbitrary position within a string.
-## Panics on failure.
+## Panics on failure. (todo: can we do better?)
pub func remove(self: mut str, i: uint): chr =
...
-## Returns the *byte* at an arbitrary position within a string.
-## Explicitly distinct from `get` as this breaks safety conventions idk
-pub func get_byte(self: str): byte =
- ...
-
-## Sets a byte at the provided index, if it exists. Do we want this?
-pub func set_byte(self: mut str, i: uint, val: byte) =
- ...
-
-## Inserts a byte into a string. Breaks string convention. Do we want this?
-pub func insert_byte(self: mut str, i: uint, val: byte) =
- ...
-
-## Removes a byte at a given offset from a string. Do we want this?
-pub func remove_byte(self: mut str, i: uint): byte? =
- ...
-
-## Converts a given string to a list of bytes for iteration purposes.
-pub func bytes(self: str): list[byte] =
- ... # many performance considerations
-
-## Converts a given string to a list of chars for iteration purposes.
-pub func chars(self: str): list[chr] # todo: useful?
-
-## The concatenation operator.
-pub func &(a: str, b: str): str =
- ...
-
## Syntactic sugar for string appending.
-pub func &=(a: mut str, b: owned str) =
+pub func &=(a: mut str, b: str) =
a.push(b)
-# todo: many things
+## The concatenation operator. Consumes two strings.
+pub func &(a: str, b: str): str =
+ a.push(b)
+ a
+
+## Conversion from a string to a list of bytes. Zero-cost.
+pub func to(self: str): list[byte] = self.data
+## Conversion from a str to a list[chr]. Reallocates.
+pub func to(self: str): list[chr] =
+ var res: list[chr]
+ for char in self do res.push(char)
+ res
+## Conversion from a char to an array of bytes. Zero-cost.
+@[safe] # possibly unsafe?? depends on repr of arrays
+pub func to(self: chr): array[byte, 4] = self.cast[array[byte, 4]]
-# pub func iter(self: str): StrIter
# pub func next(self: mut StrIter)
# pub func peek(self: mut StrIter)
# pub func peek_nth(self: mut StrIter, i: uint)
diff --git a/std/threads.pk b/std/threads.pk
index 2b1e98f..fcb65d4 100644
--- a/std/threads.pk
+++ b/std/threads.pk
@@ -1 +1,3 @@
## std.threads: Primitives for threads and thread-safe code.
+## These are system threads, not greenthreads. Does not include a threadpool.
+# reference: https://github.com/mratsim/weave