reflection foundation, tuples, debug.print
This commit is contained in:
+9
-6
@@ -150,11 +150,12 @@ as `math.divfloor(a, b)` resolve to ordinary functions.
|
|||||||
- demand-monomorphized Brolang and C-ABI functions
|
- demand-monomorphized Brolang and C-ABI functions
|
||||||
- integer comptime value parameters such as `make_array func($N usize) [N]u8`, specialized by value and omitted from the runtime ABI
|
- integer comptime value parameters such as `make_array func($N usize) [N]u8`, specialized by value and omitted from the runtime ABI
|
||||||
- explicit comptime type parameters such as `max func($T type, a, b T) T`, specialized by type and omitted from the runtime ABI
|
- explicit comptime type parameters such as `max func($T type, a, b T) T`, specialized by type and omitted from the runtime ABI
|
||||||
- leading comptime type/integer parameters may be omitted when uniquely recoverable from runtime argument types or the immediate expected result; explicit calls remain valid
|
- comptime type/integer/string parameters may appear anywhere, are erased from the runtime ABI, and may be omitted when uniquely recoverable from runtime arguments or the immediate expected result; `_` is an explicit inference hole
|
||||||
- comptime parameters must form one leading prefix before all runtime parameters
|
|
||||||
- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`
|
- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`
|
||||||
- comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
- comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
||||||
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected
|
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected
|
||||||
|
- tuple types are unnamed-field structs (`struct { i32, []u8 }`), tuple values use `{1, "bro"}` / `{1,}` / `{}`, and fields use canonical numeric names such as `.0`
|
||||||
|
- `typeinfo!`, `field!`, `compile_error!`, and semantic `inline for` provide compile-time record reflection and heterogeneous static expansion without runtime metadata; reflected aggregates remain persistent compile-time values, and inline-loop `break` / `continue` must be selected entirely at comptime
|
||||||
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
||||||
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
||||||
- native function pointer values and types with `@func(...) R`, fallible `@func(...) R ! E`, optional `?@func(...) R`, and non-variadic native indirect calls
|
- native function pointer values and types with `@func(...) R`, fallible `@func(...) R ! E`, optional `?@func(...) R`, and non-variadic native indirect calls
|
||||||
@@ -173,7 +174,9 @@ as `math.divfloor(a, b)` resolve to ordinary functions.
|
|||||||
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
||||||
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
||||||
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
||||||
- `std/io` explicit `Io` capabilities, `Reader`/`Writer` stream values, one-shot `read`/`write`, and allocation-free `write_all`
|
- `std/io` explicit `Io` capabilities, `Reader`/`Writer` stream values, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting currently supports sequential `{s}` / `{d}` plus `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
||||||
|
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
||||||
|
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
||||||
|
|
||||||
### compiler behavior
|
### compiler behavior
|
||||||
|
|
||||||
@@ -181,18 +184,18 @@ as `math.divfloor(a, b)` resolve to ordinary functions.
|
|||||||
- lazy semantic checking of demanded function specializations
|
- lazy semantic checking of demanded function specializations
|
||||||
- static, eager runtime, mutable runtime, and deferred problematic globals with cycle diagnostics
|
- static, eager runtime, mutable runtime, and deferred problematic globals with cycle diagnostics
|
||||||
- demand-driven LLVM declarations for referenced foreign functions
|
- demand-driven LLVM declarations for referenced foreign functions
|
||||||
- root `main` may be parameterless or accept the canonical `@std/io Io`; the injected form is called through a synthesized no-argument C entry point
|
- root `main` may be parameterless or accept canonical `@std/process Init`; the generated C entry point obtains the hidden system I/O provider and constructs the init value
|
||||||
- replaceable dynamically loaded libclang C-import backend
|
- replaceable dynamically loaded libclang C-import backend
|
||||||
- C-header import caching by canonical path, target, include paths, and defines
|
- C-header import caching by canonical path, target, include paths, and defines
|
||||||
|
|
||||||
## PLANNED / DEFERRED
|
## PLANNED / DEFERRED
|
||||||
|
|
||||||
- aggregate comptime parameters and stable aggregate specialization keys
|
- aggregate comptime parameters and stable aggregate specialization keys
|
||||||
- tuples and native Brolang variadic functions
|
- native Brolang variadic functions
|
||||||
- exporting Brolang functions to C and broader target-specific C ABI lowering
|
- exporting Brolang functions to C and broader target-specific C ABI lowering
|
||||||
- non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments
|
- non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments
|
||||||
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
|
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
|
||||||
- recursive type factories, type reflection, and type-producing unions/enums
|
- recursive type factories, reflection payloads beyond records, and type-producing unions/enums
|
||||||
- broader Zig-style pointer/result casts beyond V1 `ptrcast!(T, ptr)`
|
- broader Zig-style pointer/result casts beyond V1 `ptrcast!(T, ptr)`
|
||||||
- sum-type ABI/layout polish, including dynamic tag-width shrinking, all-void channel collapse, and cross-module global-id determinism
|
- sum-type ABI/layout polish, including dynamic tag-width shrinking, all-void channel collapse, and cross-module global-id determinism
|
||||||
- backed/C enum composition and must-consume fallible linting
|
- backed/C enum composition and must-consume fallible linting
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ pair that implementation with a stream; `main func() ...` remains valid.
|
|||||||
|
|
||||||
```bro
|
```bro
|
||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
main func(system io.Io) void {
|
main func(init process.Init) void {
|
||||||
io.write_all(io.Writer {
|
io.print(io.Writer {
|
||||||
impl = system,
|
impl = init.io,
|
||||||
stream = .stdout,
|
stream = .stdout,
|
||||||
}, "hello\n") catch |_| {
|
}, "hello {s} {d}\n", {"bro", 37}) catch |_| {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +203,7 @@ Current prototype features:
|
|||||||
- Plain imported C structs/unions, fixed arrays, and C function pointer typedefs, including keyed literals, field access, callbacks, and Apple Silicon by-value ABI lowering
|
- Plain imported C structs/unions, fixed arrays, and C function pointer typedefs, including keyed literals, field access, callbacks, and Apple Silicon by-value ABI lowering
|
||||||
- Qualified imported globals and functions with package-aware symbol mangling
|
- Qualified imported globals and functions with package-aware symbol mangling
|
||||||
- Demand-monomorphized Brolang and C-ABI functions
|
- Demand-monomorphized Brolang and C-ABI functions
|
||||||
- Integer and type comptime parameters (`func($N usize) [N]u8`, `func($T type, value T) T`) specialized by explicit or uniquely inferred leading comptime arguments
|
- Integer, type, and immutable byte-string comptime parameters may be interleaved with runtime parameters, are erased from the ABI, and specialize from explicit or uniquely inferred arguments
|
||||||
- Forced typed comptime expressions (`$sum(1, 2)`, `$Point { x = 1, y = 2 }`) and comptime value blocks (`${ yield 4 }`)
|
- Forced typed comptime expressions (`$sum(1, 2)`, `$Point { x = 1, y = 2 }`) and comptime value blocks (`${ yield 4 }`)
|
||||||
- Zig-style comptime type factories returning anonymous native structs (`Box func($T type) type`, used as `Box(i32)`)
|
- Zig-style comptime type factories returning anonymous native structs (`Box func($T type) type`, used as `Box(i32)`)
|
||||||
- Comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`/`errdefer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
- Comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`/`errdefer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
||||||
|
|||||||
@@ -777,17 +777,17 @@
|
|||||||
- keep functions package-scoped and keep the explicit form valid; this preserves simple name
|
- keep functions package-scoped and keep the explicit form valid; this preserves simple name
|
||||||
resolution and gives ambiguous calls an escape hatch
|
resolution and gives ambiguous calls an escape hatch
|
||||||
|
|
||||||
31.5. inferred leading comptime parameters (implemented)
|
31.5. inferred comptime parameters (implemented)
|
||||||
- a native call may omit its complete leading `$T type` / integer comptime prefix when every
|
- comptime parameters may appear anywhere and are erased while preserving runtime parameter order
|
||||||
|
- a native call may omit `$T type`, integer, or immutable byte-string comptime arguments when every
|
||||||
value is uniquely recoverable from runtime argument types and/or the immediate expected result
|
value is uniquely recoverable from runtime argument types and/or the immediate expected result
|
||||||
- inference structurally matches direct type parameters, pointers/slices/arrays/optionals/
|
- inference structurally matches direct type parameters, pointers/slices/arrays/optionals/
|
||||||
fallibles/functions, direct array counts, and canonical generated type-factory provenance;
|
fallibles/functions, direct array counts, and canonical generated type-factory provenance;
|
||||||
forwarding/non-invertible factories keep the explicit spelling
|
forwarding/non-invertible factories keep the explicit spelling
|
||||||
- concrete evidence is exact; contextual numeric constants are weak evidence and are rebuilt with
|
- concrete evidence is exact; contextual numeric constants are weak evidence and are rebuilt with
|
||||||
the resolved parameter type before ordinary coercion
|
the resolved parameter type before ordinary coercion
|
||||||
- calls are all-explicit or all-inferred (no partial prefix omission); unconstrained/conflicting
|
- `_` explicitly leaves one comptime argument to inference; exactly one complete argument mapping
|
||||||
values diagnose with the explicit call as the escape hatch
|
must succeed, with missing and ambiguous mappings diagnosed
|
||||||
- comptime parameters must form one leading prefix before every runtime parameter
|
|
||||||
- the existing specialization/HIR/LLVM ABI is unchanged; `std/mem` and `std/arraylist` now use the
|
- the existing specialization/HIR/LLVM ABI is unchanged; `std/mem` and `std/arraylist` now use the
|
||||||
inferred form where their arguments or result provide enough information
|
inferred form where their arguments or result provide enough information
|
||||||
|
|
||||||
@@ -809,8 +809,8 @@
|
|||||||
- migrated `std/mem`, `std/arraylist`, and the compound-assignment example to `divtrunc!`
|
- migrated `std/mem`, `std/arraylist`, and the compound-assignment example to `divtrunc!`
|
||||||
|
|
||||||
33. explicit I/O provider (implemented)
|
33. explicit I/O provider (implemented)
|
||||||
- `main` may take one canonical `@std/io Io`; parameterless entry points remain valid
|
- `main` may take one canonical `@std/process Init`; parameterless entry points remain valid
|
||||||
- the compiler supplies a `hide system` macOS provider through an external no-argument C wrapper
|
- the compiler supplies a `hide system` macOS provider and constructs `Init` in the external C wrapper
|
||||||
- readers and writers pair an explicit provider with `stdin`, `stdout`, or `stderr`
|
- readers and writers pair an explicit provider with `stdin`, `stdout`, or `stderr`
|
||||||
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
||||||
- the system provider uses unbuffered libc `read`/`write`, retries interruption, and allocates nothing
|
- the system provider uses unbuffered libc `read`/`write`, retries interruption, and allocates nothing
|
||||||
@@ -847,9 +847,22 @@
|
|||||||
- `hide` is reserved for named top-level declarations and is rejected on imports, locals,
|
- `hide` is reserved for named top-level declarations and is rejected on imports, locals,
|
||||||
parameters, fields, and anonymous declarations
|
parameters, fields, and anonymous declarations
|
||||||
|
|
||||||
37. add a debug package in `std` that provides debugging utilities
|
37. tuples, reflection, process init, and printing (implemented)
|
||||||
- add `debug.print` function making use of `std/io` to print values to the console
|
- tuples are unnamed-field structs with structural anonymous values, nominal named declarations,
|
||||||
- this may either require native variadic arguments or a tuple value to like zig's approach (consider pros and cons)
|
brace literals, numeric fields, and no runtime metadata
|
||||||
|
- `@std/meta`, `typeinfo!`, `field!`, `compile_error!`, specialization-time branches, and semantic
|
||||||
|
`inline for` use checker-owned persistent compile-time values for aggregate-first reflection and
|
||||||
|
heterogeneous static expansion; inline-loop control is recursively resolved at comptime
|
||||||
|
- interleaved comptime parameters use semantic candidate resolution, immutable byte values specialize
|
||||||
|
by contents, and all comptime parameters remain erased from the runtime ABI
|
||||||
|
- `io.print(writer, format, args)` validates and expands `{s}` / `{d}` formatting at comptime,
|
||||||
|
is implemented in ordinary `@std/io` code, performs no allocation or runtime parsing, and
|
||||||
|
propagates the first write error
|
||||||
|
- `debug.print` reuses formatting through an independent stderr backend, ignores failures, and adds
|
||||||
|
no newline
|
||||||
|
- entrypoint validation accepts only parameterless `main` or canonical `main(process.Init)`, validates
|
||||||
|
the hidden `@std/io.system` provider, and injects the provider through the generated C entrypoint;
|
||||||
|
native variadics and additional startup data remain deferred
|
||||||
|
|
||||||
38. place every intrinsic behind direct unqualified `name!(...)` syntax, freeing the bare names for
|
38. place every intrinsic behind direct unqualified `name!(...)` syntax, freeing the bare names for
|
||||||
user functions (implemented)
|
user functions (implemented)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ Expr_Kind :: enum u8 {
|
|||||||
Array,
|
Array,
|
||||||
None,
|
None,
|
||||||
Undefined,
|
Undefined,
|
||||||
|
Inference_Hole,
|
||||||
Type,
|
Type,
|
||||||
Name,
|
Name,
|
||||||
Enum_Literal,
|
Enum_Literal,
|
||||||
@@ -121,6 +122,7 @@ Expr :: struct {
|
|||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
parenthesized: bool,
|
parenthesized: bool,
|
||||||
intrinsic: bool,
|
intrinsic: bool,
|
||||||
|
tuple: bool,
|
||||||
kind: Expr_Kind,
|
kind: Expr_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +172,7 @@ Stmt :: struct {
|
|||||||
immutable: bool,
|
immutable: bool,
|
||||||
value_control_flow: bool,
|
value_control_flow: bool,
|
||||||
pointer_capture: bool,
|
pointer_capture: bool,
|
||||||
|
inline: bool,
|
||||||
error_only: bool,
|
error_only: bool,
|
||||||
// Assignments store the lvalue in `target`, the right-hand side in `expr`,
|
// Assignments store the lvalue in `target`, the right-hand side in `expr`,
|
||||||
// and the source operator in `assignment_op`. `Set` is ordinary `=`;
|
// and the source operator in `assignment_op`. `Set` is ordinary `=`;
|
||||||
|
|||||||
+1634
-208
File diff suppressed because it is too large
Load Diff
@@ -7,20 +7,24 @@ import "../symbol"
|
|||||||
import "../types"
|
import "../types"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
COMPTIME_EVAL_QUOTA :: 100_000
|
COMPTIME_EVAL_QUOTA :: 100_000
|
||||||
|
|
||||||
Comptime_Value_Kind :: enum u8 {
|
Comptime_Value_Kind :: enum u8 {
|
||||||
Integer,
|
Integer,
|
||||||
Type,
|
Type,
|
||||||
|
String,
|
||||||
}
|
}
|
||||||
|
|
||||||
Comptime_Value :: struct {
|
Comptime_Value :: struct {
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
value: i128,
|
value: i128,
|
||||||
|
text: string,
|
||||||
kind: Comptime_Value_Kind,
|
kind: Comptime_Value_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +61,15 @@ current_comptime_value :: proc(checker: ^Checker, name: symbol.Id) -> (Comptime_
|
|||||||
return find_comptime_value(checker.current_comptime_values, name)
|
return find_comptime_value(checker.current_comptime_values, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_static_binding :: proc(checker: ^Checker, name: symbol.Id) -> (Static_Binding, bool) {
|
||||||
|
for index := len(checker.static_bindings)-1; index >= 0; index -= 1 {
|
||||||
|
if checker.static_bindings[index].name == name {
|
||||||
|
return checker.static_bindings[index], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
current_comptime_type :: proc(checker: ^Checker, name: symbol.Id) -> (types.Type, bool) {
|
current_comptime_type :: proc(checker: ^Checker, name: symbol.Id) -> (types.Type, bool) {
|
||||||
if value, ok := current_comptime_value(checker, name); ok && value.kind == .Type {
|
if value, ok := current_comptime_value(checker, name); ok && value.kind == .Type {
|
||||||
return value.type, true
|
return value.type, true
|
||||||
@@ -71,7 +84,8 @@ comptime_values_equal :: proc(left, right: []Comptime_Value) -> bool {
|
|||||||
for value, index in left {
|
for value, index in left {
|
||||||
other := right[index]
|
other := right[index]
|
||||||
if value.name != other.name || value.kind != other.kind || !types.equal(value.type, other.type) ||
|
if value.name != other.name || value.kind != other.kind || !types.equal(value.type, other.type) ||
|
||||||
(value.kind == .Integer && value.value != other.value) {
|
(value.kind == .Integer && value.value != other.value) ||
|
||||||
|
(value.kind == .String && value.text != other.text) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,8 +351,22 @@ ct_state_make :: proc(
|
|||||||
} else if value.kind == .Type {
|
} else if value.kind == .Type {
|
||||||
id := ct_add_value(&state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)})
|
id := ct_add_value(&state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)})
|
||||||
ct_bind_value(&state, value.name, types.INVALID, id, false)
|
ct_bind_value(&state, value.name, types.INVALID, id, false)
|
||||||
|
} else if value.kind == .String {
|
||||||
|
string_id := u64(0)
|
||||||
|
for text, index in checker.ast_module.strings {
|
||||||
|
if text == value.text {
|
||||||
|
string_id = u64(index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id := ct_add_value(&state, Ct_Value{kind=.String, type=value.type, index=string_id})
|
||||||
|
ct_bind_value(&state, value.name, value.type, id, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for binding in checker.static_bindings {
|
||||||
|
id := ct_clone_graph(&state, &checker.static_state, binding.value)
|
||||||
|
ct_bind_value(&state, binding.name, binding.type, id, false)
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,6 +571,12 @@ ct_coerce_value :: proc(state: ^Ct_State, id: Ct_Value_Id, expected: types.Type,
|
|||||||
return id, true
|
return id, true
|
||||||
}
|
}
|
||||||
store := &state.checker.module.types
|
store := &state.checker.module.types
|
||||||
|
if value.kind == .String {
|
||||||
|
if item, ok := types.node(store, expected); ok && item.kind == .Slice && !item.mutable && item.child == types.U8 {
|
||||||
|
value.type = expected
|
||||||
|
return ct_add_value(state, value), true
|
||||||
|
}
|
||||||
|
}
|
||||||
if value.kind == .Pointer && types.can_weaken_pointer(value.type, expected, store) {
|
if value.kind == .Pointer && types.can_weaken_pointer(value.type, expected, store) {
|
||||||
value.type = expected
|
value.type = expected
|
||||||
return ct_add_value(state, value), true
|
return ct_add_value(state, value), true
|
||||||
@@ -881,6 +915,50 @@ ct_materialize_value :: proc(
|
|||||||
return invalid_hir_expr(checker, span, source.INVALID_DIAGNOSTIC, value.type)
|
return invalid_hir_expr(checker, span, source.INVALID_DIAGNOSTIC, value.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct_undefined_value :: proc(state: ^Ct_State, value_type: types.Type, depth := 0) -> (Ct_Value_Id, bool) {
|
||||||
|
if depth > 64 || !types.is_valid(value_type) {
|
||||||
|
return INVALID_CT_VALUE, false
|
||||||
|
}
|
||||||
|
store := &state.checker.module.types
|
||||||
|
if types.is_concrete_integer(value_type) || types.is_enum(value_type, store) {
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Integer, type=value_type}), true
|
||||||
|
}
|
||||||
|
if types.is_bool(value_type) {
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Bool, type=value_type}), true
|
||||||
|
}
|
||||||
|
if types.is_float(value_type, state.checker.target) {
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Float, type=value_type}), true
|
||||||
|
}
|
||||||
|
item, ok := types.node(store, value_type)
|
||||||
|
if !ok {
|
||||||
|
return INVALID_CT_VALUE, false
|
||||||
|
}
|
||||||
|
if item.kind == .Array {
|
||||||
|
children := make([]Ct_Value_Id, int(item.count), state.checker.allocator)
|
||||||
|
defer delete(children, state.checker.allocator)
|
||||||
|
for &child in children {
|
||||||
|
child, ok = ct_undefined_value(state, item.child, depth+1)
|
||||||
|
if !ok { return INVALID_CT_VALUE, false }
|
||||||
|
}
|
||||||
|
start := u32(len(state.children))
|
||||||
|
append(&state.children, ..children)
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Array, type=value_type, start=start, count=u32(len(children))}), true
|
||||||
|
}
|
||||||
|
if item.kind == .Struct && !item.opaque {
|
||||||
|
fields := types.fields_for(store, value_type)
|
||||||
|
children := make([]Ct_Value_Id, len(fields), state.checker.allocator)
|
||||||
|
defer delete(children, state.checker.allocator)
|
||||||
|
for field, index in fields {
|
||||||
|
children[index], ok = ct_undefined_value(state, field.type, depth+1)
|
||||||
|
if !ok { return INVALID_CT_VALUE, false }
|
||||||
|
}
|
||||||
|
start := u32(len(state.children))
|
||||||
|
append(&state.children, ..children)
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Struct, type=value_type, start=start, count=u32(len(children))}), true
|
||||||
|
}
|
||||||
|
return INVALID_CT_VALUE, false
|
||||||
|
}
|
||||||
|
|
||||||
ct_eval_expr :: proc(
|
ct_eval_expr :: proc(
|
||||||
state: ^Ct_State,
|
state: ^Ct_State,
|
||||||
expr_id: ast.Expr_Id,
|
expr_id: ast.Expr_Id,
|
||||||
@@ -921,6 +999,16 @@ ct_eval_expr :: proc(
|
|||||||
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
|
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
|
||||||
return id, ct_flow(.Normal), true
|
return id, ct_flow(.Normal), true
|
||||||
}
|
}
|
||||||
|
if value.kind == .String {
|
||||||
|
string_id := u64(0)
|
||||||
|
for text, index in checker.ast_module.strings {
|
||||||
|
if text == value.text {
|
||||||
|
string_id = u64(index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.String, type=value.type, index=string_id}), ct_flow(.Normal), true
|
||||||
|
}
|
||||||
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)}), ct_flow(.Normal), true
|
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)}), ct_flow(.Normal), true
|
||||||
}
|
}
|
||||||
} else if find_import(checker, state.file, expr.qualifier) == ast.INVALID_IMPORT {
|
} else if find_import(checker, state.file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||||
@@ -1164,7 +1252,12 @@ ct_eval_expr :: proc(
|
|||||||
return value, ct_flow(.Normal), true
|
return value, ct_flow(.Normal), true
|
||||||
case .Slice:
|
case .Slice:
|
||||||
return ct_eval_slice_expr(state, expr, depth+1)
|
return ct_eval_slice_expr(state, expr, depth+1)
|
||||||
case .Undefined, .Keyed:
|
case .Undefined:
|
||||||
|
if value, ok := ct_undefined_value(state, expected); ok {
|
||||||
|
return value, ct_flow(.Normal), true
|
||||||
|
}
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "undefined comptime value requires a concrete scalar or aggregate type")
|
||||||
|
case .Keyed:
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
||||||
}
|
}
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
||||||
@@ -1237,9 +1330,49 @@ ct_eval_struct_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Ty
|
|||||||
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
|
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
|
||||||
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, state.file))) if available else types.INVALID
|
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, state.file))) if available else types.INVALID
|
||||||
struct_type = types.resolve_alias(struct_type, store)
|
struct_type = types.resolve_alias(struct_type, store)
|
||||||
|
} else if expr.tuple {
|
||||||
|
struct_type = types.INVALID
|
||||||
} else {
|
} else {
|
||||||
struct_type = types.resolve_alias(expected, store)
|
struct_type = types.resolve_alias(expected, store)
|
||||||
}
|
}
|
||||||
|
if expr.tuple {
|
||||||
|
resolved := types.is_valid(struct_type)
|
||||||
|
fields := types.fields_for(store, struct_type) if resolved else nil
|
||||||
|
if resolved {
|
||||||
|
item, item_ok := types.node(store, struct_type)
|
||||||
|
if !item_ok || item.kind != .Struct || !item.tuple || len(fields) != len(expr.args) {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "invalid tuple construction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values := make([]Ct_Value_Id, len(expr.args), checker.allocator)
|
||||||
|
inferred_fields := make([]types.Field, len(expr.args), checker.allocator)
|
||||||
|
defer {
|
||||||
|
delete(values, checker.allocator)
|
||||||
|
delete(inferred_fields, checker.allocator)
|
||||||
|
}
|
||||||
|
for arg, index in expr.args {
|
||||||
|
expected_field := fields[index].type if resolved else types.INVALID
|
||||||
|
value, flow, ok := ct_eval_expr(state, arg, expected_field, depth+1)
|
||||||
|
if !ok || flow.kind != .Normal {
|
||||||
|
return INVALID_CT_VALUE, flow, ok
|
||||||
|
}
|
||||||
|
if resolved {
|
||||||
|
value, ok = ct_coerce_value(state, value, expected_field, checker.ast_module.exprs[arg].span)
|
||||||
|
if !ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inferred_fields[index].type = state.values[value].type
|
||||||
|
}
|
||||||
|
values[index] = value
|
||||||
|
}
|
||||||
|
if !resolved {
|
||||||
|
struct_type = types.struct_anonymous(store, inferred_fields, true)
|
||||||
|
}
|
||||||
|
start := u32(len(state.children))
|
||||||
|
append(&state.children, ..values)
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Struct, type=struct_type, start=start, count=u32(len(values))}), ct_flow(.Normal), true
|
||||||
|
}
|
||||||
if !types.is_record(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
if !types.is_record(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unknown or opaque record type '%s'", symbol_text(checker, expr.name))
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unknown or opaque record type '%s'", symbol_text(checker, expr.name))
|
||||||
}
|
}
|
||||||
@@ -1502,6 +1635,30 @@ ct_eval_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, name: symbol
|
|||||||
return children[index], ct_flow(.Normal), true
|
return children[index], ct_flow(.Normal), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct_eval_tuple_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: u64, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||||
|
if base_id == INVALID_CT_VALUE || int(base_id) >= len(state.values) {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||||
|
}
|
||||||
|
base := state.values[base_id]
|
||||||
|
base_type := base.type
|
||||||
|
if base.kind == .Pointer {
|
||||||
|
place, child, _ := ct_pointer_place(state, base)
|
||||||
|
field_index, field, ok := find_tuple_field(state.checker, child, index)
|
||||||
|
if place == INVALID_CT_PLACE || !ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "tuple field index is out of bounds")
|
||||||
|
}
|
||||||
|
field_place := ct_extend_place(state, place, Ct_Path_Elem{kind=.Field, index=u32(field_index)}, field.type, false)
|
||||||
|
value, value_ok := ct_place_get(state, field_place)
|
||||||
|
return value, ct_flow(.Normal), value_ok
|
||||||
|
}
|
||||||
|
field_index, _, ok := find_tuple_field(state.checker, base_type, index)
|
||||||
|
children := ct_child_slice(state, base)
|
||||||
|
if !ok || field_index < 0 || field_index >= len(children) {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "tuple field index is out of bounds")
|
||||||
|
}
|
||||||
|
return children[field_index], ct_flow(.Normal), true
|
||||||
|
}
|
||||||
|
|
||||||
ct_eval_index_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: int, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
ct_eval_index_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: int, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||||
checker := state.checker
|
checker := state.checker
|
||||||
if base_id == INVALID_CT_VALUE || int(base_id) >= len(state.values) {
|
if base_id == INVALID_CT_VALUE || int(base_id) >= len(state.values) {
|
||||||
@@ -2045,6 +2202,179 @@ ct_scalar_cast :: proc(state: ^Ct_State, id: Ct_Value_Id, target: types.Type, sp
|
|||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "scalar cast requires numeric scalar types")
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "scalar cast requires numeric scalar types")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intern_comptime_string :: proc(checker: ^Checker, text: string) -> u64 {
|
||||||
|
string_id := u64(len(checker.ast_module.strings))
|
||||||
|
for value, index in checker.ast_module.strings {
|
||||||
|
if value == text {
|
||||||
|
string_id = u64(index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if string_id == u64(len(checker.ast_module.strings)) {
|
||||||
|
append(&checker.ast_module.strings, strings.clone(text, checker.ast_module.allocator))
|
||||||
|
append(&checker.module.strings, strings.clone(text, checker.allocator))
|
||||||
|
}
|
||||||
|
return string_id
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_reflection_string :: proc(state: ^Ct_State, text: string) -> Ct_Value_Id {
|
||||||
|
checker := state.checker
|
||||||
|
string_id := intern_comptime_string(checker, text)
|
||||||
|
return ct_add_value(state, Ct_Value{
|
||||||
|
kind=.String,
|
||||||
|
type=types.slice(&checker.module.types, types.U8, false),
|
||||||
|
index=string_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_value_bytes :: proc(state: ^Ct_State, id: Ct_Value_Id) -> (string, bool) {
|
||||||
|
if id == INVALID_CT_VALUE || int(id) >= len(state.values) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
value := state.values[id]
|
||||||
|
if value.kind == .String {
|
||||||
|
if value.index < u64(len(state.checker.ast_module.strings)) {
|
||||||
|
return state.checker.ast_module.strings[value.index], true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
item, ok := types.container(value.type, &state.checker.module.types)
|
||||||
|
if !ok || item.child != types.U8 || item.mutable ||
|
||||||
|
(value.kind != .Array && value.kind != .Slice) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
count := int(value.count)
|
||||||
|
bytes := make([]u8, count, state.checker.allocator)
|
||||||
|
defer delete(bytes, state.checker.allocator)
|
||||||
|
for index in 0..<count {
|
||||||
|
child := INVALID_CT_VALUE
|
||||||
|
if value.kind == .Array {
|
||||||
|
children := ct_child_slice(state, value)
|
||||||
|
if index >= len(children) { return "", false }
|
||||||
|
child = children[index]
|
||||||
|
} else {
|
||||||
|
place, _, place_ok := ct_slice_element_place(state, value, index)
|
||||||
|
if !place_ok { return "", false }
|
||||||
|
child, place_ok = ct_place_get(state, place)
|
||||||
|
if !place_ok { return "", false }
|
||||||
|
}
|
||||||
|
integer, integer_ok := ct_integer_value(state, child)
|
||||||
|
if !integer_ok || integer < 0 || integer > 255 { return "", false }
|
||||||
|
bytes[index] = u8(integer)
|
||||||
|
}
|
||||||
|
string_id := intern_comptime_string(state.checker, string(bytes))
|
||||||
|
return state.checker.ast_module.strings[string_id], true
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_struct_value :: proc(state: ^Ct_State, value_type: types.Type, children: []Ct_Value_Id) -> Ct_Value_Id {
|
||||||
|
start := u32(len(state.children))
|
||||||
|
append(&state.children, ..children)
|
||||||
|
return ct_add_value(state, Ct_Value{kind=.Struct, type=value_type, start=start, count=u32(len(children))})
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||||
|
checker := state.checker
|
||||||
|
store := &checker.module.types
|
||||||
|
typeinfo_type := std_named_type(checker, "@std/meta", "TypeInfo")
|
||||||
|
fieldinfo_type := std_named_type(checker, "@std/meta", "FieldInfo")
|
||||||
|
recordinfo_type := std_named_type(checker, "@std/meta", "RecordInfo")
|
||||||
|
layout_type := std_named_type(checker, "@std/meta", "Layout")
|
||||||
|
if !types.is_valid(typeinfo_type) || !types.is_valid(fieldinfo_type) ||
|
||||||
|
!types.is_valid(recordinfo_type) || !types.is_valid(layout_type) {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||||
|
state, .Not_Comptime, span,
|
||||||
|
"typeinfo! requires importing @std/meta",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
resolved := types.resolve_alias(target, store)
|
||||||
|
item, item_ok := types.node(store, resolved)
|
||||||
|
tag := "invalid"
|
||||||
|
if !item_ok {
|
||||||
|
if types.is_void(resolved) { tag = "void" }
|
||||||
|
else if types.is_anyopaque(resolved) { tag = "anyopaque" }
|
||||||
|
else if types.is_bool(resolved) { tag = "bool" }
|
||||||
|
else if types.is_concrete_integer(resolved) { tag = "integer" }
|
||||||
|
else if types.is_float(resolved, checker.target) { tag = "float" }
|
||||||
|
} else {
|
||||||
|
#partial switch item.kind {
|
||||||
|
case .Array: tag = "array"
|
||||||
|
case .Pointer: tag = "pointer"
|
||||||
|
case .Slice: tag = "slice"
|
||||||
|
case .Range: tag = "range"
|
||||||
|
case .Optional: tag = "optional"
|
||||||
|
case .Function: tag = "function"
|
||||||
|
case .Enum: tag = "enum"
|
||||||
|
case .Struct: tag = "record"
|
||||||
|
case .Union: tag = "union"
|
||||||
|
case .Fallible: tag = "fallible"
|
||||||
|
case .Distinct: tag = "distinct"
|
||||||
|
case .Alias:
|
||||||
|
tag = "invalid"
|
||||||
|
case:
|
||||||
|
tag = "invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variant_index, _, variant_ok := find_struct_field(checker, typeinfo_type, symbol.intern(checker.symbols, tag))
|
||||||
|
if !variant_ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta TypeInfo is malformed")
|
||||||
|
}
|
||||||
|
if tag != "record" {
|
||||||
|
start := u32(len(state.children))
|
||||||
|
return ct_add_value(state, Ct_Value{
|
||||||
|
kind=.Struct, type=typeinfo_type, start=start, count=0, active=i64(variant_index),
|
||||||
|
}), ct_flow(.Normal), true
|
||||||
|
}
|
||||||
|
fields := types.fields_for(store, resolved)
|
||||||
|
field_values := make([]Ct_Value_Id, len(fields), checker.allocator)
|
||||||
|
defer delete(field_values, checker.allocator)
|
||||||
|
for field, index in fields {
|
||||||
|
name := ""
|
||||||
|
if item.tuple {
|
||||||
|
name = fmt.aprintf("%d", index, allocator=checker.allocator)
|
||||||
|
} else {
|
||||||
|
name = symbol_text(checker, symbol.Id(field.name))
|
||||||
|
}
|
||||||
|
name_value := ct_reflection_string(state, name)
|
||||||
|
if item.tuple {
|
||||||
|
delete(name, checker.allocator)
|
||||||
|
}
|
||||||
|
type_value := ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(field.type)})
|
||||||
|
index_value := ct_add_value(state, Ct_Value{kind=.Integer, type=types.USIZE, integer=i128(index)})
|
||||||
|
children := []Ct_Value_Id{name_value, type_value, index_value}
|
||||||
|
field_values[index] = ct_struct_value(state, fieldinfo_type, children)
|
||||||
|
}
|
||||||
|
fields_start := u32(len(state.children))
|
||||||
|
append(&state.children, ..field_values)
|
||||||
|
fields_type := types.array(store, fieldinfo_type, u64(len(field_values)), false)
|
||||||
|
fields_value := ct_add_value(state, Ct_Value{
|
||||||
|
kind=.Array, type=fields_type, start=fields_start, count=u32(len(field_values)),
|
||||||
|
})
|
||||||
|
name_optional_type := types.optional(store, types.slice(store, types.U8, false))
|
||||||
|
record_name := ct_add_value(state, Ct_Value{kind=.None, type=name_optional_type})
|
||||||
|
if item.name != 0 {
|
||||||
|
name_value := ct_reflection_string(state, symbol_text(checker, symbol.Id(item.name)))
|
||||||
|
name_start := u32(len(state.children))
|
||||||
|
append(&state.children, name_value)
|
||||||
|
record_name = ct_add_value(state, Ct_Value{
|
||||||
|
kind=.Optional_Some, type=name_optional_type, start=name_start, count=1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
tuple_value := ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if item.tuple else 0})
|
||||||
|
layout_name := symbol.intern(checker.symbols, "c" if item.c_layout else "auto")
|
||||||
|
layout_member, layout_ok := find_enum_member(checker, layout_type, layout_name)
|
||||||
|
if !layout_ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta Layout is malformed")
|
||||||
|
}
|
||||||
|
layout_value := ct_add_value(state, Ct_Value{kind=.Integer, type=layout_type, integer=layout_member.value})
|
||||||
|
record_children := []Ct_Value_Id{record_name, fields_value, tuple_value, layout_value}
|
||||||
|
record_value := ct_struct_value(state, recordinfo_type, record_children)
|
||||||
|
payload_start := u32(len(state.children))
|
||||||
|
append(&state.children, record_value)
|
||||||
|
return ct_add_value(state, Ct_Value{
|
||||||
|
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
|
||||||
|
}), ct_flow(.Normal), true
|
||||||
|
}
|
||||||
|
|
||||||
ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||||
checker := state.checker
|
checker := state.checker
|
||||||
if expr.left != ast.INVALID_EXPR {
|
if expr.left != ast.INVALID_EXPR {
|
||||||
@@ -2057,6 +2387,49 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
|||||||
}
|
}
|
||||||
return ct_eval_template_call(state, ast.Function_Id(u32(state.values[callee].index)), expr.args, expr.span, expected, depth+1)
|
return ct_eval_template_call(state, ast.Function_Id(u32(state.values[callee].index)), expr.args, expr.span, expected, depth+1)
|
||||||
}
|
}
|
||||||
|
if is_intrinsic_call(checker, expr, "compile_error") {
|
||||||
|
message := "compile_error! requires one comptime string argument"
|
||||||
|
if len(expr.args) == 1 {
|
||||||
|
value, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||||
|
if ok && flow.kind == .Normal {
|
||||||
|
if text, text_ok := ct_value_bytes(state, value); text_ok {
|
||||||
|
message = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, message)
|
||||||
|
}
|
||||||
|
if is_intrinsic_call(checker, expr, "field") {
|
||||||
|
if len(expr.args) != 2 {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "field! expects 2 arguments, got %d", len(expr.args))
|
||||||
|
}
|
||||||
|
base, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||||
|
if !ok || flow.kind != .Normal {
|
||||||
|
return INVALID_CT_VALUE, flow, ok
|
||||||
|
}
|
||||||
|
name_value, name_flow, name_ok := ct_eval_expr(state, expr.args[1], types.INVALID, depth+1)
|
||||||
|
if !name_ok || name_flow.kind != .Normal {
|
||||||
|
return INVALID_CT_VALUE, name_flow, name_ok
|
||||||
|
}
|
||||||
|
name, text_ok := ct_value_bytes(state, name_value)
|
||||||
|
if !text_ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "field! name must be a comptime immutable byte string")
|
||||||
|
}
|
||||||
|
if index, numeric := canonical_decimal_index(name); numeric {
|
||||||
|
return ct_eval_tuple_field_value(state, base, index, expr.span)
|
||||||
|
}
|
||||||
|
return ct_eval_field_value(state, base, symbol.intern(checker.symbols, name), expr.span)
|
||||||
|
}
|
||||||
|
if is_intrinsic_call(checker, expr, "typeinfo") {
|
||||||
|
if len(expr.args) != 1 {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "typeinfo! expects 1 argument, got %d", len(expr.args))
|
||||||
|
}
|
||||||
|
target, ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file)
|
||||||
|
if !ok {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "typeinfo! argument must be a type")
|
||||||
|
}
|
||||||
|
return ct_typeinfo_value(state, target, expr.span)
|
||||||
|
}
|
||||||
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
||||||
if len(expr.args) != 1 {
|
if len(expr.args) != 1 {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||||
@@ -2234,6 +2607,107 @@ ct_clone_value :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
|
|||||||
return ct_add_value(dst, value)
|
return ct_add_value(dst, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ct_Clone_Context :: struct {
|
||||||
|
dst, src: ^Ct_State,
|
||||||
|
values: []Ct_Value_Id,
|
||||||
|
cells: []Ct_Cell_Id,
|
||||||
|
places: []Ct_Place_Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_clone_graph_value :: proc(ctx: ^Ct_Clone_Context, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||||
|
if id == INVALID_CT_VALUE || int(id) >= len(ctx.src.values) {
|
||||||
|
return INVALID_CT_VALUE
|
||||||
|
}
|
||||||
|
if ctx.values[id] != INVALID_CT_VALUE {
|
||||||
|
return ctx.values[id]
|
||||||
|
}
|
||||||
|
value := ctx.src.values[id]
|
||||||
|
children := ct_child_slice(ctx.src, value)
|
||||||
|
value.start = 0
|
||||||
|
value.count = 0
|
||||||
|
dst_id := ct_add_value(ctx.dst, value)
|
||||||
|
ctx.values[id] = dst_id
|
||||||
|
if len(children) > 0 {
|
||||||
|
cloned := make([]Ct_Value_Id, len(children), ctx.dst.checker.allocator)
|
||||||
|
defer delete(cloned, ctx.dst.checker.allocator)
|
||||||
|
for child, index in children {
|
||||||
|
cloned[index] = ct_clone_graph_value(ctx, child)
|
||||||
|
}
|
||||||
|
start := u32(len(ctx.dst.children))
|
||||||
|
append(&ctx.dst.children, ..cloned)
|
||||||
|
ctx.dst.values[dst_id].start = start
|
||||||
|
ctx.dst.values[dst_id].count = u32(len(children))
|
||||||
|
}
|
||||||
|
if value.kind == .Pointer || value.kind == .Slice {
|
||||||
|
ctx.dst.values[dst_id].index = u64(ct_clone_graph_place(ctx, Ct_Place_Id(value.index)))
|
||||||
|
}
|
||||||
|
return dst_id
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_clone_graph_cell :: proc(ctx: ^Ct_Clone_Context, id: Ct_Cell_Id) -> Ct_Cell_Id {
|
||||||
|
if id == INVALID_CT_CELL || int(id) >= len(ctx.src.cells) {
|
||||||
|
return INVALID_CT_CELL
|
||||||
|
}
|
||||||
|
if ctx.cells[id] != INVALID_CT_CELL {
|
||||||
|
return ctx.cells[id]
|
||||||
|
}
|
||||||
|
cell := ctx.src.cells[id]
|
||||||
|
cell.value = INVALID_CT_VALUE
|
||||||
|
dst_id := ct_cell_id(len(ctx.dst.cells))
|
||||||
|
append(&ctx.dst.cells, cell)
|
||||||
|
ctx.cells[id] = dst_id
|
||||||
|
ctx.dst.cells[dst_id].value = ct_clone_graph_value(ctx, ctx.src.cells[id].value)
|
||||||
|
return dst_id
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_clone_graph_place :: proc(ctx: ^Ct_Clone_Context, id: Ct_Place_Id) -> Ct_Place_Id {
|
||||||
|
if id == INVALID_CT_PLACE || int(id) >= len(ctx.src.places) {
|
||||||
|
return INVALID_CT_PLACE
|
||||||
|
}
|
||||||
|
if ctx.places[id] != INVALID_CT_PLACE {
|
||||||
|
return ctx.places[id]
|
||||||
|
}
|
||||||
|
place := ctx.src.places[id]
|
||||||
|
path := ct_place_path(ctx.src, place)
|
||||||
|
place.start = u32(len(ctx.dst.paths))
|
||||||
|
place.count = u32(len(path))
|
||||||
|
append(&ctx.dst.paths, ..path)
|
||||||
|
place.cell = INVALID_CT_CELL
|
||||||
|
dst_id := Ct_Place_Id(len(ctx.dst.places))
|
||||||
|
append(&ctx.dst.places, place)
|
||||||
|
ctx.places[id] = dst_id
|
||||||
|
ctx.dst.places[dst_id].cell = ct_clone_graph_cell(ctx, ctx.src.places[id].cell)
|
||||||
|
return dst_id
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_clone_graph :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||||
|
ctx := Ct_Clone_Context{
|
||||||
|
dst=dst,
|
||||||
|
src=src,
|
||||||
|
values=make([]Ct_Value_Id, len(src.values), dst.checker.allocator),
|
||||||
|
cells=make([]Ct_Cell_Id, len(src.cells), dst.checker.allocator),
|
||||||
|
places=make([]Ct_Place_Id, len(src.places), dst.checker.allocator),
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
delete(ctx.values, dst.checker.allocator)
|
||||||
|
delete(ctx.cells, dst.checker.allocator)
|
||||||
|
delete(ctx.places, dst.checker.allocator)
|
||||||
|
}
|
||||||
|
for &value in ctx.values { value = INVALID_CT_VALUE }
|
||||||
|
for &cell in ctx.cells { cell = INVALID_CT_CELL }
|
||||||
|
for &place in ctx.places { place = INVALID_CT_PLACE }
|
||||||
|
return ct_clone_graph_value(&ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
store_static_binding :: proc(checker: ^Checker, source: ^Ct_State, id: Ct_Value_Id, name: symbol.Id) -> Static_Binding {
|
||||||
|
value := ct_clone_graph(&checker.static_state, source, id)
|
||||||
|
value_type := types.INVALID
|
||||||
|
if value != INVALID_CT_VALUE && int(value) < len(checker.static_state.values) {
|
||||||
|
value_type = checker.static_state.values[value].type
|
||||||
|
}
|
||||||
|
return Static_Binding{name=name, type=value_type, value=value}
|
||||||
|
}
|
||||||
|
|
||||||
ct_eval_try_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
ct_eval_try_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||||
if state.defer_depth > 0 {
|
if state.defer_depth > 0 {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "cannot 'try' inside a 'defer'")
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "cannot 'try' inside a 'defer'")
|
||||||
|
|||||||
@@ -630,13 +630,18 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
|
|
||||||
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||||
stack := state.expr_stack
|
stack := state.expr_stack
|
||||||
|
state.expr_stack = nil
|
||||||
clear_dynamic_array(&stack)
|
clear_dynamic_array(&stack)
|
||||||
defer {
|
defer {
|
||||||
for frame in stack {
|
for frame in stack {
|
||||||
delete(frame.args, state.allocator)
|
delete(frame.args, state.allocator)
|
||||||
}
|
}
|
||||||
clear_dynamic_array(&stack)
|
clear_dynamic_array(&stack)
|
||||||
state.expr_stack = stack
|
if state.expr_stack == nil {
|
||||||
|
state.expr_stack = stack
|
||||||
|
} else {
|
||||||
|
delete(stack)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
||||||
last := ir.INVALID_INSTRUCTION
|
last := ir.INVALID_INSTRUCTION
|
||||||
@@ -1656,12 +1661,29 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
|||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
|
main_function := &hir_module.functions[main_index]
|
||||||
|
param_index, param_ok := hir.index(main_function.params[0], hir.INVALID_LOCAL, len(main_function.locals))
|
||||||
|
if !param_ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
init_args := make([]ir.Instruction_Id, 1, allocator)
|
||||||
|
init_args[0] = provider_call
|
||||||
|
init_value := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Aggregate,
|
||||||
|
type=main_function.locals[param_index].type,
|
||||||
|
args=init_args,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
args := make([]ir.Instruction_Id, 1, allocator)
|
args := make([]ir.Instruction_Id, 1, allocator)
|
||||||
args[0] = provider_call
|
args[0] = init_value
|
||||||
main_call := ir.instruction_id(len(instructions))
|
main_call := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Call,
|
op=.Call,
|
||||||
type=hir_module.functions[main_index].result,
|
type=main_function.result,
|
||||||
args=args,
|
args=args,
|
||||||
target=ir.function_ref(ir.Function_Id(main_index)),
|
target=ir.function_ref(ir.Function_Id(main_index)),
|
||||||
a=ir.INVALID_INSTRUCTION,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
|||||||
+186
-19
@@ -497,7 +497,17 @@ parse_call_args :: proc(parser: ^Parser, nesting: int) -> ([]ast.Expr_Id, token.
|
|||||||
args.allocator = parser.module.allocator
|
args.allocator = parser.module.allocator
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
|
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
|
||||||
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
if hole, ok := allow(parser, .Underscore); ok {
|
||||||
|
append(&args, add_expr(parser, ast.Expr{
|
||||||
|
kind=.Inference_Hole,
|
||||||
|
span=hole.span,
|
||||||
|
left=ast.INVALID_EXPR,
|
||||||
|
right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
||||||
|
}
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
if _, ok := allow(parser, .Comma); ok {
|
if _, ok := allow(parser, .Comma); ok {
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
@@ -612,6 +622,74 @@ parse_keyed_initializers :: proc(
|
|||||||
return args[:], right_brace
|
return args[:], right_brace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse_positional_initializers :: proc(
|
||||||
|
parser: ^Parser,
|
||||||
|
left_brace: token.Token,
|
||||||
|
nesting: int,
|
||||||
|
close_message: string,
|
||||||
|
) -> ([]ast.Expr_Id, token.Token) {
|
||||||
|
parser.delimiter_depth += 1
|
||||||
|
defer parser.delimiter_depth -= 1
|
||||||
|
args: [dynamic]ast.Expr_Id
|
||||||
|
args.allocator = parser.module.allocator
|
||||||
|
skip_newlines(parser)
|
||||||
|
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||||
|
append(&args, parse_expression_bp(parser, 0, nesting+1))
|
||||||
|
skip_newlines(parser)
|
||||||
|
if _, ok := allow(parser, .Comma); ok {
|
||||||
|
skip_newlines(parser)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
right_brace, ok := allow(parser, .Right_Brace)
|
||||||
|
if !ok {
|
||||||
|
source.add(parser.diagnostics, current(parser).span, close_message)
|
||||||
|
right_brace = left_brace
|
||||||
|
}
|
||||||
|
return args[:], right_brace
|
||||||
|
}
|
||||||
|
|
||||||
|
brace_starts_tuple :: proc(parser: ^Parser) -> bool {
|
||||||
|
if current(parser).kind != .Left_Brace {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
depth := 0
|
||||||
|
for cursor := parser.cursor; cursor < len(parser.tokens.items); cursor += 1 {
|
||||||
|
#partial switch parser.tokens.items[cursor].kind {
|
||||||
|
case .Left_Brace, .Left_Paren, .Left_Bracket:
|
||||||
|
depth += 1
|
||||||
|
case .Right_Brace, .Right_Paren, .Right_Bracket:
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0 {
|
||||||
|
return cursor == parser.cursor+1
|
||||||
|
}
|
||||||
|
case .Comma:
|
||||||
|
if depth == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case .Eof:
|
||||||
|
return false
|
||||||
|
case:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_tuple_literal :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||||
|
left_brace := advance(parser)
|
||||||
|
args, right_brace := parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||||
|
return add_expr(parser, ast.Expr{
|
||||||
|
kind=.Struct_Literal,
|
||||||
|
span=span_from(left_brace.span, right_brace.span),
|
||||||
|
args=args,
|
||||||
|
tuple=true,
|
||||||
|
left=ast.INVALID_EXPR,
|
||||||
|
right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
parse_struct_literal :: proc(
|
parse_struct_literal :: proc(
|
||||||
parser: ^Parser,
|
parser: ^Parser,
|
||||||
qualifier: symbol.Id,
|
qualifier: symbol.Id,
|
||||||
@@ -619,13 +697,24 @@ parse_struct_literal :: proc(
|
|||||||
nesting: int,
|
nesting: int,
|
||||||
) -> ast.Expr_Id {
|
) -> ast.Expr_Id {
|
||||||
left_brace := advance(parser)
|
left_brace := advance(parser)
|
||||||
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
skip_newlines(parser)
|
||||||
|
keyed_start := (current(parser).kind == .Identifier || token.is_keyword(current(parser).kind)) &&
|
||||||
|
(peek(parser).kind == .Equal || peek(parser).kind == .Right_Brace || token.is_keyword(current(parser).kind))
|
||||||
|
positional := current(parser).kind == .Right_Brace || !keyed_start
|
||||||
|
args: []ast.Expr_Id
|
||||||
|
right_brace: token.Token
|
||||||
|
if positional {
|
||||||
|
args, right_brace = parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||||
|
} else {
|
||||||
|
args, right_brace = parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||||
|
}
|
||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
kind=.Struct_Literal,
|
kind=.Struct_Literal,
|
||||||
span=source.Span{file=name.span.file, start=first.span.start, end=right_brace.span.end},
|
span=source.Span{file=name.span.file, start=first.span.start, end=right_brace.span.end},
|
||||||
qualifier=qualifier,
|
qualifier=qualifier,
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
args=args[:],
|
args=args[:],
|
||||||
|
tuple=positional,
|
||||||
left=ast.INVALID_EXPR,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_EXPR,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
@@ -636,7 +725,8 @@ parse_anonymous_struct_type_expr :: proc(parser: ^Parser) -> ast.Expr_Id {
|
|||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
fields: [dynamic]types.Field
|
fields: [dynamic]types.Field
|
||||||
fields.allocator = parser.module.allocator
|
fields.allocator = parser.module.allocator
|
||||||
if !parse_record_body(parser, &fields, "expected '{' after anonymous struct type") {
|
tuple := false
|
||||||
|
if !parse_record_body(parser, &fields, "expected '{' after anonymous struct type", tuple_result=&tuple) {
|
||||||
delete(fields)
|
delete(fields)
|
||||||
return invalid_expr(parser, start.span, "invalid anonymous struct type")
|
return invalid_expr(parser, start.span, "invalid anonymous struct type")
|
||||||
}
|
}
|
||||||
@@ -649,6 +739,7 @@ parse_anonymous_struct_type_expr :: proc(parser: ^Parser) -> ast.Expr_Id {
|
|||||||
kind=.Anonymous_Struct_Type,
|
kind=.Anonymous_Struct_Type,
|
||||||
span=span_from(start.span, end.span),
|
span=span_from(start.span, end.span),
|
||||||
integer=u64(field_start)<<32 | u64(field_count),
|
integer=u64(field_start)<<32 | u64(field_count),
|
||||||
|
tuple=tuple,
|
||||||
left=ast.INVALID_EXPR,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_EXPR,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
@@ -837,6 +928,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
return parse_array_literal(parser, nesting)
|
return parse_array_literal(parser, nesting)
|
||||||
|
case .Left_Brace:
|
||||||
|
return parse_tuple_literal(parser, nesting)
|
||||||
case .Dot:
|
case .Dot:
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
member, member_ok := parse_member_name(parser)
|
member, member_ok := parse_member_name(parser)
|
||||||
@@ -901,7 +994,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
|||||||
first := advance(parser)
|
first := advance(parser)
|
||||||
name := first
|
name := first
|
||||||
qualifier := symbol.INVALID
|
qualifier := symbol.INVALID
|
||||||
if _, ok := allow(parser, .Dot); ok {
|
if current(parser).kind == .Dot && peek(parser).kind != .Integer {
|
||||||
|
advance(parser)
|
||||||
member, member_ok := parse_member_name(parser)
|
member, member_ok := parse_member_name(parser)
|
||||||
if !member_ok {
|
if !member_ok {
|
||||||
return invalid_expr(parser, current(parser).span, "expected a package member after '.'")
|
return invalid_expr(parser, current(parser).span, "expected a package member after '.'")
|
||||||
@@ -914,11 +1008,22 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
|||||||
call := parse_call(parser, qualifier, first, name, nesting, intrinsic)
|
call := parse_call(parser, qualifier, first, name, nesting, intrinsic)
|
||||||
if !intrinsic && current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
if !intrinsic && current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
||||||
left_brace := advance(parser)
|
left_brace := advance(parser)
|
||||||
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
skip_newlines(parser)
|
||||||
|
keyed_start := (current(parser).kind == .Identifier || token.is_keyword(current(parser).kind)) &&
|
||||||
|
(peek(parser).kind == .Equal || peek(parser).kind == .Right_Brace || token.is_keyword(current(parser).kind))
|
||||||
|
positional := current(parser).kind == .Right_Brace || !keyed_start
|
||||||
|
args: []ast.Expr_Id
|
||||||
|
right_brace: token.Token
|
||||||
|
if positional {
|
||||||
|
args, right_brace = parse_positional_initializers(parser, left_brace, nesting, "expected '}' after tuple literal")
|
||||||
|
} else {
|
||||||
|
args, right_brace = parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||||
|
}
|
||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
kind=.Struct_Literal,
|
kind=.Struct_Literal,
|
||||||
span=span_from(parser.module.exprs[call].span, right_brace.span),
|
span=span_from(parser.module.exprs[call].span, right_brace.span),
|
||||||
args=args,
|
args=args,
|
||||||
|
tuple=positional,
|
||||||
left=call,
|
left=call,
|
||||||
right=ast.INVALID_EXPR,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
@@ -1113,6 +1218,26 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
|||||||
}
|
}
|
||||||
if current(parser).kind == .Dot {
|
if current(parser).kind == .Dot {
|
||||||
advance(parser)
|
advance(parser)
|
||||||
|
if current(parser).kind == .Integer {
|
||||||
|
field := advance(parser)
|
||||||
|
text := token_text(parser, field)
|
||||||
|
index, index_ok := parse_integer_magnitude(text)
|
||||||
|
left_expr := parser.module.exprs[left]
|
||||||
|
if !index_ok || (len(text) > 1 && text[0] == '0') {
|
||||||
|
left = invalid_expr(parser, field.span, "tuple field indices must be canonical decimal integers")
|
||||||
|
} else {
|
||||||
|
left = add_expr(parser, ast.Expr{
|
||||||
|
kind=.Field,
|
||||||
|
span=span_from(left_expr.span, field.span),
|
||||||
|
name=symbol.INVALID,
|
||||||
|
integer=index,
|
||||||
|
left=left,
|
||||||
|
right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
field, field_ok := parse_member_name(parser)
|
field, field_ok := parse_member_name(parser)
|
||||||
if !field_ok {
|
if !field_ok {
|
||||||
left = invalid_expr(parser, field.span, "expected a field name after '.'")
|
left = invalid_expr(parser, field.span, "expected a field name after '.'")
|
||||||
@@ -1544,6 +1669,14 @@ parse_value_control_flow :: proc(parser: ^Parser) -> (ast.Stmt_Id, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||||
|
if current(parser).kind == .Identifier && token_text(parser, current(parser)) == "inline" &&
|
||||||
|
peek(parser).kind == .Keyword_For {
|
||||||
|
start := advance(parser)
|
||||||
|
id := parse_for(parser)
|
||||||
|
parser.module.statements[id].inline = true
|
||||||
|
parser.module.statements[id].span = span_from(start.span, parser.module.statements[id].span)
|
||||||
|
return id
|
||||||
|
}
|
||||||
if current(parser).kind == .Keyword_Return {
|
if current(parser).kind == .Keyword_Return {
|
||||||
return parse_return(parser)
|
return parse_return(parser)
|
||||||
}
|
}
|
||||||
@@ -1623,7 +1756,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
}
|
}
|
||||||
// A `{` on the right is a value block: parse its statements now; the
|
// A `{` on the right is a value block: parse its statements now; the
|
||||||
// checker turns its final `yield` into the declared/assigned value.
|
// checker turns its final `yield` into the declared/assigned value.
|
||||||
if current(parser).kind == .Left_Brace {
|
if current(parser).kind == .Left_Brace && !brace_starts_tuple(parser) {
|
||||||
brace := current(parser)
|
brace := current(parser)
|
||||||
body := parse_block(parser)
|
body := parse_block(parser)
|
||||||
id := ast.stmt_id(len(parser.module.statements))
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
@@ -1697,7 +1830,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
// A value block assigned to a complex target (`a[i] = { ... }`, `p.f = { ... }`).
|
// A value block assigned to a complex target (`a[i] = { ... }`, `p.f = { ... }`).
|
||||||
if current(parser).kind == .Left_Brace {
|
if current(parser).kind == .Left_Brace && !brace_starts_tuple(parser) {
|
||||||
brace := current(parser)
|
brace := current(parser)
|
||||||
body := parse_block(parser)
|
body := parse_block(parser)
|
||||||
id := ast.stmt_id(len(parser.module.statements))
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
@@ -2339,26 +2472,51 @@ parse_record_body :: proc(
|
|||||||
expected_open: string,
|
expected_open: string,
|
||||||
allow_anonymous_struct_payload := false,
|
allow_anonymous_struct_payload := false,
|
||||||
allow_keyword_names := false,
|
allow_keyword_names := false,
|
||||||
|
tuple_result: ^bool = nil,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||||
source.add(parser.diagnostics, current(parser).span, expected_open)
|
source.add(parser.diagnostics, current(parser).span, expected_open)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
|
mode := 0 // 0 unknown, 1 named, 2 tuple
|
||||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||||
field_name, field_ok := parse_member_name(parser, allow_keyword_names)
|
unnamed := false
|
||||||
if !field_ok {
|
if !allow_keyword_names {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected a struct field name")
|
if current(parser).kind == .Identifier {
|
||||||
for current(parser).kind != .Newline &&
|
next := peek(parser).kind
|
||||||
current(parser).kind != .Right_Brace &&
|
unnamed = next == .Comma || next == .Newline || next == .Right_Brace ||
|
||||||
current(parser).kind != .Eof {
|
next == .Dot || next == .Left_Paren
|
||||||
advance(parser)
|
} else {
|
||||||
|
unnamed = is_type_token(current(parser).kind)
|
||||||
}
|
}
|
||||||
skip_newlines(parser)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
field_type := parse_record_field_type(parser, allow_anonymous_struct_payload)
|
if unnamed {
|
||||||
append(fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
if mode == 1 {
|
||||||
|
source.add(parser.diagnostics, current(parser).span, "struct fields cannot mix named and unnamed forms")
|
||||||
|
}
|
||||||
|
mode = 2
|
||||||
|
field_type := parse_type(parser)
|
||||||
|
append(fields, types.Field{name=0, type=field_type})
|
||||||
|
} else {
|
||||||
|
if mode == 2 {
|
||||||
|
source.add(parser.diagnostics, current(parser).span, "struct fields cannot mix named and unnamed forms")
|
||||||
|
}
|
||||||
|
mode = 1
|
||||||
|
field_name, field_ok := parse_member_name(parser, allow_keyword_names)
|
||||||
|
if !field_ok {
|
||||||
|
source.add(parser.diagnostics, current(parser).span, "expected a struct field name or tuple element type")
|
||||||
|
for current(parser).kind != .Newline &&
|
||||||
|
current(parser).kind != .Right_Brace &&
|
||||||
|
current(parser).kind != .Eof {
|
||||||
|
advance(parser)
|
||||||
|
}
|
||||||
|
skip_newlines(parser)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
field_type := parse_record_field_type(parser, allow_anonymous_struct_payload)
|
||||||
|
append(fields, types.Field{name=u32(field_name.symbol), type=field_type})
|
||||||
|
}
|
||||||
if _, ok := allow(parser, .Comma); ok {
|
if _, ok := allow(parser, .Comma); ok {
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
continue
|
continue
|
||||||
@@ -2368,6 +2526,9 @@ parse_record_body :: proc(
|
|||||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||||
}
|
}
|
||||||
|
if tuple_result != nil {
|
||||||
|
tuple_result^ = mode == 2
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2459,17 +2620,23 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, file_hidden:
|
|||||||
fields.allocator = parser.module.allocator
|
fields.allocator = parser.module.allocator
|
||||||
defer delete(fields)
|
defer delete(fields)
|
||||||
allow_anonymous_struct_payload := is_union && (inferred_tag || types.is_valid(declared_tag))
|
allow_anonymous_struct_payload := is_union && (inferred_tag || types.is_valid(declared_tag))
|
||||||
|
tuple := false
|
||||||
_ = parse_record_body(
|
_ = parse_record_body(
|
||||||
parser,
|
parser,
|
||||||
&fields,
|
&fields,
|
||||||
"expected '{' after struct fields",
|
"expected '{' after struct fields",
|
||||||
allow_anonymous_struct_payload,
|
allow_anonymous_struct_payload,
|
||||||
allow_anonymous_struct_payload,
|
allow_anonymous_struct_payload,
|
||||||
|
&tuple,
|
||||||
)
|
)
|
||||||
|
if tuple && (c_layout || is_union) {
|
||||||
|
source.add(parser.diagnostics, start.span, "unnamed fields are only supported by native structs")
|
||||||
|
tuple = false
|
||||||
|
}
|
||||||
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
||||||
tag = synthesize_union_tag(parser, fields[:])
|
tag = synthesize_union_tag(parser, fields[:])
|
||||||
}
|
}
|
||||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag) {
|
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple) {
|
||||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ Node :: struct {
|
|||||||
c_abi: bool,
|
c_abi: bool,
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
c_layout: bool,
|
c_layout: bool,
|
||||||
|
tuple: bool,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
declared: bool,
|
declared: bool,
|
||||||
file_hidden: bool,
|
file_hidden: bool,
|
||||||
@@ -273,6 +274,7 @@ define_record :: proc(
|
|||||||
explicit_alignment: u32 = 0,
|
explicit_alignment: u32 = 0,
|
||||||
tag: Type = INVALID,
|
tag: Type = INVALID,
|
||||||
declared_tag: Type = INVALID,
|
declared_tag: Type = INVALID,
|
||||||
|
tuple := false,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
||||||
@@ -282,6 +284,7 @@ define_record :: proc(
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Union if is_union else .Struct
|
store.nodes[index].kind = .Union if is_union else .Struct
|
||||||
store.nodes[index].c_layout = c_layout
|
store.nodes[index].c_layout = c_layout
|
||||||
|
store.nodes[index].tuple = tuple
|
||||||
store.nodes[index].opaque = opaque
|
store.nodes[index].opaque = opaque
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
store.nodes[index].explicit_size = explicit_size
|
store.nodes[index].explicit_size = explicit_size
|
||||||
@@ -342,10 +345,10 @@ anonymous_struct_fields_equal :: proc(store: ^Store, item: Node, fields: []Field
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
struct_anonymous :: proc(store: ^Store, fields: []Field) -> Type {
|
struct_anonymous :: proc(store: ^Store, fields: []Field, tuple := false) -> Type {
|
||||||
for existing, index in store.nodes {
|
for existing, index in store.nodes {
|
||||||
if existing.kind == .Struct && existing.name == 0 && existing.declared &&
|
if existing.kind == .Struct && existing.name == 0 && existing.declared &&
|
||||||
!existing.c_layout && !existing.opaque &&
|
!existing.c_layout && existing.tuple == tuple && !existing.opaque &&
|
||||||
anonymous_struct_fields_equal(store, existing, fields) {
|
anonymous_struct_fields_equal(store, existing, fields) {
|
||||||
return DYNAMIC_START+Type(index)
|
return DYNAMIC_START+Type(index)
|
||||||
}
|
}
|
||||||
@@ -356,13 +359,14 @@ struct_anonymous :: proc(store: ^Store, fields: []Field) -> Type {
|
|||||||
kind=.Struct,
|
kind=.Struct,
|
||||||
field_start=start,
|
field_start=start,
|
||||||
field_count=u32(len(fields)),
|
field_count=u32(len(fields)),
|
||||||
|
tuple=tuple,
|
||||||
declared=true,
|
declared=true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generated structs are nominal per comptime type-expression specialization.
|
// Generated structs are nominal per comptime type-expression specialization.
|
||||||
// The checker owns canonicalization; this routine deliberately creates a fresh node.
|
// The checker owns canonicalization; this routine deliberately creates a fresh node.
|
||||||
struct_generated :: proc(store: ^Store, fields: []Field) -> Type {
|
struct_generated :: proc(store: ^Store, fields: []Field, tuple := false) -> Type {
|
||||||
start := u32(len(store.fields))
|
start := u32(len(store.fields))
|
||||||
append(&store.fields, ..fields)
|
append(&store.fields, ..fields)
|
||||||
id := DYNAMIC_START+Type(len(store.nodes))
|
id := DYNAMIC_START+Type(len(store.nodes))
|
||||||
@@ -370,6 +374,7 @@ struct_generated :: proc(store: ^Store, fields: []Field) -> Type {
|
|||||||
kind=.Struct,
|
kind=.Struct,
|
||||||
field_start=start,
|
field_start=start,
|
||||||
field_count=u32(len(fields)),
|
field_count=u32(len(fields)),
|
||||||
|
tuple=tuple,
|
||||||
declared=true,
|
declared=true,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
|
|||||||
+275
-13
@@ -2258,7 +2258,12 @@ milestone_33_injects_explicit_io_provider_and_runs_std_io :: proc(t: ^testing.T)
|
|||||||
defer delete(stdout)
|
defer delete(stdout)
|
||||||
defer delete(stderr)
|
defer delete(stderr)
|
||||||
testing.expect_value(t, state.exit_code, 0)
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
testing.expect_value(t, string(stdout), "io-ok\n")
|
testing.expect_value(t, string(stdout),
|
||||||
|
"io-ok 37 {bro}\n" +
|
||||||
|
"bounds=-128/255 -32768/65535 -2147483648/4294967295 -9223372036854775808/18446744073709551615 " +
|
||||||
|
"-9223372036854775808/18446744073709551615 -128/127 -128/255 -32768/65535 -2147483648/4294967295 " +
|
||||||
|
"-9223372036854775808/18446744073709551615 -9223372036854775808/18446744073709551615 0/0 1/1 -1/1\n",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -2266,6 +2271,7 @@ milestone_33_rejects_non_io_and_extra_main_parameters :: proc(t: ^testing.T) {
|
|||||||
cases := [?]string{
|
cases := [?]string{
|
||||||
"main func(value i32) void {}\n",
|
"main func(value i32) void {}\n",
|
||||||
"main func(left, right i32) void {}\n",
|
"main func(left, right i32) void {}\n",
|
||||||
|
"main func($value i32) void {}\n",
|
||||||
}
|
}
|
||||||
for text in cases {
|
for text in cases {
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -2279,7 +2285,7 @@ milestone_33_rejects_non_io_and_extra_main_parameters :: proc(t: ^testing.T) {
|
|||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
found = found || strings.contains(
|
found = found || strings.contains(
|
||||||
diagnostic.message,
|
diagnostic.message,
|
||||||
"take no parameters or one @std/io Io",
|
"take no parameters or one @std/process Init",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
testing.expect(t, found)
|
testing.expect(t, found)
|
||||||
@@ -2292,6 +2298,239 @@ milestone_33_rejects_non_io_and_extra_main_parameters :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_rejects_direct_io_main_parameter :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-direct-io-main"
|
||||||
|
main_path := "/tmp/brolang-test-direct-io-main/main.bro"
|
||||||
|
text := `io :: import "@std/io"
|
||||||
|
main func(system io.Io) void {
|
||||||
|
_ = system
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
|
||||||
|
sources := source.init_store()
|
||||||
|
defer source.destroy_store(&sources)
|
||||||
|
diagnostics := source.init_store_diagnostics(&sources)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
ast_module, loaded := loader.load(directory, &sources, &diagnostics, &symbols, project_root_path=".")
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
found = found || strings.contains(
|
||||||
|
diagnostic.message,
|
||||||
|
"take no parameters or one @std/process Init",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
testing.expect(t, loaded)
|
||||||
|
testing.expect(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_validates_process_init_and_hidden_system_provider :: proc(t: ^testing.T) {
|
||||||
|
Case :: struct {
|
||||||
|
io_source: string,
|
||||||
|
process_source: string,
|
||||||
|
message: string,
|
||||||
|
}
|
||||||
|
cases := []Case{
|
||||||
|
{
|
||||||
|
io_source=`Io :: struct { value i32 }
|
||||||
|
system func() Io { return Io {value = 0} }
|
||||||
|
`,
|
||||||
|
process_source=`io :: import "@std/io"
|
||||||
|
Init :: struct { io io.Io }
|
||||||
|
`,
|
||||||
|
message="does not provide the required 'hide system func() Io'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
io_source=`Io :: struct { value i32 }
|
||||||
|
hide system func() Io { return Io {value = 0} }
|
||||||
|
`,
|
||||||
|
process_source=`io :: import "@std/io"
|
||||||
|
Init :: struct { io io.Io, extra i32 }
|
||||||
|
`,
|
||||||
|
message="Init must be an auto-layout record containing exactly 'io io.Io'",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for test_case, index in cases {
|
||||||
|
root := fmt.tprintf("/tmp/brolang-test-process-schema-%d", index)
|
||||||
|
app := fmt.tprintf("%s/app", root)
|
||||||
|
io_dir := fmt.tprintf("%s/std/io", root)
|
||||||
|
process_dir := fmt.tprintf("%s/std/process", root)
|
||||||
|
main_source: string = `process :: import "@std/process"
|
||||||
|
main func(init process.Init) void { _ = init }
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(root)
|
||||||
|
defer _ = os2.remove_all(root)
|
||||||
|
testing.expect(t, os2.make_directory_all(app) == nil)
|
||||||
|
testing.expect(t, os2.make_directory_all(io_dir) == nil)
|
||||||
|
testing.expect(t, os2.make_directory_all(process_dir) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(
|
||||||
|
fmt.tprintf("%s/main.bro", app), transmute([]byte)main_source,
|
||||||
|
))
|
||||||
|
testing.expect(t, os.write_entire_file(
|
||||||
|
fmt.tprintf("%s/io.bro", io_dir), transmute([]byte)test_case.io_source,
|
||||||
|
))
|
||||||
|
testing.expect(t, os.write_entire_file(
|
||||||
|
fmt.tprintf("%s/process.bro", process_dir), transmute([]byte)test_case.process_source,
|
||||||
|
))
|
||||||
|
|
||||||
|
sources := source.init_store()
|
||||||
|
diagnostics := source.init_store_diagnostics(&sources)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
ast_module, loaded := loader.load(app, &sources, &diagnostics, &symbols, project_root_path=root)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
found := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
found = found || strings.contains(diagnostic.message, test_case.message)
|
||||||
|
}
|
||||||
|
testing.expect(t, loaded)
|
||||||
|
testing.expect(t, found)
|
||||||
|
hir.destroy_module(&hir_module)
|
||||||
|
ast.destroy_module(&ast_module)
|
||||||
|
symbol.destroy_table(&symbols)
|
||||||
|
source.destroy_diagnostics(&diagnostics)
|
||||||
|
source.destroy_store(&sources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_tuples_reflection_inline_for_and_debug_print_compile_and_run :: proc(t: ^testing.T) {
|
||||||
|
sources := source.init_store()
|
||||||
|
defer source.destroy_store(&sources)
|
||||||
|
diagnostics := source.init_store_diagnostics(&sources)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
ast_module, loaded := loader.load(
|
||||||
|
"examples/programs/tuples", &sources, &diagnostics, &symbols, project_root_path=".",
|
||||||
|
)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
ir_module := lower.lower(&hir_module)
|
||||||
|
defer ir.destroy_module(&ir_module)
|
||||||
|
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||||
|
defer delete(llvm_text)
|
||||||
|
testing.expect(t, loaded)
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
testing.expect(t, !strings.contains(llvm_text, "FormatToken"))
|
||||||
|
testing.expect(t, !strings.contains(llvm_text, "parse_format"))
|
||||||
|
testing.expect(t, !strings.contains(llvm_text, "FieldInfo"))
|
||||||
|
testing.expect(t, !strings.contains(llvm_text, "RecordInfo"))
|
||||||
|
|
||||||
|
output := "/tmp/brolang-test-tuples"
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
status := compiler_core.compile_package(
|
||||||
|
"examples/programs/tuples", output, nil, target.DEFAULT, cimport.Options{}, ".",
|
||||||
|
)
|
||||||
|
testing.expect_value(t, status, 0)
|
||||||
|
state, stdout, stderr, _ := os2.process_exec(
|
||||||
|
os2.Process_Desc{command=[]string{output}}, context.allocator,
|
||||||
|
)
|
||||||
|
defer delete(stdout)
|
||||||
|
defer delete(stderr)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
testing.expect_value(t, string(stdout), "")
|
||||||
|
testing.expect_value(t, string(stderr), "tuple=40/bro, limits=-9223372036854775808/18446744073709551615")
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_format_errors_are_reported_at_comptime :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-format-errors"
|
||||||
|
main_path := "/tmp/brolang-test-format-errors/main.bro"
|
||||||
|
text := `io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
|
main func(init process.Init) void {
|
||||||
|
writer io.Writer :: io.Writer {impl = init.io, stream = .stdout}
|
||||||
|
stored :: typeinfo!(i32)
|
||||||
|
_ = stored
|
||||||
|
io.print(writer, "", 1) catch |_| {}
|
||||||
|
io.print(writer, "{s}", {1,}) catch |_| {}
|
||||||
|
io.print(writer, "{d}", {"bro",}) catch |_| {}
|
||||||
|
io.print(writer, "{d}{d}", {1,}) catch |_| {}
|
||||||
|
io.print(writer, "{d}", {1, 2}) catch |_| {}
|
||||||
|
io.print(writer, "{x}", {}) catch |_| {}
|
||||||
|
io.print(writer, "}", {}) catch |_| {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
|
||||||
|
sources := source.init_store()
|
||||||
|
defer source.destroy_store(&sources)
|
||||||
|
diagnostics := source.init_store_diagnostics(&sources)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
ast_module, loaded := loader.load(directory, &sources, &diagnostics, &symbols, project_root_path=".")
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
found := [7]bool{}
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
message := diagnostic.message
|
||||||
|
found[0] = found[0] || strings.contains(message, "arguments must be a tuple")
|
||||||
|
found[1] = found[1] || strings.contains(message, "cannot implicitly convert i8 to []u8")
|
||||||
|
found[2] = found[2] || strings.contains(message, "'{d}' requires an integer")
|
||||||
|
found[3] = found[3] || strings.contains(message, "argument count does not match")
|
||||||
|
found[4] = found[4] || strings.contains(message, "unknown specifier")
|
||||||
|
found[5] = found[5] || strings.contains(message, "unmatched '}'")
|
||||||
|
found[6] = found[6] || strings.contains(message, "compile-time-only metadata")
|
||||||
|
}
|
||||||
|
testing.expect(t, loaded)
|
||||||
|
for present in found {
|
||||||
|
testing.expect(t, present)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_inline_loop_control_must_be_statically_resolvable :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
total i32 = 0
|
||||||
|
inline for {1, 2} |value| {
|
||||||
|
if total == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
total += value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
found = found || strings.contains(
|
||||||
|
diagnostic.message,
|
||||||
|
"break or continue targeting an inline loop must be compile-time-resolvable",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
testing.expect(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
milestone_33_rejects_an_incompatible_runtime_write_declaration :: proc(t: ^testing.T) {
|
milestone_33_rejects_an_incompatible_runtime_write_declaration :: proc(t: ^testing.T) {
|
||||||
text := `write c_func(_ c_int, _ c_int, _ c_ulong) c_long
|
text := `write c_func(_ c_int, _ c_int, _ c_ulong) c_long
|
||||||
@@ -2931,10 +3170,10 @@ main func() void {
|
|||||||
message := diagnostic.message
|
message := diagnostic.message
|
||||||
found_runtime_arg = found_runtime_arg || strings.contains(message, "must be a compile-time integer expression")
|
found_runtime_arg = found_runtime_arg || strings.contains(message, "must be a compile-time integer expression")
|
||||||
found_missing = found_missing || strings.contains(message, "cannot infer comptime parameter 'N'")
|
found_missing = found_missing || strings.contains(message, "cannot infer comptime parameter 'N'")
|
||||||
found_extra = found_extra || strings.contains(message, "expects 1 arguments with explicit comptime parameters or 0 with inferred comptime parameters, got 2")
|
found_extra = found_extra || strings.contains(message, "has no unique complete argument mapping")
|
||||||
found_negative = found_negative || strings.contains(message, "integer constant -1 does not fit in usize")
|
found_negative = found_negative || strings.contains(message, "integer constant -1 does not fit in usize")
|
||||||
found_range = found_range || strings.contains(message, "integer constant 300 does not fit in u8")
|
found_range = found_range || strings.contains(message, "integer constant 300 does not fit in u8")
|
||||||
found_bad_type = found_bad_type || strings.contains(message, "requires a concrete integer type")
|
found_bad_type = found_bad_type || strings.contains(message, "requires type, a concrete integer type, or immutable []u8")
|
||||||
found_assignment = found_assignment || strings.contains(message, "cannot assign comptime parameter 'N'")
|
found_assignment = found_assignment || strings.contains(message, "cannot assign comptime parameter 'N'")
|
||||||
found_address = found_address || strings.contains(message, "'&' requires an addressable location")
|
found_address = found_address || strings.contains(message, "'&' requires an addressable location")
|
||||||
}
|
}
|
||||||
@@ -2996,7 +3235,7 @@ main func() void {
|
|||||||
message := diagnostic.message
|
message := diagnostic.message
|
||||||
found_conflict = found_conflict || strings.contains(message, "conflicting inference for comptime parameter 'T': i32 and u32")
|
found_conflict = found_conflict || strings.contains(message, "conflicting inference for comptime parameter 'T': i32 and u32")
|
||||||
found_unknown = found_unknown || strings.contains(message, "cannot infer comptime parameter 'T'")
|
found_unknown = found_unknown || strings.contains(message, "cannot infer comptime parameter 'T'")
|
||||||
found_partial = found_partial || strings.contains(message, "expects 3 arguments with explicit comptime parameters or 1 with inferred comptime parameters, got 2")
|
found_partial = found_partial || strings.contains(message, "cannot infer comptime parameter 'N'")
|
||||||
if strings.contains(message, "cannot infer comptime parameter 'T'") {
|
if strings.contains(message, "cannot infer comptime parameter 'T'") {
|
||||||
unrecoverable += 1
|
unrecoverable += 1
|
||||||
}
|
}
|
||||||
@@ -3008,19 +3247,38 @@ main func() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
comptime_params_must_form_leading_prefix :: proc(t: ^testing.T) {
|
comptime_params_may_be_interleaved_and_are_erased_from_the_abi :: proc(t: ^testing.T) {
|
||||||
text := `valid func($T type, $N usize, value T) [N]T {
|
text := `valid func($T type, $N usize, value T) [N]T {
|
||||||
result [N]T = undefined
|
result [N]T = undefined
|
||||||
_ = value
|
_ = value
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
runtime_first func(value i32, $T type, $N usize) T { return value }
|
runtime_first func(value T, $T type, $N usize) T { return value }
|
||||||
split func($T type, value T, $N usize) [N]T {
|
split func($T type, value T, $N usize) [N]T {
|
||||||
result [N]T = undefined
|
result [N]T = undefined
|
||||||
_ = value
|
_ = value
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
main func() void {}
|
from_result func($T type) T {
|
||||||
|
value T = undefined
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
choose_mapping func($A usize, value i32, $B usize) [A]u8 {
|
||||||
|
result [A]u8 = undefined
|
||||||
|
_ = value
|
||||||
|
_ = B
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
main func() void {
|
||||||
|
_ = runtime_first(42, i32, 4)
|
||||||
|
_ = runtime_first(42, _, 4)
|
||||||
|
_ = split(i32, 42, 4)
|
||||||
|
_ = split(42, 4)
|
||||||
|
value i32 = from_result()
|
||||||
|
chosen [1]u8 :: choose_mapping(7, 2)
|
||||||
|
_ = value
|
||||||
|
_ = chosen
|
||||||
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
diagnostics := source.init_diagnostics(&source_file)
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
@@ -3034,13 +3292,17 @@ main func() void {}
|
|||||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
defer hir.destroy_module(&hir_module)
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
count := 0
|
for function in hir_module.functions {
|
||||||
for diagnostic in diagnostics.items {
|
name := symbol.resolve(&symbols, function.name)
|
||||||
if strings.contains(diagnostic.message, "comptime parameters must form a leading parameter prefix") {
|
if name == "runtime_first" || name == "split" {
|
||||||
count += 1
|
testing.expect_value(t, len(function.params), 1)
|
||||||
|
} else if name == "from_result" {
|
||||||
|
testing.expect_value(t, len(function.params), 0)
|
||||||
|
} else if name == "choose_mapping" {
|
||||||
|
testing.expect_value(t, len(function.params), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testing.expect_value(t, count, 2)
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
hide read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
hide read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
||||||
if buffer.len == 0 {
|
if buffer.len == 0 {
|
||||||
@@ -83,7 +84,7 @@ rejects_bad_read func() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rejects_no_progress func() bool {
|
rejects_no_progress func() bool {
|
||||||
io.write_all(writer_for(&write_none_vtable), "x") catch |err| {
|
io.print(writer_for(&write_none_vtable), "{s}", {"x",}) catch |err| {
|
||||||
return err == .no_progress
|
return err == .no_progress
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -96,7 +97,8 @@ rejects_bad_write func() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
main func(system std.Io) i32 {
|
main func(init process.Init) i32 {
|
||||||
|
system io.Io :: init.io
|
||||||
buffer [2]mut u8 = [0, 0]
|
buffer [2]mut u8 = [0, 0]
|
||||||
count usize :: io.read(reader_for(&ok_vtable), buffer[..]) catch 0
|
count usize :: io.read(reader_for(&ok_vtable), 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' {
|
||||||
@@ -108,17 +110,38 @@ main func(system std.Io) i32 {
|
|||||||
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
io.write_all(writer_for(&ok_vtable), "partial") catch |_| {
|
io.print(writer_for(&ok_vtable), "{s}{d}", {"partial", 37}) catch |_| {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
io.write_all(io.Writer {
|
io.print(io.Writer {
|
||||||
impl = system,
|
impl = system,
|
||||||
stream = .stdout,
|
stream = .stdout,
|
||||||
}, "io-ok\n") catch |_| {
|
}, "io-ok {d} {{bro}}\n", {37,}) catch |_| {
|
||||||
return 4
|
return 4
|
||||||
}
|
}
|
||||||
|
io.print(io.Writer {
|
||||||
|
impl = system,
|
||||||
|
stream = .stdout,
|
||||||
|
}, "bounds={d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d}\n", {
|
||||||
|
minval!(i8), maxval!(u8),
|
||||||
|
minval!(i16), maxval!(u16),
|
||||||
|
minval!(i32), maxval!(u32),
|
||||||
|
minval!(i64), maxval!(u64),
|
||||||
|
minval!(isize), maxval!(usize),
|
||||||
|
minval!(c_char), maxval!(c_char),
|
||||||
|
minval!(c_schar), maxval!(c_uchar),
|
||||||
|
minval!(c_short), maxval!(c_ushort),
|
||||||
|
minval!(c_int), maxval!(c_uint),
|
||||||
|
minval!(c_long), maxval!(c_ulong),
|
||||||
|
minval!(c_longlong), maxval!(c_ulonglong),
|
||||||
|
0, 0,
|
||||||
|
1, 1,
|
||||||
|
-1, 1,
|
||||||
|
}) catch |_| {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
debug :: import "@std/debug"
|
||||||
|
meta :: import "@std/meta"
|
||||||
|
|
||||||
|
Numbers :: struct { i8, i16, i32 }
|
||||||
|
Row :: struct { value i32 }
|
||||||
|
|
||||||
|
format func() []u8 {
|
||||||
|
return "tuple={d}/{s}, limits={d}/{d}"
|
||||||
|
}
|
||||||
|
|
||||||
|
sum func($T type, value T) i32 {
|
||||||
|
total i32 = 0
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.record |record|: inline for record.fields |field| {
|
||||||
|
total += i32(field!(value, field.name))
|
||||||
|
}
|
||||||
|
else: compile_error!("sum requires a record")
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
static_control func($T type, value T) i32 {
|
||||||
|
total i32 = 0
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.record |record|: inline for record.fields |field| {
|
||||||
|
{
|
||||||
|
if field.index == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
total += i32(field!(value, field.name))
|
||||||
|
match typeinfo!(field.type) {
|
||||||
|
.integer: {
|
||||||
|
if field.index == 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: _ = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total += 100
|
||||||
|
}
|
||||||
|
else: compile_error!("static_control requires a record")
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
row_value func(row Row) i32 {
|
||||||
|
return row.value
|
||||||
|
}
|
||||||
|
|
||||||
|
static_aggregates func() i32 {
|
||||||
|
total i32 = 0
|
||||||
|
inline for {Row {value = 2}, Row {value = 40}} |row| {
|
||||||
|
total += row_value(row)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
main func() i32 {
|
||||||
|
numbers Numbers = Numbers {1, 2, 39}
|
||||||
|
singleton :: {42,}
|
||||||
|
empty :: {}
|
||||||
|
block_value :: {
|
||||||
|
yield 42
|
||||||
|
}
|
||||||
|
_ = empty
|
||||||
|
if singleton.0 != block_value {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if sum(Numbers, numbers) != 42 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if static_control(Numbers, numbers) != 140 {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
if static_aggregates() != 42 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
field!(&numbers, "2") += 1
|
||||||
|
debug.print(format(), {
|
||||||
|
numbers.2,
|
||||||
|
"bro",
|
||||||
|
minval!(i64),
|
||||||
|
maxval!(u64),
|
||||||
|
})
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+1
-1
@@ -9,5 +9,5 @@ languages = ["languages/brolang"]
|
|||||||
|
|
||||||
[grammars.brolang]
|
[grammars.brolang]
|
||||||
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
||||||
rev = "ba171a2d5e248fa24f5f4ee960e21056893f72e7"
|
rev = "03f5445509575fb72226e8b4bbf230d7905b7b8e"
|
||||||
path = "tree-sitter-brolang"
|
path = "tree-sitter-brolang"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
(call_expression function: (expression (identifier) @function))
|
(call_expression function: (expression (identifier) @function))
|
||||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||||
(field_expression field: (identifier) @property)
|
(field_expression field: (identifier) @property)
|
||||||
|
(field_expression field: (integer) @property)
|
||||||
(field_initializer name: (identifier) @property)
|
(field_initializer name: (identifier) @property)
|
||||||
(keyed_field_initializer name: (identifier) @property)
|
(keyed_field_initializer name: (identifier) @property)
|
||||||
(record_field name: (identifier) @property)
|
(record_field name: (identifier) @property)
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
"distinct"
|
"distinct"
|
||||||
"alias"
|
"alias"
|
||||||
"import"
|
"import"
|
||||||
|
"hide"
|
||||||
"return"
|
"return"
|
||||||
"try"
|
"try"
|
||||||
"catch"
|
"catch"
|
||||||
@@ -70,6 +72,7 @@
|
|||||||
"if"
|
"if"
|
||||||
"while"
|
"while"
|
||||||
"for"
|
"for"
|
||||||
|
"inline"
|
||||||
"break"
|
"break"
|
||||||
"continue"
|
"continue"
|
||||||
"defer"
|
"defer"
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
c :: import "@ffi/c"
|
||||||
|
io :: import "@std/io"
|
||||||
|
|
||||||
|
hide write func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
||||||
|
request usize = bytes.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.write(2, bytes.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
if c.__error()^ != 4 {
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide read func(_ ?*mut anyopaque, _ io.ReadStream, _ []mut u8) usize ! io.ReadError {
|
||||||
|
return .read_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
hide vtable io.IoVTable :: io.IoVTable {
|
||||||
|
read = read,
|
||||||
|
write = write,
|
||||||
|
}
|
||||||
|
|
||||||
|
print func($format []u8, $Args type, args Args) void {
|
||||||
|
writer io.Writer :: io.Writer {
|
||||||
|
impl = io.Io {context = none, vtable = &vtable},
|
||||||
|
stream = .stderr,
|
||||||
|
}
|
||||||
|
io.print(writer, format, Args, args) catch |_| {}
|
||||||
|
}
|
||||||
+177
@@ -1,4 +1,5 @@
|
|||||||
c :: import "@ffi/c"
|
c :: import "@ffi/c"
|
||||||
|
meta :: import "@std/meta"
|
||||||
|
|
||||||
ReadError :: enum {
|
ReadError :: enum {
|
||||||
read_failed
|
read_failed
|
||||||
@@ -74,6 +75,182 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hide write_decimal_signed func(writer Writer, value i64) void ! WriteError {
|
||||||
|
buffer [21]mut u8 = undefined
|
||||||
|
end usize = buffer.len
|
||||||
|
current i64 = value
|
||||||
|
while true {
|
||||||
|
digit_value i64 :: rem!(current, 10)
|
||||||
|
digit u8 = 0
|
||||||
|
if digit_value < 0 {
|
||||||
|
digit = u8(-digit_value)
|
||||||
|
} else {
|
||||||
|
digit = u8(digit_value)
|
||||||
|
}
|
||||||
|
end -= 1
|
||||||
|
buffer[end] = '0' + digit
|
||||||
|
current = divtrunc!(current, 10)
|
||||||
|
if current == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value < 0 {
|
||||||
|
end -= 1
|
||||||
|
buffer[end] = '-'
|
||||||
|
}
|
||||||
|
try write_all(writer, buffer[end..])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hide write_decimal_unsigned func(writer Writer, value u64) void ! WriteError {
|
||||||
|
buffer [21]mut u8 = undefined
|
||||||
|
end usize = buffer.len
|
||||||
|
current u64 = value
|
||||||
|
while true {
|
||||||
|
digit u8 :: u8(rem!(current, 10))
|
||||||
|
end -= 1
|
||||||
|
buffer[end] = '0' + digit
|
||||||
|
current = divtrunc!(current, 10)
|
||||||
|
if current == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try write_all(writer, buffer[end..])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hide FormatTokenKind :: enum {
|
||||||
|
unused
|
||||||
|
literal
|
||||||
|
string
|
||||||
|
decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
hide FormatToken :: struct {
|
||||||
|
kind FormatTokenKind
|
||||||
|
start usize
|
||||||
|
end usize
|
||||||
|
arg usize
|
||||||
|
}
|
||||||
|
|
||||||
|
hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
||||||
|
tokens [N]mut FormatToken = undefined
|
||||||
|
for (usize(0))..format.len |index| {
|
||||||
|
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, arg = 0}
|
||||||
|
}
|
||||||
|
field_count usize = 0
|
||||||
|
match typeinfo!(Args) {
|
||||||
|
.record |record|: {
|
||||||
|
if !record.is_tuple {
|
||||||
|
compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
field_count = record.fields.len
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
|
||||||
|
token_count usize = 0
|
||||||
|
argument_count usize = 0
|
||||||
|
literal_start usize = 0
|
||||||
|
cursor usize = 0
|
||||||
|
while cursor < format.len {
|
||||||
|
byte :: format[cursor]
|
||||||
|
if byte == '{' {
|
||||||
|
if cursor + 1 >= format.len {
|
||||||
|
compile_error!("io.print format has an unmatched '{'")
|
||||||
|
}
|
||||||
|
if cursor > literal_start {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, arg = 0}
|
||||||
|
token_count += 1
|
||||||
|
}
|
||||||
|
next :: format[cursor + 1]
|
||||||
|
if next == '{' {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
|
||||||
|
token_count += 1
|
||||||
|
cursor += 2
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cursor + 2 >= format.len or format[cursor + 2] != '}' {
|
||||||
|
compile_error!("io.print format expects '{s}' or '{d}'")
|
||||||
|
}
|
||||||
|
kind FormatTokenKind = .unused
|
||||||
|
if next == 's' {
|
||||||
|
kind = .string
|
||||||
|
} else if next == 'd' {
|
||||||
|
kind = .decimal
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print format has an unknown specifier")
|
||||||
|
}
|
||||||
|
tokens[token_count] = FormatToken {kind = kind, start = 0, end = 0, arg = argument_count}
|
||||||
|
token_count += 1
|
||||||
|
argument_count += 1
|
||||||
|
cursor += 3
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if byte == '}' {
|
||||||
|
if cursor + 1 >= format.len or format[cursor + 1] != '}' {
|
||||||
|
compile_error!("io.print format has an unmatched '}'")
|
||||||
|
}
|
||||||
|
if cursor > literal_start {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, arg = 0}
|
||||||
|
token_count += 1
|
||||||
|
}
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
|
||||||
|
token_count += 1
|
||||||
|
cursor += 2
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cursor += 1
|
||||||
|
}
|
||||||
|
if literal_start < format.len {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, arg = 0}
|
||||||
|
}
|
||||||
|
if argument_count != field_count {
|
||||||
|
compile_error!("io.print format argument count does not match the tuple")
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
print func(
|
||||||
|
writer Writer,
|
||||||
|
$format []u8,
|
||||||
|
$Args type,
|
||||||
|
args Args,
|
||||||
|
) void ! WriteError {
|
||||||
|
match typeinfo!(Args) {
|
||||||
|
.record |record|: inline for $parse_format(format.len, format, Args) |token| {
|
||||||
|
if (token.kind == .literal) {
|
||||||
|
try write_all(writer, format[token.start..token.end])
|
||||||
|
}
|
||||||
|
if (token.kind == .string or token.kind == .decimal) {
|
||||||
|
inline for record.fields |field| {
|
||||||
|
if (field.index == token.arg) {
|
||||||
|
if (token.kind == .string) {
|
||||||
|
try write_all(writer, field!(args, field.name))
|
||||||
|
} else {
|
||||||
|
match typeinfo!(field.type) {
|
||||||
|
.integer: {
|
||||||
|
if minval!(field.type) < 0 {
|
||||||
|
try write_decimal_signed(writer, i64(field!(args, field.name)))
|
||||||
|
} else {
|
||||||
|
try write_decimal_unsigned(writer, u64(field!(args, field.name)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print '{d}' requires an integer argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
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))
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
Layout :: enum {
|
||||||
|
auto
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo :: struct {
|
||||||
|
name []u8
|
||||||
|
type type
|
||||||
|
index usize
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordInfo :: struct {
|
||||||
|
name ?[]u8
|
||||||
|
fields []FieldInfo
|
||||||
|
is_tuple bool
|
||||||
|
layout Layout
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInfo :: union(enum) {
|
||||||
|
invalid void
|
||||||
|
void void
|
||||||
|
anyopaque void
|
||||||
|
bool void
|
||||||
|
integer void
|
||||||
|
float void
|
||||||
|
array void
|
||||||
|
pointer void
|
||||||
|
slice void
|
||||||
|
range void
|
||||||
|
optional void
|
||||||
|
function void
|
||||||
|
enum void
|
||||||
|
record RecordInfo
|
||||||
|
union void
|
||||||
|
fallible void
|
||||||
|
distinct void
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
io :: import "@std/io"
|
||||||
|
|
||||||
|
Init :: struct {
|
||||||
|
io io.Io
|
||||||
|
}
|
||||||
@@ -30,6 +30,9 @@ module.exports = grammar({
|
|||||||
[$.array_type, $.expression],
|
[$.array_type, $.expression],
|
||||||
[$.array_type, $.array_literal],
|
[$.array_type, $.array_literal],
|
||||||
[$.expression_statement, $.parenthesized_expression],
|
[$.expression_statement, $.parenthesized_expression],
|
||||||
|
[$.block, $.tuple_literal],
|
||||||
|
[$.field_initializer, $.expression],
|
||||||
|
[$.tuple_literal],
|
||||||
],
|
],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
@@ -124,8 +127,8 @@ module.exports = grammar({
|
|||||||
'}',
|
'}',
|
||||||
),
|
),
|
||||||
|
|
||||||
record_field: $ => seq(
|
record_field: $ => choice(
|
||||||
field('name', $.identifier),
|
seq(field('name', $.identifier), field('type', choice($.type, $.struct_type))),
|
||||||
field('type', choice($.type, $.struct_type)),
|
field('type', choice($.type, $.struct_type)),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -336,6 +339,7 @@ module.exports = grammar({
|
|||||||
),
|
),
|
||||||
|
|
||||||
for_statement: $ => seq(
|
for_statement: $ => seq(
|
||||||
|
optional('inline'),
|
||||||
'for',
|
'for',
|
||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
field('iterable', $.expression),
|
field('iterable', $.expression),
|
||||||
@@ -393,6 +397,7 @@ module.exports = grammar({
|
|||||||
$.slice_expression,
|
$.slice_expression,
|
||||||
$.postfix_expression,
|
$.postfix_expression,
|
||||||
$.struct_literal,
|
$.struct_literal,
|
||||||
|
$.tuple_literal,
|
||||||
$.comptime_block,
|
$.comptime_block,
|
||||||
$.function_literal,
|
$.function_literal,
|
||||||
$.struct_type,
|
$.struct_type,
|
||||||
@@ -442,7 +447,7 @@ module.exports = grammar({
|
|||||||
field_expression: $ => prec.left(PREC.POSTFIX, seq(
|
field_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||||
field('value', $.expression),
|
field('value', $.expression),
|
||||||
'.',
|
'.',
|
||||||
field('field', $.identifier),
|
field('field', choice($.identifier, $.integer)),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
intrinsic_call_expression: $ => prec(PREC.POSTFIX, seq(
|
intrinsic_call_expression: $ => prec(PREC.POSTFIX, seq(
|
||||||
@@ -495,8 +500,8 @@ module.exports = grammar({
|
|||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
seq(
|
seq(
|
||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
$.field_initializer,
|
choice($.field_initializer, $.expression),
|
||||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.field_initializer)),
|
repeat(seq(repeat($._newline), ',', repeat($._newline), choice($.field_initializer, $.expression))),
|
||||||
optional(seq(repeat($._newline), ',')),
|
optional(seq(repeat($._newline), ',')),
|
||||||
repeat($._newline),
|
repeat($._newline),
|
||||||
),
|
),
|
||||||
@@ -509,6 +514,16 @@ module.exports = grammar({
|
|||||||
optional(seq('=', repeat($._newline), field('value', $.expression))),
|
optional(seq('=', repeat($._newline), field('value', $.expression))),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
tuple_literal: $ => prec(1, choice(
|
||||||
|
seq('{', repeat($._newline), '}'),
|
||||||
|
seq(
|
||||||
|
'{', repeat($._newline), $.expression,
|
||||||
|
',', repeat($._newline),
|
||||||
|
repeat(seq($.expression, ',', repeat($._newline))),
|
||||||
|
optional($.expression), repeat($._newline), '}',
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
|
||||||
enum_literal: $ => seq(
|
enum_literal: $ => seq(
|
||||||
'.',
|
'.',
|
||||||
field('name', $.identifier),
|
field('name', $.identifier),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
(call_expression function: (expression (identifier) @function))
|
(call_expression function: (expression (identifier) @function))
|
||||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||||
(field_expression field: (identifier) @property)
|
(field_expression field: (identifier) @property)
|
||||||
|
(field_expression field: (integer) @property)
|
||||||
(field_initializer name: (identifier) @property)
|
(field_initializer name: (identifier) @property)
|
||||||
(keyed_field_initializer name: (identifier) @property)
|
(keyed_field_initializer name: (identifier) @property)
|
||||||
(record_field name: (identifier) @property)
|
(record_field name: (identifier) @property)
|
||||||
@@ -71,6 +72,7 @@
|
|||||||
"if"
|
"if"
|
||||||
"while"
|
"while"
|
||||||
"for"
|
"for"
|
||||||
|
"inline"
|
||||||
"break"
|
"break"
|
||||||
"continue"
|
"continue"
|
||||||
"defer"
|
"defer"
|
||||||
|
|||||||
@@ -656,15 +656,37 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"record_field": {
|
"record_field": {
|
||||||
"type": "SEQ",
|
"type": "CHOICE",
|
||||||
"members": [
|
"members": [
|
||||||
{
|
{
|
||||||
"type": "FIELD",
|
"type": "SEQ",
|
||||||
"name": "name",
|
"members": [
|
||||||
"content": {
|
{
|
||||||
"type": "SYMBOL",
|
"type": "FIELD",
|
||||||
"name": "identifier"
|
"name": "name",
|
||||||
}
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "FIELD",
|
||||||
|
"name": "type",
|
||||||
|
"content": {
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "struct_type"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "FIELD",
|
"type": "FIELD",
|
||||||
@@ -2375,6 +2397,18 @@
|
|||||||
"for_statement": {
|
"for_statement": {
|
||||||
"type": "SEQ",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "inline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BLANK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"value": "for"
|
"value": "for"
|
||||||
@@ -2768,6 +2802,10 @@
|
|||||||
"type": "SYMBOL",
|
"type": "SYMBOL",
|
||||||
"name": "struct_literal"
|
"name": "struct_literal"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "tuple_literal"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "SYMBOL",
|
"type": "SYMBOL",
|
||||||
"name": "comptime_block"
|
"name": "comptime_block"
|
||||||
@@ -3316,8 +3354,17 @@
|
|||||||
"type": "FIELD",
|
"type": "FIELD",
|
||||||
"name": "field",
|
"name": "field",
|
||||||
"content": {
|
"content": {
|
||||||
"type": "SYMBOL",
|
"type": "CHOICE",
|
||||||
"name": "identifier"
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -3708,8 +3755,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "SYMBOL",
|
"type": "CHOICE",
|
||||||
"name": "field_initializer"
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "field_initializer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "expression"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "REPEAT",
|
"type": "REPEAT",
|
||||||
@@ -3735,8 +3791,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "SYMBOL",
|
"type": "CHOICE",
|
||||||
"name": "field_initializer"
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "field_initializer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "expression"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -3827,6 +3892,112 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"tuple_literal": {
|
||||||
|
"type": "PREC",
|
||||||
|
"value": 1,
|
||||||
|
"content": {
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SEQ",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "{"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SEQ",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "{"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": ","
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SEQ",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": ","
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "CHOICE",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BLANK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_newline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"enum_literal": {
|
"enum_literal": {
|
||||||
"type": "SEQ",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
@@ -4436,6 +4607,17 @@
|
|||||||
[
|
[
|
||||||
"expression_statement",
|
"expression_statement",
|
||||||
"parenthesized_expression"
|
"parenthesized_expression"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"block",
|
||||||
|
"tuple_literal"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"field_initializer",
|
||||||
|
"expression"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"tuple_literal"
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"precedences": [],
|
"precedences": [],
|
||||||
|
|||||||
@@ -770,6 +770,10 @@
|
|||||||
"type": "struct_type",
|
"type": "struct_type",
|
||||||
"named": true
|
"named": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "tuple_literal",
|
||||||
|
"named": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "unary_expression",
|
"type": "unary_expression",
|
||||||
"named": true
|
"named": true
|
||||||
@@ -807,6 +811,10 @@
|
|||||||
{
|
{
|
||||||
"type": "identifier",
|
"type": "identifier",
|
||||||
"named": true
|
"named": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"named": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1306,6 +1314,10 @@
|
|||||||
"multiple": true,
|
"multiple": true,
|
||||||
"required": false,
|
"required": false,
|
||||||
"types": [
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "expression",
|
||||||
|
"named": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "field_initializer",
|
"type": "field_initializer",
|
||||||
"named": true
|
"named": true
|
||||||
@@ -1677,7 +1689,7 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"name": {
|
"name": {
|
||||||
"multiple": false,
|
"multiple": false,
|
||||||
"required": true,
|
"required": false,
|
||||||
"types": [
|
"types": [
|
||||||
{
|
{
|
||||||
"type": "identifier",
|
"type": "identifier",
|
||||||
@@ -1950,6 +1962,21 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "tuple_literal",
|
||||||
|
"named": true,
|
||||||
|
"fields": {},
|
||||||
|
"children": {
|
||||||
|
"multiple": true,
|
||||||
|
"required": false,
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "expression",
|
||||||
|
"named": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "type",
|
"type": "type",
|
||||||
"named": true,
|
"named": true,
|
||||||
@@ -2608,6 +2635,10 @@
|
|||||||
"type": "import",
|
"type": "import",
|
||||||
"named": false
|
"named": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "inline",
|
||||||
|
"named": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"named": false
|
"named": false
|
||||||
|
|||||||
+123242
-115100
File diff suppressed because it is too large
Load Diff
@@ -277,3 +277,91 @@ choose func() i32 {
|
|||||||
(return_statement
|
(return_statement
|
||||||
(expression
|
(expression
|
||||||
(identifier)))))))
|
(identifier)))))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
Tuples and inline for
|
||||||
|
==================
|
||||||
|
|
||||||
|
Pair :: struct { i32, []u8 }
|
||||||
|
|
||||||
|
main func() void {
|
||||||
|
pair Pair = Pair {42, "bro"}
|
||||||
|
singleton :: {1,}
|
||||||
|
empty :: {}
|
||||||
|
_ = pair.0
|
||||||
|
inline for singleton |value| {
|
||||||
|
_ = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(type_declaration
|
||||||
|
(identifier)
|
||||||
|
(struct_type
|
||||||
|
(record_body
|
||||||
|
(record_field
|
||||||
|
(type
|
||||||
|
(builtin_type)))
|
||||||
|
(record_field
|
||||||
|
(type
|
||||||
|
(array_type
|
||||||
|
(type
|
||||||
|
(builtin_type))))))))
|
||||||
|
(function_declaration
|
||||||
|
(identifier)
|
||||||
|
(parameter_list)
|
||||||
|
(type
|
||||||
|
(builtin_type))
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(variable_declaration
|
||||||
|
(identifier)
|
||||||
|
(type
|
||||||
|
(named_type
|
||||||
|
(qualified_identifier
|
||||||
|
(identifier))))
|
||||||
|
(expression
|
||||||
|
(struct_literal
|
||||||
|
(qualified_identifier
|
||||||
|
(identifier))
|
||||||
|
(initializer_list
|
||||||
|
(expression
|
||||||
|
(integer))
|
||||||
|
(expression
|
||||||
|
(string
|
||||||
|
(string_content))))))))
|
||||||
|
(statement
|
||||||
|
(constant_declaration
|
||||||
|
(identifier)
|
||||||
|
(expression
|
||||||
|
(tuple_literal
|
||||||
|
(expression
|
||||||
|
(integer))))))
|
||||||
|
(statement
|
||||||
|
(constant_declaration
|
||||||
|
(identifier)
|
||||||
|
(expression
|
||||||
|
(tuple_literal))))
|
||||||
|
(statement
|
||||||
|
(assignment_statement
|
||||||
|
(expression
|
||||||
|
(sink))
|
||||||
|
(expression
|
||||||
|
(field_expression
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(integer)))))
|
||||||
|
(statement
|
||||||
|
(for_statement
|
||||||
|
(expression
|
||||||
|
(identifier))
|
||||||
|
(identifier)
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(assignment_statement
|
||||||
|
(expression
|
||||||
|
(sink))
|
||||||
|
(expression
|
||||||
|
(identifier))))))))))
|
||||||
|
|||||||
Reference in New Issue
Block a user