# Peon - Manual Peon is a functional, statically typed, garbage-collected, C-like programming language with a focus on speed and correctness, but whose main feature is the ability to natively perform highly efficient parallel I/O operations by implementing the [structured concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/) paradigm. __Note__: Peon is currently a WIP (Work In Progress), and much of the content of this manual is purely theoretical as of now. If you want to help make this into a reality, feel free to contribute! ## Table of contents - [Manual](#peon---manual) - [Design Goals](#design-goals) - [Examples](#peon-by-example) - [Grammar](grammar.md) - [Bytecode](bytecode.md) ## Design Goals While peon is inspired from Bob Nystrom's [book](https://craftinginterpreters.com), where he describes a simple toy language named Lox, the aspiration for it is to become a programming language that could actually be used in the real world. For that to happen, we need: - Exceptions (`try/except/finally`) - An import system (with namespaces, like Python) - Multithreading support (with a global VM lock when GC'ing) - Built-in collections (list, tuple, set, etc.) - Coroutines (w/ structured concurrency) - Generators - Generics - C/Nim FFI - A package manager Peon ~~steals~~ borrows many ideas from Python and Nim (the latter being the language peon itself is written in). ## Peon by Example Here follow a few examples of peon code to make it clear what the end product should look like ### Variable declarations ``` var x = 5; # Inferred type is int64 var y = 3'u16; # Type is specified as uint16 x = 6; # Works: type matches x = 3.0; # Cannot assign float64 to x var x = 3.14; # Cannot re-declare x ``` __Note__: Peon supports [name stropping](https://en.wikipedia.org/wiki/Stropping_(syntax)), meaning that almost any ASCII sequence of characters can be used as an identifier, including language keywords, but stropped names need to be enclosed by matching pairs of backticks (`\``) ### Functions ``` fn fib(n: int): int { if (n < 3) { return n; } return fib(n - 1) + fib(n - 2); } fib(30); ``` ### Type declarations ``` type Foo = object { # Can also be "ref object" for reference types (managed automatically) fieldOne*: int # Asterisk means the field is public outside the current module fieldTwo*: int } ``` ### Operator overloading ``` operator `+`(a, b: Foo) { return Foo(fieldOne: a.fieldOne + b.fieldOne, fieldTwo: a.fieldTwo + b.fieldTwo); } Foo(fieldOne: 1, fieldTwo: 3) + Foo(fieldOne: 2, fieldTwo: 3); # Foo(fieldOne: 3, fieldTwo: 6) ``` __Note__: Custom operators (e.g. `foo`) can also be defined! The backticks around the plus sign serve to mark it as an identifier instead of a symbol (which is a requirement for function names, since operators are basically functions). In fact, even the built-in peon operators are implemented partially in peon (well, their forward declarations are) and they are then specialized in the compiler to emit a single bytecode instruction. ### Function calls ``` foo(1, 2 + 3, 3.14, bar(baz)); ``` __Note__: Operators can be called as functions too. Just wrap their name in backticks, like so: ``` `+`(1, 2) ``` __Note__: Code the likes of `a.b()` is desugared to `b(a)` if there exists a function `b` whose signature is compatible with the value of of `a` (assuming `a` doesn't have a `b` field, in which case the attribute resolution takes precedence) ### Generic declarations ``` fn genericSum[T](a, b: T): T { # Note: "a, b: T" means that both a and b are of type T return a + b; } # This allows for a single implementation to be # re-used multiple times without any code duplication! genericSum(1, 2); genericSum(3.14, 0.1); genericSum(1'u8, 250'u8); ``` #### Multiple generics ``` fn genericSth[T, K](a: T, b: K) { # Note: no return type == void function! # code... } genericSth(1, 3.0); ``` __Note__: The `*` modifier to make a name visible outside the current module must be put __before__ generics declarations, so only `fn foo*[T](a: T) {}` is the correct syntax ### Forward declarations ``` fn someF: int; # Semicolon, no body! someF(); # This works! fn someF: int { return 42; } ``` ### Generators ``` generator count(n: int): int { while (n > 0) { yield n; n -= 1; } } foreach (n: count(10)) { print(n); } ``` ### Coroutines ``` import concur; import http; coroutine req(url: string): string { return (await http.AsyncClient().get(url)).content; } coroutine main(urls: list[string]) { pool = concur.pool(); # Creates a task pool: like a nursery in njsmith's article for (var i = 0; i < urls.len(); i += 1) { pool.spawn(req, urls[i]); } # The pool has internal machinery that makes the parent # task wait until all child exit! When this function # returns, ALL child tasks will have exited somehow } concur.run(main, newList[string]("https://google.com", "https://debian.org")) ```