Fun Project to write my own language, to learn Go and to see how a good language would look like for me. So it will probably end up like a modern C or Rust
A statically and strongly typed programming language similar to Go and Rust.
- fast
- easy
- compiled
- statically and strongly typed
- lightweigth
- important build-in functions
- general purpose language and good for low level tasks
- crossplatform
- Linux
- MacOS
- windows
- x86_64
- ARM
v := 69 // infer type from value
v u32 := 64 // explicit type
c :: -64 // just change "=" to ":" to make it a const
c i64 :: -420
// print, println, eprint, eprintln
println("hello world!")
println(fmt("print a number {} or {}", 69, "somthing else"))
eprintln("ERROR") // write to stderr
v := [$]i32{ len: 1, cap: 6 }
v[0] = 0
v = append::<i32>(v, 2)
v = append::<i32>(v, 3)
println(vtos(v)) // vectors are not yet supported for 'fmt'
struct Test {
a i32,
b u64
}
t1 := Test{ a: 1, b: 6 }
t2 := Test{ 1, 6 } // omit field names -> go by order (will be changed later)
println(fmt("Test{ a: {}, b: {} }", t1.a, t1.b))
fn func(a i32, b i32) {
println(fmt("{}", a + b))
}
fn func(a i32, b i32) -> i32 {
ret a + b
}
fn func<T>(v [$]T) -> [$]T {
for i u64, v.len {
v[i] = i
}
ret v
}
// calling generic functions
v = func::<i32>(v)
// define an interface
interface Number {
fn add(a u64, b u64) -> u64
fn inc(self) -> u64
fn dec(*self) -> u64
}
struct Test {
a u64
}
// implement an interface
impl Test :: Number {
fn add(a u64, b u64) -> u64 {
ret a + b
}
// optional: use explicitly Self type
// optional: use explicitly actual Struct type
fn inc(self) -> u64 {
ret self.a + 1
}
fn dec(*self) -> u64 {
self.a = self.a + 1
ret self.a
}
}
// call interface functions
res := Test::add(30, 39)
// call like methods
t := Test{ 64 }
res := t.inc()
res2 := t.dec() // causes a side effect due to *self
// calling like a function is possible too
_ := Test::inc(t)
_ := Test::dec(&t)
struct Test {
a u64
}
// implement without an interface
impl Test {
fn inc(self) -> u64 {
ret self.a + 1
}
fn dec(*self) -> u64 {
self.a = self.a + 1
ret self.a
}
}
t := Test{ 64 }
res := t.inc()
res2 := t.dec()
_ := Test::inc(t)
_ := Test::dec(&t)
enum Test {
// 0, 1, 2 (u64)
A, B, C
}
// optional id type (default is u64)
enum Test2 u8 {
// enums are like tagged unions
A(i64), B(str), C(bool)
}
a := Test::A
b := Test2::A(64)
b = Test2::B("string")
if a == Test::A {
println("a == Test::A")
}
// unwrap b to get the string (creates a new var / not a reference)
if b : Test::B(s) {
println("unwraped b contains: " + s)
}
// only tmp sytnax for funcs (will be changed)
cfn func(a i32, b i32) -> i32 {
// non const stmts will cause an error (like "print")
if a > b * 2 {
ret b
} else {
ret a + b
}
}
cfn func2(a i32) -> i32 {
// vars are allowed
res := 0
for i i32, a {
res = res + i
}
ret res
}
a :: 30
b :: 39
c := func(a, b) // func will be executed at compile time and "69" will be "hardcoded"
d := 69
e := func(a, d) // if one or more args are not const the func gets executed at runtime (like a normal func)
cfn fib(i u64) -> u64 {
ret $ i == {
0, 1: 1 as u64
_: fib(i-1) + fib(i-2)
}
}
// check at compile time if your machine is little or big endian
cfn isBigEndian() -> bool {
// even pointers (and dereferencing) are allowed (only addresses in the stack)
word i16 := 0x0001
ptr *bool := &word as u64 as *bool
ret *ptr == false
}
if {
// cases only have one statement so use {} for multiple statements
x == 0: {
do_stuff()
do_stuff()
}
_: do_stuff()
}
if x == {
0: do_stuff()
1: do_stuff()
2, 3, 4: do_stuff()
_: do_stuff()
}
// cases in one-line-switches are seperated with ";"
if x == { 0: do_stuff(); _: do_stuff() }
// same as a normal switch but with a $ instead of an "if"
res := $ {
x == 0: -1 // only an expression is allowed as case body (statments are allowed later too)
_: 69
}
// assign to one of vars depending on a condition
$ i <= { 1: v1; _: v2 } = 64
$ go run gamma <source_file>
$ go test ./test -v
$ go run gamma --help
gamma usage:
-ast
show the AST
-r run the compiled executable
$ go run gamma -r ./examples/http.gma
run on http://localhost:6969
...
$ go run gamma -r ./test/rule110.gma
o
oo
ooo
oo o
ooooo
oo o
ooo oo
oo o ooo
ooooooo o
oo ooo
ooo oo o
oo o ooooo
ooooo oo o
oo o ooo oo
ooo oooo o ooo
oo o oo ooooo o
oooooooo oo ooo
...
- generate assembly file
- nasm
- fasm (preferable!)
- variables
- functions
- define/call
- System V AMD64 ABI calling convention
- lambda
- const function
- packages
- import
- import only once
- detected import cycles
- pub keyword
- access by package name
- stdlib
- sockets
- io
- read/write files
- arithmetics
- unary ops
- binary ops
- parse by precedence
- parentheses
- controll structures
- if, else, elif
- while, for
- switch
- xswitch (expr switch)
- pointer
- define/assign
- deref
- get addr (via "&")
- arithmetic
- consts
- define/use
- compile time eval
- arrays
- define/use
- multi-dimensionale
- compile time eval
- structs
- define struct type
- define object
- access fields (read/write)
- compile time eval
- turing complete -> actual programming language
- proof with Rule 110 programm
- type checking
- tests
- examples
- simple http server
- game of life
- doom
- self-hosted
- cross-platform