the big syntax updated

the big syntax updated
Photo by Markus Winkler / Unsplash

It's been N months since the last 'syntax so far' post so it's probably time to revisit absolutely everything. If you're reading the blog in order then this is a spoilers page. Revisit it any time you want to know how the language should look.

(updated 11th April 2025)

// booleans
true
false
// nothing
ø
// integers
1234567890
-1234567890
1_234_567_890
-1_234_567_890
0b0110101001
0o81828375
-0o81828375
0xF23BEEF
-0xF23BEEF
// floats
1234.56789
-1234.56789
1_234.567_89
-1_234.567_89
// raw strings
`a raw string`
delim`a raw stringdelim`
// escaping strings
'a\tstring\n'
foo'a\tstring\nfoo'
// template strings
"Hello ~{name}"
x"Hello ~[name]x"
"Hello ~xnamex"
// variables and assignment
foo := 1
foo: UnsignedInteger = 1
foo: UnsignedInteger
// constant variables
pi :: 3.1415
// immediate arrays
a, b, c
{a, b, c}
{a}
{}
// typed immediate arrays
{Integer | 1, 2, 3}
{Integer | 1}
foo: (Array of: Integer) = {1, 2, 3}
// copying/combining arrays
{...other-array}
{...array-one, 4, 5, 6, ...array-two}
// immediate maps
{"a": 1, "b": 2, "c": 3}
{"a": 1}

the-key := "a".
{(the-key): 1}.
// typed immediate maps
{String, Integer | "a": 1, "b": 2, "c": 3}
{String, Integer | "a": 1}
{String, Integer | }
foo: (Map of: String to: Integer) = {"a": 1, "b": 2, "c": 3}
// copying/combining maps
{...other-map}
{...map-one, a: 4, b: 5, c: 6, ...map-two}
// class definition
Person :: {name: String, height: Length}
// class subtyping
RGBA32 ::
 {...Color, ...Vector4 of: UnsignedInteger8,
  r, g, b, a: UnsignedInteger8}.
// untagged-union definition
Pet :: {UntaggedUnion | as-cat: Cat, as-dog: Dog}
// tagged-union definition
Pet :: Cat + Dog.
[my-pet: Pet] [ ...code... ].

// tagged-union resolving
[cat speak] [cat: Cat | 'meow'].
[dog speak] [dog: Dog | 'woof'].
[pet speak] [pet: Pet | 'hello'].

my-pet := io download-a-pet.
my-pet speak.
// enum definition
Cardinality :: {north, east, south, west}
Cardinality :: {north = 1, east, south, west}
Cardinality :: {UnsignedInteger | north, east, south, west}
// method calls

// unary method call
person name

// binary method call
// (order of operations: subexpr, mul/div, add/sub, comparison, misc)
1 + 2
2 * (3 + 4)
foo copy> bar copy> baz
2 special* (3 special+ 4)

// keyword method call
person name: 'jane'
person shake: body-part near: another-person
// deferred method calls
--- person free
person --- [free]
// object creation
foo := {Person | name: 'jane', height: 175cm}
foo: Person = {name: 'jane', height: 175cm}
foo := {&Person | new, name: 'jane', height: 175cm}
foo: &Person = {new, name: 'jane', height: 175cm}
--- foo free
// focused-object mutation
foo {name: 'bob', age: 27}
// blocks

// declare everything
[a: Integer, b: Integer -> return: Integer | return a + b + 1]

// declare parameter names and types
[a: Integer, b: Integer -> return | return a + b + 1]
[a: Integer, b: Integer | a + b + 1]

// declare parameter names and return type
[a, b -> return: Integer | return a + b + 1]
[a, b -> Integer | a + b + 1]

// declare parameter names and return name
[a, b -> return | return a + b + 1]

// declare parameter names
[a, b | a + b + 1]

// default input/output
[+ 1]
// block closure
x := 1.
my-block := [a: String, b: Integer -> return: Integer]
            [return a length + b + x].
// signature

// unary signature
person name

// binary signature
a + b
a else> b

// keyword signature
person name: a-name
person shake: body-part near: another-person
// methods

// declare everything
[self my-method: arg] [self: MyClass, arg: String -> return: String |
  return `prefix ~[arg] suffix`]

