I I Language C-shaped systems programming

Language Reference

I docs

Small examples for the current language surface: unified declarations, native generics, typed operations, reflection, formatting, the packaged standard library, and direct C interop.

Unified Syntax

: [] <> () -> T = {}

One declaration shape

value: T = {};
items: [16]T = {};
ptr: *T = value.&;

Payload: struct = {
    x: f32;
    y: f32;
}

Array: struct<T> = {
    data: *T;
    length: u64;
}

SaveSlot: struct[serialize]<T> = {
    name: *const char;
    value: T;
}

make_payload: proc(x: f32, y: f32) -> Payload = {
    return { .x = x, .y = y };
}

WinProc: proc[WinCall](hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT = {
    return 0;
}

Generic proc with a typed operation

add: proc<i32>(x: i32, y: i32) -> i32 = {
    return x + y;
}

sum: proc<T>(items: *T, count: u64) -> T = {
    result: T = {};
    for (i: u64 = 0; i < count; i += 1) {
        result = add<T>(result, items[i]);
    }
    return result;
}

Basics

Declarations and expressions

Entry point

import "std/Print.i"

main: proc()->i32 = {
    printfmt("hello {}\\n", 7);
    return 0;
}

Variables

count: i32 = 4;
scale: f32 = 1.5;
name: *const char = "payload";
ready: bool = true;

Address and dereference

value: i32 = 12;
ptr: *i32 = value.&;
ptr[0] += 1;

Casts and sizes

bytes: usize = sizeof(i32) * 16;
alignment: usize = alignof(f32);
value: i32 = cast(bytes, i32);

Types

Structs, enums, aliases, arrays

Struct

Vec2: struct = {
    x: f32;
    y: f32;
}

pos: Vec2 = {.x = 10, .y = 20};

Enum

Mode: enum = {
    Idle,
    Run = 4,
    Attack,
}

mode: Mode = Mode.Attack;

Enum values are referenced with dot syntax. The generated C still uses concrete names behind the scenes.

Alias

CStr: alias = *const char;
Index: alias = u32;

label: CStr = "hero";
index: Index = 3;

Fixed arrays

Mesh: struct = {
    joints: [4]u16;
}

mesh: Mesh = {.joints = {[0] = 1, [3] = 9}};

Union

Value: union = {
    i: i32;
    f: f32;
}

v: Value = {.i = 42};

Const pointers

text: *const char = "read only";
bytes: *const u8 = cast(text, *const u8);

Procedures

Functions and control flow

Procedure

add: proc(a: i32, b: i32)->i32 = {
    return a + b;
}

sum: i32 = add(2, 5);

Function pointer

Callback: alias = *proc(value: i32)->i32;

apply: proc(cb: Callback, x: i32)->i32 = {
    return cb(x);
}

Loops

total: i32 = 0;
for (i: i32 = 0; i < 8; i += 1) {
    total += i;
}

Switch

switch (mode) {
    case Mode.Run:
        speed = 8;
        break;
    default:
        speed = 1;
        break;
}

Word operators

if (ready and !blocked) {
    mask = mask shl 1;
}

Call convention

WndProc: proc[WINCALL](hwnd: HWND, msg: UINT)->LRESULT = {
    return 0;
}

Generics

Monomorphized structs and procs

Generic struct

Array: struct<T> = {
    length: u64;
    border: u64;
    data: *T;
}

items: Array<i32> = {};

Generic procedure

identity: proc<T>(value: T)->T = {
    return value;
}

x: i32 = identity<i32>(9);

Namespaced generic call

Array<T>at: proc<T>(array: *Array<T>, index: u64)->*T = {
    return array[0].data + index;
}

item: *i32 = Array<i32>at(items.&, 0);

Concrete specialization

Payload: struct = {
    x: i32;
}

print: proc<Payload>(payload: Payload)->void = {
    printfmt("Payload{x: {}}", payload.x);
}

Type-operation generic

eq: proc<i32>(a: i32, b: i32) -> b32 = {
    return a == b;
}

contains: proc<T>(items: *T, count: u64, needle: T) -> b32 = {
    for (i: u64 = 0; i < count; i += 1) {
        if (eq<T>(items[i], needle)) {
            return true;
        }
    }
    return false;
}

Metaprogramming

Reflection and typed formatting

Formatted printing

printfmt("x: {}, y: {}\\n", payload.x, payload.y);

The compiler lowers each value to print<T>(value).

Custom printer

print: proc<Payload>(payload: Payload)->void = {
    printfmt("Payload({}, {})", payload.x, payload.y);
}

Reflect type

name: *const char = Payload<>.name;
field_count: u64 = Payload<>.field_count;

Reflect fields

for (i: u64 = 0; i < Payload<>.field_count; i += 1) {
    printfmt("field[{}] = {}\\n", i, Payload<>.fields[i].name);
}

Typed extension point

hash: proc<Payload>(payload: Payload)->u64 = {
    return cast(payload.x, u64);
}

C Interop

Headers, macros, external symbols

C include

cinclude "stdio.h"
cinclude "windows.h"

Macro define

define("SAHA_IMPLEMENTATION")
define("gin_debug_draw")

External proc

puts: proc(text: *const char)->i32 = {
    external;
}

Emit prototype

fx_step: proc(dt: f32)->void = {
    external_emit;
}

External global

extern_ticks: u64 = { external; };

Variadic declaration

printf: proc(fmt: *const char, ...)->i32 = {
    external;
}

Std

Small standard library pieces

Option

opt: Option<i32> = Option<i32>some(7);
value: i32 = Option<i32>unwrap_or(opt, 0);

Result

ok: Result<i32> = Result<i32>ok(11);
failed: bool = Result<i32>is_err(ok);

Array

arr: Array<i32> = Array<i32>reserve(arena.&, 16);
arr.data[0] = 99;

Vec

vec: Vec<i32> = {};
Vec<i32>append(arena.&, vec.&, 5);

Map

map: *Map<i32> = Map<i32>create(arena.&);
Map<i32>set(arena.&, map, "hp", 100);

Print

print<i32>(7);
print_cstr("\\n");
printfmt("{}", 7);

Compiler

Build, CLI, and editor support

Package layout

C:\devel\i-windows-x64\I.exe
C:\devel\i-windows-x64\std\Print.i
C:\devel\i-windows-x64\std\Array.i

Add the package folder to PATH. I.exe automatically imports from its sibling std folder.

Bunyan project

python bunyan.py build debug
python bunyan.py run debug

Project builds generate C under build\i_gen, then compile it with the configured C compiler.

Compile

I.exe compile src\main.i -o build\i_gen\main.c --header build\i_gen\main.h

Check

I.exe check src\main.i --diagnostics=json

Symbols

I.exe symbols src\main.i --format=json

LSP

I.exe lsp src\main.i --format=json