disambiguate enum blocks and complete distinct type operations
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
/build/
|
||||
/grammars/
|
||||
.DS_Store
|
||||
|
||||
+21
-4
@@ -29,7 +29,7 @@ roadmap and milestone history.
|
||||
- target-dependent C scalar primitives from `c_char` through `c_longdouble`, kept semantically distinct from native scalars
|
||||
- contextual integer/float/character literals, backward type-demand inference through names and arithmetic/bitwise expressions, and typed compile-time evaluation for numeric constant expressions
|
||||
- strict numeric conversion by default, widening where valid, C scalar coercions at C boundaries, and explicit scalar casts through keywords or transparent aliases, such as `i32(x)`, `c_float(x)`, or `StringId(x)`
|
||||
- compile-time `minval!(T)` and `maxval!(T)` bounds for concrete native and C integer scalar types
|
||||
- compile-time `minval!(T)` and `maxval!(T)` bounds for concrete native, C, and scalar-backed distinct integer types; the result retains `T`
|
||||
- arrays `[N]T`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T`
|
||||
- pointer mutability via `mut`, optional pointers as nullable pointers, pointer arithmetic for many-item pointers, postfix dereference `^`, and trapping optional unwrap `?`
|
||||
- pointer-to-array `.len`, indexing, slicing, `.ptr` on slices and pointers-to-arrays, implicit address-taking for array-variable slices, and pointer/slice sentinel weakening
|
||||
@@ -38,13 +38,31 @@ roadmap and milestone history.
|
||||
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings
|
||||
- narrow immutable zero-terminated byte pointer/slice conversion to `*c_char` / `?*c_char` without general `u8`/`c_char` interchange
|
||||
- optionals with `null`, `orelse`, postfix `?`, conditional unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps
|
||||
- nominal distinct types with exact backing construction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
|
||||
- nominal distinct types with exact backing construction and explicit scalar backing extraction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
|
||||
- source-order native structs, opaque nominal records with `Name :: opaque`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
|
||||
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
|
||||
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
||||
- native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids
|
||||
- fallible channel types `T ! E`, where `E` is a native enum/tagged union or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield`
|
||||
|
||||
#### distinct types
|
||||
|
||||
`Name :: distinct T` creates a nominal identity and reuses `T`'s runtime representation.
|
||||
Construction accepts exactly one value of the immediate backing type. There is no implicit
|
||||
conversion in either direction, and separate distinct declarations never mix. An explicit scalar
|
||||
cast extracts exactly one layer: `u32(id)` works for `UserID :: distinct u32`, while nested
|
||||
distinct values must be peeled one declared layer at a time.
|
||||
|
||||
Scalar-backed distinct values support the operations of their representation while preserving the
|
||||
nominal result type: checked integer `+`, `-`, `*`, unary `-`, bitwise operators, shifts,
|
||||
comparisons, and compound assignments; float arithmetic, unary `-`, comparisons, and compound
|
||||
assignments; and boolean equality/inequality. Integer literals and float literals are contextual,
|
||||
but typed backing values remain barred. Distinct integers also work as indices and slice bounds;
|
||||
`minval!` / `maxval!` return the distinct type. Runtime and comptime behavior match.
|
||||
|
||||
`typeinfo!(Distinct).backing` reports the immediate declared backing. Standard formatting peels
|
||||
distinct layers recursively, so all scalar format verbs behave like the final scalar backing.
|
||||
|
||||
#### native record constraint fields
|
||||
|
||||
A direct `int`, `uint`, `float`, or `range` field in a named native struct or union is a
|
||||
@@ -240,7 +258,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
|
||||
- `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/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `null` means no field default
|
||||
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
||||
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, recursively scalar-backed distinct values, and `{{` / `}}`, 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`
|
||||
- `std/testing` supplies fallible `expect`, expected-first `expect_equal`, and exact compile-time `expect_type`; direct calls through
|
||||
@@ -268,5 +286,4 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
|
||||
- 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
|
||||
- result-to-argument type-demand propagation through function call boundaries
|
||||
- distinct-type backing operators and reverse explicit conversions
|
||||
- string concatenation operator
|
||||
|
||||
@@ -228,6 +228,7 @@ Current prototype features:
|
||||
- Pointer-preserving `.ptr`/`.len`, pointer-to-array indexing and slicing, postfix pointer dereference and optional unwrap, and keyed struct literals
|
||||
- Contextual integer constants and typed compile-time evaluation of arithmetic and Zig-style bitwise expressions
|
||||
- Integer `~`, `&`, `|`, `xor`, guarded `<<` / `>>`, saturating `<<|`, and their compound assignments; postfix `^` remains pointer dereference
|
||||
- Scalar-backed nominal `distinct` types with same-identity runtime/comptime operators, explicit backing extraction casts, integer bounds/indexing, reflection, and recursive standard formatting
|
||||
- Directory packages with merged declarations and file-local relative imports
|
||||
- Relative C header imports as synthetic package namespaces
|
||||
- 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
|
||||
|
||||
@@ -139,10 +139,12 @@
|
||||
|
||||
8. distinct types (implemented; see below)
|
||||
- nominal declarations preserve identity across packages and reuse the backing runtime representation
|
||||
- construction uses `Type(value)` with exactly one value of the exact backing type
|
||||
- no implicit conversion to or from the backing type
|
||||
- backing-type operators and reverse explicit conversions remain deferred
|
||||
- concrete runtime backing types are supported; unresolved, `int`, `void`, function, and opaque backings are rejected
|
||||
- construction accepts exactly one value of the immediate backing type; no implicit conversion
|
||||
crosses the nominal boundary or mixes separate distinct declarations
|
||||
- explicit scalar casts extract one declared distinct layer at a time
|
||||
- scalar-backed values support matching runtime/comptime arithmetic, bitwise, shift, comparison,
|
||||
compound-assignment, bounds, indexing, reflection, and standard formatting behavior
|
||||
- unresolved, `int`, `void`, function, and opaque backings remain invalid runtime declarations
|
||||
|
||||
9. allow pointer field access pass-through (implemented)
|
||||
- having a pointer (`ptr`) to a struct, we should allow access through `ptr.field` as opposed to mandating `ptr^.field`
|
||||
@@ -829,9 +831,6 @@
|
||||
conflicting targets
|
||||
- root `std` re-exports only `ArrayList(T)` for now; operations remain under `std/arraylist`
|
||||
|
||||
35. syntax highlighting (tree-sitter) updates (implemented)
|
||||
- pointer sigils are highlighted as operators
|
||||
- type-factory calls in type positions and struct literals are highlighted as functions
|
||||
|
||||
36. transitive package namespaces (spike completed; no language change)
|
||||
- imports remain file-local implementation details, including explicitly named imports such as
|
||||
@@ -1062,16 +1061,28 @@ For-loop captures are immutable and scoped to the loop body. Sequence index capt
|
||||
|
||||
## A word on distinct types
|
||||
|
||||
Distinct types are considered distinct from their backing type. They do not implicitly coerce to their backing type.
|
||||
Distinct declarations are nominal even when they share a backing type. Construction requires the
|
||||
exact immediate backing, implicit conversion is forbidden in either direction, and an explicit
|
||||
scalar cast extracts one layer:
|
||||
|
||||
```
|
||||
# distinct type
|
||||
```bro
|
||||
UserID :: distinct u32
|
||||
OuterID :: distinct UserID
|
||||
|
||||
# instantiate distinct type
|
||||
my_id UserID :: UserID(42) # value must have the exact backing type
|
||||
id UserID :: UserID(u32(42))
|
||||
raw u32 :: u32(id)
|
||||
outer OuterID :: OuterID(id)
|
||||
inner UserID :: UserID(outer)
|
||||
```
|
||||
|
||||
Scalar-backed distinct values retain their nominal type across the operations supported by the
|
||||
backing scalar. Integer forms support checked arithmetic, bitwise operations, shifts, comparisons,
|
||||
compound assignments, indexing, slicing, and `minval!` / `maxval!`; float forms support arithmetic
|
||||
and comparisons; boolean forms support equality and inequality. Separate distinct identities and
|
||||
typed backing operands never mix, though literals receive the distinct context. Runtime and
|
||||
comptime rules are identical. Reflection reports the immediate backing, while standard formatting
|
||||
recursively follows nested distinct backings to the final scalar.
|
||||
|
||||
## A word on enums
|
||||
|
||||
```
|
||||
|
||||
+388
-128
@@ -942,13 +942,21 @@ type_builtin_value :: proc(checker: ^Checker, kind: Type_Builtin, value: types.T
|
||||
case .Align_Of:
|
||||
return i128(types.alignment_of(value, &checker.module.types, checker.target))
|
||||
case .Min_Value:
|
||||
if types.is_unsigned(value, checker.target) {
|
||||
representation := value
|
||||
if backing, ok := types.distinct_scalar_backing(value, &checker.module.types); ok {
|
||||
representation = backing
|
||||
}
|
||||
if types.is_unsigned(representation, checker.target) {
|
||||
return 0
|
||||
}
|
||||
return -(i128(1) << u32(types.bits(value, checker.target)-1))
|
||||
return -(i128(1) << u32(types.bits(representation, checker.target)-1))
|
||||
case .Max_Value:
|
||||
bit_count := types.bits(value, checker.target)
|
||||
sign_bit_count := 1 if types.is_signed(value, checker.target) else 0
|
||||
representation := value
|
||||
if backing, ok := types.distinct_scalar_backing(value, &checker.module.types); ok {
|
||||
representation = backing
|
||||
}
|
||||
bit_count := types.bits(representation, checker.target)
|
||||
sign_bit_count := 1 if types.is_signed(representation, checker.target) else 0
|
||||
return (i128(1) << u32(bit_count-sign_bit_count))-1
|
||||
case:
|
||||
return 0
|
||||
@@ -976,16 +984,22 @@ build_type_builtin :: proc(
|
||||
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a sized runtime value type, got %s", type_label(checker, target))
|
||||
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
||||
}
|
||||
if (kind == .Min_Value || kind == .Max_Value) && !types.is_concrete_integer(target) {
|
||||
bound_representation := target
|
||||
if backing, ok := types.distinct_scalar_backing(target, &checker.module.types); ok {
|
||||
bound_representation = backing
|
||||
}
|
||||
if (kind == .Min_Value || kind == .Max_Value) && !types.is_concrete_integer(bound_representation) {
|
||||
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "integer bound target must be a concrete integer type, got %s", type_label(checker, target))
|
||||
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
||||
}
|
||||
result_type := types.USIZE if kind == .Size_Of || kind == .Align_Of else target
|
||||
_, distinct_ok := types.distinct_scalar_backing(result_type, &checker.module.types)
|
||||
return build_constant_expr(
|
||||
checker,
|
||||
expr,
|
||||
Constant{kind=.Value, value=type_builtin_value(checker, kind, target)},
|
||||
result_type,
|
||||
distinct_ok,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4363,7 +4377,9 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
(tag == "array" && !types.equal(field.type, array_info)) ||
|
||||
(tag == "record" && !types.equal(field.type, record_info)) ||
|
||||
(tag == "enum" && !types.equal(field.type, enum_info)) ||
|
||||
(tag != "array" && tag != "record" && tag != "enum" && !types.is_void(field.type)) {
|
||||
(tag == "distinct" && !is_type_metatype_syntax(checker, field.type)) ||
|
||||
(tag != "array" && tag != "record" && tag != "enum" && tag != "distinct" &&
|
||||
!types.is_void(field.type)) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
@@ -4704,6 +4720,43 @@ mark_spec_demanded :: proc(checker: ^Checker, id: Spec_Id, stack: ^[dynamic]Spec
|
||||
append(stack, id)
|
||||
}
|
||||
|
||||
Numeric_Operation_Type :: struct {
|
||||
result: types.Type,
|
||||
representation: types.Type,
|
||||
}
|
||||
|
||||
numeric_operation_type :: proc(checker: ^Checker, left, right: types.Type) -> (Numeric_Operation_Type, bool) {
|
||||
if types.equal(left, right) {
|
||||
if representation, ok := types.distinct_scalar_backing(left, &checker.module.types); ok {
|
||||
return Numeric_Operation_Type{result=left, representation=representation}, true
|
||||
}
|
||||
}
|
||||
result := types.widest(left, right)
|
||||
if !types.is_concrete_scalar(result) {
|
||||
return {}, false
|
||||
}
|
||||
return Numeric_Operation_Type{result=result, representation=result}, true
|
||||
}
|
||||
|
||||
numeric_literal_accepts_type :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr_Id,
|
||||
demand: types.Type,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> bool {
|
||||
representation := demand
|
||||
if backing, ok := types.distinct_scalar_backing(demand, &checker.module.types); ok {
|
||||
representation = backing
|
||||
}
|
||||
if constant := eval_integer_constant_in_context(checker, expr, pkg, file); constant.kind == .Value {
|
||||
return types.is_float(representation, checker.target) ||
|
||||
types.is_concrete_integer(representation) &&
|
||||
fits_integer_type(constant.value, representation, checker.target)
|
||||
}
|
||||
return is_float_constant_expr(checker, expr) && types.is_float(representation, checker.target)
|
||||
}
|
||||
|
||||
Infer_Frame :: struct {
|
||||
expr: ast.Expr_Id,
|
||||
expected: types.Type,
|
||||
@@ -4715,6 +4768,8 @@ Infer_Frame :: struct {
|
||||
mapping: []int,
|
||||
args: []types.Type,
|
||||
template: ast.Function_Id,
|
||||
numeric_operation: bool,
|
||||
reverse_operands: bool,
|
||||
}
|
||||
|
||||
merge_inferred_test_error :: proc(checker: ^Checker, incoming: types.Type) {
|
||||
@@ -4772,8 +4827,16 @@ infer_division_builtin :: proc(
|
||||
right_hint := hint if types.is_valid(hint) else left
|
||||
right = infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types, right_hint)
|
||||
}
|
||||
result := types.widest(left, right)
|
||||
return result if types.is_concrete_scalar(result) && !types.is_bool(result) else types.INVALID
|
||||
if left_const && types.is_distinct(right, &checker.module.types) &&
|
||||
numeric_literal_accepts_type(checker, expr.args[0], right, pkg, file) {
|
||||
left = right
|
||||
}
|
||||
if right_const && types.is_distinct(left, &checker.module.types) &&
|
||||
numeric_literal_accepts_type(checker, expr.args[1], left, pkg, file) {
|
||||
right = left
|
||||
}
|
||||
operation, ok := numeric_operation_type(checker, left, right)
|
||||
return operation.result if ok && !types.is_bool(operation.representation) else types.INVALID
|
||||
}
|
||||
|
||||
infer_compound_expr :: proc(
|
||||
@@ -4796,28 +4859,38 @@ infer_compound_expr :: proc(
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
return types.BOOL
|
||||
case .Bit_Not:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
operand := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, hint)
|
||||
return operand if types.is_concrete_integer(operand) else types.INVALID
|
||||
operand := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
representation := types.runtime_representation(operand, store)
|
||||
return operand if types.is_concrete_integer(representation) else types.INVALID
|
||||
case .Bit_And, .Bit_Or, .Bit_Xor:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
left_const := is_numeric_constant_expr(checker, expr.left)
|
||||
right_const := is_numeric_constant_expr(checker, expr.right)
|
||||
left, right := types.INVALID, types.INVALID
|
||||
if left_const && !right_const && !types.is_valid(hint) {
|
||||
if types.is_concrete_integer(expected) {
|
||||
left = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, expected)
|
||||
right = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, expected)
|
||||
} else if left_const && !right_const {
|
||||
right = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
||||
left = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, right)
|
||||
left = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
} else {
|
||||
left = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, hint)
|
||||
right = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, hint if types.is_valid(hint) else left)
|
||||
left = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
right = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
||||
}
|
||||
result := types.widest(left, right)
|
||||
return result if types.is_concrete_integer(result) else types.INVALID
|
||||
if left_const && types.is_distinct(right, store) &&
|
||||
numeric_literal_accepts_type(checker, expr.left, right, pkg, file) {
|
||||
left = right
|
||||
}
|
||||
if right_const && types.is_distinct(left, store) &&
|
||||
numeric_literal_accepts_type(checker, expr.right, left, pkg, file) {
|
||||
right = left
|
||||
}
|
||||
operation, ok := numeric_operation_type(checker, left, right)
|
||||
return operation.result if ok && types.is_concrete_integer(operation.representation) else types.INVALID
|
||||
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, hint)
|
||||
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
right := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, types.U64)
|
||||
return left if types.is_concrete_integer(left) && types.is_unsigned(right, checker.target) else types.INVALID
|
||||
representation := types.runtime_representation(left, store)
|
||||
return left if types.is_concrete_integer(representation) && types.is_unsigned(right, checker.target) else types.INVALID
|
||||
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
|
||||
left_expr := checker.ast_module.exprs[expr.left]
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
@@ -5135,6 +5208,16 @@ infer_expr :: proc(
|
||||
continue
|
||||
}
|
||||
if constant.kind == .Value {
|
||||
if frame.numeric_operation {
|
||||
if representation, ok := types.distinct_scalar_backing(frame.expected, &checker.module.types);
|
||||
ok && (types.is_float(representation, checker.target) ||
|
||||
types.is_concrete_integer(representation) &&
|
||||
fits_integer_type(constant.value, representation, checker.target)) {
|
||||
last = frame.expected
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
}
|
||||
last = constraint_integer_literal_type(frame.expected, constant.value)
|
||||
if !types.is_valid(last) {
|
||||
last = types.I64
|
||||
@@ -5162,7 +5245,15 @@ infer_expr :: proc(
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Float:
|
||||
last = types.F64
|
||||
if frame.numeric_operation {
|
||||
if representation, ok := types.distinct_scalar_backing(frame.expected, &checker.module.types);
|
||||
ok && types.is_float(representation, checker.target) {
|
||||
last = frame.expected
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
}
|
||||
last = frame.expected if types.is_float(frame.expected, checker.target) else types.F64
|
||||
_ = pop(&stack)
|
||||
case .String, .Array, .Null, .Unreachable, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal, .Cast,
|
||||
@@ -5294,7 +5385,17 @@ infer_expr :: proc(
|
||||
append(&stack, Infer_Frame{expr=expr.left, template=ast.INVALID_FUNCTION})
|
||||
case .Add, .Sub, .Mul, .Div:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Infer_Frame{expr=expr.left, template=ast.INVALID_FUNCTION})
|
||||
left_const := is_numeric_constant_expr(checker, expr.left)
|
||||
right_const := is_numeric_constant_expr(checker, expr.right)
|
||||
stack[frame_index].reverse_operands = left_const && !right_const
|
||||
first := expr.right if stack[frame_index].reverse_operands else expr.left
|
||||
first_expected := frame.expected if
|
||||
types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) else types.INVALID
|
||||
append(&stack, Infer_Frame{
|
||||
expr=first,
|
||||
expected=first_expected,
|
||||
template=ast.INVALID_FUNCTION,
|
||||
})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
callee_type := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
@@ -5567,38 +5668,54 @@ infer_expr :: proc(
|
||||
if frame.stage == 1 {
|
||||
stack[frame_index].left = last
|
||||
stack[frame_index].stage = 2
|
||||
append(&stack, Infer_Frame{expr=expr.right, template=ast.INVALID_FUNCTION})
|
||||
second := expr.left if frame.reverse_operands else expr.right
|
||||
second_expected := types.INVALID
|
||||
numeric_operation := false
|
||||
if is_numeric_constant_expr(checker, second) {
|
||||
second_expected = last
|
||||
if representation, ok := types.distinct_scalar_backing(last, &checker.module.types);
|
||||
ok && !types.is_bool(representation) {
|
||||
numeric_operation = true
|
||||
}
|
||||
}
|
||||
append(&stack, Infer_Frame{
|
||||
expr=second,
|
||||
expected=second_expected,
|
||||
template=ast.INVALID_FUNCTION,
|
||||
numeric_operation=numeric_operation,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if frame.stage == 2 {
|
||||
right := last
|
||||
if expr.kind == .Add && types.is_many_pointer(frame.left, &checker.module.types) && types.is_concrete_integer(right) {
|
||||
last = frame.left
|
||||
first, second := frame.left, last
|
||||
left, right := first, second
|
||||
if frame.reverse_operands {
|
||||
left, right = second, first
|
||||
}
|
||||
if expr.kind == .Add && types.is_many_pointer(left, &checker.module.types) && types.is_concrete_integer(right) {
|
||||
last = left
|
||||
} else if operation, ok := numeric_operation_type(checker, left, right); ok {
|
||||
last = operation.result
|
||||
} else if is_numeric_constant_expr(checker, expr.right) &&
|
||||
is_numeric_demand(frame.left, checker.target) &&
|
||||
expr_accepts_numeric_demand(checker, expr.right, frame.left, locals, pkg, file) {
|
||||
last = frame.left
|
||||
is_numeric_demand(left, checker.target) &&
|
||||
expr_accepts_numeric_demand(checker, expr.right, left, locals, pkg, file) {
|
||||
last = left
|
||||
} else if is_numeric_constant_expr(checker, expr.left) &&
|
||||
is_numeric_demand(right, checker.target) &&
|
||||
expr_accepts_numeric_demand(checker, expr.left, right, locals, pkg, file) {
|
||||
last = right
|
||||
} else if is_numeric_demand(frame.left, checker.target) &&
|
||||
} else if is_numeric_demand(left, checker.target) &&
|
||||
!numeric_operand_is_open(checker, expr.left, locals, pkg, file) &&
|
||||
expr_accepts_numeric_demand(checker, expr.right, frame.left, locals, pkg, file) {
|
||||
// Propagate only from an authoritative (fixed-type) left operand. A left
|
||||
// operand that is still a provisional open constant carries only its
|
||||
// smallest-signed default, which must not poison the sibling's family;
|
||||
// two provisional operands are resolved together by the backward demand
|
||||
// from the declaration/use.
|
||||
_ = record_demand(checker, expr.right, frame.left, locals, local_types, pkg, file)
|
||||
last = frame.left
|
||||
expr_accepts_numeric_demand(checker, expr.right, left, locals, pkg, file) {
|
||||
_ = record_demand(checker, expr.right, left, locals, local_types, pkg, file)
|
||||
last = left
|
||||
} else if is_numeric_demand(right, checker.target) &&
|
||||
!numeric_operand_is_open(checker, expr.right, locals, pkg, file) &&
|
||||
expr_accepts_numeric_demand(checker, expr.left, right, locals, pkg, file) {
|
||||
_ = record_demand(checker, expr.left, right, locals, local_types, pkg, file)
|
||||
last = right
|
||||
} else {
|
||||
last = types.widest(frame.left, right)
|
||||
last = types.INVALID
|
||||
}
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
@@ -7214,23 +7331,33 @@ build_constant_expr :: proc(
|
||||
expr: ast.Expr,
|
||||
constant: Constant,
|
||||
expected: types.Type,
|
||||
numeric_operation := false,
|
||||
) -> hir.Expr_Id {
|
||||
materialized := expected
|
||||
nominal := types.INVALID
|
||||
if numeric_operation {
|
||||
if representation, ok := types.distinct_scalar_backing(expected, &checker.module.types);
|
||||
ok && !types.is_bool(representation) {
|
||||
materialized = representation
|
||||
nominal = expected
|
||||
}
|
||||
}
|
||||
recovery_type := types.I64
|
||||
if types.is_concrete_integer(expected) {
|
||||
if types.is_concrete_integer(materialized) {
|
||||
recovery_type = expected
|
||||
}
|
||||
// An integer constant in a float context (e.g. `pi float = 3`) folds to a
|
||||
// float literal, mirroring build_float_expr's bit packing.
|
||||
if constant.kind == .Value && types.is_float(expected, checker.target) {
|
||||
if constant.kind == .Value && types.is_float(materialized, checker.target) {
|
||||
fval := f64(constant.value) // ponytail: silent precision loss past 2^53, like C int->double
|
||||
bits := transmute(i64)fval
|
||||
if types.bits(expected, checker.target) == 32 {
|
||||
if types.bits(materialized, checker.target) == 32 {
|
||||
bits = i64(transmute(u32)f32(fval))
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind = .Float,
|
||||
span = expr.span,
|
||||
type = expected,
|
||||
type = nominal if types.is_valid(nominal) else materialized,
|
||||
integer = bits,
|
||||
target = hir.INVALID_REF,
|
||||
left = hir.INVALID_EXPR,
|
||||
@@ -7251,7 +7378,7 @@ build_constant_expr :: proc(
|
||||
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
||||
}
|
||||
if constant.kind == .Overflow ||
|
||||
(!types.is_concrete_integer(expected) && expected != types.UINT && !fits_i64(constant.value)) {
|
||||
(!types.is_concrete_integer(materialized) && materialized != types.UINT && !fits_i64(constant.value)) {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -7264,8 +7391,8 @@ build_constant_expr :: proc(
|
||||
if constant.value >= 0 && constant.value <= i128(0xffff_ffff_ffff_ffff) {
|
||||
value = transmute(i64)u64(constant.value)
|
||||
}
|
||||
result_type := constraint_integer_literal_type(expected, constant.value)
|
||||
if expected == types.UINT && !types.is_valid(result_type) {
|
||||
result_type := constraint_integer_literal_type(materialized, constant.value)
|
||||
if materialized == types.UINT && !types.is_valid(result_type) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -7274,8 +7401,8 @@ build_constant_expr :: proc(
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, types.U64)
|
||||
}
|
||||
if types.is_concrete_integer(expected) {
|
||||
if !fits_integer_type(constant.value, expected, checker.target) {
|
||||
if types.is_concrete_integer(materialized) {
|
||||
if !fits_integer_type(constant.value, materialized, checker.target) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -7285,7 +7412,7 @@ build_constant_expr :: proc(
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, expected)
|
||||
}
|
||||
result_type = expected
|
||||
result_type = nominal if types.is_valid(nominal) else materialized
|
||||
}
|
||||
return add_hir_expr(
|
||||
checker,
|
||||
@@ -7302,11 +7429,26 @@ build_constant_expr :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
build_float_expr :: proc(checker: ^Checker, expr: ast.Expr, expected: types.Type) -> hir.Expr_Id {
|
||||
build_float_expr :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
expected: types.Type,
|
||||
numeric_operation := false,
|
||||
) -> hir.Expr_Id {
|
||||
materialized := expected
|
||||
result_type := types.F64
|
||||
if types.is_float(expected, checker.target) {
|
||||
result_type = expected
|
||||
} else if types.is_valid(expected) {
|
||||
if numeric_operation {
|
||||
if representation, ok := types.distinct_scalar_backing(expected, &checker.module.types);
|
||||
ok && types.is_float(representation, checker.target) {
|
||||
materialized = representation
|
||||
result_type = expected
|
||||
}
|
||||
}
|
||||
if types.is_float(materialized, checker.target) {
|
||||
if !types.is_valid(result_type) || !types.is_distinct(result_type, &checker.module.types) {
|
||||
result_type = materialized
|
||||
}
|
||||
} else if types.is_valid(materialized) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -7317,7 +7459,7 @@ build_float_expr :: proc(checker: ^Checker, expr: ast.Expr, expected: types.Type
|
||||
}
|
||||
value := transmute(f64)expr.integer
|
||||
bits := transmute(i64)value
|
||||
if types.bits(result_type, checker.target) == 32 {
|
||||
if types.bits(materialized, checker.target) == 32 {
|
||||
bits = i64(transmute(u32)f32(value))
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
@@ -7346,6 +7488,8 @@ Build_Expr_Frame :: struct {
|
||||
arg_types: []types.Type,
|
||||
template: ast.Function_Id,
|
||||
resolution: int,
|
||||
numeric_operation: bool,
|
||||
reverse_operands: bool,
|
||||
}
|
||||
|
||||
hir_location_writable :: proc(checker: ^Checker, expr_id: hir.Expr_Id, locals: []Build_Local) -> bool {
|
||||
@@ -7731,11 +7875,23 @@ build_scalar_cast :: proc(
|
||||
) -> hir.Expr_Id {
|
||||
store := &checker.module.types
|
||||
actual := checker.module.exprs[value].type
|
||||
if types.can_retype_distinct(actual, target, store) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Retype,
|
||||
span=span,
|
||||
type=target,
|
||||
left=value,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
valid_target := types.is_concrete_scalar(target) && !types.is_bool(target)
|
||||
actual_repr := types.runtime_representation(actual, store)
|
||||
actual_item, actual_item_ok := types.node(store, actual)
|
||||
explicit_enum := actual_item_ok && actual_item.kind == .Enum && actual_item.explicit_backing
|
||||
valid_actual := (types.is_concrete_scalar(actual) || explicit_enum) &&
|
||||
_, distinct_scalar := types.distinct_scalar_backing(actual, store)
|
||||
valid_actual := (types.is_concrete_scalar(actual) || explicit_enum || distinct_scalar) &&
|
||||
types.is_concrete_scalar(actual_repr) && !types.is_bool(actual_repr)
|
||||
if !valid_target || !valid_actual {
|
||||
id := source.addf(
|
||||
@@ -7757,6 +7913,38 @@ build_scalar_cast :: proc(
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
normalize_index_expr :: proc(checker: ^Checker, value: hir.Expr_Id, span: source.Span) -> hir.Expr_Id {
|
||||
store := &checker.module.types
|
||||
current := value
|
||||
current_type := checker.module.exprs[current].type
|
||||
representation, distinct_ok := types.distinct_scalar_backing(current_type, store)
|
||||
if distinct_ok && types.is_concrete_integer(representation) {
|
||||
for {
|
||||
backing, ok := types.distinct_backing(current_type, store)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
current = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Retype,
|
||||
span=span,
|
||||
type=backing,
|
||||
left=current,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
current_type = backing
|
||||
}
|
||||
}
|
||||
current_expr := &checker.module.exprs[current]
|
||||
if current_expr.kind == .Integer &&
|
||||
fits_integer_type(i128(current_expr.integer), types.USIZE, checker.target) {
|
||||
current_expr.type = types.USIZE
|
||||
return current
|
||||
}
|
||||
return coerce_expr(checker, current, types.USIZE, span)
|
||||
}
|
||||
|
||||
|
||||
build_function_value :: proc(
|
||||
checker: ^Checker,
|
||||
@@ -7836,11 +8024,12 @@ build_nested_expr :: proc(
|
||||
expected: types.Type,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
numeric_operation := false,
|
||||
) -> hir.Expr_Id {
|
||||
outer := checker.build_stack
|
||||
checker.build_stack = nil
|
||||
checker.build_stack.allocator = checker.allocator
|
||||
result := build_expr(checker, expr_id, locals, global_reads, calls, expected, pkg, file)
|
||||
result := build_expr(checker, expr_id, locals, global_reads, calls, expected, pkg, file, numeric_operation)
|
||||
delete(checker.build_stack)
|
||||
checker.build_stack = outer
|
||||
return result
|
||||
@@ -7901,19 +8090,29 @@ build_division_builtin :: proc(
|
||||
left, right := hir.INVALID_EXPR, hir.INVALID_EXPR
|
||||
if left_const && !right_const && !types.is_valid(hint) {
|
||||
right = build_nested_expr(checker, expr.args[1], locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left = build_nested_expr(checker, expr.args[0], locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
|
||||
right_type := checker.module.exprs[right].type
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_type, &checker.module.types)
|
||||
left = build_nested_expr(checker, expr.args[0], locals, global_reads, calls, right_type, pkg, file, distinct_ok)
|
||||
} else {
|
||||
left = build_nested_expr(checker, expr.args[0], locals, global_reads, calls, hint, pkg, file)
|
||||
right_hint := hint if types.is_valid(hint) else checker.module.exprs[left].type
|
||||
right = build_nested_expr(checker, expr.args[1], locals, global_reads, calls, right_hint, pkg, file)
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_hint, &checker.module.types)
|
||||
right = build_nested_expr(
|
||||
checker, expr.args[1], locals, global_reads, calls, right_hint, pkg, file,
|
||||
distinct_ok && right_const,
|
||||
)
|
||||
}
|
||||
result := types.widest(checker.module.exprs[left].type, checker.module.exprs[right].type)
|
||||
if !types.is_concrete_scalar(result) || types.is_bool(result) {
|
||||
operation, ok := numeric_operation_type(
|
||||
checker,
|
||||
checker.module.exprs[left].type,
|
||||
checker.module.exprs[right].type,
|
||||
)
|
||||
if !ok || types.is_bool(operation.representation) {
|
||||
id := source.add(checker.diagnostics, expr.span, "division builtins require compatible numeric operands")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
left = coerce_expr(checker, left, result, checker.module.exprs[left].span)
|
||||
right = coerce_expr(checker, right, result, checker.module.exprs[right].span)
|
||||
left = coerce_expr(checker, left, operation.result, checker.module.exprs[left].span)
|
||||
right = coerce_expr(checker, right, operation.result, checker.module.exprs[right].span)
|
||||
result_kind := hir.Expr_Kind.Div_Trunc
|
||||
#partial switch kind {
|
||||
case .Floor: result_kind = .Div_Floor
|
||||
@@ -7924,7 +8123,7 @@ build_division_builtin :: proc(
|
||||
case:
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=result_kind, span=expr.span, type=result, left=left, right=right,
|
||||
kind=result_kind, span=expr.span, type=operation.result, left=left, right=right,
|
||||
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
@@ -8303,8 +8502,8 @@ build_compound_expr :: proc(
|
||||
})
|
||||
case .Index:
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
index := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
index = coerce_expr(checker, index, types.USIZE, expr.span)
|
||||
index := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
index = normalize_index_expr(checker, index, expr.span)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, container, index); propagated {
|
||||
return invalid
|
||||
}
|
||||
@@ -8347,8 +8546,8 @@ build_compound_expr :: proc(
|
||||
bounds[1] = hir.INVALID_EXPR
|
||||
for bound, index in expr.args {
|
||||
if bound != ast.INVALID_EXPR {
|
||||
bounds[index] = build_nested_expr(checker, bound, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
bounds[index] = coerce_expr(checker, bounds[index], types.USIZE, checker.ast_module.exprs[bound].span)
|
||||
bounds[index] = build_nested_expr(checker, bound, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
bounds[index] = normalize_index_expr(checker, bounds[index], checker.ast_module.exprs[bound].span)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, bounds[index]); propagated {
|
||||
delete(bounds, checker.allocator)
|
||||
return invalid
|
||||
@@ -8578,13 +8777,13 @@ build_compound_expr :: proc(
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Bit_Not:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
operand := build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file)
|
||||
operand := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, operand); propagated {
|
||||
return invalid
|
||||
}
|
||||
operand_type := checker.module.exprs[operand].type
|
||||
if !types.is_concrete_integer(operand_type) {
|
||||
representation := types.runtime_representation(operand_type, store)
|
||||
if !types.is_concrete_integer(representation) {
|
||||
id := source.add(checker.diagnostics, expr.span, "'~' requires a concrete integer operand")
|
||||
return invalid_hir_expr(checker, expr.span, id, operand_type)
|
||||
}
|
||||
@@ -8593,47 +8792,59 @@ build_compound_expr :: proc(
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Bit_And, .Bit_Or, .Bit_Xor:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
left_const := is_numeric_constant_expr(checker, expr.left)
|
||||
right_const := is_numeric_constant_expr(checker, expr.right)
|
||||
left, right: hir.Expr_Id
|
||||
if left_const && !right_const && !types.is_valid(hint) {
|
||||
if types.is_concrete_integer(expected) {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, expected, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, expected, pkg, file)
|
||||
} else if left_const && !right_const {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
|
||||
right_type := checker.module.exprs[right].type
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_type, store)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, right_type, pkg, file, distinct_ok)
|
||||
} else {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file)
|
||||
right_hint := hint if types.is_valid(hint) else checker.module.exprs[left].type
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, right_hint, pkg, file)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right_hint := checker.module.exprs[left].type
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_hint, store)
|
||||
right = build_nested_expr(
|
||||
checker, expr.right, locals, global_reads, calls, right_hint, pkg, file,
|
||||
distinct_ok && right_const,
|
||||
)
|
||||
}
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
result_type := types.widest(checker.module.exprs[left].type, checker.module.exprs[right].type)
|
||||
if !types.is_concrete_integer(result_type) {
|
||||
operation, ok := numeric_operation_type(
|
||||
checker,
|
||||
checker.module.exprs[left].type,
|
||||
checker.module.exprs[right].type,
|
||||
)
|
||||
if !ok || !types.is_concrete_integer(operation.representation) {
|
||||
id := source.add(checker.diagnostics, expr.span, "bitwise operation requires compatible concrete integer operands")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
left = coerce_expr(checker, left, result_type, checker.module.exprs[left].span)
|
||||
right = coerce_expr(checker, right, result_type, checker.module.exprs[right].span)
|
||||
left = coerce_expr(checker, left, operation.result, checker.module.exprs[left].span)
|
||||
right = coerce_expr(checker, right, operation.result, checker.module.exprs[right].span)
|
||||
kind := hir.Expr_Kind.Bit_And
|
||||
#partial switch expr.kind {
|
||||
case .Bit_Or: kind = .Bit_Or
|
||||
case .Bit_Xor: kind = .Bit_Xor
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=kind, span=expr.span, type=result_type, left=left, right=right,
|
||||
kind=kind, span=expr.span, type=operation.result, left=left, right=right,
|
||||
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
|
||||
hint := expected if types.is_concrete_integer(expected) else types.INVALID
|
||||
left := build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file)
|
||||
left := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.U64, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
left_type := checker.module.exprs[left].type
|
||||
right_type := checker.module.exprs[right].type
|
||||
if !types.is_concrete_integer(left_type) {
|
||||
left_representation := types.runtime_representation(left_type, store)
|
||||
if !types.is_concrete_integer(left_representation) {
|
||||
id := source.add(checker.diagnostics, checker.module.exprs[left].span, "shifted value must be a concrete integer")
|
||||
return invalid_hir_expr(checker, expr.span, id, left_type)
|
||||
}
|
||||
@@ -8643,7 +8854,7 @@ build_compound_expr :: proc(
|
||||
}
|
||||
if constant := eval_integer_constant_in_context(checker, expr.right, pkg, file);
|
||||
constant.kind == .Value && expr.kind != .Shift_Left_Saturating &&
|
||||
constant.value >= i128(types.bits(left_type, checker.target)) {
|
||||
constant.value >= i128(types.bits(left_representation, checker.target)) {
|
||||
id := source.addf(
|
||||
checker.diagnostics, checker.module.exprs[right].span,
|
||||
"shift count %d exceeds %s width", constant.value, types.name(left_type),
|
||||
@@ -8703,11 +8914,13 @@ build_compound_expr :: proc(
|
||||
} else if right_numeric_const && !left_numeric_const {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
hint := checker.module.exprs[left].type
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, hint, pkg, file)
|
||||
_, distinct_ok := types.distinct_scalar_backing(hint, store)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, hint, pkg, file, distinct_ok)
|
||||
} else if left_numeric_const && !right_numeric_const {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
hint := checker.module.exprs[right].type
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file)
|
||||
_, distinct_ok := types.distinct_scalar_backing(hint, store)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file, distinct_ok)
|
||||
} else {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
@@ -8738,11 +8951,13 @@ build_compound_expr :: proc(
|
||||
}
|
||||
operand_type = types.BOOL
|
||||
} else {
|
||||
operand_type = types.widest(left_type, right_type)
|
||||
if !types.is_concrete_scalar(operand_type) || types.is_bool(operand_type) {
|
||||
operation, ok := numeric_operation_type(checker, left_type, right_type)
|
||||
if !ok ||
|
||||
types.is_bool(operation.representation) && expr.kind != .Eq && expr.kind != .Ne {
|
||||
id := source.add(checker.diagnostics, expr.span, "comparison requires compatible numeric operands")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
|
||||
}
|
||||
operand_type = operation.result
|
||||
}
|
||||
left = coerce_expr(checker, left, operand_type, checker.module.exprs[left].span)
|
||||
right = coerce_expr(checker, right, operand_type, checker.module.exprs[right].span)
|
||||
@@ -8978,17 +9193,21 @@ build_binary_arith :: proc(
|
||||
left=left, right=right, target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
result := types.widest(checker.module.exprs[left].type, checker.module.exprs[right].type)
|
||||
if !types.is_concrete_scalar(result) {
|
||||
operation, ok := numeric_operation_type(
|
||||
checker,
|
||||
checker.module.exprs[left].type,
|
||||
checker.module.exprs[right].type,
|
||||
)
|
||||
if !ok || types.is_bool(operation.representation) {
|
||||
id := source.add(checker.diagnostics, span, "arithmetic requires compatible numeric operands")
|
||||
return invalid_hir_expr(checker, span, id)
|
||||
}
|
||||
if op == .Div && !types.is_float(result, checker.target) {
|
||||
if op == .Div && !types.is_float(operation.representation, checker.target) {
|
||||
id := source.add(
|
||||
checker.diagnostics, span,
|
||||
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id, result)
|
||||
return invalid_hir_expr(checker, span, id, operation.result)
|
||||
}
|
||||
result_kind := hir.Expr_Kind.Add
|
||||
#partial switch op {
|
||||
@@ -8996,10 +9215,10 @@ build_binary_arith :: proc(
|
||||
case .Mul: result_kind = .Mul
|
||||
case .Div: result_kind = .Div
|
||||
}
|
||||
coerced_left := coerce_expr(checker, left, result, checker.module.exprs[left].span)
|
||||
coerced_right := coerce_expr(checker, right, result, checker.module.exprs[right].span)
|
||||
coerced_left := coerce_expr(checker, left, operation.result, checker.module.exprs[left].span)
|
||||
coerced_right := coerce_expr(checker, right, operation.result, checker.module.exprs[right].span)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=result_kind, span=span, type=result, left=coerced_left, right=coerced_right,
|
||||
kind=result_kind, span=span, type=operation.result, left=coerced_left, right=coerced_right,
|
||||
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
@@ -9013,6 +9232,7 @@ build_expr :: proc(
|
||||
expected := types.INVALID,
|
||||
pkg := ast.Package_Id(0),
|
||||
file := ast.File_Id(0),
|
||||
numeric_operation := false,
|
||||
) -> hir.Expr_Id {
|
||||
stack := checker.build_stack
|
||||
checker.build_stack = nil
|
||||
@@ -9030,7 +9250,12 @@ build_expr :: proc(
|
||||
delete(stack)
|
||||
}
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr_id, expected=expected, template=ast.INVALID_FUNCTION})
|
||||
append(&stack, Build_Expr_Frame{
|
||||
expr=expr_id,
|
||||
expected=expected,
|
||||
template=ast.INVALID_FUNCTION,
|
||||
numeric_operation=numeric_operation,
|
||||
})
|
||||
last := hir.INVALID_EXPR
|
||||
|
||||
for len(stack) > 0 {
|
||||
@@ -9067,7 +9292,7 @@ build_expr :: proc(
|
||||
constant = eval_constant(checker, frame.expr)
|
||||
}
|
||||
if constant.kind == .Value || constant.kind == .Overflow || constant.kind == .Div_By_Zero || constant.kind == .Non_Exact {
|
||||
last = build_constant_expr(checker, expr, constant, frame.expected)
|
||||
last = build_constant_expr(checker, expr, constant, frame.expected, frame.numeric_operation)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
@@ -9093,7 +9318,7 @@ build_expr :: proc(
|
||||
last = invalid_hir_expr(checker, expr.span, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
case .Float:
|
||||
last = build_float_expr(checker, expr, frame.expected)
|
||||
last = build_float_expr(checker, expr, frame.expected, frame.numeric_operation)
|
||||
_ = pop(&stack)
|
||||
case .Name:
|
||||
last = hir.INVALID_EXPR
|
||||
@@ -9269,13 +9494,20 @@ build_expr :: proc(
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
case .Add, .Sub, .Mul, .Div:
|
||||
stack[frame_index].stage = 1
|
||||
// Preserve assignment/return context for literal operands, e.g.
|
||||
// assigning `i + 1` back into a `u32` local.
|
||||
left_expected := types.INVALID
|
||||
if types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) {
|
||||
left_expected = frame.expected
|
||||
left_const := is_numeric_constant_expr(checker, expr.left)
|
||||
right_const := is_numeric_constant_expr(checker, expr.right)
|
||||
stack[frame_index].reverse_operands = left_const && !right_const
|
||||
first := expr.right if stack[frame_index].reverse_operands else expr.left
|
||||
first_expected := types.INVALID
|
||||
if !stack[frame_index].reverse_operands &&
|
||||
types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) {
|
||||
first_expected = frame.expected
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=left_expected, template=ast.INVALID_FUNCTION})
|
||||
append(&stack, Build_Expr_Frame{
|
||||
expr=first,
|
||||
expected=first_expected,
|
||||
template=ast.INVALID_FUNCTION,
|
||||
})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
stack[frame_index].stage = 6
|
||||
@@ -9481,7 +9713,7 @@ build_expr :: proc(
|
||||
constructor_type := types.resolve_alias(named_type, &checker.module.types)
|
||||
constructor_item, constructor_ok := types.node(&checker.module.types, constructor_type)
|
||||
scalar_alias := named_ok && named_item.kind == .Alias &&
|
||||
types.is_concrete_scalar(constructor_type) && !types.is_bool(constructor_type)
|
||||
types.is_concrete_scalar(constructor_type)
|
||||
if scalar_alias {
|
||||
if len(expr.args) != 1 {
|
||||
id := source.addf(
|
||||
@@ -9681,7 +9913,8 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
operand_type := checker.module.exprs[operand].type
|
||||
if !types.is_signed(operand_type, checker.target) && !types.is_float(operand_type, checker.target) {
|
||||
representation := types.runtime_representation(operand_type, &checker.module.types)
|
||||
if !types.is_signed(representation, checker.target) && !types.is_float(representation, checker.target) {
|
||||
id := source.add(checker.diagnostics, expr.span, "negation requires a signed integer or float")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else {
|
||||
@@ -9689,7 +9922,10 @@ build_expr :: proc(
|
||||
kind=.Negate, span=expr.span, type=operand_type, left=operand,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
if types.is_signed(frame.expected, checker.target) || types.is_float(frame.expected, checker.target) {
|
||||
expected_representation := types.runtime_representation(frame.expected, &checker.module.types)
|
||||
if types.equal(operand_type, frame.expected) &&
|
||||
(types.is_signed(expected_representation, checker.target) ||
|
||||
types.is_float(expected_representation, checker.target)) {
|
||||
last = coerce_expr(checker, last, frame.expected, expr.span)
|
||||
}
|
||||
}
|
||||
@@ -9699,21 +9935,36 @@ build_expr :: proc(
|
||||
if frame.stage == 1 {
|
||||
stack[frame_index].left = last
|
||||
stack[frame_index].stage = 2
|
||||
right_expected := types.INVALID
|
||||
if types.is_many_pointer(checker.module.exprs[last].type, &checker.module.types) {
|
||||
right_expected = types.USIZE
|
||||
} else if eval_constant(checker, expr.right).kind == .Value {
|
||||
// A constant RHS adopts the concrete LHS type before numeric
|
||||
// compatibility is checked.
|
||||
right_expected = checker.module.exprs[last].type
|
||||
first_type := checker.module.exprs[last].type
|
||||
second := expr.left if frame.reverse_operands else expr.right
|
||||
second_expected := types.INVALID
|
||||
numeric_operation := false
|
||||
if types.is_many_pointer(first_type, &checker.module.types) {
|
||||
second_expected = types.USIZE
|
||||
} else if is_numeric_constant_expr(checker, second) {
|
||||
second_expected = first_type
|
||||
if representation, ok := types.distinct_scalar_backing(first_type, &checker.module.types);
|
||||
ok && !types.is_bool(representation) {
|
||||
numeric_operation = true
|
||||
}
|
||||
} else if types.is_concrete_scalar(frame.expected) && !types.is_bool(frame.expected) {
|
||||
right_expected = frame.expected
|
||||
second_expected = frame.expected
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.right, expected=right_expected, template=ast.INVALID_FUNCTION})
|
||||
append(&stack, Build_Expr_Frame{
|
||||
expr=second,
|
||||
expected=second_expected,
|
||||
template=ast.INVALID_FUNCTION,
|
||||
numeric_operation=numeric_operation,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if frame.stage == 2 {
|
||||
last = build_binary_arith(checker, expr.kind, frame.left, last, expr.span)
|
||||
first, second := frame.left, last
|
||||
left, right := first, second
|
||||
if frame.reverse_operands {
|
||||
left, right = second, first
|
||||
}
|
||||
last = build_binary_arith(checker, expr.kind, left, right, expr.span)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
@@ -10062,7 +10313,8 @@ build_expr :: proc(
|
||||
if frame.stage == 8 {
|
||||
distinct_item, ok := types.node(&checker.module.types, frame.target_type)
|
||||
actual := checker.module.exprs[last].type
|
||||
if !ok || distinct_item.kind != .Distinct || !types.equal(actual, distinct_item.child) {
|
||||
if !ok || distinct_item.kind != .Distinct ||
|
||||
!types.can_retype_distinct(actual, frame.target_type, &checker.module.types) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -10892,9 +11144,16 @@ build_block :: proc(
|
||||
statement.assignment_op == .Shift_Left_Saturating {
|
||||
rhs_expected = types.U64
|
||||
}
|
||||
rhs_numeric_operation := false
|
||||
if statement.assignment_op != .Shift_Left &&
|
||||
statement.assignment_op != .Shift_Right &&
|
||||
statement.assignment_op != .Shift_Left_Saturating &&
|
||||
is_numeric_constant_expr(checker, statement.expr) {
|
||||
_, rhs_numeric_operation = types.distinct_scalar_backing(target_type, &checker.module.types)
|
||||
}
|
||||
value = build_expr(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
rhs_expected, ctx.pkg, ctx.file,
|
||||
rhs_expected, ctx.pkg, ctx.file, rhs_numeric_operation,
|
||||
)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, value); invalid {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
@@ -10937,14 +11196,15 @@ build_block :: proc(
|
||||
is_bitwise := statement.assignment_op == .Bit_And ||
|
||||
statement.assignment_op == .Bit_Or ||
|
||||
statement.assignment_op == .Bit_Xor
|
||||
result_type := types.widest(target_type, rhs_type)
|
||||
operation, compatible := numeric_operation_type(checker, target_type, rhs_type)
|
||||
target_representation := types.runtime_representation(target_type, &checker.module.types)
|
||||
if is_shift {
|
||||
if !types.is_concrete_integer(target_type) || !types.is_unsigned(rhs_type, checker.target) {
|
||||
if !types.is_concrete_integer(target_representation) || !types.is_unsigned(rhs_type, checker.target) {
|
||||
id := source.add(checker.diagnostics, statement.span, "shift assignment requires an integer target and unsigned integer count")
|
||||
value = invalid_hir_expr(checker, statement.span, id, target_type)
|
||||
} else if constant := eval_integer_constant_in_context(checker, statement.expr, ctx.pkg, ctx.file);
|
||||
constant.kind == .Value && statement.assignment_op != .Shift_Left_Saturating &&
|
||||
constant.value >= i128(types.bits(target_type, checker.target)) {
|
||||
constant.value >= i128(types.bits(target_representation, checker.target)) {
|
||||
id := source.addf(
|
||||
checker.diagnostics, statement.span,
|
||||
"shift count %d exceeds %s width", constant.value, types.name(target_type),
|
||||
@@ -10952,21 +11212,21 @@ build_block :: proc(
|
||||
value = invalid_hir_expr(checker, statement.span, id, target_type)
|
||||
}
|
||||
} else if is_bitwise {
|
||||
if !types.is_concrete_integer(result_type) {
|
||||
if !compatible || !types.is_concrete_integer(operation.representation) {
|
||||
id := source.add(checker.diagnostics, statement.span, "bitwise assignment requires compatible concrete integer operands")
|
||||
value = invalid_hir_expr(checker, statement.span, id, target_type)
|
||||
} else {
|
||||
value = coerce_expr(checker, value, target_type, statement.span)
|
||||
}
|
||||
} else if statement.assignment_op == .Div && types.is_concrete_integer(result_type) {
|
||||
} else if statement.assignment_op == .Div &&
|
||||
compatible && types.is_concrete_integer(operation.representation) {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"integer '/=' is not allowed; assign through an explicit division builtin",
|
||||
)
|
||||
value = invalid_hir_expr(checker, statement.span, id, target_type)
|
||||
} else if !types.is_concrete_scalar(result_type) ||
|
||||
types.is_bool(result_type) {
|
||||
} else if !compatible || types.is_bool(operation.representation) {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
|
||||
+263
-93
@@ -10,7 +10,6 @@ import "base:intrinsics"
|
||||
import "core:fmt"
|
||||
import "core:hash"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
|
||||
COMPTIME_EVAL_QUOTA :: 100_000
|
||||
@@ -79,6 +78,13 @@ current_comptime_type :: proc(checker: ^Checker, name: symbol.Id) -> (types.Type
|
||||
if value, ok := current_comptime_value(checker, name); ok && value.kind == .Type {
|
||||
return value.type, true
|
||||
}
|
||||
if binding, ok := current_static_binding(checker, name);
|
||||
ok && binding.value != INVALID_CT_VALUE && int(binding.value) < len(checker.static_state.values) {
|
||||
value := checker.static_state.values[binding.value]
|
||||
if value.kind == .Type {
|
||||
return types.Type(value.index), true
|
||||
}
|
||||
}
|
||||
return types.INVALID, false
|
||||
}
|
||||
|
||||
@@ -1089,16 +1095,21 @@ ct_materialize_value :: proc(
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
return build_constant_expr(checker, ast.Expr{span=span}, Constant{kind=.Value, value=value.integer}, value.type)
|
||||
_, distinct_ok := types.distinct_scalar_backing(value.type, &checker.module.types)
|
||||
return build_constant_expr(
|
||||
checker, ast.Expr{span=span}, Constant{kind=.Value, value=value.integer},
|
||||
value.type, distinct_ok,
|
||||
)
|
||||
case .Bool:
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Bool, span=span, type=types.BOOL, integer=i64(value.integer),
|
||||
kind=.Bool, span=span, type=value.type, integer=i64(value.integer),
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Float:
|
||||
bits := transmute(i64)value.float
|
||||
if types.bits(value.type, checker.target) == 32 {
|
||||
representation := types.runtime_representation(value.type, &checker.module.types)
|
||||
if types.bits(representation, checker.target) == 32 {
|
||||
bits = i64(transmute(u32)f32(value.float))
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
@@ -1251,6 +1262,7 @@ ct_eval_expr :: proc(
|
||||
expr_id: ast.Expr_Id,
|
||||
expected := types.INVALID,
|
||||
depth := 0,
|
||||
numeric_operation := false,
|
||||
) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
if depth > 128 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
||||
@@ -1263,6 +1275,22 @@ ct_eval_expr :: proc(
|
||||
store := &checker.module.types
|
||||
#partial switch expr.kind {
|
||||
case .Integer:
|
||||
if numeric_operation {
|
||||
if representation, distinct_ok := types.distinct_scalar_backing(expected, store); distinct_ok {
|
||||
if types.is_float(representation, checker.target) {
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.Float, type=expected, float=f64(expr.integer),
|
||||
}), ct_flow(.Normal), true
|
||||
}
|
||||
value := i128(expr.integer)
|
||||
if types.is_concrete_integer(representation) &&
|
||||
fits_integer_type(value, representation, checker.target) {
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.Integer, type=expected, integer=value,
|
||||
}), ct_flow(.Normal), true
|
||||
}
|
||||
}
|
||||
}
|
||||
value_type := expected if types.is_concrete_integer(expected) || types.is_enum(expected, store) else ct_default_integer_type(i128(expr.integer))
|
||||
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value_type, integer=i128(expr.integer)})
|
||||
if types.is_valid(expected) {
|
||||
@@ -1272,6 +1300,14 @@ ct_eval_expr :: proc(
|
||||
case .Bool:
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=i128(expr.integer)}), ct_flow(.Normal), true
|
||||
case .Float:
|
||||
if numeric_operation {
|
||||
if representation, distinct_ok := types.distinct_scalar_backing(expected, store);
|
||||
distinct_ok && types.is_float(representation, checker.target) {
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.Float, type=expected, float=transmute(f64)expr.integer,
|
||||
}), ct_flow(.Normal), true
|
||||
}
|
||||
}
|
||||
value_type := expected if types.is_float(expected, checker.target) else types.F64
|
||||
return ct_add_value(state, Ct_Value{kind=.Float, type=value_type, float=transmute(f64)expr.integer}), ct_flow(.Normal), true
|
||||
case .String:
|
||||
@@ -1416,10 +1452,17 @@ ct_eval_expr :: proc(
|
||||
}
|
||||
return ct_eval_field_value(state, base_id, expr.name, expr.span)
|
||||
case .Index:
|
||||
index_id, index_flow, index_ok := ct_eval_expr(state, expr.right, types.USIZE, depth+1)
|
||||
index_id, index_flow, index_ok := ct_eval_expr(state, expr.right, types.INVALID, depth+1)
|
||||
if !index_ok || index_flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, index_flow, index_ok
|
||||
}
|
||||
index_type := state.values[index_id].type
|
||||
index_representation := types.runtime_representation(index_type, store)
|
||||
index_literal := is_numeric_constant_expr(checker, expr.right)
|
||||
if !types.is_concrete_integer(index_representation) ||
|
||||
!index_literal && !can_implicitly_convert_type(checker, index_representation, types.USIZE) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime index must be coercible to usize")
|
||||
}
|
||||
index_value, index_is_int := ct_integer_value(state, index_id)
|
||||
if !index_is_int || index_value < 0 || index_value > i128(0x7fff_ffff) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime index must be a non-negative integer")
|
||||
@@ -1541,17 +1584,12 @@ ct_eval_expr :: proc(
|
||||
kind=.Range, type=types.range(store, child_type), start=start, count=2, active=i64(expr.integer),
|
||||
}), ct_flow(.Normal), true
|
||||
case .Negate, .Not, .Bit_Not:
|
||||
value, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1)
|
||||
value, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1, numeric_operation)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
return ct_eval_unary(state, expr.kind, value, expr.span)
|
||||
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
|
||||
left_expected := expected if expr.kind == .Div && types.is_float(expected, checker.target) else types.INVALID
|
||||
if (expr.kind == .Bit_And || expr.kind == .Bit_Or || expr.kind == .Bit_Xor) &&
|
||||
types.is_concrete_integer(expected) {
|
||||
left_expected = expected
|
||||
}
|
||||
left_expr := checker.ast_module.exprs[expr.left]
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
if left_expr.kind == .Null && right_expr.kind != .Null &&
|
||||
@@ -1566,14 +1604,39 @@ ct_eval_expr :: proc(
|
||||
}
|
||||
return ct_eval_binary(state, expr.kind, left, right, expr.span)
|
||||
}
|
||||
left, flow, ok := ct_eval_expr(state, expr.left, left_expected, depth+1)
|
||||
left_const := is_numeric_constant_expr(checker, expr.left)
|
||||
right_const := is_numeric_constant_expr(checker, expr.right)
|
||||
left, right := INVALID_CT_VALUE, INVALID_CT_VALUE
|
||||
flow := ct_flow(.Normal)
|
||||
ok := false
|
||||
if left_const && !right_const {
|
||||
right, flow, ok = ct_eval_expr(state, expr.right, types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
_, distinct_ok := types.distinct_scalar_backing(state.values[right].type, store)
|
||||
left, flow, ok = ct_eval_expr(
|
||||
state, expr.left, state.values[right].type, depth+1, distinct_ok,
|
||||
)
|
||||
} else {
|
||||
left_expected := types.INVALID
|
||||
if types.is_concrete_integer(expected) ||
|
||||
expr.kind == .Div && types.is_float(expected, checker.target) {
|
||||
left_expected = expected
|
||||
}
|
||||
left, flow, ok = ct_eval_expr(state, expr.left, left_expected, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
right_expected := state.values[left].type
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_expected, store)
|
||||
right, flow, ok = ct_eval_expr(
|
||||
state, expr.right, right_expected, depth+1, distinct_ok && right_const,
|
||||
)
|
||||
}
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
right, right_flow, right_ok := ct_eval_expr(state, expr.right, state.values[left].type, depth+1)
|
||||
if !right_ok || right_flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, right_flow, right_ok
|
||||
}
|
||||
return ct_eval_binary(state, expr.kind, left, right, expr.span)
|
||||
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
|
||||
left, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1)
|
||||
@@ -1899,10 +1962,17 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
|
||||
if bound == ast.INVALID_EXPR {
|
||||
continue
|
||||
}
|
||||
value, bound_flow, bound_ok := ct_eval_expr(state, bound, types.USIZE, depth+1)
|
||||
value, bound_flow, bound_ok := ct_eval_expr(state, bound, types.INVALID, depth+1)
|
||||
if !bound_ok || bound_flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, bound_flow, bound_ok
|
||||
}
|
||||
bound_type := state.values[value].type
|
||||
bound_representation := types.runtime_representation(bound_type, store)
|
||||
bound_literal := is_numeric_constant_expr(checker, bound)
|
||||
if !types.is_concrete_integer(bound_representation) ||
|
||||
!bound_literal && !can_implicitly_convert_type(checker, bound_representation, types.USIZE) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "slice bounds must be coercible to usize")
|
||||
}
|
||||
integer, integer_ok := ct_integer_value(state, value)
|
||||
if !integer_ok || integer < 0 || integer > i128(0x7fff_ffff) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "slice bounds must be non-negative integers")
|
||||
@@ -2356,10 +2426,11 @@ ct_unwrap_optional :: proc(state: ^Ct_State, id: Ct_Value_Id, span: source.Span)
|
||||
}
|
||||
|
||||
ct_normalize_integer :: proc(state: ^Ct_State, value: i128, type: types.Type) -> i128 {
|
||||
bits := types.bits(type, state.checker.target)
|
||||
representation := types.runtime_representation(type, &state.checker.module.types)
|
||||
bits := types.bits(representation, state.checker.target)
|
||||
mask := (i128(1) << u32(bits))-1
|
||||
raw := value & mask
|
||||
if types.is_signed(type, state.checker.target) {
|
||||
if types.is_signed(representation, state.checker.target) {
|
||||
sign := i128(1) << u32(bits-1)
|
||||
if raw & sign != 0 {
|
||||
return raw-(i128(1) << u32(bits))
|
||||
@@ -2380,13 +2451,15 @@ ct_eval_unary :: proc(state: ^Ct_State, op: ast.Expr_Kind, id: Ct_Value_Id, span
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if value.integer == 0 else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if op == .Bit_Not {
|
||||
if value.kind != .Integer || !types.is_concrete_integer(value.type) {
|
||||
representation := types.runtime_representation(value.type, &state.checker.module.types)
|
||||
if value.kind != .Integer || !types.is_concrete_integer(representation) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'~' requires a concrete integer operand")
|
||||
}
|
||||
value.integer = ct_normalize_integer(state, ~value.integer, value.type)
|
||||
return ct_add_value(state, value), ct_flow(.Normal), true
|
||||
}
|
||||
if value.kind == .Integer {
|
||||
representation := types.runtime_representation(value.type, &state.checker.module.types)
|
||||
if value.kind == .Integer && types.is_signed(representation, state.checker.target) {
|
||||
result, overflow := intrinsics.overflow_sub(i128(0), value.integer)
|
||||
if overflow {
|
||||
state.error = .Overflow
|
||||
@@ -2395,7 +2468,7 @@ ct_eval_unary :: proc(state: ^Ct_State, op: ast.Expr_Kind, id: Ct_Value_Id, span
|
||||
value.integer = result
|
||||
return ct_add_value(state, value), ct_flow(.Normal), true
|
||||
}
|
||||
if value.kind == .Float {
|
||||
if value.kind == .Float && types.is_float(representation, state.checker.target) {
|
||||
value.float = -value.float
|
||||
return ct_add_value(state, value), ct_flow(.Normal), true
|
||||
}
|
||||
@@ -2409,10 +2482,11 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
}
|
||||
left := state.values[left_id]
|
||||
right := state.values[right_id]
|
||||
store := &state.checker.module.types
|
||||
is_compare := op == .Eq || op == .Ne || op == .Lt || op == .Le || op == .Gt || op == .Ge
|
||||
if left.kind == .Null || right.kind == .Null {
|
||||
if (op != .Eq && op != .Ne) ||
|
||||
!types.is_optional(left.type, &state.checker.module.types) ||
|
||||
!types.is_optional(left.type, store) ||
|
||||
!types.equal(left.type, right.type) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'null' only supports '==' and '!=' with an optional value")
|
||||
}
|
||||
@@ -2426,36 +2500,43 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
if left.kind != .Type || right.kind != .Type || (op != .Eq && op != .Ne) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "type values only support '==' and '!=' with another type")
|
||||
}
|
||||
ok := types.equal(types.Type(left.index), types.Type(right.index))
|
||||
equal := types.equal(types.Type(left.index), types.Type(right.index))
|
||||
if op == .Ne {
|
||||
ok = !ok
|
||||
equal = !equal
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if left.kind == .Bool && right.kind == .Bool {
|
||||
if op != .Eq && op != .Ne {
|
||||
if left.kind == .Bool || right.kind == .Bool {
|
||||
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
|
||||
if left.kind != .Bool || right.kind != .Bool || !compatible ||
|
||||
!types.is_bool(operation.representation) || (op != .Eq && op != .Ne) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bool values only support '==' and '!='")
|
||||
}
|
||||
ok := left.integer == right.integer
|
||||
equal := left.integer == right.integer
|
||||
if op == .Ne {
|
||||
ok = !ok
|
||||
equal = !equal
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if left.kind == .Float || right.kind == .Float {
|
||||
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
|
||||
distinct_operation := types.is_distinct(left.type, store) || types.is_distinct(right.type, store)
|
||||
if distinct_operation && (!compatible || !types.is_float(operation.representation, state.checker.target)) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
|
||||
}
|
||||
lf := left.float if left.kind == .Float else f64(left.integer)
|
||||
rf := right.float if right.kind == .Float else f64(right.integer)
|
||||
if is_compare {
|
||||
ok := false
|
||||
result := false
|
||||
#partial switch op {
|
||||
case .Eq: ok = lf == rf
|
||||
case .Ne: ok = lf != rf
|
||||
case .Lt: ok = lf < rf
|
||||
case .Le: ok = lf <= rf
|
||||
case .Gt: ok = lf > rf
|
||||
case .Ge: ok = lf >= rf
|
||||
case .Eq: result = lf == rf
|
||||
case .Ne: result = lf != rf
|
||||
case .Lt: result = lf < rf
|
||||
case .Le: result = lf <= rf
|
||||
case .Gt: result = lf > rf
|
||||
case .Ge: result = lf >= rf
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if result else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
result := lf
|
||||
#partial switch op {
|
||||
@@ -2463,57 +2544,46 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
case .Sub: result = lf - rf
|
||||
case .Mul: result = lf * rf
|
||||
case .Div: result = lf / rf
|
||||
case:
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
|
||||
}
|
||||
result_type := types.widest(left.type, right.type)
|
||||
if !types.is_float(result_type, state.checker.target) {
|
||||
result_type = types.F64
|
||||
result_type := operation.result
|
||||
if !distinct_operation {
|
||||
result_type = types.widest(left.type, right.type)
|
||||
if !types.is_float(result_type, state.checker.target) {
|
||||
result_type = types.F64
|
||||
}
|
||||
}
|
||||
if types.bits(result_type, state.checker.target) == 32 {
|
||||
result = f64(f32(result))
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Float, type=result_type, float=result}), ct_flow(.Normal), true
|
||||
}
|
||||
if ct_is_integer_like(left) && ct_is_integer_like(right) {
|
||||
if is_compare {
|
||||
ok := false
|
||||
#partial switch op {
|
||||
case .Eq: ok = left.integer == right.integer
|
||||
case .Ne: ok = left.integer != right.integer
|
||||
case .Lt: ok = left.integer < right.integer
|
||||
case .Le: ok = left.integer <= right.integer
|
||||
case .Gt: ok = left.integer > right.integer
|
||||
case .Ge: ok = left.integer >= right.integer
|
||||
if types.is_enum(left.type, store) || types.is_enum(right.type, store) {
|
||||
if !types.equal(left.type, right.type) || (op != .Eq && op != .Ne) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "enum values only support '==' and '!=' with the same enum type")
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if op == .Div {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||
state, .Integer_Division, span,
|
||||
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
|
||||
)
|
||||
}
|
||||
if op == .Bit_And || op == .Bit_Or || op == .Bit_Xor {
|
||||
result_type := types.widest(left.type, right.type)
|
||||
if !types.is_concrete_integer(result_type) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bitwise operation requires compatible concrete integer operands")
|
||||
equal := left.integer == right.integer
|
||||
if op == .Ne {
|
||||
equal = !equal
|
||||
}
|
||||
value := left.integer & right.integer
|
||||
#partial switch op {
|
||||
case .Bit_Or: value = left.integer | right.integer
|
||||
case .Bit_Xor: value = left.integer ~ right.integer
|
||||
}
|
||||
value = ct_normalize_integer(state, value, result_type)
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if op == .Shift_Left || op == .Shift_Right || op == .Shift_Left_Saturating {
|
||||
if !types.is_concrete_integer(left.type) || !types.is_unsigned(right.type, state.checker.target) || right.integer < 0 {
|
||||
left_representation := types.runtime_representation(left.type, store)
|
||||
if !types.is_concrete_integer(left_representation) ||
|
||||
!types.is_unsigned(right.type, state.checker.target) || right.integer < 0 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "shift requires a concrete integer value and unsigned integer count")
|
||||
}
|
||||
bits := types.bits(left.type, state.checker.target)
|
||||
bits := types.bits(left_representation, state.checker.target)
|
||||
if right.integer >= i128(bits) {
|
||||
if op != .Shift_Left_Saturating {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "shift count exceeds integer width")
|
||||
}
|
||||
endpoint := i128(0)
|
||||
if left.integer != 0 {
|
||||
if types.is_signed(left.type, state.checker.target) {
|
||||
if types.is_signed(left_representation, state.checker.target) {
|
||||
endpoint = -(i128(1) << u32(bits-1)) if left.integer < 0 else (i128(1) << u32(bits-1))-1
|
||||
} else {
|
||||
endpoint = (i128(1) << u32(bits))-1
|
||||
@@ -2524,7 +2594,7 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
count := u32(right.integer)
|
||||
if op == .Shift_Right {
|
||||
value := left.integer >> count
|
||||
if !types.is_signed(left.type, state.checker.target) {
|
||||
if !types.is_signed(left_representation, state.checker.target) {
|
||||
value = ct_normalize_integer(state, left.integer, left.type) >> count
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=value}), ct_flow(.Normal), true
|
||||
@@ -2532,7 +2602,7 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
if op == .Shift_Left_Saturating {
|
||||
factor := i128(1) << count
|
||||
value := left.integer*factor
|
||||
if types.is_signed(left.type, state.checker.target) {
|
||||
if types.is_signed(left_representation, state.checker.target) {
|
||||
minimum := -(i128(1) << u32(bits-1))
|
||||
maximum := (i128(1) << u32(bits-1))-1
|
||||
value = max(minimum, min(maximum, value))
|
||||
@@ -2545,6 +2615,42 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
value := ct_normalize_integer(state, ct_normalize_integer(state, left.integer, left.type) << count, left.type)
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=value}), ct_flow(.Normal), true
|
||||
}
|
||||
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
|
||||
distinct_operation := types.is_distinct(left.type, store) || types.is_distinct(right.type, store)
|
||||
if distinct_operation && (!compatible || !types.is_concrete_integer(operation.representation)) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
|
||||
}
|
||||
if is_compare {
|
||||
result := false
|
||||
#partial switch op {
|
||||
case .Eq: result = left.integer == right.integer
|
||||
case .Ne: result = left.integer != right.integer
|
||||
case .Lt: result = left.integer < right.integer
|
||||
case .Le: result = left.integer <= right.integer
|
||||
case .Gt: result = left.integer > right.integer
|
||||
case .Ge: result = left.integer >= right.integer
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if result else 0}), ct_flow(.Normal), true
|
||||
}
|
||||
if op == .Div {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||
state, .Integer_Division, span,
|
||||
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
|
||||
)
|
||||
}
|
||||
if op == .Bit_And || op == .Bit_Or || op == .Bit_Xor {
|
||||
result_type := operation.result if distinct_operation else types.widest(left.type, right.type)
|
||||
if !types.is_concrete_integer(types.runtime_representation(result_type, store)) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bitwise operation requires compatible concrete integer operands")
|
||||
}
|
||||
value := left.integer & right.integer
|
||||
#partial switch op {
|
||||
case .Bit_Or: value = left.integer | right.integer
|
||||
case .Bit_Xor: value = left.integer ~ right.integer
|
||||
}
|
||||
value = ct_normalize_integer(state, value, result_type)
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
|
||||
}
|
||||
value: i128
|
||||
overflow := false
|
||||
#partial switch op {
|
||||
@@ -2559,9 +2665,12 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
||||
state.error = .Overflow
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
result_type := types.widest(left.type, right.type)
|
||||
if !types.is_concrete_integer(result_type) && !types.is_enum(result_type, &state.checker.module.types) {
|
||||
result_type = ct_default_integer_type(value)
|
||||
result_type := operation.result
|
||||
if !distinct_operation {
|
||||
result_type = types.widest(left.type, right.type)
|
||||
if !types.is_concrete_integer(result_type) {
|
||||
result_type = ct_default_integer_type(value)
|
||||
}
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
|
||||
}
|
||||
@@ -2580,14 +2689,14 @@ ct_eval_division_builtin :: proc(
|
||||
}
|
||||
left := state.values[left_id]
|
||||
right := state.values[right_id]
|
||||
result_type := types.widest(left.type, right.type)
|
||||
if !types.is_concrete_scalar(result_type) || types.is_bool(result_type) {
|
||||
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
|
||||
if !compatible || types.is_bool(operation.representation) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||
state, .Not_Comptime, span, "division builtins require compatible numeric operands",
|
||||
)
|
||||
}
|
||||
left_id, left_ok := ct_coerce_value(state, left_id, result_type, span)
|
||||
right_id, right_ok := ct_coerce_value(state, right_id, result_type, span)
|
||||
left_id, left_ok := ct_coerce_value(state, left_id, operation.result, span)
|
||||
right_id, right_ok := ct_coerce_value(state, right_id, operation.result, span)
|
||||
if !left_ok || !right_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
@@ -2614,10 +2723,10 @@ ct_eval_division_builtin :: proc(
|
||||
result += right.float
|
||||
}
|
||||
}
|
||||
if types.bits(result_type, state.checker.target) == 32 {
|
||||
if types.bits(operation.representation, state.checker.target) == 32 {
|
||||
result = f64(f32(result))
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Float, type=result_type, float=result}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Float, type=operation.result, float=result}), ct_flow(.Normal), true
|
||||
}
|
||||
if left.kind != .Integer || right.kind != .Integer {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "division builtins require compatible numeric operands")
|
||||
@@ -2626,8 +2735,8 @@ ct_eval_division_builtin :: proc(
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Div_By_Zero, span, "division builtin denominator is zero")
|
||||
}
|
||||
is_quotient := kind == .Trunc || kind == .Floor || kind == .Exact || kind == .Ceil
|
||||
if is_quotient && types.is_signed(result_type, state.checker.target) {
|
||||
minimum := -(i128(1) << u32(types.bits(result_type, state.checker.target)-1))
|
||||
if is_quotient && types.is_signed(operation.representation, state.checker.target) {
|
||||
minimum := -(i128(1) << u32(types.bits(operation.representation, state.checker.target)-1))
|
||||
if left.integer == minimum && right.integer == -1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Overflow, span, "signed integer division overflow")
|
||||
}
|
||||
@@ -2656,7 +2765,7 @@ ct_eval_division_builtin :: proc(
|
||||
}
|
||||
case:
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=result}), ct_flow(.Normal), true
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=operation.result, integer=result}), ct_flow(.Normal), true
|
||||
}
|
||||
|
||||
ct_eval_division_call :: proc(
|
||||
@@ -2684,14 +2793,20 @@ ct_eval_division_call :: proc(
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
left, flow, ok = ct_eval_expr(state, expr.args[0], state.values[right].type, depth+1)
|
||||
_, distinct_ok := types.distinct_scalar_backing(state.values[right].type, &checker.module.types)
|
||||
left, flow, ok = ct_eval_expr(
|
||||
state, expr.args[0], state.values[right].type, depth+1, distinct_ok,
|
||||
)
|
||||
} else {
|
||||
left, flow, ok = ct_eval_expr(state, expr.args[0], hint, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
right_hint := hint if types.is_valid(hint) else state.values[left].type
|
||||
right, flow, ok = ct_eval_expr(state, expr.args[1], right_hint, depth+1)
|
||||
_, distinct_ok := types.distinct_scalar_backing(right_hint, &checker.module.types)
|
||||
right, flow, ok = ct_eval_expr(
|
||||
state, expr.args[1], right_hint, depth+1, distinct_ok && right_const,
|
||||
)
|
||||
}
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
@@ -2864,6 +2979,10 @@ ct_scalar_cast :: proc(state: ^Ct_State, id: Ct_Value_Id, target: types.Type, sp
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
value := state.values[id]
|
||||
if types.can_retype_distinct(value.type, target, &state.checker.module.types) {
|
||||
value.type = target
|
||||
return ct_add_value(state, value), ct_flow(.Normal), true
|
||||
}
|
||||
if !types.is_concrete_scalar(target) || types.is_bool(target) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "scalar cast requires numeric scalar types")
|
||||
}
|
||||
@@ -3039,6 +3158,16 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
|
||||
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
|
||||
}), ct_flow(.Normal), true
|
||||
}
|
||||
if tag == "distinct" {
|
||||
child_value := ct_add_value(state, Ct_Value{
|
||||
kind=.Type, type=types.INVALID, index=u64(item.child),
|
||||
})
|
||||
payload_start := u32(len(state.children))
|
||||
append(&state.children, child_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
|
||||
}
|
||||
if tag != "record" {
|
||||
start := u32(len(state.children))
|
||||
return ct_add_value(state, Ct_Value{
|
||||
@@ -3522,7 +3651,11 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
if (builtin == .Size_Of || builtin == .Align_Of) && !valid_layout_type(checker, target) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a sized runtime value type, got %s", type_label(checker, target))
|
||||
}
|
||||
if (builtin == .Min_Value || builtin == .Max_Value) && !types.is_concrete_integer(target) {
|
||||
bound_representation := target
|
||||
if backing, ok := types.distinct_scalar_backing(target, &checker.module.types); ok {
|
||||
bound_representation = backing
|
||||
}
|
||||
if (builtin == .Min_Value || builtin == .Max_Value) && !types.is_concrete_integer(bound_representation) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "integer bound target must be a concrete integer type, got %s", type_label(checker, target))
|
||||
}
|
||||
result_type := types.USIZE if builtin == .Size_Of || builtin == .Align_Of else target
|
||||
@@ -3593,8 +3726,39 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
)
|
||||
named_item, named_ok := types.node(&checker.module.types, named_type)
|
||||
target := types.resolve_alias(named_type, &checker.module.types)
|
||||
if named_ok && named_item.kind == .Alias &&
|
||||
types.is_concrete_scalar(target) && !types.is_bool(target) {
|
||||
target_item, target_ok := types.node(&checker.module.types, target)
|
||||
if target_ok && target_item.kind == .Distinct {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
|
||||
state,
|
||||
.Not_Comptime,
|
||||
expr.span,
|
||||
"distinct type '%s' expects 1 argument, got %d",
|
||||
symbol_text(checker, expr.name),
|
||||
len(expr.args),
|
||||
)
|
||||
}
|
||||
value, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
actual := state.values[value].type
|
||||
if !types.can_retype_distinct(actual, target, &checker.module.types) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
|
||||
state,
|
||||
.Not_Comptime,
|
||||
expr.span,
|
||||
"distinct type '%s' requires an exact %s value, got %s",
|
||||
symbol_text(checker, expr.name),
|
||||
types.name(target_item.child),
|
||||
types.name(actual),
|
||||
)
|
||||
}
|
||||
result := state.values[value]
|
||||
result.type = target
|
||||
return ct_add_value(state, result), ct_flow(.Normal), true
|
||||
}
|
||||
if named_ok && named_item.kind == .Alias && types.is_concrete_scalar(target) {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
|
||||
state,
|
||||
@@ -5027,11 +5191,17 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
|
||||
}
|
||||
} else {
|
||||
value_expected := expected
|
||||
numeric_operation := false
|
||||
if statement.assignment_op == .Shift_Left || statement.assignment_op == .Shift_Right ||
|
||||
statement.assignment_op == .Shift_Left_Saturating {
|
||||
value_expected = types.U64
|
||||
} else if statement.assignment_op != .Set &&
|
||||
is_numeric_constant_expr(checker, statement.expr) {
|
||||
_, numeric_operation = types.distinct_scalar_backing(expected, &checker.module.types)
|
||||
}
|
||||
value, flow, ok = ct_eval_expr(state, statement.expr, value_expected, depth+1)
|
||||
value, flow, ok = ct_eval_expr(
|
||||
state, statement.expr, value_expected, depth+1, numeric_operation,
|
||||
)
|
||||
}
|
||||
if !ok || flow.kind != .Normal {
|
||||
return flow, ok
|
||||
|
||||
+46
-28
@@ -417,8 +417,9 @@ emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction:
|
||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
|
||||
emit_trap_call(emitter, message)
|
||||
if types.is_runtime_value(instruction.type, &emitter.module.types) {
|
||||
if !types.is_float(instruction.type, emitter.module.target) {
|
||||
if !types.is_concrete_scalar(instruction.type) {
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !types.is_float(representation, emitter.module.target) {
|
||||
if !types.is_concrete_scalar(representation) {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = freeze %s zeroinitializer\n",
|
||||
@@ -583,8 +584,9 @@ emit_checked_arithmetic :: proc(
|
||||
float_op: string,
|
||||
overflow_message: string,
|
||||
) {
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
if types.is_float(representation, emitter.module.target) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, float_op, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
@@ -592,7 +594,7 @@ emit_checked_arithmetic :: proc(
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
return
|
||||
}
|
||||
prefix := "u" if types.is_unsigned(instruction.type, emitter.module.target) else "s"
|
||||
prefix := "u" if types.is_unsigned(representation, emitter.module.target) else "s"
|
||||
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.%s%s.with.overflow.%s(%s ", type_name, prefix, mnemonic, type_name, type_name)
|
||||
@@ -625,8 +627,9 @@ emit_division_zero_guard :: proc(
|
||||
instruction_index: int,
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
if types.is_float(representation, emitter.module.target) {
|
||||
fmt.sbprintf(&emitter.builder, " %%divzero%d = fcmp oeq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
|
||||
@@ -655,7 +658,8 @@ emit_division_overflow_guard :: proc(
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
|
||||
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n %%divminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||
@@ -683,7 +687,8 @@ emit_float_division_builtin :: proc(
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
suffix := "f32" if types.bits(instruction.type, emitter.module.target) == 32 else "f64"
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
suffix := "f32" if types.bits(representation, emitter.module.target) == 32 else "f64"
|
||||
|
||||
if instruction.op == .Rem_Checked || instruction.op == .Mod_Checked {
|
||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||
@@ -740,7 +745,8 @@ emit_integer_remainder_builtin :: proc(
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(representation, emitter.module.target)
|
||||
raw_name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked || !signed else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||
if !signed {
|
||||
fmt.sbprintf(&emitter.builder, " %s = urem %s ", raw_name, type_name)
|
||||
@@ -749,7 +755,7 @@ emit_integer_remainder_builtin :: proc(
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
} else {
|
||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
||||
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
|
||||
fmt.sbprintf(&emitter.builder, " %%remminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n %%remminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||
@@ -782,7 +788,8 @@ emit_integer_quotient_builtin :: proc(
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
signed := types.is_signed(representation, emitter.module.target)
|
||||
operation := "sdiv" if signed else "udiv"
|
||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Div_Trunc_Checked else fmt.tprintf("%%divq%d", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %s = %s %s ", name, operation, type_name)
|
||||
@@ -832,13 +839,14 @@ emit_division_builtin :: proc(
|
||||
instruction: ir.Instruction,
|
||||
) {
|
||||
emit_division_zero_guard(emitter, instructions, instruction_index, instruction)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if types.is_float(representation, emitter.module.target) {
|
||||
emit_float_division_builtin(emitter, instructions, instruction_index, instruction)
|
||||
return
|
||||
}
|
||||
quotient := instruction.op == .Div_Trunc_Checked || instruction.op == .Div_Floor_Checked ||
|
||||
instruction.op == .Div_Exact_Checked || instruction.op == .Div_Ceil_Checked
|
||||
if quotient && types.is_signed(instruction.type, emitter.module.target) {
|
||||
if quotient && types.is_signed(representation, emitter.module.target) {
|
||||
emit_division_overflow_guard(emitter, instructions, instruction_index, instruction)
|
||||
}
|
||||
if quotient {
|
||||
@@ -858,7 +866,8 @@ emit_shift :: proc(
|
||||
if valid_instruction(instructions, instruction.b) {
|
||||
count_type = instructions[instruction.b].type
|
||||
}
|
||||
if !types.is_concrete_integer(instruction.type) ||
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !types.is_concrete_integer(representation) ||
|
||||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!types.is_concrete_integer(count_type) || !types.is_unsigned(count_type, emitter.module.target) ||
|
||||
!valid_value(instructions, instruction.b, count_type, &emitter.module.types) {
|
||||
@@ -866,7 +875,7 @@ emit_shift :: proc(
|
||||
return
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
bits := types.bits(instruction.type, emitter.module.target)
|
||||
bits := types.bits(representation, emitter.module.target)
|
||||
count_bits := types.bits(count_type, emitter.module.target)
|
||||
if count_bits < 64 {
|
||||
fmt.sbprintf(&emitter.builder, " %%shift_count64_%d = zext %s ", instruction_index, llvm_type(count_type, &emitter.module.types))
|
||||
@@ -893,7 +902,7 @@ emit_shift :: proc(
|
||||
}
|
||||
operation := "shl"
|
||||
if instruction.op == .Shift_Right {
|
||||
operation = "ashr" if types.is_signed(instruction.type, emitter.module.target) else "lshr"
|
||||
operation = "ashr" if types.is_signed(representation, emitter.module.target) else "lshr"
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
@@ -911,7 +920,7 @@ emit_shift :: proc(
|
||||
if bits < 64 {
|
||||
fmt.sbprintf(&emitter.builder, " %%shift_safe%d = trunc i64 %%shift_safe64_%d to %s\n", instruction_index, instruction_index, type_name)
|
||||
}
|
||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
||||
signed := types.is_signed(representation, emitter.module.target)
|
||||
intrinsic := "sshl" if signed else "ushl"
|
||||
fmt.sbprintf(&emitter.builder, " %%shift_saturated%d = call %s @llvm.%s.sat.%s(%s ", instruction_index, type_name, intrinsic, type_name, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
@@ -1868,8 +1877,8 @@ emit_instruction_stream :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
||||
case .Retype:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!types.can_construct_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct type construction")
|
||||
!types.can_retype_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct retype")
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
@@ -1891,7 +1900,8 @@ emit_instruction_stream :: proc(
|
||||
from_repr := types.runtime_representation(from_type, &emitter.module.types)
|
||||
from_item, from_item_ok := types.node(&emitter.module.types, from_type)
|
||||
explicit_enum := from_item_ok && from_item.kind == .Enum && from_item.explicit_backing
|
||||
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum) &&
|
||||
_, distinct_scalar := types.distinct_scalar_backing(from_type, &emitter.module.types)
|
||||
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum || distinct_scalar) &&
|
||||
types.is_concrete_scalar(from_repr) && !types.is_bool(from_repr)
|
||||
if !valid_from || !types.is_concrete_scalar(instruction.type) || types.is_bool(instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid scalar cast operand")
|
||||
@@ -1973,12 +1983,15 @@ emit_instruction_stream :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count)
|
||||
}
|
||||
case .Neg_Checked:
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
(!types.is_signed(representation, emitter.module.target) &&
|
||||
!types.is_float(representation, emitter.module.target)) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand")
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
if types.is_float(instruction.type, emitter.module.target) {
|
||||
if types.is_float(representation, emitter.module.target) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = fneg %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
@@ -2028,19 +2041,21 @@ emit_instruction_stream :: proc(
|
||||
}
|
||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "mul", "fmul", "integer multiplication overflow")
|
||||
case .Div_Checked:
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||
!types.is_float(instruction.type, emitter.module.target) {
|
||||
!types.is_float(representation, emitter.module.target) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division operand")
|
||||
continue
|
||||
}
|
||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "div", "fdiv", "")
|
||||
case .Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||
.Rem_Checked, .Mod_Checked:
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||
(!types.is_concrete_integer(instruction.type) &&
|
||||
!types.is_float(instruction.type, emitter.module.target)) {
|
||||
(!types.is_concrete_integer(representation) &&
|
||||
!types.is_float(representation, emitter.module.target)) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division builtin operands")
|
||||
continue
|
||||
}
|
||||
@@ -2062,7 +2077,8 @@ emit_instruction_stream :: proc(
|
||||
write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
case .Bit_Not:
|
||||
if !types.is_concrete_integer(instruction.type) ||
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !types.is_concrete_integer(representation) ||
|
||||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise complement operand")
|
||||
continue
|
||||
@@ -2072,7 +2088,8 @@ emit_instruction_stream :: proc(
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", -1\n")
|
||||
case .Bit_And, .Bit_Or, .Bit_Xor:
|
||||
if !types.is_concrete_integer(instruction.type) ||
|
||||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||
if !types.is_concrete_integer(representation) ||
|
||||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise operands")
|
||||
@@ -2367,10 +2384,11 @@ emit_instruction_stream :: proc(
|
||||
}
|
||||
predicate := ir.Compare_Predicate(instruction.integer)
|
||||
type_name := llvm_type(operand_type, &emitter.module.types)
|
||||
if types.is_float(operand_type, emitter.module.target) {
|
||||
representation := types.runtime_representation(operand_type, &emitter.module.types)
|
||||
if types.is_float(representation, emitter.module.target) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = fcmp %s %s ", instruction_index, float_predicate(predicate), type_name)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(operand_type, emitter.module.target)), type_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(representation, emitter.module.target)), type_name)
|
||||
}
|
||||
write_operand(&emitter.builder, instructions, instruction.a, operand_type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
|
||||
@@ -1035,7 +1035,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
}
|
||||
payload := ast.INVALID_EXPR
|
||||
end := member.span
|
||||
if left_brace, ok := allow(parser, .Left_Brace); ok {
|
||||
if !(parser.no_struct_literal && parser.delimiter_depth == 0) && current(parser).kind == .Left_Brace {
|
||||
left_brace := advance(parser)
|
||||
parser.delimiter_depth += 1
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Identifier && peek(parser).kind == .Equal {
|
||||
|
||||
@@ -1106,9 +1106,33 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
can_construct_distinct :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, to)
|
||||
return ok && item.kind == .Distinct && item.declared && equal(from, item.child)
|
||||
distinct_backing :: proc(value: Type, store: ^Store) -> (Type, bool) {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Distinct || !item.declared {
|
||||
return INVALID, false
|
||||
}
|
||||
return item.child, true
|
||||
}
|
||||
|
||||
distinct_scalar_backing :: proc(value: Type, store: ^Store) -> (Type, bool) {
|
||||
_, ok := distinct_backing(value, store)
|
||||
if !ok {
|
||||
return INVALID, false
|
||||
}
|
||||
backing := runtime_representation(value, store)
|
||||
return backing, is_concrete_scalar(backing)
|
||||
}
|
||||
|
||||
can_retype_distinct :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
if backing, ok := distinct_backing(to, store); ok && equal(from, backing) {
|
||||
return true
|
||||
}
|
||||
backing, ok := distinct_backing(from, store)
|
||||
if !ok || !equal(to, backing) {
|
||||
return false
|
||||
}
|
||||
_, scalar := distinct_scalar_backing(from, store)
|
||||
return scalar
|
||||
}
|
||||
|
||||
runtime_representation :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||
|
||||
+199
-20
@@ -313,7 +313,9 @@ main func() i32 {
|
||||
|
||||
@(test)
|
||||
typed_bitwise_constants_fold_in_runtime_expressions :: proc(t: ^testing.T) {
|
||||
text := `main func() void {
|
||||
text := `D :: distinct u8
|
||||
main func() void {
|
||||
_ = ~D(1)
|
||||
_ = ~u8(0)
|
||||
_ = (u8(240) & u8(204)) xor u8(15)
|
||||
_ = u8(129) << 1
|
||||
@@ -332,12 +334,17 @@ typed_bitwise_constants_fold_in_runtime_expressions :: proc(t: ^testing.T) {
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
for statement_id in hir_module.functions[0].body {
|
||||
d_type := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "D")))
|
||||
for statement_id, index in hir_module.functions[0].body {
|
||||
statement := hir_module.statements[statement_id]
|
||||
testing.expect(t, statement.expr != hir.INVALID_EXPR)
|
||||
testing.expect_value(t, hir_module.exprs[statement.expr].kind, hir.Expr_Kind.Integer)
|
||||
if index == 0 {
|
||||
testing.expect_value(t, hir_module.exprs[statement.expr].kind, hir.Expr_Kind.Bit_Not)
|
||||
testing.expect_value(t, hir_module.exprs[statement.expr].type, d_type)
|
||||
} else {
|
||||
testing.expect_value(t, hir_module.exprs[statement.expr].kind, hir.Expr_Kind.Integer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,7 +360,7 @@ main func() void {
|
||||
_ = true & false
|
||||
_ = 1.0 | 2.0
|
||||
_ = ~p
|
||||
_ = ~d
|
||||
_ = d
|
||||
_ = ~e
|
||||
_ = u8(1) & i8(1)
|
||||
_ = u8(1) << signed_count
|
||||
@@ -410,6 +417,32 @@ main func() i32 {
|
||||
testing.expect(t, state.exit_code != 0)
|
||||
testing.expect(t, strings.contains(string(stderr), "shift count exceeds integer width"))
|
||||
}
|
||||
@(test)
|
||||
runtime_distinct_ordinary_overshift_traps :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-distinct-bitwise-overshift"
|
||||
main_path := "/tmp/brolang-test-distinct-bitwise-overshift/main.bro"
|
||||
output := "/tmp/brolang-test-distinct-bitwise-overshift-output"
|
||||
text := `D :: distinct u8
|
||||
shift func(value D, count u8) D { return value << count }
|
||||
main func() i32 {
|
||||
_ = shift(D(1), 8)
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
state, stdout, stderr, err := os2.process_exec(os2.Process_Desc{command=[]string{output}}, context.allocator)
|
||||
defer delete(stdout)
|
||||
defer delete(stderr)
|
||||
testing.expect(t, err == nil)
|
||||
testing.expect(t, state.exit_code != 0)
|
||||
testing.expect(t, strings.contains(string(stderr), "shift count exceeds integer width"))
|
||||
}
|
||||
|
||||
|
||||
@(test)
|
||||
parser_accepts_grouped_params_and_multiline_statements :: proc(t: ^testing.T) {
|
||||
@@ -1704,6 +1737,11 @@ integer_bound_builtins_compile_and_run :: proc(t: ^testing.T) {
|
||||
main_path := "/tmp/brolang-test-integer-bounds/main.bro"
|
||||
output := "/tmp/brolang-test-integer-bounds-output"
|
||||
text := `MAX_U64 u64 :: maxval!(u64)
|
||||
Signed :: distinct i16
|
||||
Unsigned :: distinct u16
|
||||
Inner :: distinct u8
|
||||
Outer :: distinct Inner
|
||||
|
||||
|
||||
maximum func($T type) T {
|
||||
return maxval!(T)
|
||||
@@ -1720,6 +1758,16 @@ main func() i32 {
|
||||
if (maximum(u16) != 65535) return 8
|
||||
if (minval!(c_int) != -2147483648) return 9
|
||||
if (maxval!(c_ulong) != 18446744073709551615) return 10
|
||||
signed_min Signed = minval!(Signed)
|
||||
unsigned_max Unsigned = maxval!(Unsigned)
|
||||
nested_max Outer = maxval!(Outer)
|
||||
generic_min Signed = maximum(Signed)
|
||||
generic_nested Outer = maximum(Outer)
|
||||
if i16(signed_min) != -32768 { return 11 }
|
||||
if u16(unsigned_max) != 65535 { return 12 }
|
||||
if u8(nested_max) != 255 { return 13 }
|
||||
if i16(generic_min) != 32767 { return 14 }
|
||||
if u8(generic_nested) != 255 { return 15 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
@@ -1736,7 +1784,9 @@ main func() i32 {
|
||||
|
||||
@(test)
|
||||
integer_bound_builtins_reject_invalid_targets :: proc(t: ^testing.T) {
|
||||
text := `Named :: distinct u8
|
||||
text := `BadFloat :: distinct f32
|
||||
BadBool :: distinct bool
|
||||
BadAggregate :: distinct [2]u8
|
||||
Choice :: enum { one }
|
||||
|
||||
main func() void {
|
||||
@@ -1747,7 +1797,9 @@ main func() void {
|
||||
_ = maxval!(uint)
|
||||
_ = maxval!(f32)
|
||||
_ = maxval!(bool)
|
||||
_ = maxval!(Named)
|
||||
_ = maxval!(BadFloat)
|
||||
_ = maxval!(BadBool)
|
||||
_ = maxval!(BadAggregate)
|
||||
_ = maxval!(Choice)
|
||||
}
|
||||
`
|
||||
@@ -1773,7 +1825,7 @@ main func() void {
|
||||
}
|
||||
testing.expect_value(t, bad_arity, 2)
|
||||
testing.expect(t, bad_type)
|
||||
testing.expect_value(t, bad_target, 6)
|
||||
testing.expect_value(t, bad_target, 8)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -3284,6 +3336,8 @@ milestone_37_format_errors_are_reported_at_comptime :: proc(t: ^testing.T) {
|
||||
main_path := "/tmp/brolang-test-format-errors/main.bro"
|
||||
text := `io :: import "@std/io"
|
||||
process :: import "@std/process"
|
||||
Aggregate :: distinct [2]u8
|
||||
|
||||
|
||||
main func(init process.Init) void {
|
||||
writer io.Writer :: io.stdout(init.io)
|
||||
@@ -3299,6 +3353,7 @@ main func(init process.Init) void {
|
||||
io.print(writer, "{e}", {1,}) catch |_| {}
|
||||
io.print(writer, "{c}", {i16(65),}) catch |_| {}
|
||||
io.print(writer, "}", {}) catch |_| {}
|
||||
io.print(writer, "{}", {Aggregate([1, 2]),}) catch |_| {}
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
@@ -3317,7 +3372,7 @@ main func(init process.Init) void {
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := [10]bool{}
|
||||
found := [11]bool{}
|
||||
for diagnostic in diagnostics.items {
|
||||
message := diagnostic.message
|
||||
found[0] = found[0] || strings.contains(message, "arguments must be a tuple")
|
||||
@@ -3330,6 +3385,7 @@ main func(init process.Init) void {
|
||||
found[7] = found[7] || strings.contains(message, "integer format requires an integer argument")
|
||||
found[8] = found[8] || strings.contains(message, "float format requires a float argument")
|
||||
found[9] = found[9] || strings.contains(message, "'{c}' requires an unsigned integer that fits in u8")
|
||||
found[10] = found[10] || strings.contains(message, "io.print '{}' does not support this argument type")
|
||||
}
|
||||
testing.expect(t, loaded)
|
||||
for present in found {
|
||||
@@ -3402,7 +3458,7 @@ milestone_39_stable_values_and_richer_formatting_compile_and_run :: proc(t: ^tes
|
||||
testing.expect_value(
|
||||
t,
|
||||
string(stdout),
|
||||
"true -42 1.5 .running bro 2.5 1010 12 ff FF A 1.5000000000000000e+00 {} -9223372036854775808 0 inf nan 1.50000000e+00\n",
|
||||
"true -42 1.5 .running bro 2.5 1010 12 ff FF A 1.5000000000000000e+00 {} -9223372036854775808 0 inf nan 1.50000000e+00\n-42 -42 1010 12 ff FF A 1.50000000e+00 7\n",
|
||||
)
|
||||
testing.expect_value(t, string(stderr), "debug=.idle 2a\n")
|
||||
}
|
||||
@@ -7927,6 +7983,28 @@ checked_addition_traps_on_overflow :: proc(t: ^testing.T) {
|
||||
state := run_executable(output)
|
||||
testing.expect(t, !state.success)
|
||||
}
|
||||
@(test)
|
||||
checked_distinct_addition_traps_on_backing_overflow :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-distinct-overflow"
|
||||
main_path := "/tmp/brolang-test-distinct-overflow/main.bro"
|
||||
output := "/tmp/brolang-test-distinct-overflow-output"
|
||||
text := `D :: distinct i8
|
||||
add func(left, right D) D { return left + right }
|
||||
main func() i32 {
|
||||
_ = add(D(127), D(1))
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
state := run_executable(output)
|
||||
testing.expect(t, !state.success)
|
||||
}
|
||||
|
||||
|
||||
@(test)
|
||||
checked_runtime_negation_traps_for_every_signed_width :: proc(t: ^testing.T) {
|
||||
@@ -10708,6 +10786,32 @@ parser_diagnoses_braceless_if_without_parens_or_call :: proc(t: ^testing.T) {
|
||||
testing.expect(t, strings.contains(diagnostics.items[0].message, "parenthesized"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_separates_contextual_enum_literal_from_if_block :: proc(t: ^testing.T) {
|
||||
text := `Kind :: enum {
|
||||
newline
|
||||
other
|
||||
}
|
||||
main func() void {
|
||||
kind Kind = .other
|
||||
if kind != .newline {
|
||||
}
|
||||
}
|
||||
`
|
||||
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)
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, module.statements[module.functions[0].body[1]].kind, ast.Stmt_Kind.If)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_accepts_braceless_while_bodies :: proc(t: ^testing.T) {
|
||||
text := `ready func() bool { return false }
|
||||
@@ -13125,6 +13229,7 @@ OtherID :: distinct u32
|
||||
PointID :: distinct Point
|
||||
Bytes :: distinct [2]u8
|
||||
WrappedID :: distinct UserID
|
||||
Flag :: distinct bool
|
||||
static_id UserID :: UserID(42)
|
||||
take func(value UserID) UserID {
|
||||
return value
|
||||
@@ -13137,11 +13242,27 @@ main func() i32 {
|
||||
point PointID :: PointID(Point { x = 1, y = 2 })
|
||||
bytes Bytes :: Bytes([3, 4])
|
||||
wrapped WrappedID :: WrappedID(id)
|
||||
sum UserID = id + 1
|
||||
matches bool = id == 7
|
||||
raw u32 = u32(id)
|
||||
wide usize = usize(id)
|
||||
real f64 = f64(id)
|
||||
inner UserID = UserID(wrapped)
|
||||
terminal u32 = u32(wrapped)
|
||||
flag_matches bool = Flag(true) == Flag(true)
|
||||
_ = maybe
|
||||
_ = pointer
|
||||
_ = point
|
||||
_ = bytes
|
||||
_ = wrapped
|
||||
_ = sum
|
||||
_ = matches
|
||||
_ = raw
|
||||
_ = wide
|
||||
_ = real
|
||||
_ = inner
|
||||
_ = terminal
|
||||
_ = flag_matches
|
||||
return 0
|
||||
}
|
||||
`
|
||||
@@ -13177,17 +13298,33 @@ main func() i32 {
|
||||
testing.expect(t, strings.contains(llvm_text, "@bro.g.0 = internal constant i32 42"))
|
||||
testing.expect(t, strings.contains(llvm_text, "select i1 true, i32"))
|
||||
|
||||
nominal_hir_add := false
|
||||
for hir_expr in hir_module.exprs {
|
||||
nominal_hir_add = nominal_hir_add || hir_expr.kind == .Add && hir_expr.type == user_id
|
||||
}
|
||||
testing.expect(t, nominal_hir_add)
|
||||
|
||||
retype_count := 0
|
||||
nominal_ir_add := false
|
||||
u32_cast, usize_cast, f64_cast := false, false, false
|
||||
for function in ir_module.functions {
|
||||
for instruction in function.instructions {
|
||||
retype_count += 1 if instruction.op == .Retype else 0
|
||||
nominal_ir_add = nominal_ir_add || instruction.op == .Add_Checked && instruction.type == user_id
|
||||
if instruction.op == .Scalar_Cast {
|
||||
u32_cast = u32_cast || instruction.type == types.U32
|
||||
usize_cast = usize_cast || instruction.type == types.USIZE
|
||||
f64_cast = f64_cast || instruction.type == types.F64
|
||||
}
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, retype_count, 4)
|
||||
testing.expect(t, nominal_ir_add)
|
||||
testing.expect(t, u32_cast && usize_cast && f64_cast)
|
||||
testing.expect(t, retype_count >= 5)
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_types_reject_implicit_conversions_operators_and_invalid_backings :: proc(t: ^testing.T) {
|
||||
distinct_types_reject_implicit_conversions_and_invalid_backings :: proc(t: ^testing.T) {
|
||||
text := `Opaque :: opaque
|
||||
UserID :: distinct u32
|
||||
OtherID :: distinct u32
|
||||
@@ -13207,9 +13344,14 @@ main func() void {
|
||||
_ = UserID()
|
||||
_ = UserID(1, 2)
|
||||
left UserID :: UserID(5)
|
||||
right UserID :: UserID(6)
|
||||
_ = left + right
|
||||
_ = left == right
|
||||
raw_operand u32 :: u32(6)
|
||||
other_operand OtherID :: OtherID(6)
|
||||
_ = left + raw_operand
|
||||
_ = raw_operand + left
|
||||
_ = left + other_operand
|
||||
_ = left == raw_operand
|
||||
_ = raw_operand == left
|
||||
_ = left == other_operand
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
@@ -14616,12 +14758,12 @@ dependency_passes test {
|
||||
defer delete(stderr)
|
||||
output := string(stderr)
|
||||
testing.expect_value(t, state.exit_code, 1)
|
||||
testing.expect(t, strings.contains(output, "root.root_passes [ok]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_fails [failed]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_passes...[ok]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_fails...[failed]"))
|
||||
testing.expect(t, strings.contains(output, "expected 42, found 41"))
|
||||
testing.expect(t, strings.contains(output, "root.root_continues [ok]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_errors [failed]"))
|
||||
testing.expect(t, strings.contains(output, "dependency.dependency_passes [ok]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_continues...[ok]"))
|
||||
testing.expect(t, strings.contains(output, "root.root_errors...[failed]"))
|
||||
testing.expect(t, strings.contains(output, "dependency.dependency_passes...[ok]"))
|
||||
testing.expect(t, strings.contains(output, root_path))
|
||||
testing.expect(t, strings.contains(output, "3 passed, 2 failed"))
|
||||
}
|
||||
@@ -15233,6 +15375,43 @@ main func() i32 {
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
@(test)
|
||||
distinct_reflection_reports_immediate_backing :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-distinct-reflection"
|
||||
main_path := "/tmp/brolang-test-distinct-reflection/main.bro"
|
||||
output := "/tmp/brolang-test-distinct-reflection-output"
|
||||
text := `meta :: import "@std/meta"
|
||||
|
||||
Inner :: distinct u16
|
||||
Outer :: distinct Inner
|
||||
OuterAlias :: alias Outer
|
||||
|
||||
matches func($Distinct, $Backing type) bool {
|
||||
match typeinfo!(Distinct) {
|
||||
.distinct |backing|: return backing == Backing
|
||||
else: return false
|
||||
}
|
||||
}
|
||||
|
||||
main func() i32 {
|
||||
if !$(matches(Inner, u16)) { return 1 }
|
||||
if !$(matches(Outer, Inner)) { return 2 }
|
||||
if !$(matches(OuterAlias, Inner)) { return 3 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
testing.expect_value(t, compiler_core.compile_package(
|
||||
directory, output, nil, target.DEFAULT, cimport.Options{}, ".",
|
||||
), 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
|
||||
@(test)
|
||||
static_string_map_infers_array_size_and_preserves_promoted_backing :: proc(t: ^testing.T) {
|
||||
|
||||
@@ -9,8 +9,14 @@ LocalID :: distinct u32
|
||||
PointID :: distinct Point
|
||||
Bytes :: distinct [2]u8
|
||||
WrappedID :: distinct LocalID
|
||||
Signed :: distinct i32
|
||||
Mask :: distinct u8
|
||||
Real :: distinct f64
|
||||
|
||||
|
||||
static_id LocalID :: LocalID(42)
|
||||
static_expression LocalID :: (LocalID(3) + 5) * 2
|
||||
|
||||
|
||||
take func(value LocalID) LocalID {
|
||||
return value
|
||||
@@ -26,12 +32,76 @@ main func() i32 {
|
||||
wrapped WrappedID :: WrappedID(id)
|
||||
remote ids.UserID :: ids.UserID(8)
|
||||
remote_copy ids.UserID :: ids.make(9)
|
||||
_ = static_id
|
||||
|
||||
if id + 1 != 8 or 1 + id != 8 { return 1 }
|
||||
if id - 2 != 5 or 2 * id != 14 or id * 2 != 14 { return 2 }
|
||||
if -Signed(5) != -5 { return 3 }
|
||||
|
||||
real Real :: Real(1.5)
|
||||
if real + 0.5 != 2.0 or 0.5 + real != 2.0 or real - 0.5 != 1.0 or
|
||||
real * 2.0 != 3.0 or real / 0.5 != 3.0 {
|
||||
return 4
|
||||
}
|
||||
|
||||
if !(id == 7) or !(7 == id) or !(id != 8) or !(8 != id) or
|
||||
!(id < 8) or !(6 < id) or !(id <= 7) or !(7 <= id) or
|
||||
!(id > 6) or !(8 > id) or !(id >= 7) or !(7 >= id) {
|
||||
return 5
|
||||
}
|
||||
|
||||
signed Signed :: Signed(-7)
|
||||
if divtrunc!(signed, 3) != -2 or divfloor!(signed, 3) != -3 or
|
||||
divexact!(Signed(8), 2) != 4 or divceil!(signed, 3) != -2 or
|
||||
rem!(signed, 3) != -1 or mod!(signed, 3) != 2 {
|
||||
return 6
|
||||
}
|
||||
|
||||
mask Mask :: Mask(10)
|
||||
if ~mask != 245 or (mask & 6) != 2 or (mask | 5) != 15 or (mask xor 3) != 9 {
|
||||
return 7
|
||||
}
|
||||
if mask << u8(1) != 20 or mask >> u8(1) != 5 or Mask(128) <<| u8(1) != 255 {
|
||||
return 8
|
||||
}
|
||||
|
||||
arithmetic Signed = Signed(4)
|
||||
arithmetic += 3
|
||||
arithmetic -= 2
|
||||
arithmetic *= 5
|
||||
fraction Real = Real(3.0)
|
||||
fraction /= 2.0
|
||||
if arithmetic != 25 or fraction != 1.5 { return 9 }
|
||||
|
||||
bits Mask = Mask(3)
|
||||
bits |= 8
|
||||
bits xor= 2
|
||||
bits &= 9
|
||||
bits <<= u8(1)
|
||||
bits >>= u8(1)
|
||||
bits <<|= u8(5)
|
||||
if bits != 255 { return 10 }
|
||||
|
||||
values [10]u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
if values[LocalID(4)] != 4 { return 11 }
|
||||
section []u8 = values[LocalID(2)..LocalID(5)]
|
||||
if section.len != 3 or section[usize(0)] != 2 or section[usize(2)] != 4 { return 12 }
|
||||
|
||||
if u32(id) != 7 or usize(id) != 7 or f64(id) != 7.0 { return 13 }
|
||||
extracted LocalID = LocalID(wrapped)
|
||||
if extracted != id { return 14 }
|
||||
|
||||
minimum Signed = minval!(Signed)
|
||||
maximum Mask = maxval!(Mask)
|
||||
nested_max WrappedID = maxval!(WrappedID)
|
||||
if i32(minimum) != minval!(i32) or u8(maximum) != 255 or u32(nested_max) != maxval!(u32) {
|
||||
return 15
|
||||
}
|
||||
if static_expression != 16 { return 16 }
|
||||
|
||||
_ = maybe
|
||||
_ = pointer
|
||||
_ = point
|
||||
_ = bytes
|
||||
_ = wrapped
|
||||
_ = remote
|
||||
_ = remote_copy
|
||||
return 0
|
||||
|
||||
@@ -24,6 +24,12 @@ Config :: struct {
|
||||
maybe ?i32
|
||||
payload Payload
|
||||
}
|
||||
Count :: distinct i32
|
||||
Byte :: distinct u8
|
||||
Ratio :: distinct f32
|
||||
Inner :: distinct u32
|
||||
Outer :: distinct Inner
|
||||
|
||||
|
||||
first Config :: Config {
|
||||
enabled = true,
|
||||
@@ -105,6 +111,19 @@ main func(init process.Init) i32 {
|
||||
}) catch |_| {
|
||||
return 3
|
||||
}
|
||||
io.print(writer, "{} {d} {b} {o} {x} {X} {c} {e} {}\n", {
|
||||
Count(i32(-42)),
|
||||
Count(i32(-42)),
|
||||
Byte(u8(10)),
|
||||
Byte(u8(10)),
|
||||
Byte(u8(255)),
|
||||
Byte(u8(255)),
|
||||
Byte(u8('A')),
|
||||
Ratio(f32(1.5)),
|
||||
Outer(Inner(u32(7))),
|
||||
}) catch |_| {
|
||||
return 4
|
||||
}
|
||||
debug.print("debug={} {x}\n", {State.idle, 42})
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
id = "brolang"
|
||||
name = "Brolang"
|
||||
version = "0.1.1"
|
||||
schema_version = 1
|
||||
authors = ["Brolang contributors"]
|
||||
description = "Brolang language support"
|
||||
repository = "ssh://git@gitea.hl-valdemar.dev:2222/hl-valdemar/brolang.git"
|
||||
languages = ["languages/brolang"]
|
||||
|
||||
[grammars.brolang]
|
||||
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
||||
rev = "09571ffeb9b99216eb179bd452c0749640cf80e7"
|
||||
path = "tree-sitter-brolang"
|
||||
@@ -1,14 +0,0 @@
|
||||
name = "Brolang"
|
||||
grammar = "brolang"
|
||||
path_suffixes = ["bro", "hon"]
|
||||
line_comments = ["# "]
|
||||
hard_tabs = true
|
||||
tab_size = 4
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
||||
@@ -1,129 +0,0 @@
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(string)
|
||||
(multiline_string)
|
||||
] @string
|
||||
|
||||
(character) @string
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
(boolean) @boolean
|
||||
|
||||
[
|
||||
(null)
|
||||
(unreachable)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
(builtin_type) @type.builtin
|
||||
(named_type) @type
|
||||
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier) @function .)
|
||||
(argument_list))
|
||||
|
||||
(struct_literal
|
||||
type: (qualified_identifier
|
||||
(identifier) @function .)
|
||||
(argument_list))
|
||||
|
||||
(type_declaration name: (identifier) @type)
|
||||
(function_declaration name: (identifier) @function)
|
||||
(test_declaration name: (identifier) @function)
|
||||
(parameter name: (identifier) @variable.parameter)
|
||||
|
||||
(intrinsic_call_expression function: (identifier) @function.builtin)
|
||||
(call_expression function: (expression (identifier) @function))
|
||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||
(field_expression field: (identifier) @property)
|
||||
(field_expression field: (integer) @property)
|
||||
(field_initializer name: (identifier) @property)
|
||||
(keyed_field_initializer name: (identifier) @property)
|
||||
(record_field name: (identifier) @property)
|
||||
(enum_member name: (identifier) @property)
|
||||
(enum_literal name: (identifier) @property)
|
||||
|
||||
(import_declaration alias: (identifier) @variable)
|
||||
(opaque_type) @keyword
|
||||
|
||||
[
|
||||
"func"
|
||||
"test"
|
||||
"c_func"
|
||||
"struct"
|
||||
"c_struct"
|
||||
"union"
|
||||
"enum"
|
||||
"distinct"
|
||||
"alias"
|
||||
"import"
|
||||
"hide"
|
||||
"return"
|
||||
"try"
|
||||
"catch"
|
||||
"mut"
|
||||
"orelse"
|
||||
"and"
|
||||
"or"
|
||||
"if"
|
||||
"while"
|
||||
"for"
|
||||
"expand"
|
||||
"break"
|
||||
"continue"
|
||||
"defer"
|
||||
"errdefer"
|
||||
"yield"
|
||||
"match"
|
||||
"else"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"::"
|
||||
"="
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
"=="
|
||||
"!="
|
||||
"<"
|
||||
"<="
|
||||
">"
|
||||
">="
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"!"
|
||||
"&"
|
||||
"@"
|
||||
"?"
|
||||
"^"
|
||||
".."
|
||||
"..="
|
||||
"|"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
@@ -142,17 +142,22 @@ is_version_command :: proc(arg: string) -> bool {
|
||||
}
|
||||
|
||||
template_root_valid :: proc(root: string) -> bool {
|
||||
std_build, std_error := filepath.join({root, "std", "build", "build.bro"})
|
||||
if std_error != nil {
|
||||
std_build_bro, bro_error := filepath.join({root, "std", "build", "build.bro"})
|
||||
if bro_error != nil {
|
||||
return false
|
||||
}
|
||||
defer delete(std_build)
|
||||
defer delete(std_build_bro)
|
||||
std_build_hon, hon_error := filepath.join({root, "std", "build", "build.hon"})
|
||||
if hon_error != nil {
|
||||
return false
|
||||
}
|
||||
defer delete(std_build_hon)
|
||||
ffi_stdio, ffi_error := filepath.join({root, "ffi", "c", "stdio.bro"})
|
||||
if ffi_error != nil {
|
||||
return false
|
||||
}
|
||||
defer delete(ffi_stdio)
|
||||
return os.exists(std_build) && os.exists(ffi_stdio)
|
||||
return (os.exists(std_build_bro) || os.exists(std_build_hon)) && os.exists(ffi_stdio)
|
||||
}
|
||||
|
||||
find_template_root :: proc(allocator := context.allocator) -> (string, bool) {
|
||||
|
||||
@@ -316,6 +316,21 @@ hide format_field_name func($T type, index usize) []u8 {
|
||||
else: compile_error!("io.print arguments must be a tuple")
|
||||
}
|
||||
}
|
||||
hide distinct_value func($Backing, $Distinct type, value Distinct) Backing {
|
||||
return ptrcast!(Backing, &value)^
|
||||
}
|
||||
|
||||
hide scalar_or_distinct_type func($T type) bool {
|
||||
match typeinfo!(T) {
|
||||
.bool: return true
|
||||
.integer: return true
|
||||
.float: return true
|
||||
.distinct: return true
|
||||
else: return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
hide write_integer func(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
@@ -324,6 +339,11 @@ hide write_integer func(output Writer, $T type, value T, base u64, uppercase boo
|
||||
} else {
|
||||
try write_integer_unsigned(output, u64(value), base, uppercase)
|
||||
}
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
try write_integer(output, distinct_value(backing, T, value), base, uppercase)
|
||||
} else {
|
||||
compile_error!("io.print integer format requires an integer argument")
|
||||
}
|
||||
else: compile_error!("io.print integer format requires an integer argument")
|
||||
}
|
||||
return
|
||||
@@ -351,6 +371,11 @@ hide write_float func(output Writer, $T type, value T, scientific bool) void ! W
|
||||
}
|
||||
try write_all(output, buffer[0..usize(count)])
|
||||
}
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
try write_float(output, distinct_value(backing, T, value), scientific)
|
||||
} else {
|
||||
compile_error!("io.print float format requires a float argument")
|
||||
}
|
||||
else: compile_error!("io.print float format requires a float argument")
|
||||
}
|
||||
return
|
||||
@@ -360,6 +385,11 @@ hide write_decimal func(output Writer, $T type, value T) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.integer: try write_integer(output, value, 10, false)
|
||||
.float: try write_float(output, value, false)
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
try write_decimal(output, distinct_value(backing, T, value))
|
||||
} else {
|
||||
compile_error!("io.print '{d}' requires an integer or float argument")
|
||||
}
|
||||
else: compile_error!("io.print '{d}' requires an integer or float argument")
|
||||
}
|
||||
return
|
||||
@@ -374,6 +404,11 @@ hide write_character func(output Writer, $T type, value T) void ! WriteError {
|
||||
buffer [1]u8 = [u8(value)]
|
||||
try write_all(output, buffer[..])
|
||||
}
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
try write_character(output, distinct_value(backing, T, value))
|
||||
} else {
|
||||
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||
}
|
||||
else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||
}
|
||||
return
|
||||
@@ -401,6 +436,11 @@ hide write_default func(output Writer, $T type, value T) void ! WriteError {
|
||||
}
|
||||
return .write_failed
|
||||
}
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
try write_default(output, distinct_value(backing, T, value))
|
||||
} else {
|
||||
compile_error!("io.print '{}' does not support this argument type")
|
||||
}
|
||||
else: compile_error!("io.print '{}' does not support this argument type")
|
||||
}
|
||||
return
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ TypeInfo :: union(enum) {
|
||||
record RecordInfo
|
||||
union void
|
||||
fallible void
|
||||
distinct void
|
||||
distinct type
|
||||
}
|
||||
|
||||
EnumFieldStruct func($E, $Field type, $default ?Field) type {
|
||||
|
||||
@@ -8,6 +8,10 @@ TestTokenKind :: enum(u8) {
|
||||
|
||||
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
|
||||
TestArrayAlias :: alias [3]u16
|
||||
TestInner :: distinct u16
|
||||
TestOuter :: distinct TestInner
|
||||
TestOuterAlias :: alias TestOuter
|
||||
|
||||
|
||||
hide array_info_matches func($Array, $Child type, $len usize) bool {
|
||||
match typeinfo!(Array) {
|
||||
@@ -15,6 +19,13 @@ hide array_info_matches func($Array, $Child type, $len usize) bool {
|
||||
else: return false
|
||||
}
|
||||
}
|
||||
hide distinct_info_matches func($Distinct, $Backing type) bool {
|
||||
match typeinfo!(Distinct) {
|
||||
.distinct |backing|: return backing == Backing
|
||||
else: return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
array_reflection_exposes_child_and_logical_length test {
|
||||
try testing.expect($(array_info_matches([4]i32, i32, 4)))
|
||||
@@ -23,6 +34,12 @@ array_reflection_exposes_child_and_logical_length test {
|
||||
try testing.expect($(array_info_matches([2]mut i64, i64, 2)))
|
||||
try testing.expect($(array_info_matches([2;0]u8, u8, 2)))
|
||||
}
|
||||
distinct_reflection_exposes_immediate_backing test {
|
||||
try testing.expect($(distinct_info_matches(TestInner, u16)))
|
||||
try testing.expect($(distinct_info_matches(TestOuter, TestInner)))
|
||||
try testing.expect($(distinct_info_matches(TestOuterAlias, TestInner)))
|
||||
}
|
||||
|
||||
|
||||
enum_field_struct_defaults test {
|
||||
names TestNames = {
|
||||
|
||||
@@ -1,714 +0,0 @@
|
||||
/// <reference types="tree-sitter-cli/dsl" />
|
||||
// @ts-check
|
||||
|
||||
const PREC = {
|
||||
RANGE: 1,
|
||||
FALLBACK: 2,
|
||||
OR: 3,
|
||||
AND: 4,
|
||||
COMPARE: 5,
|
||||
BITWISE: 6,
|
||||
SHIFT: 7,
|
||||
SUM: 8,
|
||||
PRODUCT: 9,
|
||||
PREFIX: 10,
|
||||
POSTFIX: 11,
|
||||
};
|
||||
|
||||
module.exports = grammar({
|
||||
name: 'brolang',
|
||||
|
||||
word: $ => $.identifier,
|
||||
|
||||
extras: $ => [/[ \t\r]/, $.comment],
|
||||
|
||||
conflicts: $ => [
|
||||
[$.expression, $.qualified_identifier],
|
||||
[$.constant_declaration, $.variable_declaration, $.expression],
|
||||
[$.constant_declaration, $.variable_declaration, $.expression, $.qualified_identifier],
|
||||
[$.function_declaration],
|
||||
[$.if_statement],
|
||||
[$.enum_literal],
|
||||
[$.array_type],
|
||||
[$.array_type, $.expression],
|
||||
[$.array_type, $.array_literal],
|
||||
[$.expression_statement, $.parenthesized_expression],
|
||||
[$.assignment_statement, $.expression_statement],
|
||||
[$.labeled_block, $.expression],
|
||||
[$.block, $.tuple_literal, $.anonymous_record_literal],
|
||||
[$.field_initializer, $.expression],
|
||||
[$.capture_list, $.expression],
|
||||
[$.match_capture, $.expression],
|
||||
[$.statement, $.labeled_block],
|
||||
],
|
||||
|
||||
rules: {
|
||||
source_file: $ => repeat(choice($._newline, $._top_level_declaration)),
|
||||
|
||||
_top_level_declaration: $ => choice(
|
||||
$.import_declaration,
|
||||
$.test_import_declaration,
|
||||
$.function_declaration,
|
||||
$.test_declaration,
|
||||
$.type_declaration,
|
||||
$.global_constant_declaration,
|
||||
$.global_variable_declaration,
|
||||
),
|
||||
|
||||
import_declaration: $ => seq(
|
||||
optional(seq(field('alias', $.identifier), '::', repeat($._newline))),
|
||||
'import',
|
||||
repeat($._newline),
|
||||
field('path', $.string),
|
||||
),
|
||||
|
||||
test_import_declaration: $ => seq(
|
||||
'test',
|
||||
'import',
|
||||
repeat($._newline),
|
||||
field('path', $.string),
|
||||
),
|
||||
|
||||
test_declaration: $ => seq(
|
||||
field('name', $.identifier),
|
||||
'test',
|
||||
repeat($._newline),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
function_declaration: $ => seq(
|
||||
optional('hide'),
|
||||
field('name', $.identifier),
|
||||
field('kind', choice('func', 'c_func')),
|
||||
field('parameters', $.parameter_list),
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
optional(choice(
|
||||
field('body', $.block),
|
||||
seq(repeat1($._newline), field('body', $.block)),
|
||||
)),
|
||||
),
|
||||
|
||||
type_declaration: $ => prec(1, seq(
|
||||
optional('hide'),
|
||||
field('name', $.identifier),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', choice(
|
||||
$.struct_type,
|
||||
$.c_struct_type,
|
||||
$.union_type,
|
||||
$.enum_type,
|
||||
$.opaque_type,
|
||||
$.distinct_type,
|
||||
$.alias_type,
|
||||
)),
|
||||
)),
|
||||
|
||||
global_constant_declaration: $ => seq(
|
||||
optional('hide'),
|
||||
field('name', $.identifier),
|
||||
optional(field('type', $.type)),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
global_variable_declaration: $ => seq(
|
||||
optional('hide'),
|
||||
field('name', $.identifier),
|
||||
optional(field('type', $.type)),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
struct_type: $ => seq('struct', repeat($._newline), $.record_body),
|
||||
c_struct_type: $ => seq('c_struct', repeat($._newline), $.record_body),
|
||||
opaque_type: _ => 'opaque',
|
||||
distinct_type: $ => seq('distinct', field('type', $.type)),
|
||||
alias_type: $ => seq('alias', field('type', $.type)),
|
||||
|
||||
union_type: $ => seq(
|
||||
'union',
|
||||
optional(seq('(', choice('enum', $.type), ')')),
|
||||
repeat($._newline),
|
||||
$.record_body,
|
||||
),
|
||||
|
||||
enum_type: $ => seq(
|
||||
'enum',
|
||||
optional(seq('(', field('backing', $.type), ')')),
|
||||
repeat($._newline),
|
||||
$.enum_body,
|
||||
),
|
||||
|
||||
record_body: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, seq($.record_field, optional(',')))),
|
||||
'}',
|
||||
),
|
||||
|
||||
record_field: $ => choice(
|
||||
seq(
|
||||
field('name', $.identifier),
|
||||
field('type', choice($.type, $.struct_type)),
|
||||
optional(seq('=', repeat($._newline), field('default', $.expression))),
|
||||
),
|
||||
field('type', choice($.type, $.struct_type)),
|
||||
),
|
||||
|
||||
enum_body: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, seq($.enum_member, optional(',')))),
|
||||
'}',
|
||||
),
|
||||
|
||||
enum_member: $ => seq(
|
||||
field('name', $.identifier),
|
||||
optional(seq('=', optional('-'), field('value', $.integer))),
|
||||
),
|
||||
|
||||
parameter_list: $ => seq(
|
||||
'(',
|
||||
commaSep($, choice($.parameter, '...')),
|
||||
')',
|
||||
),
|
||||
|
||||
parameter: $ => seq(
|
||||
optional('$'),
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
repeat(seq(',', repeat($._newline), optional('$'), field('name', choice($.identifier, $.sink)))),
|
||||
field('type', $.type),
|
||||
),
|
||||
|
||||
type: $ => prec.left(seq(
|
||||
$._type_atom,
|
||||
repeat(seq('|', $._type_atom)),
|
||||
)),
|
||||
|
||||
_type_atom: $ => choice(
|
||||
$.builtin_type,
|
||||
$.intrinsic_type,
|
||||
$.named_type,
|
||||
$.optional_type,
|
||||
$.pointer_type,
|
||||
$.array_type,
|
||||
$.function_type,
|
||||
$.parenthesized_type,
|
||||
),
|
||||
|
||||
parenthesized_type: $ => seq('(', repeat($._newline), $.type, repeat($._newline), ')'),
|
||||
|
||||
named_type: $ => prec.right(seq($.qualified_identifier, optional($.argument_list))),
|
||||
|
||||
intrinsic_type: $ => prec(PREC.POSTFIX, seq(
|
||||
field('function', alias('struct_type!', $.identifier)),
|
||||
field('arguments', $.argument_list),
|
||||
)),
|
||||
|
||||
optional_type: $ => seq('?', $.type),
|
||||
|
||||
pointer_type: $ => seq(
|
||||
choice('@', '*'),
|
||||
optional('mut'),
|
||||
$.type,
|
||||
),
|
||||
|
||||
array_type: $ => seq(
|
||||
'[',
|
||||
repeat($._newline),
|
||||
optional(choice(
|
||||
seq('*', ';', field('sentinel', $._type_constant)),
|
||||
seq(';', field('sentinel', $._type_constant)),
|
||||
seq(
|
||||
field('length', choice($.sink, $.expression)),
|
||||
optional(seq(';', field('sentinel', $._type_constant))),
|
||||
),
|
||||
)),
|
||||
repeat($._newline),
|
||||
']',
|
||||
optional('mut'),
|
||||
field('element', $.type),
|
||||
),
|
||||
|
||||
function_type: $ => prec.right(seq(
|
||||
choice('func', 'c_func'),
|
||||
$.parameter_list,
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
)),
|
||||
|
||||
_error_type: $ => choice($.type, $.enum_type, $.union_type),
|
||||
_type_constant: $ => seq(optional('-'), choice($.integer, $.character)),
|
||||
|
||||
builtin_type: _ => choice(
|
||||
'void', 'noreturn', 'type', 'anyopaque', 'bool', 'int', 'uint', 'float', 'range',
|
||||
'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
||||
'isize', 'usize', 'f32', 'f64',
|
||||
'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort',
|
||||
'c_int', 'c_uint', 'c_long', 'c_ulong', 'c_longlong',
|
||||
'c_ulonglong', 'c_float', 'c_double', 'c_longdouble',
|
||||
),
|
||||
|
||||
block: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, $.statement)),
|
||||
'}',
|
||||
),
|
||||
|
||||
statement: $ => choice(
|
||||
$.constant_declaration,
|
||||
$.variable_declaration,
|
||||
$.assignment_statement,
|
||||
$.return_statement,
|
||||
$.yield_statement,
|
||||
$.if_statement,
|
||||
$.while_statement,
|
||||
$.for_statement,
|
||||
$.match_statement,
|
||||
$.break_statement,
|
||||
$.continue_statement,
|
||||
$.defer_statement,
|
||||
$.labeled_block,
|
||||
$.block,
|
||||
$.expression_statement,
|
||||
),
|
||||
|
||||
constant_declaration: $ => seq(
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
optional(field('type', $.type)),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
variable_declaration: $ => seq(
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
field('type', $.type),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
assignment_statement: $ => seq(
|
||||
field('left', $.expression),
|
||||
field('operator', choice('=', '+=', '-=', '*=', '/=', '&=', '|=', 'xor=', '<<=', '>>=', '<<|=')),
|
||||
repeat($._newline),
|
||||
field('right', $._value),
|
||||
),
|
||||
|
||||
expression_statement: $ => $.expression,
|
||||
|
||||
return_statement: $ => prec.right(seq(
|
||||
'return',
|
||||
optional(field('value', $._value)),
|
||||
)),
|
||||
|
||||
yield_statement: $ => seq(
|
||||
'yield',
|
||||
optional(seq(':', field('label', $.identifier))),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
break_statement: $ => prec.right(seq('break', optional(seq(':', field('label', $.identifier))))),
|
||||
continue_statement: $ => prec.right(seq('continue', optional(seq(':', field('label', $.identifier))))),
|
||||
|
||||
defer_statement: $ => choice(
|
||||
seq('defer', repeat($._newline), field('body', $.statement)),
|
||||
seq(
|
||||
'errdefer',
|
||||
repeat($._newline),
|
||||
optional(seq(field('capture', $.error_capture), repeat($._newline))),
|
||||
field('body', $.statement),
|
||||
),
|
||||
),
|
||||
|
||||
error_capture: $ => seq('|', choice($.identifier, $.sink), '|'),
|
||||
|
||||
labeled_block: $ => seq(
|
||||
field('label', $.identifier),
|
||||
':',
|
||||
repeat($._newline),
|
||||
$.block,
|
||||
),
|
||||
|
||||
if_statement: $ => seq(
|
||||
'if',
|
||||
repeat($._newline),
|
||||
field('condition', $.expression),
|
||||
optional($.capture_list),
|
||||
repeat($._newline),
|
||||
field('consequence', $._branch_body),
|
||||
optional(seq(
|
||||
repeat($._newline),
|
||||
'else',
|
||||
repeat($._newline),
|
||||
field('alternative', $._branch_body),
|
||||
)),
|
||||
),
|
||||
|
||||
capture_list: $ => seq(
|
||||
'|',
|
||||
commaSep1($, choice($.identifier, $.sink)),
|
||||
optional(seq(':', field('guard', $.expression))),
|
||||
'|',
|
||||
),
|
||||
|
||||
while_statement: $ => seq(
|
||||
'while',
|
||||
repeat($._newline),
|
||||
field('condition', $.expression),
|
||||
optional(seq(':', repeat($._newline), field('update', choice(
|
||||
$.assignment_statement,
|
||||
$.expression_statement,
|
||||
seq('(', repeat($._newline), choice($.assignment_statement, $.expression_statement), repeat($._newline), ')'),
|
||||
)))),
|
||||
repeat($._newline),
|
||||
optional(prec(1, seq(field('label', $.identifier), ':', repeat($._newline)))),
|
||||
field('body', $._branch_body),
|
||||
),
|
||||
|
||||
for_statement: $ => seq(
|
||||
optional('expand'),
|
||||
'for',
|
||||
repeat($._newline),
|
||||
field('iterable', $.expression),
|
||||
repeat($._newline),
|
||||
$._for_capture_bar,
|
||||
optional('@'),
|
||||
field('item', choice($.identifier, $.sink)),
|
||||
optional(seq(',', field('index', choice($.identifier, $.sink)))),
|
||||
$._for_capture_bar,
|
||||
repeat($._newline),
|
||||
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
_for_capture_bar: _ => token(prec(1, '|')),
|
||||
|
||||
match_statement: $ => seq(
|
||||
'match',
|
||||
repeat($._newline),
|
||||
field('subject', $.expression),
|
||||
repeat($._newline),
|
||||
'{',
|
||||
repeat(choice($._newline, $.match_arm)),
|
||||
'}',
|
||||
),
|
||||
|
||||
match_arm: $ => choice(seq(
|
||||
field('pattern', choice('else', commaSep1($, $.expression))),
|
||||
optional($.match_capture),
|
||||
':',
|
||||
repeat($._newline),
|
||||
field('body', $._branch_body),
|
||||
), seq(
|
||||
'expand',
|
||||
$.expand_match_capture,
|
||||
':',
|
||||
repeat($._newline),
|
||||
field('body', $._branch_body),
|
||||
)),
|
||||
|
||||
match_capture: $ => seq('|', optional('@'), field('name', choice($.identifier, $.sink)), '|'),
|
||||
|
||||
expand_match_capture: $ => seq(
|
||||
'|',
|
||||
optional('@'),
|
||||
field('value', choice($.identifier, $.sink)),
|
||||
optional(seq(',', field('tag', choice($.identifier, $.sink)))),
|
||||
'|',
|
||||
),
|
||||
|
||||
_branch_body: $ => $.statement,
|
||||
|
||||
_value: $ => choice(
|
||||
$.labeled_block,
|
||||
$.block,
|
||||
$.if_statement,
|
||||
$.while_statement,
|
||||
$.for_statement,
|
||||
$.match_statement,
|
||||
$.expression,
|
||||
),
|
||||
|
||||
expression: $ => choice(
|
||||
$.binary_expression,
|
||||
$.catch_expression,
|
||||
$.unary_expression,
|
||||
$.field_expression,
|
||||
$.intrinsic_call_expression,
|
||||
$.call_expression,
|
||||
$.index_expression,
|
||||
$.slice_expression,
|
||||
$.postfix_expression,
|
||||
$.struct_literal,
|
||||
$.anonymous_record_literal,
|
||||
$.tuple_literal,
|
||||
$.comptime_block,
|
||||
$.function_literal,
|
||||
$.struct_type,
|
||||
$.array_type,
|
||||
$.enum_literal,
|
||||
$.array_literal,
|
||||
$.parenthesized_expression,
|
||||
$.identifier,
|
||||
$.sink,
|
||||
$.builtin_type,
|
||||
$.integer,
|
||||
$.float,
|
||||
$.string,
|
||||
$.multiline_string,
|
||||
$.character,
|
||||
$.boolean,
|
||||
$.null,
|
||||
$.unreachable,
|
||||
$.undefined,
|
||||
),
|
||||
|
||||
binary_expression: $ => choice(
|
||||
prec.left(PREC.RANGE, seq(field('left', $.expression), field('operator', choice('..', '..=')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.FALLBACK, seq(field('left', $.expression), field('operator', 'orelse'), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.OR, seq(field('left', $.expression), 'or', repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.AND, seq(field('left', $.expression), 'and', repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.COMPARE, seq(field('left', $.expression), field('operator', choice('==', '!=', '<', '<=', '>', '>=')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.BITWISE, seq(field('left', $.expression), field('operator', choice('&', 'xor', '|')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.SHIFT, seq(field('left', $.expression), field('operator', choice('<<', '>>', '<<|')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.SUM, seq(field('left', $.expression), field('operator', choice('+', '-')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.PRODUCT, seq(field('left', $.expression), field('operator', choice('*', '/')), repeat($._newline), field('right', $.expression))),
|
||||
),
|
||||
|
||||
catch_expression: $ => prec.left(PREC.FALLBACK, seq(
|
||||
field('value', $.expression),
|
||||
'catch',
|
||||
optional(field('capture', $.error_capture)),
|
||||
repeat($._newline),
|
||||
field('fallback', choice(
|
||||
$.block,
|
||||
$.if_statement,
|
||||
$.while_statement,
|
||||
$.for_statement,
|
||||
$.match_statement,
|
||||
$.expression,
|
||||
)),
|
||||
)),
|
||||
|
||||
unary_expression: $ => prec(PREC.PREFIX, seq(
|
||||
field('operator', choice('-', '&', '!', '~', '$', 'try')),
|
||||
repeat($._newline),
|
||||
field('operand', $.expression),
|
||||
)),
|
||||
|
||||
field_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'.',
|
||||
field('field', choice($.identifier, $.integer)),
|
||||
)),
|
||||
|
||||
intrinsic_call_expression: $ => prec(PREC.POSTFIX, seq(
|
||||
field('function', $.identifier),
|
||||
field('marker', '!'),
|
||||
field('arguments', $.argument_list),
|
||||
)),
|
||||
|
||||
call_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('function', $.expression),
|
||||
field('arguments', $.argument_list),
|
||||
)),
|
||||
|
||||
argument_list: $ => prec(PREC.POSTFIX, seq('(', commaSep($, $.expression), ')')),
|
||||
|
||||
index_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'[',
|
||||
repeat($._newline),
|
||||
field('index', $.expression),
|
||||
repeat($._newline),
|
||||
']',
|
||||
)),
|
||||
|
||||
slice_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'[',
|
||||
repeat($._newline),
|
||||
optional(field('start', $.expression)),
|
||||
'..',
|
||||
optional(field('end', $.expression)),
|
||||
repeat($._newline),
|
||||
']',
|
||||
)),
|
||||
|
||||
postfix_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
field('operator', choice('?', '^')),
|
||||
)),
|
||||
|
||||
struct_literal: $ => prec(PREC.POSTFIX, seq(
|
||||
field('type', $.qualified_identifier),
|
||||
optional($.argument_list),
|
||||
field('fields', $.initializer_list),
|
||||
)),
|
||||
|
||||
initializer_list: $ => seq(
|
||||
'{',
|
||||
choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
choice($.field_initializer, $.expression),
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), choice($.field_initializer, $.expression))),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline),
|
||||
),
|
||||
),
|
||||
'}',
|
||||
),
|
||||
|
||||
field_initializer: $ => seq(
|
||||
field('name', $._field_name),
|
||||
optional(seq('=', repeat($._newline), field('value', $.expression))),
|
||||
),
|
||||
|
||||
anonymous_record_literal: $ => prec(2, seq(
|
||||
'{',
|
||||
repeat($._newline),
|
||||
$.keyed_field_initializer,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.keyed_field_initializer)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline),
|
||||
'}',
|
||||
)),
|
||||
|
||||
tuple_literal: $ => prec(1, choice(
|
||||
seq('{', repeat($._newline), '}'),
|
||||
seq(
|
||||
'{', repeat($._newline), $.expression,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.expression)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline), '}',
|
||||
),
|
||||
)),
|
||||
|
||||
enum_literal: $ => seq(
|
||||
'.',
|
||||
field('name', $.identifier),
|
||||
optional($.variant_payload),
|
||||
),
|
||||
|
||||
variant_payload: $ => seq(
|
||||
'{',
|
||||
choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
choice(
|
||||
$.expression,
|
||||
seq(
|
||||
$.keyed_field_initializer,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.keyed_field_initializer)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
),
|
||||
),
|
||||
repeat($._newline),
|
||||
),
|
||||
),
|
||||
'}',
|
||||
),
|
||||
|
||||
keyed_field_initializer: $ => seq(
|
||||
field('name', $._field_name),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $.expression),
|
||||
),
|
||||
|
||||
array_literal: $ => seq('[', commaSep($, $.expression), ']'),
|
||||
|
||||
parenthesized_expression: $ => seq(
|
||||
'(',
|
||||
repeat($._newline),
|
||||
$.expression,
|
||||
repeat($._newline),
|
||||
')',
|
||||
),
|
||||
|
||||
comptime_block: $ => seq('$', repeat($._newline), $.block),
|
||||
|
||||
function_literal: $ => seq(
|
||||
'func',
|
||||
$.parameter_list,
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
repeat($._newline),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
qualified_identifier: $ => prec.right(seq(
|
||||
field('qualifier', $.identifier),
|
||||
optional(seq('.', field('name', $.identifier))),
|
||||
)),
|
||||
|
||||
boolean: _ => choice('true', 'false'),
|
||||
null: _ => 'null',
|
||||
unreachable: _ => 'unreachable',
|
||||
undefined: _ => 'undefined',
|
||||
sink: _ => '_',
|
||||
|
||||
_field_name: $ => prec(2, choice(
|
||||
$.identifier,
|
||||
alias(choice(
|
||||
'test', 'func', 'c_func', 'struct', 'c_struct', 'opaque', 'union', 'enum',
|
||||
'distinct', 'alias', 'import', 'hide', 'return', 'try', 'catch', 'mut',
|
||||
'null', 'unreachable', 'undefined', 'orelse', 'and', 'or', 'xor', 'if', 'while', 'for',
|
||||
'expand', 'break', 'continue', 'defer', 'errdefer', 'yield', 'match', 'else',
|
||||
'true', 'false',
|
||||
'void', 'noreturn', 'type', 'anyopaque', 'bool', 'int', 'uint', 'float', 'range',
|
||||
'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
||||
'isize', 'usize', 'f32', 'f64',
|
||||
'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort',
|
||||
'c_int', 'c_uint', 'c_long', 'c_ulong', 'c_longlong',
|
||||
'c_ulonglong', 'c_float', 'c_double', 'c_longdouble',
|
||||
), $.identifier),
|
||||
)),
|
||||
|
||||
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
|
||||
integer: _ => /[0-9]+/,
|
||||
float: _ => token(prec(1, /[0-9]+\.[0-9]+/)),
|
||||
|
||||
string: $ => seq(
|
||||
'"',
|
||||
repeat(choice($.string_content, $.escape_sequence)),
|
||||
'"',
|
||||
),
|
||||
string_content: _ => token.immediate(prec(1, /[^"\\\n]+/)),
|
||||
escape_sequence: _ => token.immediate(/\\(?:\\|"|n|r|t|0)/),
|
||||
multiline_string: _ => token(/`[^\n]*(?:\n[ \t]*`[^\n]*)*/),
|
||||
character: _ => token(/'(?:[^'\\\n]|\\(?:\\|'|n|r|t|0))'/),
|
||||
comment: _ => token(seq('#', /[^\n]*/)),
|
||||
_newline: _ => /\n/,
|
||||
},
|
||||
});
|
||||
|
||||
function commaSep($, rule) {
|
||||
return choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
rule,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), rule)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function commaSep1($, rule) {
|
||||
return seq(
|
||||
rule,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), rule)),
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "tree-sitter-brolang",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"generate": "tree-sitter generate",
|
||||
"test": "tree-sitter test"
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(string)
|
||||
(multiline_string)
|
||||
] @string
|
||||
|
||||
(character) @string
|
||||
(escape_sequence) @escape
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
(boolean) @constant.builtin
|
||||
|
||||
[
|
||||
(null)
|
||||
(unreachable)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
(builtin_type) @type.builtin
|
||||
(named_type) @type
|
||||
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier) @function .)
|
||||
(argument_list))
|
||||
|
||||
(struct_literal
|
||||
type: (qualified_identifier
|
||||
(identifier) @function .)
|
||||
(argument_list))
|
||||
|
||||
(type_declaration name: (identifier) @type)
|
||||
(function_declaration name: (identifier) @function)
|
||||
(test_declaration name: (identifier) @function)
|
||||
(parameter name: (identifier) @variable.parameter)
|
||||
|
||||
(intrinsic_call_expression function: (identifier) @function.builtin)
|
||||
(intrinsic_type function: (identifier) @function.builtin)
|
||||
(call_expression function: (expression (identifier) @function))
|
||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||
(field_expression field: (identifier) @property)
|
||||
(field_expression field: (integer) @property)
|
||||
(field_initializer name: (identifier) @property)
|
||||
(keyed_field_initializer name: (identifier) @property)
|
||||
(record_field name: (identifier) @property)
|
||||
(enum_member name: (identifier) @property)
|
||||
(enum_literal name: (identifier) @property)
|
||||
|
||||
(import_declaration alias: (identifier) @module)
|
||||
(opaque_type) @keyword
|
||||
|
||||
[
|
||||
"func"
|
||||
"test"
|
||||
"c_func"
|
||||
"struct"
|
||||
"c_struct"
|
||||
"union"
|
||||
"enum"
|
||||
"distinct"
|
||||
"alias"
|
||||
"import"
|
||||
"hide"
|
||||
"return"
|
||||
"try"
|
||||
"catch"
|
||||
"mut"
|
||||
"orelse"
|
||||
"and"
|
||||
"or"
|
||||
"if"
|
||||
"while"
|
||||
"for"
|
||||
"expand"
|
||||
"break"
|
||||
"continue"
|
||||
"defer"
|
||||
"errdefer"
|
||||
"yield"
|
||||
"match"
|
||||
"else"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"::"
|
||||
"="
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
"&="
|
||||
"|="
|
||||
"xor="
|
||||
"<<="
|
||||
">>="
|
||||
"<<|="
|
||||
"=="
|
||||
"!="
|
||||
"<"
|
||||
"<="
|
||||
">"
|
||||
">="
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"~"
|
||||
"xor"
|
||||
"<<"
|
||||
">>"
|
||||
"<<|"
|
||||
"!"
|
||||
"&"
|
||||
"@"
|
||||
"?"
|
||||
"^"
|
||||
".."
|
||||
"..="
|
||||
"|"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
#ifndef TREE_SITTER_ALLOC_H_
|
||||
#define TREE_SITTER_ALLOC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Allow clients to override allocation functions
|
||||
#ifdef TREE_SITTER_REUSE_ALLOCATOR
|
||||
|
||||
extern void *(*ts_current_malloc)(size_t size);
|
||||
extern void *(*ts_current_calloc)(size_t count, size_t size);
|
||||
extern void *(*ts_current_realloc)(void *ptr, size_t size);
|
||||
extern void (*ts_current_free)(void *ptr);
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc ts_current_malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc ts_current_calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc ts_current_realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free ts_current_free
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free free
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ALLOC_H_
|
||||
@@ -1,347 +0,0 @@
|
||||
#ifndef TREE_SITTER_ARRAY_H_
|
||||
#define TREE_SITTER_ARRAY_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "./alloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4101)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
#define Array(T) \
|
||||
struct { \
|
||||
T *contents; \
|
||||
uint32_t size; \
|
||||
uint32_t capacity; \
|
||||
}
|
||||
|
||||
/// Initialize an array.
|
||||
#define array_init(self) \
|
||||
((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
|
||||
|
||||
/// Create an empty array.
|
||||
#define array_new() \
|
||||
{ NULL, 0, 0 }
|
||||
|
||||
/// Get a pointer to the element at a given `index` in the array.
|
||||
#define array_get(self, _index) \
|
||||
(assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
|
||||
|
||||
/// Get a pointer to the first element in the array.
|
||||
#define array_front(self) array_get(self, 0)
|
||||
|
||||
/// Get a pointer to the last element in the array.
|
||||
#define array_back(self) array_get(self, (self)->size - 1)
|
||||
|
||||
/// Clear the array, setting its size to zero. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_clear(self) ((self)->size = 0)
|
||||
|
||||
/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is
|
||||
/// less than the array's current capacity, this function has no effect.
|
||||
#define array_reserve(self, new_capacity) \
|
||||
((self)->contents = _array__reserve( \
|
||||
(void *)(self)->contents, &(self)->capacity, \
|
||||
array_elem_size(self), new_capacity) \
|
||||
)
|
||||
|
||||
/// Free any memory allocated for this array. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_delete(self) _array__delete((self), (void *)(self)->contents, sizeof(*self))
|
||||
|
||||
/// Push a new `element` onto the end of the array.
|
||||
#define array_push(self, element) \
|
||||
do { \
|
||||
(self)->contents = _array__grow( \
|
||||
(void *)(self)->contents, (self)->size, &(self)->capacity, \
|
||||
1, array_elem_size(self) \
|
||||
); \
|
||||
(self)->contents[(self)->size++] = (element); \
|
||||
} while(0)
|
||||
|
||||
/// Increase the array's size by `count` elements.
|
||||
/// New elements are zero-initialized.
|
||||
#define array_grow_by(self, count) \
|
||||
do { \
|
||||
if ((count) == 0) break; \
|
||||
(self)->contents = _array__grow( \
|
||||
(self)->contents, (self)->size, &(self)->capacity, \
|
||||
count, array_elem_size(self) \
|
||||
); \
|
||||
memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \
|
||||
(self)->size += (count); \
|
||||
} while (0)
|
||||
|
||||
/// Append all elements from one array to the end of another.
|
||||
#define array_push_all(self, other) \
|
||||
array_extend((self), (other)->size, (other)->contents)
|
||||
|
||||
/// Append `count` elements to the end of the array, reading their values from the
|
||||
/// `contents` pointer.
|
||||
#define array_extend(self, count, other_contents) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void*)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), (self)->size, 0, count, other_contents \
|
||||
)
|
||||
|
||||
/// Remove `old_count` elements from the array starting at the given `index`. At
|
||||
/// the same index, insert `new_count` new elements, reading their values from the
|
||||
/// `new_contents` pointer.
|
||||
#define array_splice(self, _index, old_count, new_count, new_contents) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, old_count, new_count, new_contents \
|
||||
)
|
||||
|
||||
/// Insert one `element` into the array at the given `index`.
|
||||
#define array_insert(self, _index, element) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, 0, 1, &(element) \
|
||||
)
|
||||
|
||||
/// Remove one element from the array at the given `index`.
|
||||
#define array_erase(self, _index) \
|
||||
_array__erase((void *)(self)->contents, &(self)->size, array_elem_size(self), _index)
|
||||
|
||||
/// Pop the last element off the array, returning the element by value.
|
||||
#define array_pop(self) ((self)->contents[--(self)->size])
|
||||
|
||||
/// Assign the contents of one array to another, reallocating if necessary.
|
||||
#define array_assign(self, other) \
|
||||
(self)->contents = _array__assign( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
(const void *)(other)->contents, (other)->size, array_elem_size(self) \
|
||||
)
|
||||
|
||||
/// Swap one array with another
|
||||
#define array_swap(self, other) \
|
||||
do { \
|
||||
struct Swap swapped_contents = _array__swap( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
(void *)(other)->contents, &(other)->size, &(other)->capacity \
|
||||
); \
|
||||
(self)->contents = swapped_contents.self_contents; \
|
||||
(other)->contents = swapped_contents.other_contents; \
|
||||
} while (0)
|
||||
|
||||
/// Get the size of the array contents
|
||||
#define array_elem_size(self) (sizeof *(self)->contents)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
///
|
||||
/// If an existing element is found to be equal to `needle`, then the `index`
|
||||
/// out-parameter is set to the existing value's index, and the `exists`
|
||||
/// out-parameter is set to true. Otherwise, `index` is set to an index where
|
||||
/// `needle` should be inserted in order to preserve the sorting, and `exists`
|
||||
/// is set to false.
|
||||
#define array_search_sorted_with(self, compare, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, compare, , needle, _index, _exists)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using integer comparisons
|
||||
/// of a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_with`.
|
||||
#define array_search_sorted_by(self, field, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
#define array_insert_sorted_with(self, compare, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using integer comparisons of
|
||||
/// a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_by`.
|
||||
#define array_insert_sorted_by(self, field, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
// Private
|
||||
|
||||
// Pointers to individual `Array` fields (rather than the entire `Array` itself)
|
||||
// are passed to the various `_array__*` functions below to address strict aliasing
|
||||
// violations that arises when the _entire_ `Array` struct is passed as `Array(void)*`.
|
||||
//
|
||||
// The `Array` type itself was not altered as a solution in order to avoid breakage
|
||||
// with existing consumers (in particular, parsers with external scanners).
|
||||
|
||||
/// This is not what you're looking for, see `array_delete`.
|
||||
static inline void _array__delete(void *self, void *contents, size_t self_size) {
|
||||
if (contents) ts_free(contents);
|
||||
if (self) memset(self, 0, self_size);
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_erase`.
|
||||
static inline void _array__erase(void* self_contents, uint32_t *size,
|
||||
size_t element_size, uint32_t index) {
|
||||
assert(index < *size);
|
||||
char *contents = (char *)self_contents;
|
||||
memmove(contents + index * element_size, contents + (index + 1) * element_size,
|
||||
(*size - index - 1) * element_size);
|
||||
(*size)--;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_reserve`.
|
||||
static inline void *_array__reserve(void *contents, uint32_t *capacity,
|
||||
size_t element_size, uint32_t new_capacity) {
|
||||
void *new_contents = contents;
|
||||
if (new_capacity > *capacity) {
|
||||
if (contents) {
|
||||
new_contents = ts_realloc(contents, new_capacity * element_size);
|
||||
} else {
|
||||
new_contents = ts_malloc(new_capacity * element_size);
|
||||
}
|
||||
*capacity = new_capacity;
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_assign`.
|
||||
static inline void *_array__assign(void* self_contents, uint32_t *self_size, uint32_t *self_capacity,
|
||||
const void *other_contents, uint32_t other_size, size_t element_size) {
|
||||
void *new_contents = _array__reserve(self_contents, self_capacity, element_size, other_size);
|
||||
*self_size = other_size;
|
||||
memcpy(new_contents, other_contents, *self_size * element_size);
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
struct Swap {
|
||||
void *self_contents;
|
||||
void *other_contents;
|
||||
};
|
||||
|
||||
/// This is not what you're looking for, see `array_swap`.
|
||||
// static inline void _array__swap(Array *self, Array *other) {
|
||||
static inline struct Swap _array__swap(void *self_contents, uint32_t *self_size, uint32_t *self_capacity,
|
||||
void *other_contents, uint32_t *other_size, uint32_t *other_capacity) {
|
||||
void *new_self_contents = other_contents;
|
||||
uint32_t new_self_size = *other_size;
|
||||
uint32_t new_self_capacity = *other_capacity;
|
||||
|
||||
void *new_other_contents = self_contents;
|
||||
*other_size = *self_size;
|
||||
*other_capacity = *self_capacity;
|
||||
|
||||
*self_size = new_self_size;
|
||||
*self_capacity = new_self_capacity;
|
||||
|
||||
struct Swap out = {
|
||||
.self_contents = new_self_contents,
|
||||
.other_contents = new_other_contents,
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_push` or `array_grow_by`.
|
||||
static inline void *_array__grow(void *contents, uint32_t size, uint32_t *capacity,
|
||||
uint32_t count, size_t element_size) {
|
||||
void *new_contents = contents;
|
||||
uint32_t new_size = size + count;
|
||||
if (new_size > *capacity) {
|
||||
uint32_t new_capacity = *capacity * 2;
|
||||
if (new_capacity < 8) new_capacity = 8;
|
||||
if (new_capacity < new_size) new_capacity = new_size;
|
||||
new_contents = _array__reserve(contents, capacity, element_size, new_capacity);
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_splice`.
|
||||
static inline void *_array__splice(void *self_contents, uint32_t *size, uint32_t *capacity,
|
||||
size_t element_size,
|
||||
uint32_t index, uint32_t old_count,
|
||||
uint32_t new_count, const void *elements) {
|
||||
uint32_t new_size = *size + new_count - old_count;
|
||||
uint32_t old_end = index + old_count;
|
||||
uint32_t new_end = index + new_count;
|
||||
assert(old_end <= *size);
|
||||
|
||||
void *new_contents = _array__reserve(self_contents, capacity, element_size, new_size);
|
||||
|
||||
char *contents = (char *)new_contents;
|
||||
if (*size > old_end) {
|
||||
memmove(
|
||||
contents + new_end * element_size,
|
||||
contents + old_end * element_size,
|
||||
(*size - old_end) * element_size
|
||||
);
|
||||
}
|
||||
if (new_count > 0) {
|
||||
if (elements) {
|
||||
memcpy(
|
||||
(contents + index * element_size),
|
||||
elements,
|
||||
new_count * element_size
|
||||
);
|
||||
} else {
|
||||
memset(
|
||||
(contents + index * element_size),
|
||||
0,
|
||||
new_count * element_size
|
||||
);
|
||||
}
|
||||
}
|
||||
*size += new_count - old_count;
|
||||
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// A binary search routine, based on Rust's `std::slice::binary_search_by`.
|
||||
/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`.
|
||||
#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
|
||||
do { \
|
||||
*(_index) = start; \
|
||||
*(_exists) = false; \
|
||||
uint32_t size = (self)->size - *(_index); \
|
||||
if (size == 0) break; \
|
||||
int comparison; \
|
||||
while (size > 1) { \
|
||||
uint32_t half_size = size / 2; \
|
||||
uint32_t mid_index = *(_index) + half_size; \
|
||||
comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
|
||||
if (comparison <= 0) *(_index) = mid_index; \
|
||||
size -= half_size; \
|
||||
} \
|
||||
comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
|
||||
if (comparison == 0) *(_exists) = true; \
|
||||
else if (comparison < 0) *(_index) += 1; \
|
||||
} while (0)
|
||||
|
||||
/// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
|
||||
/// parameter by reference in order to work with the generic sorting function above.
|
||||
#define _compare_int(a, b) ((int)*(a) - (int)(b))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ARRAY_H_
|
||||
@@ -1,286 +0,0 @@
|
||||
#ifndef TREE_SITTER_PARSER_H_
|
||||
#define TREE_SITTER_PARSER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ts_builtin_sym_error ((TSSymbol)-1)
|
||||
#define ts_builtin_sym_end 0
|
||||
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
|
||||
|
||||
#ifndef TREE_SITTER_API_H_
|
||||
typedef uint16_t TSStateId;
|
||||
typedef uint16_t TSSymbol;
|
||||
typedef uint16_t TSFieldId;
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
typedef struct TSLanguageMetadata {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint8_t patch_version;
|
||||
} TSLanguageMetadata;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
TSFieldId field_id;
|
||||
uint8_t child_index;
|
||||
bool inherited;
|
||||
} TSFieldMapEntry;
|
||||
|
||||
// Used to index the field and supertype maps.
|
||||
typedef struct {
|
||||
uint16_t index;
|
||||
uint16_t length;
|
||||
} TSMapSlice;
|
||||
|
||||
typedef struct {
|
||||
bool visible;
|
||||
bool named;
|
||||
bool supertype;
|
||||
} TSSymbolMetadata;
|
||||
|
||||
typedef struct TSLexer TSLexer;
|
||||
|
||||
struct TSLexer {
|
||||
int32_t lookahead;
|
||||
TSSymbol result_symbol;
|
||||
void (*advance)(TSLexer *, bool);
|
||||
void (*mark_end)(TSLexer *);
|
||||
uint32_t (*get_column)(TSLexer *);
|
||||
bool (*is_at_included_range_start)(const TSLexer *);
|
||||
bool (*eof)(const TSLexer *);
|
||||
void (*log)(const TSLexer *, const char *, ...);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TSParseActionTypeShift,
|
||||
TSParseActionTypeReduce,
|
||||
TSParseActionTypeAccept,
|
||||
TSParseActionTypeRecover,
|
||||
} TSParseActionType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t type;
|
||||
TSStateId state;
|
||||
bool extra;
|
||||
bool repetition;
|
||||
} shift;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t child_count;
|
||||
TSSymbol symbol;
|
||||
int16_t dynamic_precedence;
|
||||
uint16_t production_id;
|
||||
} reduce;
|
||||
uint8_t type;
|
||||
} TSParseAction;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
} TSLexMode;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
uint16_t reserved_word_set_id;
|
||||
} TSLexerMode;
|
||||
|
||||
typedef union {
|
||||
TSParseAction action;
|
||||
struct {
|
||||
uint8_t count;
|
||||
bool reusable;
|
||||
} entry;
|
||||
} TSParseActionEntry;
|
||||
|
||||
typedef struct {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
} TSCharacterRange;
|
||||
|
||||
struct TSLanguage {
|
||||
uint32_t abi_version;
|
||||
uint32_t symbol_count;
|
||||
uint32_t alias_count;
|
||||
uint32_t token_count;
|
||||
uint32_t external_token_count;
|
||||
uint32_t state_count;
|
||||
uint32_t large_state_count;
|
||||
uint32_t production_id_count;
|
||||
uint32_t field_count;
|
||||
uint16_t max_alias_sequence_length;
|
||||
const uint16_t *parse_table;
|
||||
const uint16_t *small_parse_table;
|
||||
const uint32_t *small_parse_table_map;
|
||||
const TSParseActionEntry *parse_actions;
|
||||
const char * const *symbol_names;
|
||||
const char * const *field_names;
|
||||
const TSMapSlice *field_map_slices;
|
||||
const TSFieldMapEntry *field_map_entries;
|
||||
const TSSymbolMetadata *symbol_metadata;
|
||||
const TSSymbol *public_symbol_map;
|
||||
const uint16_t *alias_map;
|
||||
const TSSymbol *alias_sequences;
|
||||
const TSLexerMode *lex_modes;
|
||||
bool (*lex_fn)(TSLexer *, TSStateId);
|
||||
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
|
||||
TSSymbol keyword_capture_token;
|
||||
struct {
|
||||
const bool *states;
|
||||
const TSSymbol *symbol_map;
|
||||
void *(*create)(void);
|
||||
void (*destroy)(void *);
|
||||
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
|
||||
unsigned (*serialize)(void *, char *);
|
||||
void (*deserialize)(void *, const char *, unsigned);
|
||||
} external_scanner;
|
||||
const TSStateId *primary_state_ids;
|
||||
const char *name;
|
||||
const TSSymbol *reserved_words;
|
||||
uint16_t max_reserved_word_set_size;
|
||||
uint32_t supertype_count;
|
||||
const TSSymbol *supertype_symbols;
|
||||
const TSMapSlice *supertype_map_slices;
|
||||
const TSSymbol *supertype_map_entries;
|
||||
TSLanguageMetadata metadata;
|
||||
};
|
||||
|
||||
static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) {
|
||||
uint32_t index = 0;
|
||||
uint32_t size = len - index;
|
||||
while (size > 1) {
|
||||
uint32_t half_size = size / 2;
|
||||
uint32_t mid_index = index + half_size;
|
||||
const TSCharacterRange *range = &ranges[mid_index];
|
||||
if (lookahead >= range->start && lookahead <= range->end) {
|
||||
return true;
|
||||
} else if (lookahead > range->end) {
|
||||
index = mid_index;
|
||||
}
|
||||
size -= half_size;
|
||||
}
|
||||
const TSCharacterRange *range = &ranges[index];
|
||||
return (lookahead >= range->start && lookahead <= range->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexer Macros
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define UNUSED __pragma(warning(suppress : 4101))
|
||||
#else
|
||||
#define UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#define START_LEXER() \
|
||||
bool result = false; \
|
||||
bool skip = false; \
|
||||
UNUSED \
|
||||
bool eof = false; \
|
||||
int32_t lookahead; \
|
||||
goto start; \
|
||||
next_state: \
|
||||
lexer->advance(lexer, skip); \
|
||||
start: \
|
||||
skip = false; \
|
||||
lookahead = lexer->lookahead;
|
||||
|
||||
#define ADVANCE(state_value) \
|
||||
{ \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ADVANCE_MAP(...) \
|
||||
{ \
|
||||
static const uint16_t map[] = { __VA_ARGS__ }; \
|
||||
for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \
|
||||
if (map[i] == lookahead) { \
|
||||
state = map[i + 1]; \
|
||||
goto next_state; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SKIP(state_value) \
|
||||
{ \
|
||||
skip = true; \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ACCEPT_TOKEN(symbol_value) \
|
||||
result = true; \
|
||||
lexer->result_symbol = symbol_value; \
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
#define END_STATE() return result;
|
||||
|
||||
/*
|
||||
* Parse Table Macros
|
||||
*/
|
||||
|
||||
#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
|
||||
|
||||
#define STATE(id) id
|
||||
|
||||
#define ACTIONS(id) id
|
||||
|
||||
#define SHIFT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value) \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_REPEAT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value), \
|
||||
.repetition = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_EXTRA() \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.extra = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define REDUCE(symbol_name, children, precedence, prod_id) \
|
||||
{{ \
|
||||
.reduce = { \
|
||||
.type = TSParseActionTypeReduce, \
|
||||
.symbol = symbol_name, \
|
||||
.child_count = children, \
|
||||
.dynamic_precedence = precedence, \
|
||||
.production_id = prod_id \
|
||||
}, \
|
||||
}}
|
||||
|
||||
#define RECOVER() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeRecover \
|
||||
}}
|
||||
|
||||
#define ACCEPT_INPUT() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeAccept \
|
||||
}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_PARSER_H_
|
||||
@@ -1,110 +0,0 @@
|
||||
==================
|
||||
Catch value sources
|
||||
==================
|
||||
|
||||
recover func() void {
|
||||
a :: fail() catch |err| handle(err)
|
||||
b :: fail() catch |err| match err {
|
||||
.bad: 1
|
||||
else: 2
|
||||
}
|
||||
c :: fail() catch { yield 3 }
|
||||
d :: fail() catch if true { yield 4 } else { yield 5 }
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(catch_expression
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list)))
|
||||
(error_capture
|
||||
(identifier))
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list
|
||||
(expression
|
||||
(identifier)))))))))
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(catch_expression
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list)))
|
||||
(error_capture
|
||||
(identifier))
|
||||
(match_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(match_arm
|
||||
(expression
|
||||
(enum_literal
|
||||
(identifier)))
|
||||
(statement
|
||||
(expression_statement
|
||||
(expression
|
||||
(integer)))))
|
||||
(match_arm
|
||||
(statement
|
||||
(expression_statement
|
||||
(expression
|
||||
(integer))))))))))
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(catch_expression
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list)))
|
||||
(block
|
||||
(statement
|
||||
(yield_statement
|
||||
(expression
|
||||
(integer)))))))))
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(catch_expression
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list)))
|
||||
(if_statement
|
||||
(expression
|
||||
(boolean))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(yield_statement
|
||||
(expression
|
||||
(integer))))))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(yield_statement
|
||||
(expression
|
||||
(integer))))))))))))))
|
||||
@@ -1,842 +0,0 @@
|
||||
==================
|
||||
Core syntax
|
||||
==================
|
||||
|
||||
io :: import "@std/io"
|
||||
|
||||
hide private_value :: 1
|
||||
|
||||
hide private_counter i32 = 0
|
||||
|
||||
hide Private :: opaque
|
||||
|
||||
hide private_sum func(a, b i32) i32 {
|
||||
return a + b
|
||||
}
|
||||
|
||||
Status :: enum {
|
||||
ok
|
||||
bad
|
||||
}
|
||||
|
||||
Pair :: struct {
|
||||
left i32
|
||||
right i32
|
||||
}
|
||||
|
||||
sum func(a, b i32) i32 ! Status {
|
||||
result :: a + b
|
||||
if result > 0 {
|
||||
return result
|
||||
} else {
|
||||
return .bad
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(import_declaration
|
||||
(identifier)
|
||||
(string
|
||||
(string_content)))
|
||||
(global_constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(integer)))
|
||||
(global_variable_declaration
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))
|
||||
(expression
|
||||
(integer)))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(opaque_type))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))))))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(enum_type
|
||||
(enum_body
|
||||
(enum_member
|
||||
(identifier))
|
||||
(enum_member
|
||||
(identifier)))))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(struct_type
|
||||
(record_body
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type)))
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(block
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))))
|
||||
(statement
|
||||
(if_statement
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(identifier))))))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(enum_literal
|
||||
(identifier))))))))))))
|
||||
|
||||
==================
|
||||
Anonymous keyed records
|
||||
==================
|
||||
|
||||
inferred :: {
|
||||
x = 1,
|
||||
int = "bro",
|
||||
}
|
||||
|
||||
empty :: {}
|
||||
pair :: {1, 2}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(global_constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(anonymous_record_literal
|
||||
(keyed_field_initializer
|
||||
(identifier)
|
||||
(expression
|
||||
(integer)))
|
||||
(keyed_field_initializer
|
||||
(identifier)
|
||||
(expression
|
||||
(string
|
||||
(string_content)))))))
|
||||
(global_constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(tuple_literal)))
|
||||
(global_constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(tuple_literal
|
||||
(expression
|
||||
(integer))
|
||||
(expression
|
||||
(integer))))))
|
||||
|
||||
==================
|
||||
Intrinsic calls
|
||||
==================
|
||||
|
||||
main func() void {
|
||||
_ = sizeof ! (i32)
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(intrinsic_call_expression
|
||||
(identifier)
|
||||
(argument_list
|
||||
(expression
|
||||
(builtin_type))))))))))
|
||||
|
||||
==================
|
||||
Intrinsic type construction
|
||||
==================
|
||||
|
||||
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {null})
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(alias_type
|
||||
(type
|
||||
(intrinsic_type
|
||||
(identifier)
|
||||
(argument_list
|
||||
(expression
|
||||
(enum_literal
|
||||
(identifier)))
|
||||
(expression
|
||||
(tuple_literal
|
||||
(expression
|
||||
(string
|
||||
(string_content)))))
|
||||
(expression
|
||||
(tuple_literal
|
||||
(expression
|
||||
(builtin_type))))
|
||||
(expression
|
||||
(tuple_literal
|
||||
(expression
|
||||
(null))))))))))
|
||||
|
||||
==================
|
||||
Errdefer
|
||||
==================
|
||||
|
||||
Failure :: enum { bad }
|
||||
|
||||
work func() i32 ! Failure {
|
||||
errdefer cleanup()
|
||||
errdefer |err| {
|
||||
_ = err
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(enum_type
|
||||
(enum_body
|
||||
(enum_member
|
||||
(identifier)))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(block
|
||||
(statement
|
||||
(defer_statement
|
||||
(statement
|
||||
(expression_statement
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(argument_list)))))))
|
||||
(statement
|
||||
(defer_statement
|
||||
(error_capture
|
||||
(identifier))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(identifier))))))))
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(integer)))))))
|
||||
|
||||
==================
|
||||
Bare return and value yield
|
||||
==================
|
||||
|
||||
done func() void {
|
||||
return
|
||||
}
|
||||
|
||||
inline func() void { return }
|
||||
|
||||
choose func() i32 {
|
||||
result :: { yield 1 }
|
||||
return result
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(return_statement))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(return_statement))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(block
|
||||
(statement
|
||||
(yield_statement
|
||||
(expression
|
||||
(integer)))))))
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(identifier)))))))
|
||||
|
||||
==================
|
||||
Tuples and expand for
|
||||
==================
|
||||
|
||||
Pair :: struct { i32, []u8 }
|
||||
|
||||
main func() void {
|
||||
pair Pair = Pair {42, "bro"}
|
||||
singleton :: {1,}
|
||||
empty :: {}
|
||||
_ = pair.0
|
||||
expand 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))))))))))
|
||||
|
||||
==================
|
||||
Native tests
|
||||
==================
|
||||
|
||||
math :: import "../math"
|
||||
test import "../math"
|
||||
|
||||
addition test {}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(import_declaration
|
||||
(identifier)
|
||||
(string
|
||||
(string_content)))
|
||||
(test_import_declaration
|
||||
(string
|
||||
(string_content)))
|
||||
(test_declaration
|
||||
(identifier)
|
||||
(block)))
|
||||
|
||||
==================
|
||||
Expanded match arms
|
||||
==================
|
||||
|
||||
Kind :: enum { one, two }
|
||||
Value :: union(enum) { number i32, empty void }
|
||||
|
||||
visit func(kind Kind, value Value) void {
|
||||
match kind {
|
||||
.one: {}
|
||||
expand |tag|: _ = tag
|
||||
}
|
||||
match value {
|
||||
expand |@payload, tag|: {
|
||||
_ = payload
|
||||
_ = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(enum_type
|
||||
(enum_body
|
||||
(enum_member
|
||||
(identifier))
|
||||
(enum_member
|
||||
(identifier)))))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(union_type
|
||||
(record_body
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type)))
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
(parameter
|
||||
(identifier)
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier))))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(match_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(match_arm
|
||||
(expression
|
||||
(enum_literal
|
||||
(identifier)))
|
||||
(statement
|
||||
(expression_statement
|
||||
(expression
|
||||
(tuple_literal)))))
|
||||
(match_arm
|
||||
(expand_match_capture
|
||||
(identifier))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(identifier)))))))
|
||||
(statement
|
||||
(match_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(match_arm
|
||||
(expand_match_capture
|
||||
(identifier)
|
||||
(identifier))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(identifier))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(identifier))))))))))))
|
||||
|
||||
==================
|
||||
Unsigned constraint
|
||||
==================
|
||||
|
||||
value uint :: 255
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(global_constant_declaration
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))
|
||||
(expression
|
||||
(integer))))
|
||||
|
||||
==================
|
||||
Bitwise operations
|
||||
==================
|
||||
|
||||
ops func(value u8, count u8) u8 {
|
||||
value &= ~u8(1)
|
||||
value |= 2
|
||||
value xor= 3
|
||||
value <<= count
|
||||
value >>= count
|
||||
value <<|= count
|
||||
if (value | 1) |captured| { return captured }
|
||||
return (value & 15) xor (value << 1) | (value >> 1) | (value <<| 8)
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type)))
|
||||
(parameter
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(unary_expression
|
||||
(expression
|
||||
(call_expression
|
||||
(expression
|
||||
(builtin_type))
|
||||
(argument_list
|
||||
(expression
|
||||
(integer)))))))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))
|
||||
(statement
|
||||
(if_statement
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))))
|
||||
(capture_list
|
||||
(identifier))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(identifier))))))))
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))))
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))))))
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))))))
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer)))))))))))))
|
||||
|
||||
==================
|
||||
Struct field defaults and grouped types
|
||||
==================
|
||||
|
||||
Config :: struct {
|
||||
capacity usize = 8
|
||||
}
|
||||
|
||||
work func() void ! (Config | errors.Full) {}
|
||||
|
||||
clear func($K, $V type) void {
|
||||
for 0..entries.len |i| {
|
||||
entries[i].hash = 0
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(struct_type
|
||||
(record_body
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))
|
||||
(expression
|
||||
(integer))))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(type
|
||||
(parenthesized_type
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))))))
|
||||
(block))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(for_statement
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(integer))
|
||||
(expression
|
||||
(field_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(identifier)))))
|
||||
(identifier)
|
||||
(block
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(field_expression
|
||||
(expression
|
||||
(index_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))
|
||||
(identifier)))
|
||||
(expression
|
||||
(integer))))))))))
|
||||
|
||||
==================
|
||||
Sink for captures and braceless while
|
||||
==================
|
||||
|
||||
main func() void {
|
||||
for [1] |_, _| {}
|
||||
while (true) _ = 1
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list)
|
||||
(type
|
||||
(builtin_type))
|
||||
(block
|
||||
(statement
|
||||
(for_statement
|
||||
(expression
|
||||
(array_literal
|
||||
(expression
|
||||
(integer))))
|
||||
(sink)
|
||||
(sink)
|
||||
(block)))
|
||||
(statement
|
||||
(while_statement
|
||||
(expression
|
||||
(parenthesized_expression
|
||||
(expression
|
||||
(boolean))))
|
||||
(statement
|
||||
(assignment_statement
|
||||
(expression
|
||||
(sink))
|
||||
(expression
|
||||
(integer)))))))))
|
||||
@@ -1,14 +0,0 @@
|
||||
value u8 = 1
|
||||
|
||||
value xor= 2
|
||||
# ^^^^ operator
|
||||
|
||||
value <<|= 3
|
||||
# ^^^^ operator
|
||||
|
||||
masked :: ~value & 15
|
||||
# ^ operator
|
||||
# ^ operator
|
||||
|
||||
shifted :: value <<| 8
|
||||
# ^^^ operator
|
||||
@@ -1,16 +0,0 @@
|
||||
hide helper func() void {}
|
||||
# <- keyword
|
||||
|
||||
value uint :: 1
|
||||
# ^^^^ type.builtin
|
||||
|
||||
main func() void {
|
||||
_ = sizeof!(i32)
|
||||
# ^^^^^^ function.builtin
|
||||
# ^ operator
|
||||
_ = sizeof(i32)
|
||||
# ^^^^^^ function
|
||||
_ = constcast!(memory)
|
||||
# ^^^^^^^^^ function.builtin
|
||||
# ^ operator
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
Config :: struct {
|
||||
# ^^^^^^ type
|
||||
capacity usize = 8
|
||||
# ^^^^^^^^ property
|
||||
# ^ operator
|
||||
}
|
||||
|
||||
work func() void ! (Config | errors.Full) {}
|
||||
# ^^^^ keyword
|
||||
# ^ operator
|
||||
# ^^^^^^ type
|
||||
# ^ operator
|
||||
|
||||
clear func($K, $V type) void {
|
||||
# ^ variable.parameter
|
||||
# ^ variable.parameter
|
||||
# ^^^^ type.builtin
|
||||
for 0..entries.len |i| {}
|
||||
# ^^^ keyword
|
||||
# ^ operator
|
||||
}
|
||||
|
||||
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {null})
|
||||
# ^^^^^^^^^^^^ function.builtin
|
||||
|
||||
fail func() noreturn {
|
||||
# ^^^^^^^^ type.builtin
|
||||
unreachable
|
||||
# ^^^^^^^^^^^ constant.builtin
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"grammars": [
|
||||
{
|
||||
"name": "brolang",
|
||||
"camelcase": "Brolang",
|
||||
"scope": "source.bro",
|
||||
"path": ".",
|
||||
"file-types": ["bro", "hon"],
|
||||
"highlights": "queries/highlights.scm"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"version": "0.1.0",
|
||||
"description": "Tree-sitter grammar for Brolang",
|
||||
"authors": [{"name": "Brolang contributors"}]
|
||||
},
|
||||
"bindings": {
|
||||
"c": false,
|
||||
"go": false,
|
||||
"java": false,
|
||||
"node": false,
|
||||
"python": false,
|
||||
"rust": false,
|
||||
"swift": false,
|
||||
"zig": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user