From d5f89ba0e2fd0458f4e51e13233069583d5a89ac Mon Sep 17 00:00:00 2001 From: JJ Date: Sun, 22 Oct 2023 19:34:17 -0700 Subject: std: broad updates; add ast, iterators, format, tests, logs --- std/default/format.pk | 51 ++++++++++++++++++++ std/default/iterators.pk | 38 +++++++++++++++ std/default/options.pk | 18 +++---- std/default/results.pk | 22 +++++---- std/fundamental/ast.pk | 119 +++++++++++++++++++++++++++++++++++++++++++++++ std/fundamental/logs.pk | 1 + std/fundamental/tests.pk | 5 ++ 7 files changed, 236 insertions(+), 18 deletions(-) create mode 100644 std/default/format.pk create mode 100644 std/default/iterators.pk create mode 100644 std/fundamental/ast.pk create mode 100644 std/fundamental/logs.pk create mode 100644 std/fundamental/tests.pk diff --git a/std/default/format.pk b/std/default/format.pk new file mode 100644 index 0000000..c7d7ab8 --- /dev/null +++ b/std/default/format.pk @@ -0,0 +1,51 @@ +## std/format: Niceties around printing and debugging. +## This module is imported by default. + +## The Display interface. Any type implementing `str` is printable. +## Any type that is Display must also implement Debug. +pub type Display = interface + str(Self): str + dbg(Self): str + +## The Debug interface. Broadly implemented for every type with magic, +## types can (and should) override the generic implementations. +pub type Debug = interface + dbg(Self): str + +## Prints all of its arguments to the command line. +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. +pub func dbg(params: varargs[Debug]) = + stdout.write(params.map(x => x.dbg).join(" "), "\n") + +## A dummy implementation of the Display interface for strings. +pub func str(self: str): str = self +## An implementation of the Debug interface for strings. +pub func dbg(self: str): str = "\"" & self & "\"" + +## An implementation of the Debug interface for all structs. +## Uses the special `struct` typeclass. +pub func dbg(self: struct): str = + "{" & self.fields.map(field => field.id & field.value.dbg) & "}" + +## An implementation of the Debug interface for all tuples. +## Uses the special `tuple` typeclass. +pub func dbg(self: tuple): str = + result &= "(" + for i, field in self.fields.enumerate(): + result &= field.id.map(id => id & " = ").get_or("") + if i != self.fields.len: + result &= ", " + result &= ")" + +## An implementation of the Debug interface for all arrays and lists. +pub func dbg(self: Iter[Debug]): str = + "[" & self.map(x => x.str).join(", ") & "]" + +## The fmt macro. Builds a formatted string from its arguments. +pub macro fmt(formatted: static[str], args: varargs[Display]) = + # if not formatted of String: + # macro_error("fmt must take a static string parameter!") + ... diff --git a/std/default/iterators.pk b/std/default/iterators.pk new file mode 100644 index 0000000..f97c61a --- /dev/null +++ b/std/default/iterators.pk @@ -0,0 +1,38 @@ +## std/iterators: The Iter interface and associated functions. +## This module is imported by default. + +## The Iter interface. Any type implementing `next()` is iterable. +pub type Iter[T] = interface + next(mut Self): Option[T] + +# todo: useful functions for an iterator +# https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods + +pub func advance_by[T](self: Iter[T], n: uint): Result[T, ...] = + for i in 0 .. n: + if self.next().is_none(): + return Error(...) + Okay + +pub func get[T](self: Iter[T], at: uint): Option[T] + self.advance_by(at-1).ok? + self.next() + +# todo: implement iter[T](self: ...): Iter[T] funcs +# todo: efficient functional methods + +## The Peek interface. Any type implementing Iter, `peek`, and `peek_nth` is peekable. +pub type Peek[T] = interface + next(mut Self): Option[T] + peek(mut Self): Option[T] + peek_nth(mut Self, n: uint): Option[T] + +# todo: implement peek[T](self: Iter[T]): Peek[T] +# todo: implement Peekable struct +# https://github.com/LukeMathWalker/multipeek/blob/main/src/lib.rs + +## We don't want a Countable. It's not terribly useful. +# pub type Countable[T] = interface +# next(mut Self): Option[T] +# len(Self): uint +# get(Self, uint): Option[T] diff --git a/std/default/options.pk b/std/default/options.pk index f1bcac4..3aaea49 100644 --- a/std/default/options.pk +++ b/std/default/options.pk @@ -1,4 +1,4 @@ -## std/options +## std/options: Optional types. ## This module is imported by default. import std/format @@ -47,20 +47,20 @@ pub func flatten[T](self: Option[Option[T]]): Option[T] = # todo: better name? None ## Returns the inner value or a default. -pub func or[T](self: Option[T], default: T): T = +pub func get_or[T](self: Option[T], default: T): T = if self of Some(x): x else: default ## Directly accesses the inner value. Throws an exception if None. -pub yeet func get[T](self: Option[T]): T = +pub yeet func `!`[T](self: Option[T]): T = if self of Some(x): x else: raise Exception # todo: syntax?? -# todo: direct access, alias to get -macro `!`[T](self: Option[T]): T -# todo: indirect access, ??? do we propagate? is this useful? probably not -macro `?`[T](self: Option[T]): T -# todo: field access? useful? assignment? -macro `.?`[T](self: Option[T]) +## Indirect access. Propagates None. +pub macro `?`[T](self: Option[T]) = + quote: + match self + of Some(x): x + of None: return None ## Overloads the == operation for use on Options. pub func `==`[T](a, b: Option[T]): bool = diff --git a/std/default/results.pk b/std/default/results.pk index 2ac47d0..187ece9 100644 --- a/std/default/results.pk +++ b/std/default/results.pk @@ -1,4 +1,4 @@ -## std/results +## std/results: Result types. ## This module is imported by default. import std/[options, format] @@ -7,9 +7,11 @@ pub type Result[T, E] = union Okay: T Error: E +# todo: determine the difference between interfaces and types +# ErrorInterface? Errorable? Err? pub type Error = interface - func str(self: Self) - func dbg(self: Self) + str(Self): str + dbg(Self): str pub type Result[T] = Result[T, ref Error] @@ -79,11 +81,11 @@ pub func transpose[T, E](self: Option[Result[T, E]], error: E): Result[Option[T] Error(error) ## Returns the inner value or a default. -pub func or[T, E](self: Result[T, E], default: T): T = +pub func get_or[T, E](self: Result[T, E], default: T): T = if self of Okay(x): x else: default ## Directly accesses the inner value. Throws an exception if Error(e). -pub yeet func get[T, E](self: Result[T, E]): T = +pub yeet func `!`[T, E](self: Result[T, E]): T = match self of Okay(x): x of Error(e): raise Exception(e) # todo: syntax?? @@ -92,10 +94,12 @@ pub yeet func get_err[T, E](self: Result[T, E]): E = of Error(e): e of Okay(x): raise Exception(x) # todo: syntax?? -# todo: direct access, alias to get -macro `!`[T, E](self: Result[T, E]): T -# todo: indirect access, propagates Err -macro `?`[T, E](self: Result[T]): T +## Indirect access. Propagates Error. +macro `?`[T, E](self: Result[T, E]) = + quote: + match self + of Okay(x): x + of Error(e): return Error(e) ## Overloads the == operation for use on Results. pub func `==`[T, E, F](a: Result[T, E], b: Result[T, F]): bool = diff --git a/std/fundamental/ast.pk b/std/fundamental/ast.pk new file mode 100644 index 0000000..25bdc02 --- /dev/null +++ b/std/fundamental/ast.pk @@ -0,0 +1,119 @@ +## std/ast: Exposes the AST for building and operating on with macros. + +pub type Ident = string +pub type Number = int +pub type Float = float +pub type Char = char +pub type String = str +pub type Struct = list[struct[field: str, value: Expr]] +pub type Tuple = list[struct[field: Option[string], value: Expr]] +pub type List = list[Expr] + +pub type Let = struct + id: Pattern + kind: Option[Type] + value: ref Expr +pub type Var = struct + id: Pattern + kind: Option[Type] + value: Option[ref Expr] # variable bindings can be delayed +pub type Const = struct + id: Pattern + kind: Option[Type] + value = ref Expr +pub type FuncDecl = struct + public: bool + effect: Option[str] + id: str + generics: list[struct[name: str, kind: Option[Type]]] + params: list[struct[name: str, kind: Type]] + kind: Type + body: list[Expr] +pub type TypeDecl = struct + id: str + generics: list[str] + alias: Type +pub type Import = struct + mod_from: Option[string] + imports: list[str] + alias: Option[str] +pub type Module = struct + name: str + body: list[ref Expr] + +pub type Call = struct + id: str + params: list[Expr] +pub type Cond = struct + branches: list[CondBranch] + else_body: Option[list[Expr]] +pub type Try = struct + body: ref Expr + catches: list[struct[exceptions: list[str], body: list[Expr]]] + finally_body: Option[list[Expr]] +pub type Match = struct + item: ref Expr + branches: list[MatchBranch] +pub type Block = struct + id: Option[str] + body: list[Expr] +pub type Static = struct + body: list[Expr] +pub type For = struct + binding: Pattern + range: ref Expr + body: list[Expr] +pub type While = struct + cond: ref Expr + body: list[Expr] +pub type Loop = struct + body: list[Expr] + +# the first style of union. naming anonymous types. +pub type Type = union + Void, Never, + Integer, Float, String, + Func: struct[from, to: ref Type] # todo: multiple parameters + Struct: list[struct[id: str, kind: Type]] + Tuple: list[struct[id: Option[str], kind: Type]] + Union: list[struct[id: str, kind: Type]] + Interface: struct # todo: generics + funcs: list[Signature] + for_type: Option[ref Type] + Array: struct[size: uint, kind: ref Type] + List: ref Type + Slice: ref Type # todo: plus ownership + Alias: str # todo: params?? huh? + Static: ref Type + Mutable: ref Type + Reference: ref Type +pub type Signature = struct # todo: generics + id: str + effect: Option[str] + params: list[Type] + kind: Option[Type] +pub type Pattern = union + Ident: str + Struct: struct[name: str, params: list[]] + Tuple: list[Pattern] + List +pub type CondBranch = struct + cond: Expr + body: list[Expr] +pub type MatchBranch = struct + pattern: Pattern + guard: Option[Expr] + body: Expr + +# the second style of union. A union of literal types, no names. +pub type Expr = union + Ident, Number, Float, Char, String, Struct, Tuple, List, + Let, Var, Const, FuncDecl, TypeDecl, Import, Module, + Call, Cond, Try, Match, Block, Static, For, While, Loop, + +# todo: decide on a style of union + +# anonymous struct objects can be constructed with {} +# anonymous tuple objects can be constructed with () +# anonymous list objects can be constructed with [] +# anonymous union *types* can be constructed with | (in parameters) diff --git a/std/fundamental/logs.pk b/std/fundamental/logs.pk new file mode 100644 index 0000000..7a0060f --- /dev/null +++ b/std/fundamental/logs.pk @@ -0,0 +1 @@ +## std/logs: Utility macros and functions for logging. diff --git a/std/fundamental/tests.pk b/std/fundamental/tests.pk new file mode 100644 index 0000000..7eb00a8 --- /dev/null +++ b/std/fundamental/tests.pk @@ -0,0 +1,5 @@ +## std/tests: Utility macros and functions for testing. + +## A macro for runnable examples. Runs them on compilation. +pub macro examples(body) = + ... -- cgit v1.2.3-70-g09d2