mutable decl syntax change

This commit is contained in:
2026-08-05 21:19:41 +02:00
parent 88b94197c9
commit 081a3fd5df
74 changed files with 870 additions and 733 deletions
+2 -1
View File
@@ -8,7 +8,8 @@ roadmap and milestone history.
### source, declarations, and packages ### source, declarations, and packages
- newline-terminated statements and `#` comments - newline-terminated statements and `#` comments
- immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks - immutable `name :: value` / `name Type :: value` bindings and mutable `name := value` / `name Type := value` bindings
- `=` is assignment, including `_ = value` sinks; keyed record initializers and named struct field defaults also use `=`
- immutable package globals, mutable runtime globals, function-local mutable locals, and mutable declarations initialized with `undefined` - immutable package globals, mutable runtime globals, function-local mutable locals, and mutable declarations initialized with `undefined`
- package-level functions, globals, native type declarations, and `Name :: alias T` - package-level functions, globals, native type declarations, and `Name :: alias T`
- directory packages with merged declarations - directory packages with merged declarations
+1 -1
View File
@@ -222,7 +222,7 @@ Current prototype features:
- Newline-terminated, multiline statements; `}` may terminate a block's final statement - Newline-terminated, multiline statements; `}` may terminate a block's final statement
- `#` comments - `#` comments
- Immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks - Immutable inferred/typed `::` bindings, mutable inferred/typed `:=` locals/globals, and `_` sinks
- Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int` - Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int`
- Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptrcast!(T, ptr)` - Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptrcast!(T, ptr)`
- Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs - Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs
+19 -20
View File
@@ -12,7 +12,7 @@
- unsigned integers, floats, and target-dependent c scalar types - unsigned integers, floats, and target-dependent c scalar types
- atomic `c_*` primitive types remain distinct until target-aware lowering - atomic `c_*` primitive types remain distinct until target-aware lowering
- `c_func`, complete `c_struct`, and pointer-only `opaque`; `c` remains an ordinary identifier - `c_func`, complete `c_struct`, and pointer-only `opaque`; `c` remains an ordinary identifier
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`) - keep binding mutability (`::` / `:=`) separate from element or pointee mutability (`mut`)
- arrays and indexing - arrays and indexing
- `[N]T`: array with `N` logical elements - `[N]T`: array with `N` logical elements
- `[N;S]T`: array with `N` logical elements followed by sentinel `S` - `[N;S]T`: array with `N` logical elements followed by sentinel `S`
@@ -184,7 +184,7 @@
- undefined values are assigned a poison value (0xaa...) - undefined values are assigned a poison value (0xaa...)
- allows for something like: - allows for something like:
``` ```
a int = undefined a int := undefined
if (condition) { if (condition) {
a = 42 a = 42
} else { } else {
@@ -323,7 +323,7 @@
- a `{ ... }` on the right of a declaration or assignment is a *value block*: its final - a `{ ... }` on the right of a declaration or assignment is a *value block*: its final
statement must be `yield <expr>`, which supplies the block's value (the block analogue statement must be `yield <expr>`, which supplies the block's value (the block analogue
of `return`). Supported: `x :: { ...; yield v }` (untyped — the local takes the yield's of `return`). Supported: `x :: { ...; yield v }` (untyped — the local takes the yield's
natural type), `x T = { ... }` (coerces to `T`), and `target = { ... }` (coerces to the natural type), `x T := { ... }` (coerces to `T`), and `target = { ... }` (coerces to the
target's type, including complex targets like `a[i] = { ... }`) target's type, including complex targets like `a[i] = { ... }`)
- the yielded value is captured *before* the block's defers run (a defer that mutates a - the yielded value is captured *before* the block's defers run (a defer that mutates a
block local can't change what is yielded), reusing the `return` spill-to-temp pattern block local can't change what is yielded), reusing the `return` spill-to-temp pattern
@@ -695,9 +695,9 @@
27.8 source-defined mutable runtime globals (implemented) 27.8 source-defined mutable runtime globals (implemented)
- allow mutable global declarations in Brolang source for process-global runtime - allow mutable global declarations in Brolang source for process-global runtime
state, matching the writable-global support already needed for imported C globals state, matching the writable-global support already needed for imported C globals
- require source type syntax and an initializer; constraints (`int`/`float`/`range`) - require an initializer and infer or explicitly declare a concrete runtime storage type;
and inferred array counts may resolve through the existing inference fixpoint, but constraints (`int`/`float`/`range`) and inferred array counts resolve through the existing
the final type must be concrete runtime storage inference fixpoint
- emit source-defined mutable globals as writable globals, not constants - emit source-defined mutable globals as writable globals, not constants
- allow `undefined` initializers for runtime storage initialized explicitly by a function - allow `undefined` initializers for runtime storage initialized explicitly by a function
- allow assignment, address-taking, field/index mutation, and pointer passing under - allow assignment, address-taking, field/index mutation, and pointer passing under
@@ -774,7 +774,7 @@
improving layout or specialization improving layout or specialization
- the smallest fitting feature is call-local inference of omitted comptime type arguments: - the smallest fitting feature is call-local inference of omitted comptime type arguments:
``` ```
values ArrayList(i32) = arraylist.init(mem.c_allocator) values ArrayList(i32) := arraylist.init(mem.c_allocator)
defer arraylist.deinit(&values) defer arraylist.deinit(&values)
try arraylist.append(&values, 42) try arraylist.append(&values, 42)
``` ```
@@ -988,9 +988,9 @@ f: f32 = 3.14
bits := bitcast(f, u32) # IEEE 754 representation bits := bitcast(f, u32) # IEEE 754 representation
# pointer casts # pointer casts
buf *u8 = get_buffer() buf *u8 := get_buffer()
ints *u32 = ptrcast!(u32, buf) # element type change, same pointer shape ints *u32 := ptrcast!(u32, buf) # element type change, same pointer shape
writable *mut u8 = constcast!(buf) # explicit unsafe mutability restoration writable *mut u8 := constcast!(buf) # explicit unsafe mutability restoration
``` ```
## A word on multi-unwrap ## A word on multi-unwrap
@@ -1069,8 +1069,7 @@ layer:
```bro ```bro
UserID :: distinct u32 UserID :: distinct u32
OuterID :: distinct UserID OuterID :: distinct UserID
index usize := 42
index usize = 42
id UserID :: UserID(index) id UserID :: UserID(index)
raw u32 :: u32(id) raw u32 :: u32(id)
outer OuterID :: OuterID(id) outer OuterID :: OuterID(id)
@@ -1222,7 +1221,7 @@ data :: {
} }
# match arms # match arms
label []u8 = match p { label []u8 := match p {
.high: "HIGH", # single expression: implicit yield .high: "HIGH", # single expression: implicit yield
.low: { .low: {
log("low priority") log("low priority")
@@ -1231,7 +1230,7 @@ label []u8 = match p {
} }
# catch handlers (planned; block form deferred in milestone 23 v1) # catch handlers (planned; block form deferred in milestone 23 v1)
data []u8 = read(path) catch |e| { data []u8 := read(path) catch |e| {
log(e) log(e)
yield fallback_data # block: explicit yield yield fallback_data # block: explicit yield
} }
@@ -1252,7 +1251,7 @@ result :: if a {
} }
# yielding to a variable # yielding to a variable
result int = if a { result int := if a {
yield 1 yield 1
} else if b { } else if b {
yield 2 yield 2
@@ -1482,7 +1481,7 @@ Fallible functions use ordinary `return` for both channels. If the returned expr
``` ```
parse_section func(p: @mut Parser) void ! ParseError { parse_section func(p: @mut Parser) void ! ParseError {
start_line Line = p.line start_line Line := p.line
p.advance() p.advance()
# ... parsing logic ... # ... parsing logic ...
@@ -1497,7 +1496,7 @@ Since errors are just union values, you can also construct them separately:
``` ```
# Construct error value (it's just a union) # Construct error value (it's just a union)
e ParseError = .timeout{500} e ParseError := .timeout{500}
# Return it via error channel later # Return it via error channel later
return e return e
@@ -1647,7 +1646,7 @@ Brolang provides a libc-backed allocator value:
mem :: import "@std/mem" mem :: import "@std/mem"
process func(input []u8) u64 { process func(input []u8) u64 {
temp ?*mut u8 = mem.alloc(mem.c_allocator, input.len * 2, 1) temp ?*mut u8 := mem.alloc(mem.c_allocator, input.len * 2, 1)
defer mem.free(mem.c_allocator, temp, input.len * 2, 1) defer mem.free(mem.c_allocator, temp, input.len * 2, 1)
# ... work with temp ... # ... work with temp ...
@@ -1677,7 +1676,7 @@ mem :: import "@std/mem"
# Allocation escapes via return value — requires allocator # Allocation escapes via return value — requires allocator
duplicate func(input []u8, allocator mem.Allocator) ?*mut u8 { duplicate func(input []u8, allocator mem.Allocator) ?*mut u8 {
result ?*mut u8 = mem.alloc(allocator, input.len, 1) result ?*mut u8 := mem.alloc(allocator, input.len, 1)
mem.copy(result, input) mem.copy(result, input)
return result # caller manages this memory return result # caller manages this memory
} }
@@ -1690,7 +1689,7 @@ init func(obj @mut MyStruct, allocator mem.Allocator) void {
# No allocation escapes — no allocator needed # No allocation escapes — no allocator needed
process func(input []u8) u64 { process func(input []u8) u64 {
temp ?*mut u8 = mem.alloc(mem.c_allocator, input.len, 1) temp ?*mut u8 := mem.alloc(mem.c_allocator, input.len, 1)
defer mem.free(mem.c_allocator, temp, input.len, 1) defer mem.free(mem.c_allocator, temp, input.len, 1)
# ... work with temp ... # ... work with temp ...
return compute_hash(temp) return compute_hash(temp)
+5 -17
View File
@@ -5952,7 +5952,7 @@ infer_statements :: proc(
#partial switch statement.kind { #partial switch statement.kind {
case .Declaration: case .Declaration:
if statement.expr == ast.INVALID_EXPR { if statement.expr == ast.INVALID_EXPR {
// Value block (`x :: { ... yield v }` / `x T = { ... }`): register the // Value block (`x :: { ... yield v }` / `x T := { ... }`): register the
// binding (its declared type when annotated, else left open) and walk // binding (its declared type when annotated, else left open) and walk
// the block body. The build pass resolves the yielded value's type // the block body. The build pass resolves the yielded value's type
// independently value blocks don't join the demand fixpoint. // independently value blocks don't join the demand fixpoint.
@@ -6026,7 +6026,7 @@ infer_statements :: proc(
continue continue
} }
expected_assignment := types.INVALID expected_assignment := types.INVALID
if statement.target != ast.INVALID_EXPR { if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types) expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
target_expr := checker.ast_module.exprs[statement.target] target_expr := checker.ast_module.exprs[statement.target]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) { if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
@@ -6055,7 +6055,7 @@ infer_statements :: proc(
// type onto open-constant operands and poison their family. Operands of an // type onto open-constant operands and poison their family. Operands of an
// arithmetic RHS resolve from their own authoritative uses. // arithmetic RHS resolve from their own authoritative uses.
rhs_is_arith := is_arith_kind(checker.ast_module.exprs[statement.expr].kind) rhs_is_arith := is_arith_kind(checker.ast_module.exprs[statement.expr].kind)
if statement.target != ast.INVALID_EXPR { if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
target_type := expected_assignment target_type := expected_assignment
target_expr := checker.ast_module.exprs[statement.target] target_expr := checker.ast_module.exprs[statement.target]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) { if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
@@ -10922,7 +10922,7 @@ build_block :: proc(
} }
switch statement.kind { switch statement.kind {
case .Declaration: case .Declaration:
// A value block (`x :: { ... yield v }` / `x T = { ... }`): the parser // A value block (`x :: { ... yield v }` / `x T := { ... }`): the parser
// leaves `expr` invalid and stashes the block in `body`. Build it, then // leaves `expr` invalid and stashes the block in `body`. Build it, then
// declare the local from the yielded value (its type for an untyped `::`). // declare the local from the yielded value (its type for an untyped `::`).
if statement.expr == ast.INVALID_EXPR { if statement.expr == ast.INVALID_EXPR {
@@ -11123,7 +11123,7 @@ build_block :: proc(
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid
} }
case .Assignment: case .Assignment:
if statement.target != ast.INVALID_EXPR { if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
target_expr := build_expr( target_expr := build_expr(
checker, statement.target, ctx.locals^[:], ctx.global_reads, ctx.calls, checker, statement.target, ctx.locals^[:], ctx.global_reads, ctx.calls,
types.INVALID, ctx.pkg, ctx.file, types.INVALID, ctx.pkg, ctx.file,
@@ -14349,18 +14349,6 @@ build_globals :: proc(checker: ^Checker) {
) )
expr = invalid_hir_expr(checker, global.span, diagnostic) expr = invalid_hir_expr(checker, global.span, diagnostic)
} }
if !global.immutable {
if !types.is_valid(global.type) {
diagnostic = source.addf(
checker.diagnostics,
global.span,
"mutable global '%s' requires a type annotation",
symbol_text(checker, global.name),
)
global_type = types.I64
expr = invalid_hir_expr(checker, global.span, diagnostic, global_type)
}
}
static_value, is_static := static_integer_value(&checker.module, expr) static_value, is_static := static_integer_value(&checker.module, expr)
is_static = is_static && diagnostic == source.INVALID_DIAGNOSTIC && global.immutable is_static = is_static && diagnostic == source.INVALID_DIAGNOSTIC && global.immutable
_ = hir.global_id(len(checker.module.globals)) _ = hir.global_id(len(checker.module.globals))
+8 -1
View File
@@ -133,9 +133,16 @@ lex :: proc(
case ':': case ':':
start := cursor start := cursor
cursor += 1 cursor += 1
if cursor < len(bytes) && bytes[cursor] == ':' { if cursor < len(bytes) {
if bytes[cursor] == ':' {
cursor += 1 cursor += 1
append_token(&stream, source_file, .Colon_Colon, start, cursor) append_token(&stream, source_file, .Colon_Colon, start, cursor)
} else if bytes[cursor] == '=' {
cursor += 1
append_token(&stream, source_file, .Colon_Equal, start, cursor)
} else {
append_token(&stream, source_file, .Colon, start, cursor)
}
} else { } else {
append_token(&stream, source_file, .Colon, start, cursor) append_token(&stream, source_file, .Colon, start, cursor)
} }
+45 -8
View File
@@ -1868,15 +1868,35 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
had_type = true had_type = true
} }
operator := current(parser) operator := current(parser)
if operator.kind == .Colon_Colon || operator.kind == .Equal { if had_type && operator.kind == .Equal {
diagnostic := source.add(
parser.diagnostics,
operator.span,
"mutable declarations use ':=' instead of '='",
)
for current(parser).kind != .Newline &&
current(parser).kind != .Right_Brace &&
current(parser).kind != .Eof {
advance(parser)
}
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Invalid,
span=span_from(name.span, operator.span),
expr=ast.INVALID_EXPR,
diagnostic=diagnostic,
})
return id
}
is_sink := name.kind == .Underscore && operator.kind == .Equal
if operator.kind == .Colon_Colon || operator.kind == .Colon_Equal || is_sink {
advance(parser) advance(parser)
skip_newlines(parser) skip_newlines(parser)
kind := ast.Stmt_Kind.Assignment kind := ast.Stmt_Kind.Declaration
immutable := false if is_sink {
if operator.kind == .Colon_Colon || had_type { kind = .Assignment
kind = .Declaration
immutable = operator.kind == .Colon_Colon
} }
immutable := operator.kind == .Colon_Colon
// A labeled value block (`x :: blk: { … yield :blk v }`): the label lets a // A labeled value block (`x :: blk: { … yield :blk v }`): the label lets a
// `yield :blk` exit the block past a nested `if`. Block-init body + label. // `yield :blk` exit the block past a nested `if`. Block-init body + label.
if current(parser).kind == .Identifier && peek(parser).kind == .Colon { if current(parser).kind == .Identifier && peek(parser).kind == .Colon {
@@ -1954,6 +1974,11 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
} }
expr := parse_expression(parser) expr := parse_expression(parser)
assignment_name := symbol.INVALID
target_expr := parser.module.exprs[expr]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
assignment_name = target_expr.name
}
if _, ok := allow(parser, .Equal); ok { if _, ok := allow(parser, .Equal); ok {
skip_newlines(parser) skip_newlines(parser)
// A labeled value block assigned to a complex target (`a[i] = blk: { … }`). // A labeled value block assigned to a complex target (`a[i] = blk: { … }`).
@@ -1964,6 +1989,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Assignment, kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, previous(parser).span), span=span_from(parser.module.exprs[expr].span, previous(parser).span),
name=assignment_name,
target=expr, target=expr,
label=label, label=label,
expr=ast.INVALID_EXPR, expr=ast.INVALID_EXPR,
@@ -1980,6 +2006,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Assignment, kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, brace.span), span=span_from(parser.module.exprs[expr].span, brace.span),
name=assignment_name,
target=expr, target=expr,
expr=ast.INVALID_EXPR, expr=ast.INVALID_EXPR,
body=body, body=body,
@@ -1995,6 +2022,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Assignment, kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, previous(parser).span), span=span_from(parser.module.exprs[expr].span, previous(parser).span),
name=assignment_name,
target=expr, target=expr,
value_control_flow=true, value_control_flow=true,
expr=ast.INVALID_EXPR, expr=ast.INVALID_EXPR,
@@ -2008,6 +2036,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{ append(&parser.module.statements, ast.Stmt{
kind=.Assignment, kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, parser.module.exprs[value].span), span=span_from(parser.module.exprs[expr].span, parser.module.exprs[value].span),
name=assignment_name,
target=expr, target=expr,
expr=value, expr=value,
diagnostic=source.INVALID_DIAGNOSTIC, diagnostic=source.INVALID_DIAGNOSTIC,
@@ -3312,8 +3341,16 @@ parse_top_level :: proc(parser: ^Parser) {
type_syntax = parse_type(parser) type_syntax = parse_type(parser)
} }
operator := current(parser) operator := current(parser)
if operator.kind != .Colon_Colon && operator.kind != .Equal { if operator.kind == .Equal {
source.add(parser.diagnostics, operator.span, "expected '::' or '=' after top-level name") source.add(parser.diagnostics, operator.span, "mutable declarations use ':=' instead of '='")
for current(parser).kind != .Newline && current(parser).kind != .Eof {
advance(parser)
}
_ = finish_statement(parser)
return
}
if operator.kind != .Colon_Colon && operator.kind != .Colon_Equal {
source.add(parser.diagnostics, operator.span, "expected '::' or ':=' after top-level name")
_ = finish_statement(parser) _ = finish_statement(parser)
return return
} }
+1 -1
View File
@@ -180,7 +180,7 @@ append_runner :: proc(
fmt.sbprintf(&builder, "%s.%s() catch |_| ", alias, symbol.resolve(symbols, test.name)) fmt.sbprintf(&builder, "%s.%s() catch |_| ", alias, symbol.resolve(symbols, test.name))
strings.write_string(&builder, "{\n\t\treturn .expectation_failed\n\t}\n}\n\n") strings.write_string(&builder, "{\n\t\treturn .expectation_failed\n\t}\n}\n\n")
} }
strings.write_string(&builder, "main func() i32 {\n\tfailed i32 = 0\n") strings.write_string(&builder, "main func() i32 {\n\tfailed i32 := 0\n")
for entry, index in tests { for entry, index in tests {
test_id := entry.function test_id := entry.function
test := module.functions[test_id] test := module.functions[test_id]
+1
View File
@@ -16,6 +16,7 @@ Kind :: enum u8 {
Underscore, Underscore,
Colon, Colon,
Colon_Colon, Colon_Colon,
Colon_Equal,
Equal, Equal,
Equal_Equal, Equal_Equal,
Bang, Bang,
+466 -362
View File
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -79,8 +79,8 @@ read_command func() Command {
if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT) return .spawn{ rl.GetMousePosition() } if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT) return .spawn{ rl.GetMousePosition() }
if rl.IsKeyPressed(rl.KEY_SPACE) return .clear if rl.IsKeyPressed(rl.KEY_SPACE) return .clear
fx f32 = 0.0 fx f32 := 0.0
fy f32 = 0.0 fy f32 := 0.0
if rl.IsKeyDown(rl.KEY_A) fx -= FORCE if rl.IsKeyDown(rl.KEY_A) fx -= FORCE
if rl.IsKeyDown(rl.KEY_D) fx += FORCE if rl.IsKeyDown(rl.KEY_D) fx += FORCE
if rl.IsKeyDown(rl.KEY_W) fy -= FORCE if rl.IsKeyDown(rl.KEY_W) fy -= FORCE
@@ -130,14 +130,14 @@ step func(b @mut Ball) void {
draw_ball func(b @Ball, highlight bool) void { draw_ball func(b @Ball, highlight bool) void {
col :: color_for(b.kind) col :: color_for(b.kind)
center rl.Vector2 = rl.Vector2{ x = b.x, y = b.y } center rl.Vector2 := rl.Vector2{ x = b.x, y = b.y }
match b.kind { match b.kind {
.circle: rl.DrawCircleV(center, b.radius, col) .circle: rl.DrawCircleV(center, b.radius, col)
.square: rl.DrawPoly(center, 4, b.radius, 45.0, col) .square: rl.DrawPoly(center, 4, b.radius, 45.0, col)
.triangle: rl.DrawPoly(center, 3, b.radius, 0.0, col) .triangle: rl.DrawPoly(center, 3, b.radius, 0.0, col)
} }
if highlight { if highlight {
ring rl.Color = rl.Color{ r = 250, g = 245, b = 200, a = 255 } ring rl.Color := rl.Color{ r = 250, g = 245, b = 200, a = 255 }
rl.DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring) rl.DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring)
} }
} }
@@ -152,11 +152,11 @@ main func() i32 {
`[click] spawn a shape [WASD/arrows] blow wind `[click] spawn a shape [WASD/arrows] blow wind
`[space] clear `[space] clear
balls [CAP]mut Ball = undefined balls [CAP]mut Ball := undefined
count usize = 0 # number of live balls, in slots 0..count count usize := 0 # number of live balls, in slots 0..count
kc Kind = .circle # next kind to spawn kc Kind := .circle # next kind to spawn
spin f32 = 1.0 # rotates spawn velocity for variety spin f32 := 1.0 # rotates spawn velocity for variety
at_cap bool = false # show the "at capacity" banner at_cap bool := false # show the "at capacity" banner
bg :: rl.Color{ r = 24, g = 26, b = 34, a = 255 } bg :: rl.Color{ r = 24, g = 26, b = 34, a = 255 }
text :: rl.Color{ r = 225, g = 225, b = 230, a = 255 } text :: rl.Color{ r = 225, g = 225, b = 230, a = 255 }
@@ -211,7 +211,7 @@ main func() i32 {
# --- which shape is under the cursor? (optional via a value-loop) --- # --- which shape is under the cursor? (optional via a value-loop) ---
mouse :: rl.GetMousePosition() mouse :: rl.GetMousePosition()
sel :: for 0..(count) |i| hover: { sel :: for 0..(count) |i| hover: {
c rl.Vector2 = rl.Vector2{ x = balls[i].x, y = balls[i].y } c rl.Vector2 := rl.Vector2{ x = balls[i].x, y = balls[i].y }
if rl.CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :hover i if rl.CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :hover i
yield null yield null
} }
@@ -222,7 +222,7 @@ main func() i32 {
for (&balls) |@b, i| { for (&balls) |@b, i| {
if (i >= count) break if (i >= count) break
hot bool = false hot bool := false
if sel |s| { if sel |s| {
if (s == i) hot = true # true only for the hovered ball if (s == i) hot = true # true only for the hovered ball
} }
+2 -2
View File
@@ -19,7 +19,7 @@ native_pair func(value native.Pair) native.Pair {
global_pair native.Pair :: native.Pair { left = 1, right = 2 } global_pair native.Pair :: native.Pair { left = 1, right = 2 }
main func() i32 { main func() i32 {
pair native.Pair = native_pair(global_pair) pair native.Pair := native_pair(global_pair)
pair.left = 10 pair.left = 10
pair = native.echo_pair(pair) pair = native.echo_pair(pair)
pairs [1]native.Pair :: [pair] pairs [1]native.Pair :: [pair]
@@ -33,7 +33,7 @@ main func() i32 {
tail = 13, tail = 13,
}) })
arrays native.Arrays :: native.echo_arrays(native.Arrays { values = [14, 15, 16] }) arrays native.Arrays :: native.echo_arrays(native.Arrays { values = [14, 15, 16] })
choice native.Choice = native.Choice { decimal = 1.0 } choice native.Choice := native.Choice { decimal = 1.0 }
choice.integer = 17 choice.integer = 17
choice = native.echo_choice(choice) choice = native.echo_choice(choice)
forward native.Forward :: native.echo_forward(native.Forward { value = 19 }) forward native.Forward :: native.echo_forward(native.Forward { value = 19 })
+1 -1
View File
@@ -5,7 +5,7 @@ native :: import "../include/native.h"
# self-referential `?*mut Node` field as C-layout-compatible. # self-referential `?*mut Node` field as C-layout-compatible.
main func() i32 { main func() i32 {
node native.Node = native.Node { next = null, value = 7 } node native.Node := native.Node { next = null, value = 7 }
if node.next |_| { if node.next |_| {
return 1 return 1
} }
+3 -3
View File
@@ -1,5 +1,5 @@
hide helper func() i32 { hide helper func() i32 {
thing Thing = Thing { value = value } thing Thing := Thing { value = value }
return thing.value return thing.value
} }
@@ -20,7 +20,7 @@ hide Local_Distinct :: distinct i32
hide Local_Alias :: alias i32 hide Local_Alias :: alias i32
hide value :: 1 hide value :: 1
hide mutable_value i32 = 1 hide mutable_value i32 := 1
_foreign c_func() i32 { _foreign c_func() i32 {
return 1 return 1
@@ -39,6 +39,6 @@ hide Local_C_Record :: c_struct {
} }
from_a func() i32 { from_a func() i32 {
record Local_C_Record = Local_C_Record { value = 0 } record Local_C_Record := Local_C_Record { value = 0 }
return helper() + local_foreign() + i32(record.value) return helper() + local_foreign() + i32(record.value)
} }
+3 -3
View File
@@ -6,8 +6,8 @@ Box :: struct {
} }
from_b func(_input i32) i32 { from_b func(_input i32) i32 {
_local Box = Box { _value = _input } _local Box := Box { _value = _input }
record _C_Record = _C_Record { value = 0 } record _C_Record := _C_Record { value = 0 }
thing Thing = Thing { value = value } thing Thing := Thing { value = value }
return helper() + thing.value + _local._value + _foreign() + i32(record.value) + dep._visible() return helper() + thing.value + _local._value + _foreign() + i32(record.value) + dep._visible()
} }
@@ -1 +1 @@
bad = 1 bad := undefined
@@ -1,5 +1,5 @@
main func() i32 { main func() i32 {
n usize = 4 n usize := 4
items [n]mut i32 = undefined items [n]mut i32 := undefined
return 0 return 0
} }
+6 -6
View File
@@ -10,7 +10,7 @@ State :: struct {
diagnostics std.ArrayList(ScanDiagnostic) diagnostics std.ArrayList(ScanDiagnostic)
} }
arrlist_test std.ArrayList(Token) = arraylist.init(mem.c_allocator) arrlist_test std.ArrayList(Token) := arraylist.init(mem.c_allocator)
init func(allocator mem.Allocator) State { init func(allocator mem.Allocator) State {
return State { return State {
@@ -44,11 +44,11 @@ run func() i32 ! mem.AllocError {
state State :: init(mem.c_allocator) state State :: init(mem.c_allocator)
_ = state _ = state
values std.ArrayList(i32) = arraylist.init(mem.c_allocator) values std.ArrayList(i32) := arraylist.init(mem.c_allocator)
defer arraylist.deinit(&values) defer arraylist.deinit(&values)
if (values.items.len != 0 or values.capacity != 0) return 1 if (values.items.len != 0 or values.capacity != 0) return 1
i usize = 0 i usize := 0
while i < 20 : i += 1 { while i < 20 : i += 1 {
arraylist.append(&values, i32(i)) catch |_| { arraylist.append(&values, i32(i)) catch |_| {
return .out_of_memory return .out_of_memory
@@ -71,7 +71,7 @@ run func() i32 ! mem.AllocError {
} }
if (values.items.len != 1 or values.items[0] != 7 or values.capacity != capacity) return 7 if (values.items.len != 1 or values.items[0] != 7 or values.capacity != capacity) return 7
empty_values arraylist.ArrayList([0]u8) = arraylist.init(mem.c_allocator) empty_values arraylist.ArrayList([0]u8) := arraylist.init(mem.c_allocator)
defer arraylist.deinit(&empty_values) defer arraylist.deinit(&empty_values)
zero [0]u8 :: [] zero [0]u8 :: []
arraylist.append(&empty_values, zero) catch |_| { arraylist.append(&empty_values, zero) catch |_| {
@@ -79,8 +79,8 @@ run func() i32 ! mem.AllocError {
} }
if (empty_values.items.len != 1) return 8 if (empty_values.items.len != 1) return 8
failed arraylist.ArrayList(i32) = arraylist.init(i32, fail_allocator) failed arraylist.ArrayList(i32) := arraylist.init(i32, fail_allocator)
failed_as_expected bool = false failed_as_expected bool := false
arraylist.append(&failed, 1) catch |_| { arraylist.append(&failed, 1) catch |_| {
failed_as_expected = true failed_as_expected = true
} }
+2 -2
View File
@@ -18,7 +18,7 @@ c_count_shift func(value u8, count c_uint) u8 {
c_count_fold u8 :: c_count_shift(3, 2) c_count_fold u8 :: c_count_shift(3, 2)
main func() i32 { main func() i32 {
buffer Buffer = undefined buffer Buffer := undefined
if (folded != 0) return 1 if (folded != 0) return 1
if (contextual != 255) return 28 if (contextual != 255) return 28
if (contextual_shift != 128) return 29 if (contextual_shift != 128) return 29
@@ -50,7 +50,7 @@ main func() i32 {
if ((c_ushort(240) xor c_ushort(255)) != 15) return 26 if ((c_ushort(240) xor c_ushort(255)) != 15) return 26
if ((c_longlong(64) <<| 60) != maxval!(c_longlong)) return 27 if ((c_longlong(64) <<| 60) != maxval!(c_longlong)) return 27
value u8 = 3 value u8 := 3
value <<= 2 value <<= 2
value |= 1 value |= 1
value xor= 5 value xor= 5
+6 -6
View File
@@ -7,8 +7,8 @@
main func() i32 { main func() i32 {
# 1. `break` out of a `while` once i reaches 5. # 1. `break` out of a `while` once i reaches 5.
i i32 = 0 i i32 := 0
a i32 = 0 a i32 := 0
while i < 100 : i += 1 { while i < 100 : i += 1 {
if (i == 5) break if (i == 5) break
a += 1 a += 1
@@ -16,7 +16,7 @@ main func() i32 {
if (a != 5) return 101 if (a != 5) return 101
# 2. `continue` past n == 3 while summing 0..9 (45 - 3 = 42). # 2. `continue` past n == 3 while summing 0..9 (45 - 3 = 42).
b i32 = 0 b i32 := 0
for 0..10 |n| { for 0..10 |n| {
if (n == 3) continue if (n == 3) continue
b = b + n b = b + n
@@ -25,7 +25,7 @@ main func() i32 {
# 3. Nested loops: the inner `break` exits only the inner loop, so the outer # 3. Nested loops: the inner `break` exits only the inner loop, so the outer
# loop still runs all three iterations (each contributing one y == 0 pass). # loop still runs all three iterations (each contributing one y == 0 pass).
c i32 = 0 c i32 := 0
for 0..3 |x| { for 0..3 |x| {
for 0..3 |y| { for 0..3 |y| {
if (y == 1) break if (y == 1) break
@@ -38,7 +38,7 @@ main func() i32 {
# 4. `continue` on the final element of an inclusive range bounded by the # 4. `continue` on the final element of an inclusive range bounded by the
# element type's maximum must exit cleanly, not overflow the increment. # element type's maximum must exit cleanly, not overflow the increment.
hi u8 :: 255 hi u8 :: 255
d i32 = 0 d i32 := 0
for 0..=hi |v| { for 0..=hi |v| {
if (v == 255) continue if (v == 255) continue
d += 1 d += 1
@@ -47,7 +47,7 @@ main func() i32 {
# 5. `while true` is exitable via `break` (so it is not an infinite loop and # 5. `while true` is exitable via `break` (so it is not an infinite loop and
# the code after it is reachable). # the code after it is reachable).
e i32 = 0 e i32 := 0
while true { while true {
e += 1 e += 1
if (e == 7) break if (e == 7) break
@@ -1,7 +1,7 @@
# Compound assignment (`+=`, `-=`, `*=`, `/=`) and explicit integer division. # Compound assignment (`+=`, `-=`, `*=`, `/=`) and explicit integer division.
check_float func() i32 { check_float func() i32 {
x f64 = 10.0 x f64 := 10.0
x /= 4.0 # 2.5 x /= 4.0 # 2.5
x *= 2.0 # 5.0 x *= 2.0 # 5.0
x -= 1.0 # 4.0 x -= 1.0 # 4.0
@@ -13,7 +13,7 @@ check_float func() i32 {
} }
check_unsigned func() i32 { check_unsigned func() i32 {
n u32 = 100 n u32 := 100
n = divtrunc!(n, 7) # 14 n = divtrunc!(n, 7) # 14
n -= 4 # 10 n -= 4 # 10
if n == 10 { if n == 10 {
@@ -23,7 +23,7 @@ check_unsigned func() i32 {
} }
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
total += 10 # 10 total += 10 # 10
total -= 3 # 7 total -= 3 # 7
total *= 4 # 28 total *= 4 # 28
@@ -33,7 +33,7 @@ main func() i32 {
total = total + 2 * 3 - 4 total = total + 2 * 3 - 4
# compound assignment as a while-loop update # compound assignment as a while-loop update
i i32 = 0 i i32 := 0
while i < 5 : i += 1 { while i < 5 : i += 1 {
total += 1 # +5 => 21 total += 1 # +5 => 21
} }
+1 -1
View File
@@ -15,7 +15,7 @@ nested func(value int) int {
} }
make_array func($N usize) [N]u8 { make_array func($N usize) [N]u8 {
data [N]u8 = undefined data [N]u8 := undefined
return data return data
} }
@@ -14,13 +14,13 @@ id func($T type, value T) T {
} }
buffer func($T type, $N usize, value T) [N]T { buffer func($T type, $N usize, value T) [N]T {
data [N]T = undefined data [N]T := undefined
_ = value _ = value
return data return data
} }
zero func($T type) T { zero func($T type) T {
value T = undefined value T := undefined
return value return value
} }
@@ -86,12 +86,12 @@ main func() i32 {
if fixed_len(&fixed) != 3 { if fixed_len(&fixed) != 3 {
return 7 return 7
} }
assigned i32 = 1 assigned i32 := 1
assigned = zero() assigned = zero()
_ = assigned _ = assigned
_ = take_i32(zero()) _ = take_i32(zero())
_ = return_zero() _ = return_zero()
optional ?u32 = null optional ?u32 := null
if !same_type(?u32, optional) { if !same_type(?u32, optional) {
return 8 return 8
} }
+11 -11
View File
@@ -32,8 +32,8 @@ make_point func() Point {
} }
sum_loop func(limit i32) i32 { sum_loop func(limit i32) i32 {
total i32 = 0 total i32 := 0
i i32 = 0 i i32 := 0
while i < limit { while i < limit {
i += 1 i += 1
if i == 2 { if i == 2 {
@@ -45,8 +45,8 @@ sum_loop func(limit i32) i32 {
} }
sum_for func() i32 { sum_for func() i32 {
total i32 = 0 total i32 := 0
values [_]i32 = [1, 2, 3] values [_]i32 := [1, 2, 3]
for values |value, index| { for values |value, index| {
total += value + index total += value + index
} }
@@ -54,7 +54,7 @@ sum_for func() i32 {
} }
defer_value func() i32 { defer_value func() i32 {
value i32 = 1 value i32 := 1
{ {
defer value += 10 defer value += 10
value += 1 value += 1
@@ -140,7 +140,7 @@ use_try func() i32 ! Error {
} }
ct_errdefer func(fail bool) i32 ! Error { ct_errdefer func(fail bool) i32 ! Error {
trace i32 = 0 trace i32 := 0
defer trace = trace * 10 + 1 defer trace = trace * 10 + 1
errdefer |err| { errdefer |err| {
if (err == .bad) trace = trace * 10 + 2 if (err == .bad) trace = trace * 10 + 2
@@ -151,7 +151,7 @@ ct_errdefer func(fail bool) i32 ! Error {
} }
ct_try_errdefer func() i32 ! Error { ct_try_errdefer func() i32 ! Error {
trace i32 = 0 trace i32 := 0
defer trace = trace * 10 + 4 defer trace = trace * 10 + 4
errdefer |err| { errdefer |err| {
if (err == .bad) trace = trace * 10 + 5 if (err == .bad) trace = trace * 10 + 5
@@ -185,20 +185,20 @@ alias_add func(left @mut i32, right @mut i32) void {
} }
storage_mutation func() i32 { storage_mutation func() i32 {
values [3]mut i32 = [1, 2, 3] values [3]mut i32 := [1, 2, 3]
values[0] += 1 values[0] += 1
bump_ptr(&values[1]) bump_ptr(&values[1])
view []mut i32 = values[..] view []mut i32 := values[..]
for view |@item| { for view |@item| {
item^ += 1 item^ += 1
} }
pointer *mut i32 = view.ptr pointer *mut i32 := view.ptr
pointer[2] += 1 pointer[2] += 1
alias_add(&values[0], &view[0]) alias_add(&values[0], &view[0])
box Box = Box { point = Point { x = 2, y = 3 } } box Box := Box { point = Point { x = 2, y = 3 } }
match box { match box {
.point |@p|: p.x += values[1] .point |@p|: p.x += values[1]
.empty: values[0] = values[0] .empty: values[0] = values[0]
@@ -1,5 +1,5 @@
make_array func($N usize) [N]u8 { make_array func($N usize) [N]u8 {
data [N]u8 = undefined data [N]u8 := undefined
return data return data
} }
@@ -6,10 +6,10 @@ observe func(counter @mut i32, value ?i32) ?i32 {
} }
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
# present optional scalar -> binds v to the unwrapped value # present optional scalar -> binds v to the unwrapped value
a ?i32 = 40 a ?i32 := 40
if a |v| { if a |v| {
total = total + v # 40 total = total + v # 40
} else { } else {
@@ -17,7 +17,7 @@ main func() i32 {
} }
# null -> else branch taken; the binding is not in scope there # null -> else branch taken; the binding is not in scope there
b ?i32 = null b ?i32 := null
if b |v| { if b |v| {
total = total + v total = total + v
} else { } else {
@@ -25,20 +25,20 @@ main func() i32 {
} }
# optional pointer present -> binds q to a non-null @i32; deref proves it # optional pointer present -> binds q to a non-null @i32; deref proves it
n i32 = 0 n i32 := 0
p ?@i32 = &n p ?@i32 := &n
if p |q| { if p |q| {
total = total + q^ # +0 total = total + q^ # +0
} }
# optional pointer null -> skipped # optional pointer null -> skipped
z ?@i32 = null z ?@i32 := null
if z |_| { if z |_| {
total = total + 1000 total = total + 1000
} }
# guarded multi-unwrap exposes every capture to the guard and then-block # guarded multi-unwrap exposes every capture to the guard and then-block
age ?i32 = 2 age ?i32 := 2
if a and age |value, years : value + years == 42| { if a and age |value, years : value + years == 42| {
total = total total = total
} else { } else {
@@ -46,7 +46,7 @@ main func() i32 {
} }
# parenthesized chains and three-value unwraps are equivalent # parenthesized chains and three-value unwraps are equivalent
bonus ?i32 = 0 bonus ?i32 := 0
if (a and age and bonus) |value, years, extra : value + years + extra == 42| { if (a and age and bonus) |value, years, extra : value + years + extra == 42| {
total = total total = total
} else { } else {
@@ -64,7 +64,7 @@ main func() i32 {
} }
# a failed unwrap prevents later expressions from being evaluated # a failed unwrap prevents later expressions from being evaluated
calls i32 = 0 calls i32 := 0
if b and observe(&calls, age) |missing, observed| { if b and observe(&calls, age) |missing, observed| {
total = total + missing + observed total = total + missing + observed
} }
+3 -3
View File
@@ -23,7 +23,7 @@ noisy func() bool {
} }
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
# comparisons drive if / else if / else # comparisons drive if / else if / else
total = total + classify(-5) # 1 total = total + classify(-5) # 1
@@ -45,9 +45,9 @@ main func() i32 {
} }
# block scoping: inner bindings do not escape the block # block scoping: inner bindings do not escape the block
x i32 = 1 x i32 := 1
if x == 1 { if x == 1 {
inner_x i32 = 100 inner_x i32 := 100
if inner_x == 100 { if inner_x == 100 {
total = total + 5 # 40 total = total + 5 # 40
} }
+9 -9
View File
@@ -7,7 +7,7 @@
# The return value is captured before defers run, so the mutation here does not # The return value is captured before defers run, so the mutation here does not
# change what is returned (Zig semantics). # change what is returned (Zig semantics).
spill_check func() i32 { spill_check func() i32 {
x i32 = 5 x i32 := 5
defer x = 999 defer x = 999
return x return x
} }
@@ -15,7 +15,7 @@ spill_check func() i32 {
# A function-scope defer runs only at function exit; a `break` runs the loop-body # A function-scope defer runs only at function exit; a `break` runs the loop-body
# defer but NOT the enclosing function-scope defer. # defer but NOT the enclosing function-scope defer.
enclosing_defer_check func() i32 { enclosing_defer_check func() i32 {
v i32 = 0 v i32 := 0
defer v = v + 100 defer v = v + 100
for 0..3 |i| { for 0..3 |i| {
defer v = v + 1 defer v = v + 1
@@ -89,7 +89,7 @@ main func() i32 {
if (spill_check() != 5) return 101 if (spill_check() != 5) return 101
# 2. LIFO ordering, run at end of each loop iteration. # 2. LIFO ordering, run at end of each loop iteration.
r i32 = 0 r i32 := 0
for 0..1 |i| { for 0..1 |i| {
defer r = r * 2 + 1 # registered first -> runs last defer r = r * 2 + 1 # registered first -> runs last
defer r = r * 2 # registered second -> runs first defer r = r * 2 # registered second -> runs first
@@ -99,16 +99,16 @@ main func() i32 {
# 3. scoped bare block + scoped defer (defer fires at the closing brace, and # 3. scoped bare block + scoped defer (defer fires at the closing brace, and
# the block-local is not visible afterwards). # the block-local is not visible afterwards).
a i32 = 1 a i32 := 1
{ {
defer a = 4 defer a = 4
c i32 = 3 c i32 := 3
_ = c _ = c
} }
if (a != 4) return 103 if (a != 4) return 103
# 4. `defer { ... }` block: all its statements run (in order) at scope close. # 4. `defer { ... }` block: all its statements run (in order) at scope close.
s i32 = 0 s i32 := 0
{ {
defer { defer {
s = s + 1 s = s + 1
@@ -119,7 +119,7 @@ main func() i32 {
if (s != 60) return 104 # 5 -> 6 -> 60 if (s != 60) return 104 # 5 -> 6 -> 60
# 5. `break` flushes the loop-body defer. # 5. `break` flushes the loop-body defer.
bc i32 = 0 bc i32 := 0
for 0..5 |i| { for 0..5 |i| {
defer bc = bc + 1 defer bc = bc + 1
if (i == 2) break if (i == 2) break
@@ -127,7 +127,7 @@ main func() i32 {
if (bc != 3) return 105 # i=0,1 fall-through + i=2 break if (bc != 3) return 105 # i=0,1 fall-through + i=2 break
# 6. `continue` flushes the loop-body defer. # 6. `continue` flushes the loop-body defer.
cc i32 = 0 cc i32 := 0
for 0..3 |i| { for 0..3 |i| {
defer cc = cc + 1 defer cc = cc + 1
if (i == 1) continue if (i == 1) continue
@@ -139,7 +139,7 @@ main func() i32 {
if (enclosing_defer_check() != 2) return 107 if (enclosing_defer_check() != 2) return 107
# 8. errdefer is skipped on success; ordinary defers stay interleaved. # 8. errdefer is skipped on success; ordinary defers stay interleaved.
trace i32 = 0 trace i32 := 0
if ((explicit_cleanup(false, &trace) catch 0) != 7 or trace != 31) return 108 if ((explicit_cleanup(false, &trace) catch 0) != 7 or trace != 31) return 108
# 9. Explicit errors run errdefer and expose the captured error. # 9. Explicit errors run errdefer and expose the captured error.
+12 -12
View File
@@ -24,9 +24,9 @@ take func(value LocalID) LocalID {
main func() i32 { main func() i32 {
id LocalID :: LocalID(7) id LocalID :: LocalID(7)
copy LocalID = take(id) copy LocalID := take(id)
maybe ?LocalID = copy maybe ?LocalID := copy
pointer @LocalID = &copy pointer @LocalID := &copy
point PointID :: PointID(Point { x = 1, y = 2 }) point PointID :: PointID(Point { x = 1, y = 2 })
bytes Bytes :: Bytes([3, 4]) bytes Bytes :: Bytes([3, 4])
wrapped WrappedID :: WrappedID(id) wrapped WrappedID :: WrappedID(id)
@@ -64,15 +64,15 @@ main func() i32 {
return 8 return 8
} }
arithmetic Signed = Signed(4) arithmetic Signed := Signed(4)
arithmetic += 3 arithmetic += 3
arithmetic -= 2 arithmetic -= 2
arithmetic *= 5 arithmetic *= 5
fraction Real = Real(3.0) fraction Real := Real(3.0)
fraction /= 2.0 fraction /= 2.0
if arithmetic != 25 or fraction != 1.5 { return 9 } if arithmetic != 25 or fraction != 1.5 { return 9 }
bits Mask = Mask(3) bits Mask := Mask(3)
bits |= 8 bits |= 8
bits xor= 2 bits xor= 2
bits &= 9 bits &= 9
@@ -81,18 +81,18 @@ main func() i32 {
bits <<|= u8(5) bits <<|= u8(5)
if bits != 255 { return 10 } if bits != 255 { return 10 }
values [10]u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] values [10]u8 := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
if values[LocalID(4)] != 4 { return 11 } if values[LocalID(4)] != 4 { return 11 }
section []u8 = values[LocalID(2)..LocalID(5)] section []u8 := values[LocalID(2)..LocalID(5)]
if section.len != 3 or section[usize(0)] != 2 or section[usize(2)] != 4 { return 12 } if section.len != 3 or section[usize(0)] != 2 or section[usize(2)] != 4 { return 12 }
if u32(id) != 7 or usize(id) != 7 or f64(id) != 7.0 { return 13 } if u32(id) != 7 or usize(id) != 7 or f64(id) != 7.0 { return 13 }
extracted LocalID = LocalID(wrapped) extracted LocalID := LocalID(wrapped)
if extracted != id { return 14 } if extracted != id { return 14 }
minimum Signed = minval!(Signed) minimum Signed := minval!(Signed)
maximum Mask = maxval!(Mask) maximum Mask := maxval!(Mask)
nested_max WrappedID = maxval!(WrappedID) nested_max WrappedID := maxval!(WrappedID)
if i32(minimum) != minval!(i32) or u8(maximum) != 255 or u32(nested_max) != maxval!(u32) { if i32(minimum) != minval!(i32) or u8(maximum) != 255 or u32(nested_max) != maxval!(u32) {
return 15 return 15
} }
+1 -1
View File
@@ -17,7 +17,7 @@ identity c_func(value State) State {
} }
main func() i32 { main func() i32 {
state State = identity(.running) state State := identity(.running)
values [2]State :: [.started, State.stopped] values [2]State :: [.started, State.stopped]
animal animals.Animal :: animals.Animal.dog animal animals.Animal :: animals.Animal.dog
if same(state, .running) and if same(state, .running) and
+4 -4
View File
@@ -125,7 +125,7 @@ inline_detail func(value i32) i32 ! union(enum) {
} }
main func() i32 { main func() i32 {
acc i32 = 0 acc i32 := 0
a :: maybe(0) catch 7 a :: maybe(0) catch 7
b :: maybe(4) catch 99 b :: maybe(4) catch 99
@@ -166,11 +166,11 @@ main func() i32 {
acc = acc + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p acc = acc + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p
acc = acc + pick(.left) acc = acc + pick(.left)
r Right = .right r Right := .right
acc = acc + pick(r) acc = acc + pick(r)
box BoxA = .a{8} box BoxA := .a{8}
acc = acc + payload(box) acc = acc + payload(box)
empty BoxB = .b empty BoxB := .b
acc = acc + payload(empty) acc = acc + payload(empty)
acc = acc + payload(.a{9}) acc = acc + payload(.a{9})
+3 -3
View File
@@ -5,9 +5,9 @@ pass func(value range) range {
} }
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
items [3]mut i32 = [1, 2, 3] items [3]mut i32 := [1, 2, 3]
for items |item, index| { for items |item, index| {
total = total + item total = total + item
_ = index _ = index
@@ -17,7 +17,7 @@ main func() i32 {
item^ = item^ + 1 item^ = item^ + 1
} }
view []mut i32 = items[..] view []mut i32 := items[..]
for view |@item, index| { for view |@item, index| {
item^ = item^ + 1 item^ = item^ + 1
_ = index _ = index
+10 -10
View File
@@ -6,23 +6,23 @@ make_range func(calls @mut i32, end usize) range {
global_range :: 0..1 global_range :: 0..1
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
first u8 = 254 first u8 := 254
last u8 = 255 last u8 := 255
for first..=last |value| { for first..=last |value| {
_ = value _ = value
total = total + 1 total = total + 1
} }
signed_start i8 = -2 signed_start i8 := -2
signed_end i8 = 1 signed_end i8 := 1
for signed_start..signed_end |value| { for signed_start..signed_end |value| {
_ = value _ = value
total = total + 1 total = total + 1
} }
limit usize = 3 limit usize := 3
for 0..(limit + 1) |value| { for 0..(limit + 1) |value| {
_ = value _ = value
total = total + 1 total = total + 1
@@ -41,19 +41,19 @@ main func() i32 {
total = total + 100 total = total + 100
} }
empty [0]i32 = [] empty [0]i32 := []
for empty |value| { for empty |value| {
_ = value _ = value
total = total + 100 total = total + 100
} }
pointed i32 = 3 pointed i32 := 3
pointers [1]@mut i32 = [&pointed] pointers [1]@mut i32 := [&pointed]
for pointers |pointer| { for pointers |pointer| {
total = total + pointer^ total = total + pointer^
} }
calls i32 = 0 calls i32 := 0
for make_range(&calls, 2) |value| { for make_range(&calls, 2) |value| {
_ = value _ = value
total = total + 1 total = total + 1
@@ -1,6 +1,6 @@
main func() i32 { main func() i32 {
items [3]mut i32 = undefined items [3]mut i32 := undefined
i int = 1 i int := 1
items[i] = 42 items[i] = 42
return 0 return 0
} }
@@ -1,6 +1,6 @@
main func() i32 { main func() i32 {
items [3]mut i32 = undefined items [3]mut i32 := undefined
i i32 = 1 i i32 := 1
items[i] = 42 items[i] = 42
return 0 return 0
} }
+5 -5
View File
@@ -68,8 +68,8 @@ main func() i32 {
return 2 return 2
} }
a Value = .number{41} a Value := .number{41}
b Value = .number{41} b Value := .number{41}
if !equal_value(a, b) or equal_value(a, .pair{left = 41, right = 0}) { if !equal_value(a, b) or equal_value(a, .pair{left = 41, right = 0}) {
return 3 return 3
} }
@@ -78,8 +78,8 @@ main func() i32 {
return 4 return 4
} }
pair_a Value = .pair{left = 2, right = 3} pair_a Value := .pair{left = 2, right = 3}
pair_b Value = .pair{left = 2, right = 3} pair_b Value := .pair{left = 2, right = 3}
if !equal_value(pair_a, pair_b) or !equal_value(.empty, .empty) { if !equal_value(pair_a, pair_b) or !equal_value(.empty, .empty) {
return 5 return 5
} }
@@ -88,7 +88,7 @@ main func() i32 {
return 6 return 6
} }
empty Value = .empty empty Value := .empty
increment(&empty) increment(&empty)
return 0 return 0
} }
@@ -1,4 +1,4 @@
bad int = 4 bad int := 4
read_bad func() int { read_bad func() int {
return bad return bad
+2 -2
View File
@@ -85,7 +85,7 @@ bad_writer func() io.Writer {
} }
rejects_bad_read func() bool { rejects_bad_read func() bool {
buffer [1]mut u8 = [0] buffer [1]mut u8 := [0]
_ = io.read(bad_reader(), buffer[..]) catch |err| { _ = io.read(bad_reader(), buffer[..]) catch |err| {
return err == .read_failed return err == .read_failed
} }
@@ -108,7 +108,7 @@ rejects_bad_write func() bool {
main func(init process.Init) i32 { main func(init process.Init) i32 {
system io.Io :: init.io system io.Io :: init.io
buffer [2]mut u8 = [0, 0] buffer [2]mut u8 := [0, 0]
count usize :: io.read(ok_reader(), buffer[..]) catch 0 count usize :: io.read(ok_reader(), buffer[..]) catch 0
if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' { if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' {
return 1 return 1
+9 -9
View File
@@ -63,18 +63,18 @@ make_box func() Box {
} }
main func() i32 { main func() i32 {
acc i32 = 0 acc i32 := 0
dog Data = Data{ dog = 9 } dog Data := Data{ dog = 9 }
bird Data = Data{ bird = 38 } bird Data := Data{ bird = 38 }
acc = acc + describe(dog) + describe(bird) # 10 + 40 = 50 acc = acc + describe(dog) + describe(bird) # 10 + 40 = 50
# same-type multi-pattern capture # same-type multi-pattern capture
acc = acc + payload_of(dog) + payload_of(bird) # 9 + 38 = 47 acc = acc + payload_of(dog) + payload_of(bird) # 9 + 38 = 47
# enum statement match, exhaustive, with a multi-pattern arm # enum statement match, exhaustive, with a multi-pattern arm
a Animal = .bird a Animal := .bird
rank i32 = 0 rank i32 := 0
match a { match a {
.dog, .cat: rank = 1 .dog, .cat: rank = 1
.bird: rank = 3 .bird: rank = 3
@@ -89,7 +89,7 @@ main func() i32 {
acc = acc + legs # +2 acc = acc + legs # +2
# scalar match: a range arm, a multi-literal arm, and a mandatory else # scalar match: a range arm, a multi-literal arm, and a mandatory else
bucket i32 = 0 bucket i32 := 0
match rank { match rank {
0..3: bucket = 1 # exclusive 0,1,2 — does not include 3 0..3: bucket = 1 # exclusive 0,1,2 — does not include 3
3, 4: bucket = 5 # rank is 3 3, 4: bucket = 5 # rank is 3
@@ -98,8 +98,8 @@ main func() i32 {
acc = acc + bucket # +5 acc = acc + bucket # +5
# void-payload variant: contextual construction (`.empty` coerces to Box) + no-capture arm # void-payload variant: contextual construction (`.empty` coerces to Box) + no-capture arm
e Box = .empty e Box := .empty
hit i32 = 0 hit i32 := 0
match e { match e {
.point |pt|: hit = pt.x .point |pt|: hit = pt.x
.empty: hit = 7 .empty: hit = 7
@@ -107,7 +107,7 @@ main func() i32 {
acc = acc + hit # +7 acc = acc + hit # +7
# pointer capture mutates the subject's payload in place # pointer capture mutates the subject's payload in place
b Box = Box{ point = Point{ x = 1, y = 2 } } b Box := Box{ point = Point{ x = 1, y = 2 } }
match b { match b {
.point |@p|: p.x = 10 .point |@p|: p.x = 10
.empty: hit = hit .empty: hit = hit
@@ -1,7 +1,7 @@
mem :: import "@std/mem" mem :: import "@std/mem"
raw_allocator_test func() i32 { raw_allocator_test func() i32 {
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, null, 0, 4, 1) resized ?*mut u8 := mem.raw_realloc(mem.c_allocator, null, 0, 4, 1)
if resized |bytes| { if resized |bytes| {
bytes[0] = 10 bytes[0] = 10
bytes[1] = 20 bytes[1] = 20
@@ -11,7 +11,7 @@ raw_allocator_test func() i32 {
return 20 return 20
} }
grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1) grown ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1)
if grown |bytes| { if grown |bytes| {
resized = grown resized = grown
if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 { if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 {
@@ -23,7 +23,7 @@ raw_allocator_test func() i32 {
return 22 return 22
} }
shrunk ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1) shrunk ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1)
if shrunk |bytes| { if shrunk |bytes| {
resized = shrunk resized = shrunk
if bytes[0] != 10 or bytes[1] != 20 { if bytes[0] != 10 or bytes[1] != 20 {
@@ -35,7 +35,7 @@ raw_allocator_test func() i32 {
return 24 return 24
} }
invalid ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24) invalid ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24)
if invalid |memory| { if invalid |memory| {
mem.raw_free(mem.c_allocator, memory, 4, 24) mem.raw_free(mem.c_allocator, memory, 4, 24)
mem.raw_free(mem.c_allocator, resized, 2, 1) mem.raw_free(mem.c_allocator, resized, 2, 1)
@@ -54,14 +54,14 @@ raw_allocator_test func() i32 {
return 27 return 27
} }
over_aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 4, 32) over_aligned ?*mut u8 := mem.raw_alloc(mem.c_allocator, 4, 32)
if over_aligned |bytes| { if over_aligned |bytes| {
bytes[0] = 11 bytes[0] = 11
bytes[1] = 22 bytes[1] = 22
} else { } else {
return 28 return 28
} }
over_aligned_grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32) over_aligned_grown ?*mut u8 := mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32)
if over_aligned_grown |bytes| { if over_aligned_grown |bytes| {
if bytes[0] != 11 or bytes[1] != 22 { if bytes[0] != 11 or bytes[1] != 22 {
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32) mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
@@ -73,19 +73,19 @@ raw_allocator_test func() i32 {
return 30 return 30
} }
zero_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 0) zero_alignment ?*mut u8 := mem.raw_alloc(mem.c_allocator, 8, 0)
if zero_alignment |memory| { if zero_alignment |memory| {
mem.raw_free(mem.c_allocator, memory, 8, 0) mem.raw_free(mem.c_allocator, memory, 8, 0)
return 1 return 1
} }
bad_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 24) bad_alignment ?*mut u8 := mem.raw_alloc(mem.c_allocator, 8, 24)
if bad_alignment |memory| { if bad_alignment |memory| {
mem.raw_free(mem.c_allocator, memory, 8, 24) mem.raw_free(mem.c_allocator, memory, 8, 24)
return 2 return 2
} }
aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 64, 32) aligned ?*mut u8 := mem.raw_alloc(mem.c_allocator, 64, 32)
defer mem.raw_free(mem.c_allocator, aligned, 64, 32) defer mem.raw_free(mem.c_allocator, aligned, 64, 32)
if aligned |bytes| { if aligned |bytes| {
bytes[0] = 1 bytes[0] = 1
+16 -16
View File
@@ -21,9 +21,9 @@ task_list_init func(allocator mem.Allocator) TaskList {
} }
alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 { alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
fallback [1]mut i32 = undefined fallback [1]mut i32 := undefined
failed bool = false failed bool := false
values []mut i32 = mem.alloc(allocator, count) catch |_| { values []mut i32 := mem.alloc(allocator, count) catch |_| {
failed = true failed = true
yield (&fallback).ptr[..0] yield (&fallback).ptr[..0]
} }
@@ -42,14 +42,14 @@ task_list_reserve func(list @mut TaskList, capacity usize) bool {
return true return true
} }
new_ids ?[]mut i32 = alloc_i32s(list.allocator, capacity) new_ids ?[]mut i32 := alloc_i32s(list.allocator, capacity)
new_priorities ?[]mut i32 = alloc_i32s(list.allocator, capacity) new_priorities ?[]mut i32 := alloc_i32s(list.allocator, capacity)
new_durations ?[]mut i32 = alloc_i32s(list.allocator, capacity) new_durations ?[]mut i32 := alloc_i32s(list.allocator, capacity)
if (new_ids and new_priorities and new_durations) |ids, priorities, durations| { if (new_ids and new_priorities and new_durations) |ids, priorities, durations| {
if list.len > 0 { if list.len > 0 {
if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| { if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| {
i usize = 0 i usize := 0
while i < list.len : i += 1 { while i < list.len : i += 1 {
ids[i] = old_ids[i] ids[i] = old_ids[i]
priorities[i] = old_priorities[i] priorities[i] = old_priorities[i]
@@ -82,7 +82,7 @@ task_list_reserve func(list @mut TaskList, capacity usize) bool {
task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool { task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool {
if list.len == list.capacity { if list.len == list.capacity {
new_capacity usize = 2 new_capacity usize := 2
if list.capacity != 0 { if list.capacity != 0 {
new_capacity = list.capacity * 2 new_capacity = list.capacity * 2
} }
@@ -92,7 +92,7 @@ task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool
} }
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| { if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
index usize = list.len index usize := list.len
ids[index] = id ids[index] = id
priorities[index] = priority priorities[index] = priority
durations[index] = duration durations[index] = duration
@@ -113,11 +113,11 @@ task_list_best_id func(list @mut TaskList) i32 {
} }
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| { if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
best_index usize = 0 best_index usize := 0
best_score i32 = task_score(priorities[0], durations[0]) best_score i32 := task_score(priorities[0], durations[0])
i usize = 1 i usize := 1
while i < list.len : i += 1 { while i < list.len : i += 1 {
score i32 = task_score(priorities[i], durations[i]) score i32 := task_score(priorities[i], durations[i])
if score > best_score { if score > best_score {
best_score = score best_score = score
best_index = i best_index = i
@@ -130,9 +130,9 @@ task_list_best_id func(list @mut TaskList) i32 {
} }
task_list_total_duration func(list @mut TaskList) i32 { task_list_total_duration func(list @mut TaskList) i32 {
total i32 = 0 total i32 := 0
if list.durations |durations| { if list.durations |durations| {
i usize = 0 i usize := 0
while i < list.len : i += 1 { while i < list.len : i += 1 {
total += durations[i] total += durations[i]
} }
@@ -152,7 +152,7 @@ task_list_deinit func(list @mut TaskList) void {
} }
task_list_test func() i32 { task_list_test func() i32 {
tasks TaskList = task_list_init(mem.c_allocator) tasks TaskList := task_list_init(mem.c_allocator)
defer task_list_deinit(&tasks) defer task_list_deinit(&tasks)
if task_list_push(&tasks, 101, 3, 5) == false { if task_list_push(&tasks, 101, 3, 5) == false {
+14 -14
View File
@@ -30,8 +30,8 @@ hide probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
typed_allocator_test func() i32 { typed_allocator_test func() i32 {
if (sizeof!(mem.Allocator) != 16) return 31 if (sizeof!(mem.Allocator) != 16) return 31
first_calls [1]mut usize = [0] first_calls [1]mut usize := [0]
second_calls [1]mut usize = [0] second_calls [1]mut usize := [0]
first_allocator mem.Allocator :: mem.Allocator { first_allocator mem.Allocator :: mem.Allocator {
context = &first_calls, context = &first_calls,
vtable = &probe_vtable, vtable = &probe_vtable,
@@ -47,9 +47,9 @@ typed_allocator_test func() i32 {
_ = mem.raw_alloc(second_allocator, 1, 1) _ = mem.raw_alloc(second_allocator, 1, 1)
if (first_calls[0] != 3 or second_calls[0] != 1) return 32 if (first_calls[0] != 3 or second_calls[0] != 1) return 32
i32_fallback [1]mut i32 = undefined i32_fallback [1]mut i32 := undefined
empty_failed bool = false empty_failed bool := false
empty []mut i32 = mem.alloc(first_allocator, 0) catch |_| { empty []mut i32 := mem.alloc(first_allocator, 0) catch |_| {
empty_failed = true empty_failed = true
yield (&i32_fallback).ptr[..0] yield (&i32_fallback).ptr[..0]
} }
@@ -57,9 +57,9 @@ typed_allocator_test func() i32 {
mem.free(first_allocator, empty) mem.free(first_allocator, empty)
if (first_calls[0] != 3) return 33 if (first_calls[0] != 3) return 33
zero_sized_fallback [1]mut [0]u8 = undefined zero_sized_fallback [1]mut [0]u8 := undefined
zero_sized_failed bool = false zero_sized_failed bool := false
zero_sized []mut [0]u8 = mem.alloc([0]u8, first_allocator, 3) catch |_| { zero_sized []mut [0]u8 := mem.alloc([0]u8, first_allocator, 3) catch |_| {
zero_sized_failed = true zero_sized_failed = true
yield (&zero_sized_fallback).ptr[..0] yield (&zero_sized_fallback).ptr[..0]
} }
@@ -68,22 +68,22 @@ typed_allocator_test func() i32 {
mem.free([0]u8, first_allocator, zero_sized) mem.free([0]u8, first_allocator, zero_sized)
if (first_calls[0] != 3) return 35 if (first_calls[0] != 3) return 35
u64_fallback [1]mut u64 = undefined u64_fallback [1]mut u64 := undefined
overflow_fallback_failed bool = false overflow_fallback_failed bool := false
overflow_fallback []mut u64 = mem.alloc(first_allocator, 0) catch |_| { overflow_fallback []mut u64 := mem.alloc(first_allocator, 0) catch |_| {
overflow_fallback_failed = true overflow_fallback_failed = true
yield (&u64_fallback).ptr[..0] yield (&u64_fallback).ptr[..0]
} }
if (overflow_fallback_failed) return 37 if (overflow_fallback_failed) return 37
overflow_failed bool = false overflow_failed bool := false
_ = mem.alloc(u64, first_allocator, maxval!(usize)) catch |_| { _ = mem.alloc(u64, first_allocator, maxval!(usize)) catch |_| {
overflow_failed = true overflow_failed = true
yield overflow_fallback yield overflow_fallback
} }
if (overflow_failed == false or first_calls[0] != 3) return 40 if (overflow_failed == false or first_calls[0] != 3) return 40
typed_failed bool = false typed_failed bool := false
typed []mut i32 = mem.alloc(mem.c_allocator, 4) catch |_| { typed []mut i32 := mem.alloc(mem.c_allocator, 4) catch |_| {
typed_failed = true typed_failed = true
yield (&i32_fallback).ptr[..0] yield (&i32_fallback).ptr[..0]
} }
+6 -6
View File
@@ -48,26 +48,26 @@ score_for func(k Kind) i32 {
} }
main func() i32 { main func() i32 {
items [LEN]mut i32 = undefined items [LEN]mut i32 := undefined
items[0] = 10 items[0] = 10
items[1] = 20 items[1] = 20
idx u8 = 2 idx u8 := 2
items[idx] = items[0] + items[1] items[idx] = items[0] + items[1]
if (items[2] != 30) return 1 if (items[2] != 30) return 1
native_i i32 = 12 native_i i32 := 12
if (take_c_int(native_i) != 12) return 2 if (take_c_int(native_i) != 12) return 2
native_u u8 = 7 native_u u8 := 7
if (take_c_uchar(native_u) != 7) return 3 if (take_c_uchar(native_u) != 7) return 3
native_f f32 = 3.25 native_f f32 := 3.25
cf :: take_c_float(native_f) cf :: take_c_float(native_f)
if (cf < 3.0 or cf > 4.0) return 4 if (cf < 3.0 or cf > 4.0) return 4
if (cf == 0.0) return 5 if (cf == 0.0) return 5
native_d f64 = 5.0 native_d f64 := 5.0
cd :: take_c_double(native_d) cd :: take_c_double(native_d)
if (cd != 5.0) return 6 if (cd != 5.0) return 6
+2 -2
View File
@@ -86,8 +86,8 @@ main func(init process.Init) i32 {
} }
writer io.Writer :: io.stdout(init.io) writer io.Writer :: io.stdout(init.io)
positive f64 = 1.0 positive f64 := 1.0
zero f64 = 0.0 zero f64 := 0.0
infinity f64 :: positive / zero infinity f64 :: positive / zero
nan f64 :: zero / zero nan f64 :: zero / zero
io.print(writer, "{} {} {} {} {s} {d} {b} {o} {x} {X} {c} {e} {{}} {d} {b} {} {} {e}\n", { io.print(writer, "{} {} {} {} {s} {d} {b} {o} {x} {X} {c} {e} {{}} {d} {b} {} {} {e}\n", {
+6 -6
View File
@@ -2,11 +2,11 @@ Point :: struct {
x i32 x i32
} }
counter int = 0 counter := i32(0)
ratio float = 1 ratio := 1.0
span range = 0..2 span := 0..2
point Point = Point { x = 1 } point Point := Point { x = 1 }
values [_]mut i32 = [10, 20] values [_]mut i32 := [10, 20]
bump func(value @mut i32) void { bump func(value @mut i32) void {
value^ += 1 value^ += 1
@@ -19,7 +19,7 @@ main func() i32 {
point.x += counter point.x += counter
values[1] = point.x values[1] = point.x
total i32 = counter + point.x + values[1] total i32 := counter + point.x + values[1]
for span |i| { for span |i| {
total += i total += i
} }
+1 -1
View File
@@ -1,5 +1,5 @@
main func() i32 { main func() i32 {
value i32 = 1 value := i32(1)
value = value + 2 value = value + 2
return value return value
} }
+1 -1
View File
@@ -1,4 +1,4 @@
main func() void { main func() void {
value i8 = 127 value i8 := 127
_ = value + 1 _ = value + 1
} }
+1 -1
View File
@@ -11,7 +11,7 @@ sum_brolang func(a, b int) int {
} }
main func() void { main func() void {
y int = 4 y int := 4
a_add_b_c :: sum_c(1, 2) a_add_b_c :: sum_c(1, 2)
a_add_b_brolang :: sum_brolang(1, 2) a_add_b_brolang :: sum_brolang(1, 2)
_ = y _ = y
+1 -1
View File
@@ -1,5 +1,5 @@
main func() i32 { main func() i32 {
flag bool = true flag bool := true
value :: i32(flag) value :: i32(flag)
return value return value
} }
+2 -2
View File
@@ -16,7 +16,7 @@ Thing :: union(enum) {
} }
main func() i32 { main func() i32 {
x Data = Data{ bird = 37 } x Data := Data{ bird = 37 }
y Thing = Thing{ a = 5 } y Thing := Thing{ a = 5 }
return x.bird + y.a return x.bird + y.a
} }
+4 -4
View File
@@ -9,7 +9,7 @@ format func() []u8 {
} }
sum func($T type, value T) i32 { sum func($T type, value T) i32 {
total i32 = 0 total i32 := 0
match typeinfo!(T) { match typeinfo!(T) {
.record |record|: inline for record.fields |field| { .record |record|: inline for record.fields |field| {
total += i32(field!(value, field.name)) total += i32(field!(value, field.name))
@@ -20,7 +20,7 @@ sum func($T type, value T) i32 {
} }
static_control func($T type, value T) i32 { static_control func($T type, value T) i32 {
total i32 = 0 total i32 := 0
match typeinfo!(T) { match typeinfo!(T) {
.record |record|: inline for record.fields |field| { .record |record|: inline for record.fields |field| {
{ {
@@ -49,7 +49,7 @@ row_value func(row Row) i32 {
} }
static_aggregates func() i32 { static_aggregates func() i32 {
total i32 = 0 total i32 := 0
inline for {Row {value = 2}, Row {value = 40}} |row| { inline for {Row {value = 2}, Row {value = 40}} |row| {
total += row_value(row) total += row_value(row)
} }
@@ -57,7 +57,7 @@ static_aggregates func() i32 {
} }
main func() i32 { main func() i32 {
numbers Numbers = Numbers {1, 2, 39} numbers Numbers := Numbers {1, 2, 39}
singleton :: {42,} singleton :: {42,}
empty :: {} empty :: {}
block_value :: { block_value :: {
+1 -1
View File
@@ -4,6 +4,6 @@ Val :: union {
} }
main func() i32 { main func() i32 {
x Val = Val{ n = 42 } x Val := Val{ n = 42 }
return x.n return x.n
} }
+3 -3
View File
@@ -1,8 +1,8 @@
warn_only func(value i32, unused i32) i32 { warn_only func(value i32, unused i32) i32 {
local i32 = 1 local i32 := 1
write_only i32 = 2 write_only i32 := 2
write_only = 3 write_only = 3
consumed i32 = value consumed i32 := value
_ = consumed _ = consumed
return value return value
} }
+8 -8
View File
@@ -1,31 +1,31 @@
# Milestone 5: boolean while loops with optional post-iteration updates. # Milestone 5: boolean while loops with optional post-iteration updates.
return_before_update func() i32 { return_before_update func() i32 {
i i32 = 0 i i32 := 0
while true : i = i + 1 { while true : i = i + 1 {
return i return i
} }
} }
main func() i32 { main func() i32 {
total i32 = 0 total i32 := 0
# ordinary condition and update # ordinary condition and update
i u32 = 0 i u32 := 0
while i < 5 : i = i + 1 { while i < 5 : i = i + 1 {
total = total + 2 total = total + 2
} }
# equivalent parenthesized header # equivalent parenthesized header
j u32 = 0 j u32 := 0
while (j < 4) : (j = j + 1) { while (j < 4) : (j = j + 1) {
total = total + 3 total = total + 3
} }
# nested loops and body-local storage # nested loops and body-local storage
outer u32 = 0 outer u32 := 0
while outer < 2 : outer = outer + 1 { while outer < 2 : outer = outer + 1 {
inner u32 = 0 inner u32 := 0
while inner < 3 : inner = inner + 1 { while inner < 3 : inner = inner + 1 {
total = total + 2 total = total + 2
} }
@@ -33,9 +33,9 @@ main func() i32 {
# Body-local storage stays scoped to the body. The update still targets # Body-local storage stays scoped to the body. The update still targets
# the mutable k declared before the loop. # the mutable k declared before the loop.
k u32 = 0 k u32 := 0
while k < 4 : k = k + 1 { while k < 4 : k = k + 1 {
body_k u32 = 100 body_k u32 := 100
if body_k == 100 { if body_k == 100 {
total = total + 2 total = total + 2
} }
+12 -12
View File
@@ -19,7 +19,7 @@ basic func() i32 {
# Typed `T =`: the yield coerces to the annotation. # Typed `T =`: the yield coerces to the annotation.
typed func() i64 { typed func() i64 {
x i64 = { x i64 := {
yield 100 yield 100
} }
return x return x
@@ -29,7 +29,7 @@ typed func() i64 {
# local, but the captured value is unchanged. # local, but the captured value is unchanged.
spill func() i32 { spill func() i32 {
v :: { v :: {
n i32 = 5 n i32 := 5
defer n = 999 defer n = 999
yield n yield n
} }
@@ -38,7 +38,7 @@ spill func() i32 {
# Reassignment into an existing mutable local. # Reassignment into an existing mutable local.
reassign func() i32 { reassign func() i32 {
r i32 = 0 r i32 := 0
r = { r = {
yield 7 yield 7
} }
@@ -61,13 +61,13 @@ vif_untyped func(sel i32) i32 {
# Typed `T =`: every branch coerces to the annotation. # Typed `T =`: every branch coerces to the annotation.
vif_typed func(sel i32) i32 { vif_typed func(sel i32) i32 {
r i32 = if (sel == 0) { yield 100 } else { yield 200 } r i32 := if (sel == 0) { yield 100 } else { yield 200 }
return r return r
} }
# Assigned into an existing local. # Assigned into an existing local.
vif_reassign func(sel i32) i32 { vif_reassign func(sel i32) i32 {
r i32 = 0 r i32 := 0
r = if (sel == 0) { yield 7 } else { yield 9 } r = if (sel == 0) { yield 7 } else { yield 9 }
return r return r
} }
@@ -76,7 +76,7 @@ vif_reassign func(sel i32) i32 {
# the yield. # the yield.
vif_defer func() i32 { vif_defer func() i32 {
r :: if (true) { r :: if (true) {
n i32 = 5 n i32 := 5
defer n = 999 defer n = 999
yield n yield n
} else { } else {
@@ -123,7 +123,7 @@ loop_none func() i32 {
# Labeled `while` value loop (label follows the `: update` clause). # Labeled `while` value loop (label follows the `: update` clause).
loop_while func() i32 { loop_while func() i32 {
n i32 = 0 n i32 := 0
found :: while n < 100 : n += 1 blk: { found :: while n < 100 : n += 1 blk: {
if (n == 8) yield :blk n if (n == 8) yield :blk n
yield null yield null
@@ -199,7 +199,7 @@ lblock func(sel i32) i32 {
# the block's defer runs. # the block's defer runs.
lblock_defer func() i32 { lblock_defer func() i32 {
r :: blk: { r :: blk: {
n i32 = 5 n i32 := 5
defer n = 999 defer n = 999
if (true) yield :blk n if (true) yield :blk n
yield :blk 0 yield :blk 0
@@ -235,7 +235,7 @@ yield_outer func(target i32) i32 {
# Plain `break :outer` exits an outer loop from an inner loop. # Plain `break :outer` exits an outer loop from an inner loop.
break_outer func() i32 { break_outer func() i32 {
count i32 = 0 count i32 := 0
for 0..3 |a| outer: { for 0..3 |a| outer: {
for 0..3 |b| { for 0..3 |b| {
count += 1 count += 1
@@ -247,7 +247,7 @@ break_outer func() i32 {
# A labeled block *statement* (not a value source): `break :blk` exits it early. # A labeled block *statement* (not a value source): `break :blk` exits it early.
stmt_block func(early i32) i32 { stmt_block func(early i32) i32 {
x i32 = 0 x i32 := 0
blk: { blk: {
x = 1 x = 1
if (early == 1) break :blk if (early == 1) break :blk
@@ -259,7 +259,7 @@ stmt_block func(early i32) i32 {
# `break :search` escapes a nested loop and the block in one jump; the block's # `break :search` escapes a nested loop and the block in one jump; the block's
# defer still runs on the way out. # defer still runs on the way out.
stmt_block_escape func() i32 { stmt_block_escape func() i32 {
hits i32 = 0 hits i32 := 0
search: { search: {
defer hits += 1000 defer hits += 1000
for 0..10 |i| { for 0..10 |i| {
@@ -273,7 +273,7 @@ stmt_block_escape func() i32 {
# A labeled block can also be exited through an ordinary nested block. # A labeled block can also be exited through an ordinary nested block.
stmt_block_nested func() i32 { stmt_block_nested func() i32 {
hits i32 = 0 hits i32 := 0
outer: { outer: {
{ {
hits = 1 hits = 1
+1 -1
View File
@@ -28,7 +28,7 @@ reserve func($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
return return
} }
new_capacity usize = 8 new_capacity usize := 8
if list.capacity >= 8 { if list.capacity >= 8 {
half usize :: divtrunc!(list.capacity, 2) half usize :: divtrunc!(list.capacity, 2)
if list.capacity > maxval!(usize) - half { if list.capacity > maxval!(usize) - half {
+3 -3
View File
@@ -2,7 +2,7 @@ import "@std/mem"
import "@std/testing" import "@std/testing"
handles_append test { handles_append test {
list ArrayList(i32) = init(mem.c_allocator) list ArrayList(i32) := init(mem.c_allocator)
defer deinit(&list) defer deinit(&list)
try append(&list, 42) try append(&list, 42)
@@ -12,7 +12,7 @@ handles_append test {
} }
handles_clear test { handles_clear test {
list ArrayList(i32) = init(mem.c_allocator) list ArrayList(i32) := init(mem.c_allocator)
defer deinit(&list) defer deinit(&list)
try append(&list, 42) try append(&list, 42)
@@ -22,7 +22,7 @@ handles_clear test {
} }
handles_reserve test { handles_reserve test {
list ArrayList(i32) = init(mem.c_allocator) list ArrayList(i32) := init(mem.c_allocator)
defer deinit(&list) defer deinit(&list)
try reserve(&list, 10) try reserve(&list, 10)
+1 -1
View File
@@ -11,7 +11,7 @@ print func($format []u8, $Args type, args Args) void {
} }
hide write func(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError { hide write func(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
request usize = bytes.len request usize := bytes.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
request = maximum request = maximum
+1 -1
View File
@@ -14,7 +14,7 @@ init func(
$E, $V type, $E, $V type,
values meta.EnumFieldStruct(E, ?V, some!(null)), values meta.EnumFieldStruct(E, ?V, some!(null)),
) EnumMap(E, V) { ) EnumMap(E, V) {
map EnumMap(E, V) = undefined map EnumMap(E, V) := undefined
match typeinfo!(E) { match typeinfo!(E) {
.enum |info|: inline for info.fields |field, i| { .enum |info|: inline for info.fields |field, i| {
+1 -1
View File
@@ -8,7 +8,7 @@ TestEnum :: enum(u8) {
} }
handles_sparse_enum_get test { handles_sparse_enum_get test {
names EnumMap(TestEnum, []u8) = init({ names EnumMap(TestEnum, []u8) := init({
ident = "identifier", ident = "identifier",
int = "integer", int = "integer",
}) })
+5 -5
View File
@@ -59,7 +59,7 @@ get func(
if (map.count == 0) return null if (map.count == 0) return null
hash :: normalize(hash_key(key)) hash :: normalize(hash_key(key))
idx usize = hash & (map.entries.len - 1) idx usize := hash & (map.entries.len - 1)
while true { while true {
entry :: map.entries[idx] entry :: map.entries[idx]
@@ -98,7 +98,7 @@ put func(
if (entry.hash == 0) continue if (entry.hash == 0) continue
# find an empty slot # find an empty slot
idx usize = entry.hash & (new_entries.len - 1) idx usize := entry.hash & (new_entries.len - 1)
while new_entries[idx].hash != 0 { while new_entries[idx].hash != 0 {
idx = (idx + 1) & (new_entries.len - 1) idx = (idx + 1) & (new_entries.len - 1)
} }
@@ -112,7 +112,7 @@ put func(
# put new entry # put new entry
hash :: normalize(hash_key(key)) hash :: normalize(hash_key(key))
idx usize = hash & (map.entries.len - 1) idx usize := hash & (map.entries.len - 1)
while true { while true {
entry :: map.entries[idx] entry :: map.entries[idx]
@@ -144,8 +144,8 @@ hide normalize func(hash usize) usize {
#! FNV-1a hash implementation. #! FNV-1a hash implementation.
#! note: vulnerable to collision attacks. #! note: vulnerable to collision attacks.
hide str_hash func(key []u8) usize { hide str_hash func(key []u8) usize {
hash u32 = 2166136261 # offset basis hash u32 := 2166136261 # offset basis
prime u32 = 16777619 prime u32 := 16777619
for key |byte| { for key |byte| {
product u64 :: u64(hash xor u32(byte)) * prime product u64 :: u64(hash xor u32(byte)) * prime
+1 -1
View File
@@ -2,7 +2,7 @@ import "@std/mem"
import "@std/testing" import "@std/testing"
handles_put_and_get test { handles_put_and_get test {
map StringHashMap(u32) = init(mem.c_allocator) map StringHashMap(u32) := init(mem.c_allocator)
defer deinit(&map) defer deinit(&map)
try put(&map, "key", 42) try put(&map, "key", 42)
+3 -3
View File
@@ -47,7 +47,7 @@ writer func(file File) Writer {
} }
hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError { hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
request usize = buffer.len request usize := buffer.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
request = maximum request = maximum
@@ -69,7 +69,7 @@ hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize !
} }
hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError { hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
request usize = bytes.len request usize := bytes.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
request = maximum request = maximum
@@ -91,7 +91,7 @@ hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! Wri
} }
hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError { hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
flags c_int = c.O_RDONLY flags c_int := c.O_RDONLY
match mode { match mode {
.read_only: flags = c.O_RDONLY .read_only: flags = c.O_RDONLY
.write_only: flags = c.O_WRONLY .write_only: flags = c.O_WRONLY
+19 -19
View File
@@ -73,7 +73,7 @@ write func(output Writer, bytes []u8) usize ! WriteError {
} }
write_all func(output Writer, bytes []u8) void ! WriteError { write_all func(output Writer, bytes []u8) void ! WriteError {
offset usize = 0 offset usize := 0
while offset < bytes.len { while offset < bytes.len {
count usize :: write(output, bytes[offset..]) catch |err| { count usize :: write(output, bytes[offset..]) catch |err| {
return err return err
@@ -129,12 +129,12 @@ print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError
} }
hide write_integer_signed func(output Writer, value i64, base u64, uppercase bool) void ! WriteError { hide write_integer_signed func(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
buffer [65]mut u8 = undefined buffer [65]mut u8 := undefined
end usize = buffer.len end usize := buffer.len
current i64 = value current i64 := value
while true { while true {
digit_value i64 :: rem!(current, i64(base)) digit_value i64 :: rem!(current, i64(base))
digit u8 = 0 digit u8 := 0
if digit_value < 0 { if digit_value < 0 {
digit = u8(-digit_value) digit = u8(-digit_value)
} else { } else {
@@ -162,9 +162,9 @@ hide write_integer_signed func(output Writer, value i64, base u64, uppercase boo
} }
hide write_integer_unsigned func(output Writer, value u64, base u64, uppercase bool) void ! WriteError { hide write_integer_unsigned func(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
buffer [65]mut u8 = undefined buffer [65]mut u8 := undefined
end usize = buffer.len end usize := buffer.len
current u64 = value current u64 := value
while true { while true {
digit u8 :: u8(rem!(current, base)) digit u8 :: u8(rem!(current, base))
end -= 1 end -= 1
@@ -206,11 +206,11 @@ hide FormatToken :: struct {
} }
hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken { hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
tokens [N]mut FormatToken = undefined tokens [N]mut FormatToken := undefined
for (usize(0))..format.len |index| { for (usize(0))..format.len |index| {
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, field = ""} tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, field = ""}
} }
field_count usize = 0 field_count usize := 0
match typeinfo!(Args) { match typeinfo!(Args) {
.record |record|: { .record |record|: {
if !record.is_tuple { if !record.is_tuple {
@@ -221,10 +221,10 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
else: compile_error!("io.print arguments must be a tuple") else: compile_error!("io.print arguments must be a tuple")
} }
token_count usize = 0 token_count usize := 0
argument_count usize = 0 argument_count usize := 0
literal_start usize = 0 literal_start usize := 0
cursor usize = 0 cursor usize := 0
while cursor < format.len { while cursor < format.len {
byte :: format[cursor] byte :: format[cursor]
if byte == '{' { if byte == '{' {
@@ -243,8 +243,8 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
literal_start = cursor literal_start = cursor
continue continue
} }
kind FormatTokenKind = .default kind FormatTokenKind := .default
width usize = 2 width usize := 2
if next != '}' { if next != '}' {
if cursor + 2 >= format.len or format[cursor + 2] != '}' { if cursor + 2 >= format.len or format[cursor + 2] != '}' {
compile_error!("io.print format expects a one-character specifier") compile_error!("io.print format expects a one-character specifier")
@@ -353,8 +353,8 @@ hide write_integer func(output Writer, $T type, value T, base u64, uppercase boo
hide write_float func(output Writer, $T type, value T, scientific bool) void ! WriteError { hide write_float func(output Writer, $T type, value T, scientific bool) void ! WriteError {
match typeinfo!(T) { match typeinfo!(T) {
.float: { .float: {
buffer [64]mut u8 = undefined buffer [64]mut u8 := undefined
count c_int = 0 count c_int := 0
if sizeof!(T) == 4 { if sizeof!(T) == 4 {
if scientific { if scientific {
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value) count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value)
@@ -401,7 +401,7 @@ hide write_character func(output Writer, $T type, value T) void ! WriteError {
if minval!(T) < 0 or maxval!(T) > 255 { if minval!(T) < 0 or maxval!(T) > 255 {
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8") compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
} }
buffer [1]u8 = [u8(value)] buffer [1]u8 := [u8(value)]
try write_all(output, buffer[..]) try write_all(output, buffer[..])
} }
.distinct |backing|: if scalar_or_distinct_type(backing) { .distinct |backing|: if scalar_or_distinct_type(backing) {
+11 -11
View File
@@ -46,7 +46,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
return .out_of_memory return .out_of_memory
} }
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T)) memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T))
if memory |bytes| { if memory |bytes| {
pointer *mut T :: ptrcast!(T, bytes) pointer *mut T :: ptrcast!(T, bytes)
return pointer[..count] return pointer[..count]
@@ -75,13 +75,13 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
return .out_of_memory return .out_of_memory
} }
old_memory ?*mut u8 = null old_memory ?*mut u8 := null
old_size usize = 0 old_size usize := 0
if memory.len != 0 { if memory.len != 0 {
old_memory = ptrcast!(u8, memory.ptr) old_memory = ptrcast!(u8, memory.ptr)
old_size = memory.len * element_size old_size = memory.len * element_size
} }
resized ?*mut u8 = raw_realloc( resized ?*mut u8 := raw_realloc(
allocator, allocator,
old_memory, old_memory,
old_size, old_size,
@@ -119,15 +119,15 @@ empty func($T type) []mut T {
return empty_slice(T, 0) return empty_slice(T, 0)
} }
hide empty_storage [1]mut u64 = [0] hide empty_storage [1]mut u64 := [0]
hide malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption. hide malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
hide power_of_two func(value usize) bool { hide power_of_two func(value usize) bool {
if (value == 0) return false if (value == 0) return false
current usize = value current usize := value
while current > 1 { while current > 1 {
half usize = divtrunc!(current, 2) half usize := divtrunc!(current, 2)
if (half * 2 != current) return false if (half * 2 != current) return false
current = half current = half
} }
@@ -141,8 +141,8 @@ hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
return ptrcast!(u8, c.malloc(c_ulong(size))) return ptrcast!(u8, c.malloc(c_ulong(size)))
} }
memory [1]mut ?*mut anyopaque = [null] memory [1]mut ?*mut anyopaque := [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size)) status c_int := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if (status != 0) return null if (status != 0) return null
return ptrcast!(u8, memory[0]) return ptrcast!(u8, memory[0])
@@ -161,9 +161,9 @@ hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
} }
new_memory ?*mut u8 = c_alloc(null, new_size, alignment) new_memory ?*mut u8 := c_alloc(null, new_size, alignment)
if new_memory |new_bytes| { if new_memory |new_bytes| {
copy_size usize = old_size copy_size usize := old_size
if (new_size < copy_size) copy_size = new_size if (new_size < copy_size) copy_size = new_size
memcopy!(new_bytes[..copy_size], old_memory[..copy_size]) memcopy!(new_bytes[..copy_size], old_memory[..copy_size])
c.free(old_memory) c.free(old_memory)
+3 -3
View File
@@ -46,9 +46,9 @@ TypeInfo :: union(enum) {
EnumFieldStruct func($E, $Field type, $default ?Field) type { EnumFieldStruct func($E, $Field type, $default ?Field) type {
match typeinfo!(E) { match typeinfo!(E) {
.enum |info|: { .enum |info|: {
names [info.fields.len]mut []u8 = undefined names [info.fields.len]mut []u8 := undefined
field_types [info.fields.len]mut type = undefined field_types [info.fields.len]mut type := undefined
defaults [info.fields.len]mut ?Field = undefined defaults [info.fields.len]mut ?Field := undefined
inline for info.fields |field, index| { inline for info.fields |field, index| {
names[index] = field.name names[index] = field.name
field_types[index] = Field field_types[index] = Field
+2 -2
View File
@@ -42,7 +42,7 @@ distinct_reflection_exposes_immediate_backing test {
enum_field_struct_defaults test { enum_field_struct_defaults test {
names TestNames = { names TestNames := {
ident = "identifier", ident = "identifier",
int = "integer", int = "integer",
} }
@@ -60,7 +60,7 @@ enum_field_struct_defaults test {
try testing.expect(false) try testing.expect(false)
} }
empty TestNames = {} empty TestNames := {}
if field!(empty, "ident") |_| { if field!(empty, "ident") |_| {
try testing.expect(false) try testing.expect(false)
} }
+8 -8
View File
@@ -19,8 +19,8 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
compile_error!("static string map has too many entries") compile_error!("static string map has too many entries")
} }
keys [N]mut []u8 = undefined keys [N]mut []u8 := undefined
values [N]mut V = undefined values [N]mut V := undefined
# assert no duplicate keys # assert no duplicate keys
for entries |entry, i| { for entries |entry, i| {
@@ -37,7 +37,7 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
} }
if N == 0 { if N == 0 {
len_indexes [0]u32 = undefined len_indexes [0]u32 := undefined
return StaticStringMap(V){ return StaticStringMap(V){
keys = keys[..], keys = keys[..],
values = values[..], values = values[..],
@@ -51,7 +51,7 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
for 1..N |i| { for 1..N |i| {
key :: keys[i] key :: keys[i]
value :: values[i] value :: values[i]
j usize = i j usize := i
while j > 0 and keys[j - 1].len > key.len : j -= 1 { while j > 0 and keys[j - 1].len > key.len : j -= 1 {
keys[j] = keys[j - 1] keys[j] = keys[j - 1]
values[j] = values[j - 1] values[j] = values[j - 1]
@@ -62,8 +62,8 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
min_len u32 :: u32(keys[0].len) min_len u32 :: u32(keys[0].len)
max_len u32 :: u32(keys[N - 1].len) max_len u32 :: u32(keys[N - 1].len)
len_indexes [usize(max_len) + 1]mut u32 = undefined len_indexes [usize(max_len) + 1]mut u32 := undefined
entry_index usize = 0 entry_index usize := 0
for 0..=usize(max_len) |length| { for 0..=usize(max_len) |length| {
while entry_index < N and keys[entry_index].len < length : entry_index += 1 {} while entry_index < N and keys[entry_index].len < length : entry_index += 1 {}
len_indexes[length] = u32(entry_index) len_indexes[length] = u32(entry_index)
@@ -81,10 +81,10 @@ init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
get func($V type, map @StaticStringMap(V), key []u8) ?V { get func($V type, map @StaticStringMap(V), key []u8) ?V {
if (map.keys.len == 0 or key.len > maxval!(u32)) return null if (map.keys.len == 0 or key.len > maxval!(u32)) return null
length u32 = u32(key.len) length u32 := u32(key.len)
if (length < map.min_len or length > map.max_len) return null if (length < map.min_len or length > map.max_len) return null
idx usize = usize(map.len_indexes[usize(length)]) idx usize := usize(map.len_indexes[usize(length)])
while idx < map.keys.len : idx += 1 { while idx < map.keys.len : idx += 1 {
candidate :: map.keys[idx] candidate :: map.keys[idx]
if (candidate.len != key.len) return null if (candidate.len != key.len) return null
+11 -11
View File
@@ -77,8 +77,8 @@ read_command func() Command {
if IsMouseButtonPressed(MOUSE_BUTTON_LEFT) return .spawn{ GetMousePosition() } if IsMouseButtonPressed(MOUSE_BUTTON_LEFT) return .spawn{ GetMousePosition() }
if IsKeyPressed(KEY_SPACE) return .clear if IsKeyPressed(KEY_SPACE) return .clear
fx f32 = 0.0 fx f32 := 0.0
fy f32 = 0.0 fy f32 := 0.0
if IsKeyDown(KEY_A) fx -= FORCE if IsKeyDown(KEY_A) fx -= FORCE
if IsKeyDown(KEY_D) fx += FORCE if IsKeyDown(KEY_D) fx += FORCE
if IsKeyDown(KEY_W) fy -= FORCE if IsKeyDown(KEY_W) fy -= FORCE
@@ -128,14 +128,14 @@ step func(b @mut Ball) void {
draw_ball func(b @Ball, highlight bool) void { draw_ball func(b @Ball, highlight bool) void {
col :: color_for(b.kind) col :: color_for(b.kind)
center Vector2 = Vector2{ x = b.x, y = b.y } center Vector2 := Vector2{ x = b.x, y = b.y }
match b.kind { match b.kind {
.circle: DrawCircleV(center, b.radius, col) .circle: DrawCircleV(center, b.radius, col)
.square: DrawPoly(center, 4, b.radius, 45.0, col) .square: DrawPoly(center, 4, b.radius, 45.0, col)
.triangle: DrawPoly(center, 3, b.radius, 0.0, col) .triangle: DrawPoly(center, 3, b.radius, 0.0, col)
} }
if highlight { if highlight {
ring Color = Color{ r = 250, g = 245, b = 200, a = 255 } ring Color := Color{ r = 250, g = 245, b = 200, a = 255 }
DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring) DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring)
} }
} }
@@ -150,11 +150,11 @@ main func() i32 {
`[click] spawn a shape [WASD/arrows] blow wind `[click] spawn a shape [WASD/arrows] blow wind
`[space] clear `[space] clear
balls [CAP]mut Ball = undefined balls [CAP]mut Ball := undefined
count usize = 0 # number of live balls, in slots 0..count count usize := 0 # number of live balls, in slots 0..count
kc Kind = .circle # next kind to spawn kc Kind := .circle # next kind to spawn
spin f32 = 1.0 # rotates spawn velocity for variety spin f32 := 1.0 # rotates spawn velocity for variety
at_cap bool = false # show the "at capacity" banner at_cap bool := false # show the "at capacity" banner
bg :: Color{ r = 24, g = 26, b = 34, a = 255 } bg :: Color{ r = 24, g = 26, b = 34, a = 255 }
text :: Color{ r = 225, g = 225, b = 230, a = 255 } text :: Color{ r = 225, g = 225, b = 230, a = 255 }
@@ -209,7 +209,7 @@ main func() i32 {
# --- which shape is under the cursor? (optional via a value-loop) --- # --- which shape is under the cursor? (optional via a value-loop) ---
mouse :: GetMousePosition() mouse :: GetMousePosition()
sel :: for 0..(count) |i| blk: { sel :: for 0..(count) |i| blk: {
c Vector2 = Vector2{ x = balls[i].x, y = balls[i].y } c Vector2 := Vector2{ x = balls[i].x, y = balls[i].y }
if CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :blk i if CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :blk i
yield null yield null
} }
@@ -220,7 +220,7 @@ main func() i32 {
for (&balls) |@b, i| { for (&balls) |@b, i| {
if (i >= count) break if (i >= count) break
hot bool = false hot bool := false
if sel |s| { if sel |s| {
if (s == i) hot = true # true only for the hovered ball if (s == i) hot = true # true only for the hovered ball
} }
+3 -3
View File
@@ -48,7 +48,7 @@ hide append_token func(tokens @mut std.ArrayList(Token), kind Kind, start, end u
} }
lex func(source []u8, tokens @mut std.ArrayList(Token)) void ! mem.AllocError { lex func(source []u8, tokens @mut std.ArrayList(Token)) void ! mem.AllocError {
cursor usize = 0 cursor usize := 0
while cursor < source.len { while cursor < source.len {
value u8 :: source[cursor] value u8 :: source[cursor]
if value == ' ' or value == '\t' or value == '\r' { if value == ' ' or value == '\t' or value == '\r' {
@@ -112,7 +112,7 @@ hide print_token func(source []u8, token Token) void {
_ = c.printf("%-11s", kind_name(token.kind)) _ = c.printf("%-11s", kind_name(token.kind))
if token.length != 0 { if token.length != 0 {
_ = c.printf(" `") _ = c.printf(" `")
i usize = 0 i usize := 0
while i < token.length : i += 1 { while i < token.length : i += 1 {
value u8 :: source[token.start + i] value u8 :: source[token.start + i]
if value == '\n' { if value == '\n' {
@@ -132,7 +132,7 @@ main func() i32 {
` hello() ` hello()
`} `}
tokens std.ArrayList(Token) = arraylist.init(mem.c_allocator) tokens std.ArrayList(Token) := arraylist.init(mem.c_allocator)
defer arraylist.deinit(&tokens) defer arraylist.deinit(&tokens)
lex(source, &tokens) catch |_| { lex(source, &tokens) catch |_| {
+1 -1
View File
@@ -28,7 +28,7 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
return return
} }
new_capacity usize = 8 new_capacity usize := 8
if list.capacity >= 8 { if list.capacity >= 8 {
half usize :: divtrunc!(list.capacity, 2) half usize :: divtrunc!(list.capacity, 2)
if list.capacity > maxval!(usize) - half { if list.capacity > maxval!(usize) - half {
+3 -3
View File
@@ -61,7 +61,7 @@ write func(writer Writer, bytes []u8) usize ! WriteError {
} }
write_all func(writer Writer, bytes []u8) void ! WriteError { write_all func(writer Writer, bytes []u8) void ! WriteError {
offset usize = 0 offset usize := 0
while offset < bytes.len { while offset < bytes.len {
count usize :: write(writer, bytes[offset..]) catch |err| { count usize :: write(writer, bytes[offset..]) catch |err| {
return err return err
@@ -75,7 +75,7 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
} }
hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError { hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
request usize = buffer.len request usize := buffer.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
request = maximum request = maximum
@@ -93,7 +93,7 @@ hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usi
hide system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError { hide system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
fd c_int :: c_int(stream) fd c_int :: c_int(stream)
request usize = bytes.len request usize := bytes.len
maximum usize :: usize(maxval!(c_long)) maximum usize :: usize(maxval!(c_long))
if request > maximum { if request > maximum {
request = maximum request = maximum
+13 -13
View File
@@ -32,7 +32,7 @@ eql func($T type, left, right []T) bool {
return false return false
} }
i usize = 0 i usize := 0
while i < left.len : i += 1 { while i < left.len : i += 1 {
if left[i] != right[i] { if left[i] != right[i] {
return false return false
@@ -41,7 +41,7 @@ eql func($T type, left, right []T) bool {
return true return true
} }
hide empty_storage [1]mut u64 = [0] hide empty_storage [1]mut u64 := [0]
hide empty_slice func($T type, count usize) []mut T { hide empty_slice func($T type, count usize) []mut T {
pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) pointer *mut T :: ptrcast!(T, (&empty_storage).ptr)
@@ -65,7 +65,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
return .out_of_memory return .out_of_memory
} }
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T)) memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T))
if memory |bytes| { if memory |bytes| {
pointer *mut T :: ptrcast!(T, bytes) pointer *mut T :: ptrcast!(T, bytes)
return pointer[..count] return pointer[..count]
@@ -90,13 +90,13 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
return .out_of_memory return .out_of_memory
} }
old_memory ?*mut u8 = null old_memory ?*mut u8 := null
old_size usize = 0 old_size usize := 0
if memory.len != 0 { if memory.len != 0 {
old_memory = ptrcast!(u8, memory.ptr) old_memory = ptrcast!(u8, memory.ptr)
old_size = memory.len * element_size old_size = memory.len * element_size
} }
resized ?*mut u8 = raw_realloc( resized ?*mut u8 := raw_realloc(
allocator, allocator,
old_memory, old_memory,
old_size, old_size,
@@ -123,9 +123,9 @@ hide power_of_two func(value usize) bool {
return false return false
} }
current usize = value current usize := value
while current > 1 { while current > 1 {
half usize = divtrunc!(current, 2) half usize := divtrunc!(current, 2)
if half * 2 != current { if half * 2 != current {
return false return false
} }
@@ -144,8 +144,8 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
return ptrcast!(u8, c.malloc(c_ulong(size))) return ptrcast!(u8, c.malloc(c_ulong(size)))
} }
memory [1]mut ?*mut anyopaque = [null] memory [1]mut ?*mut anyopaque := [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size)) status c_int := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 { if status != 0 {
return null return null
} }
@@ -168,13 +168,13 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
} }
new_memory ?*mut u8 = c_alloc(null, new_size, alignment) new_memory ?*mut u8 := c_alloc(null, new_size, alignment)
if new_memory |new_bytes| { if new_memory |new_bytes| {
copy_size usize = old_size copy_size usize := old_size
if new_size < copy_size { if new_size < copy_size {
copy_size = new_size copy_size = new_size
} }
i usize = 0 i usize := 0
while i < copy_size : i += 1 { while i < copy_size : i += 1 {
new_bytes[i] = old_memory[i] new_bytes[i] = old_memory[i]
} }
+4 -4
View File
@@ -25,7 +25,7 @@ tier_bonus func(tier Tier) i32 {
} }
projected_score func(player Player) i32 { projected_score func(player Player) i32 {
total i32 = player.score total i32 := player.score
total += tier_bonus(player.tier) total += tier_bonus(player.tier)
if player.active and player.streak > 2 { if player.active and player.streak > 2 {
total += player.streak * 3 total += player.streak * 3
@@ -42,8 +42,8 @@ apply_decay func(players []mut Player) void {
} }
best_player func(players []mut Player) ?@mut Player { best_player func(players []mut Player) ?@mut Player {
best ?@mut Player = null best ?@mut Player := null
best_score i32 = 0 best_score i32 := 0
for players |@player| { for players |@player| {
score :: projected_score(player^) score :: projected_score(player^)
@@ -62,7 +62,7 @@ best_player func(players []mut Player) ?@mut Player {
} }
main func() i32 { main func() i32 {
players [4]mut Player = [ players [4]mut Player := [
Player { id = PlayerID(1), name = "Ada", tier = .gold, score = 41, streak = 4, active = true }, Player { id = PlayerID(1), name = "Ada", tier = .gold, score = 41, streak = 4, active = true },
Player { id = PlayerID(2), name = "Ken", tier = .silver, score = 56, streak = 1, active = true }, Player { id = PlayerID(2), name = "Ken", tier = .silver, score = 56, streak = 1, active = true },
Player { id = PlayerID(3), name = "Edsger", tier = .bronze, score = 64, streak = 0, active = false }, Player { id = PlayerID(3), name = "Edsger", tier = .bronze, score = 64, streak = 0, active = false },
+9 -9
View File
@@ -27,7 +27,7 @@ raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize)
allocator.vtable.free(allocator.context, memory, size, alignment) allocator.vtable.free(allocator.context, memory, size, alignment)
} }
hide empty_storage [1]mut u64 = [0] hide empty_storage [1]mut u64 := [0]
hide empty_slice func($T type, count usize) []mut T { hide empty_slice func($T type, count usize) []mut T {
pointer *mut T :: ptrcast!(T, (&empty_storage).ptr) pointer *mut T :: ptrcast!(T, (&empty_storage).ptr)
@@ -47,7 +47,7 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
return .out_of_memory return .out_of_memory
} }
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T)) memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T))
if memory |bytes| { if memory |bytes| {
pointer *mut T :: ptrcast!(T, bytes) pointer *mut T :: ptrcast!(T, bytes)
return pointer[..count] return pointer[..count]
@@ -68,9 +68,9 @@ hide power_of_two func(value usize) bool {
return false return false
} }
current usize = value current usize := value
while current > 1 { while current > 1 {
half usize = current / 2 half usize := current / 2
if half * 2 != current { if half * 2 != current {
return false return false
} }
@@ -89,8 +89,8 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
return ptrcast!(u8, c.malloc(c_ulong(size))) return ptrcast!(u8, c.malloc(c_ulong(size)))
} }
memory [1]mut ?*mut anyopaque = [null] memory [1]mut ?*mut anyopaque := [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size)) status c_int := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 { if status != 0 {
return null return null
} }
@@ -113,13 +113,13 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size))) return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
} }
new_memory ?*mut u8 = c_alloc(null, new_size, alignment) new_memory ?*mut u8 := c_alloc(null, new_size, alignment)
if new_memory |new_bytes| { if new_memory |new_bytes| {
copy_size usize = old_size copy_size usize := old_size
if new_size < copy_size { if new_size < copy_size {
copy_size = new_size copy_size = new_size
} }
i usize = 0 i usize := 0
while i < copy_size : i += 1 { while i < copy_size : i += 1 {
new_bytes[i] = old_memory[i] new_bytes[i] = old_memory[i]
} }