~mna/snow unlisted

4dbe761574c65a616a50b66ed5c7923381868332 — Martin Angers 1 year, 4 months ago ad37f0e
start experimenting with qbe syntax
4 files changed, 122 insertions(+), 6 deletions(-)

A .gitignore
A codegen/add_one.ssa
A codegen/hello.ssa
M syntax/overview.snow
A .gitignore => .gitignore +5 -0
@@ 0,0 1,5 @@
# qbe compiled assembly
*.s

# compiled binary
*.out

A codegen/add_one.ssa => codegen/add_one.ssa +14 -0
@@ 0,0 1,14 @@
data $fmt = { b "%d\n", b 0 }

function w $add(w %arg) {
@start
  %val =w add 1, %arg
  ret %val
}

export function w $main() {
@start
  %val =w call $add(w 42)
  call $printf(l $fmt, w %val)
  ret 0
}

A codegen/hello.ssa => codegen/hello.ssa +9 -0
@@ 0,0 1,9 @@
# Define the string constant.
data $str = { b "hello world", b 0 }

export function w $main() {
@start
        # Call the puts function with $str as argument.
        %r =w call $puts(l $str)
        ret 0
}

M syntax/overview.snow => syntax/overview.snow +94 -6
@@ 17,14 17,16 @@ import http, json, "github.com/some3rdparty/other"
var s str

# A block can be started after most keywords (`var`, `let`, `import`,
# `type`, etc.) and the included statements all have that keyword apply, so the
# `struct`, etc.) and the included statements all have that keyword apply, so the
# following declares 4 variables.
var {
#
# OR parens, because curly braces introduce a scope, which isn't the case here?
var (
  x i8
  y i32
  z u64
  b bool
}
)

# `let` declares a read-only "variable" or constant. It must be assigned
# before use, and can only be assigned once.


@@ 36,18 38,22 @@ let pi = 3.1415
# i8, i16, i32, i64 => signed integers
# f32, f64 => floating-point numbers
# bool => boolean (true, false)
# void => no type

# The array is a built-in aggregate data type. It holds values of the same type.
var ar [4]u8 = [1, 2, 3, 4]

# The struct is another built-in aggregate data type. It holds a pre-defined number
# of values of possibly different types. A new data type is introduced by the
# `type` keyword.
type Person struct {
# of values of possibly different types. A new struct is introduced by the
# `struct` keyword.
struct Person {
  # fields of a struct can be mutable or not.
  var age i8
  let id u32

  # var is optional, it is the default for a struct field.
  alive bool

  # methods are defined by implementing a function inside the struct block.
  # There is an implicit `self` argument, but it is seldom required to type
  # because unknown variables are automatically resolved by looking at the


@@ 56,3 62,85 @@ type Person struct {
    printf("person: {}, age {}", id, age)
  }
}

# `enum` introduces an enumeration. By default, it implicitly takes integer
# values from 0. Specific values can be explicitly set.
enum HttpCode {
  success = 200
  not_found = 404
  # etc.
}

# Enumerations can have associated values. Combined with generics, this allows
# avoiding nulls and elegant error-handling.
enum Optional {
  some $T
  none void

  fn is_some() bool {
    # ... using pattern matching
  }
}

enum Result {
  ok $T
  err $E
}

# lambdas and closures
fn main(args []str) {
  # short lambda syntax (types might be inferred by context)
  let add = |x, y i32| i32 => x + y
  let pi = 3.14
  # longer syntax
  let math = fn (r i32) f32 {
      return pi * r
  }
}

# protocol: the polymorphism / dynamic dispatch story, can also be used
# to restrict generic types. Defines the "structure" a type must have,
# and predefined protocols exist for built-in types.
protocol Hashable {
  fn hash() u64
}

# protocols are satisfied implicitly, no need to declare that a struct
# "implements" a protocol.
protocol User {
  let id i32      # types with a var id i32 satisfy this
  var salary u64  # types with let salary u64 do not satisfy this
  fn name() str

  # protocol extensions can be implemented too, so any type that
  # satisfies the extension can also use those extensions, provided
  # the type is "type-checked" as the protocol. Much like structs,
  # a `self` argument is implicit.
  fn format() str {
    return fmt.printf("{} {}", id, name())
  }
}

# TBD: strings, slices/vectors/etc., maps/hashes/dicts.

# dynamic arrays or vectors or slices are not really part of the language,
# built in the runtime module but have some compiler magic and syntax sugar.
var ar vec($int) ???

# dictionaries also are built in the runtime and have syntax sugar/literals.
var lookup [str:int] = [:]

# anonymous structs (tuples) can be created from syntax literals
var point {i32, i32} = {1, 32}
let emptyTuple = {}
let emptyDict = [:]
let emptySlice = []

# there is no tuple type, but anonymous structs can be created easily.
var x struct { a i32, b u64 } = { a: 1, b: 3 }
# as special shortcut, field names can be omitted and can be referenced
# as .0, .1, etc.
var x struct { i32, u64 } = { 1, 3 }
x.0 = 2
x.1 = 6