// same as above but with implied return with last statement
[self my-method: arg] [MyClass, String -> String |
  `prefix ~[arg] suffix`]

// declare parameter types, infer return type with last statement
[self my-method: arg] [MyClass, String | `prefix ~[arg] suffix`]

// declare parameter types, no return value
[self my-method: arg] [MyClass, String -> ø | `prefix ~[arg] suffix`]

// declare return type, infer parameters
[self my-method: arg] [-> return: String | return `prefix ~[arg] suffix`]
[self my-method: arg] [-> String | `prefix ~[arg] suffix`]

// infer parameters and return type
[self my-method: arg] [`prefix ~[arg] suffix`]
// method+block declaration
my-block := [a add: b] [a: Integer, b: Integer -> return: Integer
            | return a + b].
my-block evaluate: 1, 2.
1 add: 2.
// reference type
p: &Person;
jane := {&Person | allocate, name: 'jane', height: 175cm}.
bob := {Person | name: 'bob', height: 180cm}.
database save: jane.
database save: bob.
// array access
bob := my-array[0].
my-array[12] = jane.
// array slicing
my-array[start:]
my-array[:stop]
my-array[start:stop]
my-array[::length]
my-array[start::length]
// packages
package: 'my package'.
using: 'opengl'.
import: 'opengl'.
// scoping
public  Person :: {name: String}.
package Person :: {name: String}.
private Person :: {name: String}.
private [a add: b] [a: Integer, b: Integer -> return: Integer
        | return a + b].
// scoping class members
Person ::
 {public  name: String
  package nickname: String
  private superheroname: String}.
// class specialisation
Array ::
 {#specialise: [] [-> Class | element-class = Any],
  #specialise: [of: element-class] [Any -> Class | ],
  #specialise: [of: element-class length: length] [Any, UnsignedInteger -> Class | ],

  ...Iterable of: element-class,
  ...Orderable of: element-class,

  public  length: UnsignedInteger,
  private origin: (MemoryAddress of: element-class)}.

generic-array: Array.
people-array: (Array of: Person).
people-buffer: (Array of: Person length: 10).
// default block parameter values and block signature
my-block := [a add: b]
            [a: Integer = 1, b: Integer = 2 -> return: Integer
            | return a + b].

my-block evaluate.
my-block evaluate: 10.
my-block evaluate: 10, -10.
10 add: -10.
// default class member values
RGBA32 ::
 {r, g, b: UnsignedInteger8
  a: UnsignedInteger8 = 255}.
// default keyword selector parameter values
[display stroke-path: path color: color]
[display: &Drawable, path: (Path of: Float)
 color: RGBA32 = display current-color
 -> ø
| cr := display cairo-context.
  cr apply-path: path.
  cr apply-color: color.
  cr stroke ].

render-buffer stroke-path: {start: {x: 0, y: 0}, end: {x: 10, y: 10}}.
render-buffer
  stroke-path: {start: {x: 10, y: 10}, end: {x: 20, y: 20}}
  color: {r: 255, g: 0, b: 0}.
// maybe (a 'success' type and a 'failure type')
maybe-jane: Person / Failure.
maybe-jane then: [jane | stdout print: jane].
maybe-jane else: [stdout print: 'no Jane'].
maybe-jane else: [the-reason | stdout print: the-reason].
// non-failure based maybe
person_else_socket: Person / Socket.
person_else_socket then: [person: Person | ...].
person_else_socket else: [socket: Socket | ...].
// Input/Output

// Copying
// input copy> output
copy-of-people: people.
people copy> copy-of-people.

// Iterating/Processing
// input do> ø
'/important-data' directory files do> [delete].
{'a', 'b', 'c'} do> [index, value | ...].

// Mapping
// input map> transform copy> output
plus-one: (List of: Integer).
{1, 2, 3, 4} map> [+ 1] copy> plus-one.

// Narrowing
// input where> boolean-transform do> output
people where> [age ≥ 18] do> stdout.

// Reducing
total := 0.
people do> [person -> ø | total := total max: person age].

// Serialising
output := 'people.json' as-filename open-write --- [close].
people map> as-json copy> output else> panic.

// Buffering
buffer: (Buffer of: Byte capacity: 1024).
input-socket copy> buffer.

// Sub-buffering
buffer: (Buffer of: Byte capacity: 1024).
input-socket copy> buffer[::16].