summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorWormHeamer2025-07-31 22:37:38 -0400
committerWormHeamer2025-07-31 22:37:38 -0400
commit842e22e9eb0f3dff7dabdaa41bcc2133e8f015f5 (patch)
tree78f42e68da656698526ff6099e78d82adab1d582 /doc
initial commit
Diffstat (limited to 'doc')
-rw-r--r--doc/demo.txt94
-rw-r--r--doc/test.txt119
2 files changed, 213 insertions, 0 deletions
diff --git a/doc/demo.txt b/doc/demo.txt
new file mode 100644
index 0000000..58c0839
--- /dev/null
+++ b/doc/demo.txt
@@ -0,0 +1,94 @@
+type vec2 = [2]f32
+
+type Sprite = enum u8 {
+ BLANK,
+ PLAYER
+}
+
+type Player = struct {
+ pos, vel vec2
+}
+
+extern {
+ blit proc(spr Sprite, x, y i32),
+ exp func(v f32) f32
+}
+
+proc frame(dt f32) {
+ /* unary .. can splat an array as parameters */
+ blit(.PLAYER, ..plr.pos)
+ plr.pos += plr.vel * dt
+ plr.vel *= exp(-dt * 5)
+}
+
+/* statements
+
+ type Ident = T Alias Ident to type T
+ extern Ident T Define an external variable (or function!) of the given type
+ extern { a A, b B... } Block of extern declarations, formatted like a struct
+
+*/
+
+/* expressions
+
+ N Integer
+ N.N | NeN | N.NeN Float
+ [-+~] A Unary operator on term A
+ A [+-/*%&|^] B Binary operator on term A and expression B
+ A := B Type inference assignment
+ A, B, C := D
+ T { ... } Literal of type T; mostly only makes sense for structs,
+ but this can be used to do something like u8 { 2 } too.
+
+/* types
+
+ IDENT type of the given identifier
+ ^T pointer to type T
+ enum { FOO, BAR... } enum
+ enum T { FOO, BAR... } enum with backing type T, which must be an integer type
+ struct { a A, b B... } struct
+ A..B range (kinda an anonymous enum)
+ A and B must be ordinal types (enum value, integer, or character)
+ [N]T array of T of length N
+ [E]T array of T indexed by enum or range E
+ []T slice
+ (Ta, Tb...) tuple of given types; can be destructured to multiple assignment
+ set[E] a set of enum or range E
+ proc(a A, b B...) function pointer
+ func(a A, b B...) T function pointer with return value T
+
+*/
+
+Expr = LValue | Literal
+LValue = Ident | LValue '.' Ident
+Literal = Number
+Number = Integer | Float
+Integer = /[0-9]+/
+Float = Integer 'e' Integer | Integer '.' Integer | Integer '.' Integer 'e' Integer
+
+ IDENT Variable
+ LVALUE . IDENT Member access
+ [0-9]+ Integer
+ [0-9]+ . [0-9]+ Float
+ [0-9]+ . [0-9]+ e [0-9]+ Float
+ F(E1, E2...) function call with the given expression arguments
+
+*/
+
+/* import first tries to scan local directory for the package, then standard
+ * system directory; in said directory it will look for an IR cache file,
+ * or compile a new one if it's not present (or if its last-modified time
+ * is older than the compiler executable), which is imported with a name
+ * equivalent to the last part of the path.
+ *
+ * codegen is only performed on procedures that get called from main and their
+ * own dependencies, recursively. this allows to keep output binaries small.
+ * maybe supply some sort of export qualifier to do something similar for
+ * procedures that aren't main.
+ */
+
+import "io"
+
+proc main {
+ io.print("Hello, world!")
+}
diff --git a/doc/test.txt b/doc/test.txt
new file mode 100644
index 0000000..da8f501
--- /dev/null
+++ b/doc/test.txt
@@ -0,0 +1,119 @@
+type string = []char
+
+func split(src: string, by: char, a: ^Arena): [dyn]string do
+ let r: [dyn]string = {}
+ let s: string = ""
+ for c in src do
+ if c == by then
+ r.push(s, a)
+ s = ""
+ else
+ s.push(c, a)
+ end
+ end
+ return r
+end
+
+proc main do
+ let a = Arena()
+ let lines = split(stdin.readAll(@a), '\n', @a)
+ for i in 0..len(lines) do
+ print(lines[i])
+ end
+end
+
+---
+
+fn next_line(src, line: ^[]u8) -> bool {
+ if len(src) < 1 {
+ return false
+ }
+ let idx = find(src, '\n')
+ if idx <> -1 {
+ line^ := src^[0..idx]
+ } else {
+ line^ := src^
+ }
+ src^ = src^[len(line)..]
+ return true
+}
+
+fn main {
+ let a = Arena()
+ let buf = readall(stdin, &a)
+ let line: []u8
+ while next_line(&buf, &line) {
+ print(line)
+ }
+}
+
+---
+
+/* builtin slice type */
+
+fn print(str []u8) {
+ for i in str {
+ putchar(str[i])
+ }
+ putchar('\n')
+}
+
+/* iterators are just functions that return an initial state object and a
+ * step function that acts on it to produce a value */
+
+type LineIterFn = fn(it ^[]u8) ([]u8, bool)
+
+fn lines(src []u8) ([]u8, LineIterFn) {
+ fn step(it ^[]u8) []u8, bool {
+ if len(it^) < 1 { return {}, false }
+ idx := find(it^, '\n')
+ line := if idx == -1 { it^ } else { it^[0..idx] }
+ it^ = it^[len(line)..]
+ return line, true
+ }
+ return src, step
+}
+
+/* if multiple values used in if/while condition, only the last one is checked */
+
+fn main {
+ a := Arena {}
+ buf := readall(stdin, &a)
+ for line in lines(buf) {
+ print(line)
+ }
+}
+
+/* generics? */
+
+fn reverse[T any](buf []T) ([]T, fn(it ^[]T) (T, bool)) {
+ fn step(it ^[]T) (T, bool) {
+ if len(^it) < 1 { return {}, false }
+ n := len(it^) - 1
+ v := it^[n]
+ it^ := it^[0..n]
+ return v, true
+ }
+ return buf, step
+}
+
+fn foo {
+ s := "Hello, world!"
+ // print(reverse(s)) <- wouldn't actually work, because iterators can't be cast back to slices
+ a := Arena {}
+ print(collect(reverse(s), &a))
+}
+
+struct MapIter[In any, Out any] {
+ ctx: rawptr,
+ iter: fn(it rawptr) In,
+ map: fn(x ^In) Out
+}
+
+fn map[In any, Out any](p rawptr, f func(it rawptr) In, f func(x ^In) Out) (MapIter[In, Out], fn(it ^MapIter[In, Out]) (Out, bool)) {
+ fn step(it ^MapIter[In, Out]) (Out, bool) {
+ v, ok := it.iter(it.ctx)
+ if !ok { return {}, false }
+ return it.map(&v), true
+ }
+}