Merge pull request #238 from odin-lang/big-int

Big int
This commit is contained in:
gingerBill
2018-07-28 18:39:15 +01:00
committed by GitHub
13 changed files with 1801 additions and 340 deletions

View File

@@ -1,8 +1,8 @@
package types
import "core:runtime"
import rt "core:runtime"
are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
if a == b do return true;
if (a == nil && b != nil) ||
@@ -17,47 +17,47 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
}
switch x in a.variant {
case runtime.Type_Info_Named:
y, ok := b.variant.(runtime.Type_Info_Named);
case rt.Type_Info_Named:
y, ok := b.variant.(rt.Type_Info_Named);
if !ok do return false;
return x.base == y.base;
case runtime.Type_Info_Integer:
y, ok := b.variant.(runtime.Type_Info_Integer);
case rt.Type_Info_Integer:
y, ok := b.variant.(rt.Type_Info_Integer);
if !ok do return false;
return x.signed == y.signed;
case runtime.Type_Info_Rune:
_, ok := b.variant.(runtime.Type_Info_Rune);
case rt.Type_Info_Rune:
_, ok := b.variant.(rt.Type_Info_Rune);
return ok;
case runtime.Type_Info_Float:
_, ok := b.variant.(runtime.Type_Info_Float);
case rt.Type_Info_Float:
_, ok := b.variant.(rt.Type_Info_Float);
return ok;
case runtime.Type_Info_Complex:
_, ok := b.variant.(runtime.Type_Info_Complex);
case rt.Type_Info_Complex:
_, ok := b.variant.(rt.Type_Info_Complex);
return ok;
case runtime.Type_Info_String:
_, ok := b.variant.(runtime.Type_Info_String);
case rt.Type_Info_String:
_, ok := b.variant.(rt.Type_Info_String);
return ok;
case runtime.Type_Info_Boolean:
_, ok := b.variant.(runtime.Type_Info_Boolean);
case rt.Type_Info_Boolean:
_, ok := b.variant.(rt.Type_Info_Boolean);
return ok;
case runtime.Type_Info_Any:
_, ok := b.variant.(runtime.Type_Info_Any);
case rt.Type_Info_Any:
_, ok := b.variant.(rt.Type_Info_Any);
return ok;
case runtime.Type_Info_Pointer:
y, ok := b.variant.(runtime.Type_Info_Pointer);
case rt.Type_Info_Pointer:
y, ok := b.variant.(rt.Type_Info_Pointer);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case runtime.Type_Info_Procedure:
y, ok := b.variant.(runtime.Type_Info_Procedure);
case rt.Type_Info_Procedure:
y, ok := b.variant.(rt.Type_Info_Procedure);
if !ok do return false;
switch {
case x.variadic != y.variadic,
@@ -67,24 +67,24 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
case runtime.Type_Info_Array:
y, ok := b.variant.(runtime.Type_Info_Array);
case rt.Type_Info_Array:
y, ok := b.variant.(rt.Type_Info_Array);
if !ok do return false;
if x.count != y.count do return false;
return are_types_identical(x.elem, y.elem);
case runtime.Type_Info_Dynamic_Array:
y, ok := b.variant.(runtime.Type_Info_Dynamic_Array);
case rt.Type_Info_Dynamic_Array:
y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case runtime.Type_Info_Slice:
y, ok := b.variant.(runtime.Type_Info_Slice);
case rt.Type_Info_Slice:
y, ok := b.variant.(rt.Type_Info_Slice);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case runtime.Type_Info_Tuple:
y, ok := b.variant.(runtime.Type_Info_Tuple);
case rt.Type_Info_Tuple:
y, ok := b.variant.(rt.Type_Info_Tuple);
if !ok do return false;
if len(x.types) != len(y.types) do return false;
for _, i in x.types {
@@ -95,8 +95,8 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
}
return true;
case runtime.Type_Info_Struct:
y, ok := b.variant.(runtime.Type_Info_Struct);
case rt.Type_Info_Struct:
y, ok := b.variant.(rt.Type_Info_Struct);
if !ok do return false;
switch {
case len(x.types) != len(y.types),
@@ -114,8 +114,8 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
}
return true;
case runtime.Type_Info_Union:
y, ok := b.variant.(runtime.Type_Info_Union);
case rt.Type_Info_Union:
y, ok := b.variant.(rt.Type_Info_Union);
if !ok do return false;
if len(x.variants) != len(y.variants) do return false;
@@ -125,17 +125,17 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
}
return true;
case runtime.Type_Info_Enum:
case rt.Type_Info_Enum:
// NOTE(bill): Should be handled above
return false;
case runtime.Type_Info_Map:
y, ok := b.variant.(runtime.Type_Info_Map);
case rt.Type_Info_Map:
y, ok := b.variant.(rt.Type_Info_Map);
if !ok do return false;
return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
case runtime.Type_Info_Bit_Field:
y, ok := b.variant.(runtime.Type_Info_Bit_Field);
case rt.Type_Info_Bit_Field:
y, ok := b.variant.(rt.Type_Info_Bit_Field);
if !ok do return false;
if len(x.names) != len(y.names) do return false;
@@ -156,101 +156,101 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
}
is_signed :: proc(info: ^runtime.Type_Info) -> bool {
is_signed :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
switch i in runtime.type_info_base(info).variant {
case runtime.Type_Info_Integer: return i.signed;
case runtime.Type_Info_Float: return true;
switch i in rt.type_info_base(info).variant {
case rt.Type_Info_Integer: return i.signed;
case rt.Type_Info_Float: return true;
}
return false;
}
is_integer :: proc(info: ^runtime.Type_Info) -> bool {
is_integer :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Integer);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Integer);
return ok;
}
is_rune :: proc(info: ^runtime.Type_Info) -> bool {
is_rune :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Rune);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Rune);
return ok;
}
is_float :: proc(info: ^runtime.Type_Info) -> bool {
is_float :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Float);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Float);
return ok;
}
is_complex :: proc(info: ^runtime.Type_Info) -> bool {
is_complex :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Complex);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Complex);
return ok;
}
is_any :: proc(info: ^runtime.Type_Info) -> bool {
is_any :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Any);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Any);
return ok;
}
is_string :: proc(info: ^runtime.Type_Info) -> bool {
is_string :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_String);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_String);
return ok;
}
is_boolean :: proc(info: ^runtime.Type_Info) -> bool {
is_boolean :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Boolean);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Boolean);
return ok;
}
is_pointer :: proc(info: ^runtime.Type_Info) -> bool {
is_pointer :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Pointer);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Pointer);
return ok;
}
is_procedure :: proc(info: ^runtime.Type_Info) -> bool {
is_procedure :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Procedure);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Procedure);
return ok;
}
is_array :: proc(info: ^runtime.Type_Info) -> bool {
is_array :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Array);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Array);
return ok;
}
is_dynamic_array :: proc(info: ^runtime.Type_Info) -> bool {
is_dynamic_array :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Dynamic_Array);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Dynamic_Array);
return ok;
}
is_dynamic_map :: proc(info: ^runtime.Type_Info) -> bool {
is_dynamic_map :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Map);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Map);
return ok;
}
is_slice :: proc(info: ^runtime.Type_Info) -> bool {
is_slice :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Slice);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Slice);
return ok;
}
is_tuple :: proc(info: ^runtime.Type_Info) -> bool {
is_tuple :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Tuple);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Tuple);
return ok;
}
is_struct :: proc(info: ^runtime.Type_Info) -> bool {
is_struct :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
s, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Struct);
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
return ok && !s.is_raw_union;
}
is_raw_union :: proc(info: ^runtime.Type_Info) -> bool {
is_raw_union :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
s, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Struct);
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
return ok && s.is_raw_union;
}
is_union :: proc(info: ^runtime.Type_Info) -> bool {
is_union :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Union);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Union);
return ok;
}
is_enum :: proc(info: ^runtime.Type_Info) -> bool {
is_enum :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Enum);
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Enum);
return ok;
}

View File

@@ -1,7 +1,7 @@
package utf16
REPLACEMENT_CHAR :: '\uFFFD';
MAX_RUNE :: '\U0010FFFF';
REPLACEMENT_CHAR :: '\ufffd';
MAX_RUNE :: '\U0010ffff';
_surr1 :: 0xd800;
_surr2 :: 0xdc00;

1408
src/big_int.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -813,7 +813,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
if (e->Constant.value.kind != ExactValue_Integer) {
return false;
}
i64 count = e->Constant.value.value_integer;
i64 count = big_int_to_i64(&e->Constant.value.value_integer);
if (count != source->Array.count) {
return false;
}
@@ -1257,33 +1257,44 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
return true;
}
i64 i = v.value_integer;
u64 u = bit_cast<u64>(i);
BigInt i = v.value_integer;
i64 bit_size = type_size_of(type);
u64 umax = unsigned_integer_maxs[bit_size];
i64 imin = signed_integer_mins[bit_size];
i64 imax = signed_integer_maxs[bit_size];
BigInt umax = {};
BigInt imin = {};
BigInt imax = {};
big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]);
big_int_from_i64(&imin, signed_integer_mins[bit_size]);
big_int_from_i64(&imax, signed_integer_maxs[bit_size]);
switch (type->Basic.kind) {
case Basic_rune:
case Basic_i8:
case Basic_i16:
case Basic_i32:
case Basic_i64:
case Basic_int:
return imin <= i && i <= imax;
{
// return imin <= i && i <= imax;
int a = big_int_cmp(&imin, &i);
int b = big_int_cmp(&i, &imax);
return (a <= 0) && (b <= 0);
}
case Basic_u8:
case Basic_u16:
case Basic_u32:
case Basic_u64:
case Basic_uint:
case Basic_uintptr:
return 0ull <= u && u <= umax;
case Basic_u64:
return 0ull <= i;
{
// return 0ull <= i && i <= umax;
int b = big_int_cmp(&i, &umax);
return !i.neg && (b <= 0);
}
case Basic_i64:
return true;
case Basic_UntypedInteger:
return true;
@@ -1357,14 +1368,9 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
} else {
char buf[127] = {};
String str = {};
i64 i = o->value.value_integer;
if (is_type_unsigned(o->type)) {
str = u64_to_string(bit_cast<u64>(i), buf, gb_size_of(buf));
} else {
str = i64_to_string(i, buf, gb_size_of(buf));
}
gbAllocator ha = heap_allocator();
String str = big_int_to_string(ha, &o->value.value_integer);
defer (gb_free(ha, str.text));
error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b);
}
} else {
@@ -1444,10 +1450,7 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
}
i32 precision = 0;
if (is_type_unsigned(type)) {
precision = cast(i32)(8 * type_size_of(type));
}
if (op.kind == Token_Xor && is_type_untyped(type)) {
gbString err_str = expr_to_string(node);
error(op, "Bitwise not cannot be applied to untyped constants '%s'", err_str);
@@ -1463,7 +1466,17 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
return;
}
o->value = exact_unary_operator_value(op.kind, o->value, precision, is_type_unsigned(type));
i32 precision = 0;
if (is_type_typed(type)) {
precision = cast(i32)(8 * type_size_of(type));
}
bool is_unsigned = is_type_unsigned(type);
if (is_type_rune(type)) {
GB_ASSERT(!is_unsigned);
}
o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned);
if (is_type_typed(type)) {
if (node != nullptr) {
@@ -1638,8 +1651,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) {
return;
}
i64 amount = y_val.value_integer;
if (amount > 128) {
BigInt max_shift = {};
big_int_from_u64(&max_shift, 128);
if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift amount too large: '%s'", err_str);
gb_string_free(err_str);
@@ -1653,7 +1668,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) {
x->type = t_untyped_integer;
}
x->value = exact_value_shift(be->op.kind, x_val, exact_value_i64(amount));
x->value = exact_value_shift(be->op.kind, x_val, y_val);
if (is_type_typed(x->type)) {
check_is_expressible(c, x, base_type(x->type));
@@ -1673,7 +1688,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) {
}
}
if (y->mode == Addressing_Constant && y->value.value_integer < 0) {
if (y->mode == Addressing_Constant && y->value.value_integer.neg) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift amount cannot be negative: '%s'", err_str);
gb_string_free(err_str);
@@ -1691,60 +1706,60 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) {
}
Operand check_ptr_addition(CheckerContext *c, TokenKind op, Operand *ptr, Operand *offset, Ast *node) {
GB_ASSERT(node->kind == Ast_BinaryExpr);
ast_node(be, BinaryExpr, node);
GB_ASSERT(is_type_pointer(ptr->type));
GB_ASSERT(is_type_integer(offset->type));
GB_ASSERT(op == Token_Add || op == Token_Sub);
// Operand check_ptr_addition(CheckerContext *c, TokenKind op, Operand *ptr, Operand *offset, Ast *node) {
// GB_ASSERT(node->kind == Ast_BinaryExpr);
// ast_node(be, BinaryExpr, node);
// GB_ASSERT(is_type_pointer(ptr->type));
// GB_ASSERT(is_type_integer(offset->type));
// GB_ASSERT(op == Token_Add || op == Token_Sub);
Operand operand = {};
operand.mode = Addressing_Value;
operand.type = ptr->type;
operand.expr = node;
// Operand operand = {};
// operand.mode = Addressing_Value;
// operand.type = ptr->type;
// operand.expr = node;
if (base_type(ptr->type) == t_rawptr) {
gbString str = type_to_string(ptr->type);
error(node, "Invalid pointer type for pointer arithmetic: '%s'", str);
gb_string_free(str);
operand.mode = Addressing_Invalid;
return operand;
}
// if (base_type(ptr->type) == t_rawptr) {
// gbString str = type_to_string(ptr->type);
// error(node, "Invalid pointer type for pointer arithmetic: '%s'", str);
// gb_string_free(str);
// operand.mode = Addressing_Invalid;
// return operand;
// }
#if defined(NO_POINTER_ARITHMETIC)
operand.mode = Addressing_Invalid;
error(operand.expr, "Pointer arithmetic is not supported");
return operand;
#else
// #if defined(NO_POINTER_ARITHMETIC)
// operand.mode = Addressing_Invalid;
// error(operand.expr, "Pointer arithmetic is not supported");
// return operand;
// #else
Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer);
Type *elem = base_ptr->Pointer.elem;
i64 elem_size = type_size_of(elem);
// Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer);
// Type *elem = base_ptr->Pointer.elem;
// i64 elem_size = type_size_of(elem);
if (elem_size <= 0) {
gbString str = type_to_string(elem);
error(node, "Size of pointer's element type '%s' is zero and cannot be used for pointer arithmetic", str);
gb_string_free(str);
operand.mode = Addressing_Invalid;
return operand;
}
// if (elem_size <= 0) {
// gbString str = type_to_string(elem);
// error(node, "Size of pointer's element type '%s' is zero and cannot be used for pointer arithmetic", str);
// gb_string_free(str);
// operand.mode = Addressing_Invalid;
// return operand;
// }
if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) {
i64 ptr_val = ptr->value.value_pointer;
i64 offset_val = exact_value_to_integer(offset->value).value_integer;
i64 new_ptr_val = ptr_val;
if (op == Token_Add) {
new_ptr_val += elem_size*offset_val;
} else {
new_ptr_val -= elem_size*offset_val;
}
operand.mode = Addressing_Constant;
operand.value = exact_value_pointer(new_ptr_val);
}
// if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) {
// i64 ptr_val = ptr->value.value_pointer;
// i64 offset_val = exact_value_to_integer(offset->value).value_integer;
// i64 new_ptr_val = ptr_val;
// if (op == Token_Add) {
// new_ptr_val += elem_size*offset_val;
// } else {
// new_ptr_val -= elem_size*offset_val;
// }
// operand.mode = Addressing_Constant;
// operand.value = exact_value_pointer(new_ptr_val);
// }
return operand;
#endif
}
// return operand;
// #endif
// }
@@ -2030,24 +2045,24 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) {
return;
}
if (op.kind == Token_Add || op.kind == Token_Sub) {
if (is_type_pointer(x->type) && is_type_integer(y->type)) {
*x = check_ptr_addition(c, op.kind, x, y, node);
return;
} else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
if (op.kind == Token_Sub) {
gbString lhs = expr_to_string(x->expr);
gbString rhs = expr_to_string(y->expr);
error(node, "Invalid pointer arithmetic, did you mean '%s %.*s %s'?", rhs, LIT(op.string), lhs);
gb_string_free(rhs);
gb_string_free(lhs);
x->mode = Addressing_Invalid;
return;
}
*x = check_ptr_addition(c, op.kind, y, x, node);
return;
}
}
// if (op.kind == Token_Add || op.kind == Token_Sub) {
// if (is_type_pointer(x->type) && is_type_integer(y->type)) {
// *x = check_ptr_addition(c, op.kind, x, y, node);
// return;
// } else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
// if (op.kind == Token_Sub) {
// gbString lhs = expr_to_string(x->expr);
// gbString rhs = expr_to_string(y->expr);
// error(node, "Invalid pointer arithmetic, did you mean '%s %.*s %s'?", rhs, LIT(op.string), lhs);
// gb_string_free(rhs);
// gb_string_free(lhs);
// x->mode = Addressing_Invalid;
// return;
// }
// *x = check_ptr_addition(c, op.kind, y, x, node);
// return;
// }
// }
convert_to_typed(c, x, y->type);
if (x->mode == Addressing_Invalid) {
@@ -2108,7 +2123,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) {
bool fail = false;
switch (y->value.kind) {
case ExactValue_Integer:
if (y->value.value_integer == 0 ) {
if (big_int_is_zero(&y->value.value_integer)) {
fail = true;
}
break;
@@ -2246,7 +2261,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
char *extra_text = "";
if (operand->mode == Addressing_Constant) {
if (operand->value.value_integer == 0) {
if (big_int_is_zero(&operand->value.value_integer)) {
if (make_string_c(expr_str) != "nil") { // HACK NOTE(bill): Just in case
// NOTE(bill): Doesn't matter what the type is as it's still zero in the union
extra_text = " - Did you want 'nil'?";
@@ -2495,8 +2510,8 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
if (operand.mode == Addressing_Constant &&
(c->stmt_state_flags & StmtStateFlag_no_bounds_check) == 0) {
i64 i = exact_value_to_integer(operand.value).value_integer;
if (i < 0) {
BigInt i = exact_value_to_integer(operand.value).value_integer;
if (i.neg) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
gb_string_free(expr_str);
@@ -2505,13 +2520,21 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
}
if (max_count >= 0) { // NOTE(bill): Do array bound checking
if (value) *value = i;
i64 v = -1;
if (i.len <= 1) {
v = big_int_to_i64(&i);
}
if (value) *value = v;
bool out_of_bounds = false;
if (open_range) {
out_of_bounds = i > max_count;
out_of_bounds = v > max_count;
} else {
out_of_bounds = i >= max_count;
out_of_bounds = v >= max_count;
}
if (v < 0) {
out_of_bounds = true;
}
if (out_of_bounds) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld", expr_str, max_count);
@@ -3399,12 +3422,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (op.value.value_integer < 0) {
if (op.value.value_integer.neg) {
error(op.expr, "Negative 'swizzle' index");
return false;
}
if (max_count <= op.value.value_integer) {
BigInt mc = {};
big_int_from_i64(&mc, max_count);
if (big_int_cmp(&mc, &op.value.value_integer) <= 0) {
error(op.expr, "'swizzle' index exceeds length");
return false;
}
@@ -3804,7 +3829,7 @@ break;
if (operand->mode == Addressing_Constant) {
switch (operand->value.kind) {
case ExactValue_Integer:
operand->value.value_integer = gb_abs(operand->value.value_integer);
operand->value.value_integer.neg = false;
break;
case ExactValue_Float:
operand->value.value_float = gb_abs(operand->value.value_float);

View File

@@ -260,18 +260,17 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
if (rhs->mode == Addressing_Constant) {
ExactValue v = exact_value_to_integer(rhs->value);
if (v.kind == ExactValue_Integer) {
i64 i = v.value_integer;
u64 u = bit_cast<u64>(i);
u64 umax = ~cast(u64)0ull;
if (lhs_bits < 64) {
umax = (1ull << cast(u64)lhs_bits) - 1ull;
}
i64 imax = 1ll << (cast(i64)lhs_bits-1ll);
BigInt i = v.value_integer;
if (!i.neg) {
u64 imax_ = ~cast(u64)0ull;
if (lhs_bits < 64) {
imax_ = (1ull << cast(u64)lhs_bits) - 1ull;
}
bool ok = !(u < 0 || u > umax);
if (ok) {
return rhs->type;
BigInt imax = big_int_make_u64(imax_);
if (big_int_cmp(&i, &imax) > 0) {
return rhs->type;
}
}
}
} else if (is_type_integer(rhs->type)) {

View File

@@ -136,7 +136,15 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
Type *type = base_type(o.type);
if (is_type_untyped(type) || is_type_integer(type)) {
if (o.value.kind == ExactValue_Integer) {
i64 align = o.value.value_integer;
BigInt v = o.value.value_integer;
if (v.len > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
error(node, "#align too large, %.*s", LIT(str));
gb_free(a, str.text);
return false;
}
i64 align = big_int_to_i64(&v);
if (align < 1 || !gb_is_power_of_two(cast(isize)align)) {
error(node, "#align must be a power of 2, got %lld", align);
return false;
@@ -668,7 +676,7 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node)
error(value, "Bit field bit size must be a constant integer");
continue;
}
i64 bits_ = v.value_integer;
i64 bits_ = big_int_to_i64(&v.value_integer); // TODO(bill): what if the integer is huge?
if (bits_ < 0 || bits_ > 64) {
error(value, "Bit field's bit size must be within the range 1...64, got %lld", cast(long long)bits_);
continue;
@@ -1601,11 +1609,22 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
Type *type = base_type(o->type);
if (is_type_untyped(type) || is_type_integer(type)) {
if (o->value.kind == ExactValue_Integer) {
i64 count = o->value.value_integer;
if (count >= 0) {
return count;
BigInt count = o->value.value_integer;
if (o->value.value_integer.neg) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);
error(e, "Invalid negative array count, %.*s", LIT(str));
gb_free(a, str.text);
return 0;
}
error(e, "Invalid negative array count %lld", cast(long long)count);
switch (count.len) {
case 0: return 0;
case 1: return count.d.word;
}
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);
error(e, "Array count too large, %.*s", LIT(str));
gb_free(a, str.text);
return 0;
}
}

View File

@@ -3,6 +3,10 @@
#include <xmmintrin.h>
#endif
#if defined(GB_COMPILER_MSVC)
#include <intrin.h>
#endif
#define GB_IMPLEMENTATION
#include "gb/gb.h"
@@ -200,7 +204,7 @@ u64 u64_from_string(String string) {
}
String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
char buf[32] = {0};
isize i = gb_size_of(buf);
u64 b = 10;
@@ -215,7 +219,7 @@ String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
return make_string(cast(u8 *)out_buf, len);
}
String i64_to_string(i64 a, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
char buf[32] = {0};
isize i = gb_size_of(buf);
bool negative = false;
if (a < 0) {
@@ -276,6 +280,44 @@ gb_global u64 const unsigned_integer_maxs[] = {
};
bool add_overflow_u64(u64 x, u64 y, u64 *result) {
*result = x + y;
return *result < x || *result < y;
}
bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
*result = x - y;
return *result > x;
}
void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
#if defined(GB_COMPILER_MSVC)
*lo = _umul128(x, y, hi);
#else
// URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197
u64 u1, v1, w1, t, w3, k;
u1 = (x & 0xffffffff);
v1 = (y & 0xffffffff);
t = (u1 * v1);
w3 = (t & 0xffffffff);
k = (t >> 32);
x >>= 32;
t = (x * v1) + k;
k = (t & 0xffffffff);
w1 = (t >> 32);
y >>= 32;
t = (u1 * y) + k;
k = (t >> 32);
*hi = (x * y) + w1 + k;
*lo = (t << 32) + w3;
#endif
}
#include "map.cpp"
#include "ptr_set.cpp"

View File

@@ -33,7 +33,7 @@ struct ExactValue {
union {
bool value_bool;
String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
Complex128 value_complex;
@@ -54,8 +54,14 @@ HashKey hash_exact_value(ExactValue v) {
return hash_integer(u64(v.value_bool));
case ExactValue_String:
return hash_string(v.value_string);
case ExactValue_Integer:
return hash_integer(u64(v.value_integer));
case ExactValue_Integer: {
u64 *d = big_int_ptr(&v.value_integer);
u64 x = 0;
for (i32 i = 0; i < v.value_integer.len; i++) {
x |= d[i];
}
return hash_integer(x);
}
case ExactValue_Float:
return hash_f64(v.value_float);
case ExactValue_Pointer:
@@ -94,13 +100,13 @@ ExactValue exact_value_string(String string) {
ExactValue exact_value_i64(i64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i;
big_int_from_i64(&result.value_integer, i);
return result;
}
ExactValue exact_value_u64(u64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i64(i);
big_int_from_u64(&result.value_integer, i);
return result;
}
@@ -293,7 +299,7 @@ ExactValue exact_value_to_integer(ExactValue v) {
ExactValue exact_value_to_float(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_float(cast(f64)v.value_integer);
return exact_value_float(big_int_to_f64(&v.value_integer));
case ExactValue_Float:
return v;
}
@@ -304,7 +310,7 @@ ExactValue exact_value_to_float(ExactValue v) {
ExactValue exact_value_to_complex(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(cast(f64)v.value_integer, 0);
return exact_value_complex(big_int_to_f64(&v.value_integer), 0);
case ExactValue_Float:
return exact_value_complex(v.value_float, 0);
case ExactValue_Complex:
@@ -371,8 +377,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision,
case ExactValue_Invalid:
return v;
case ExactValue_Integer: {
ExactValue i = v;
i.value_integer = -i.value_integer;
ExactValue i = {ExactValue_Integer};
big_int_neg(&i.value_integer, &v.value_integer);
return i;
}
case ExactValue_Float: {
@@ -390,24 +396,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision,
}
case Token_Xor: {
i64 i = 0;
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer:
i = ~v.value_integer;
break;
case ExactValue_Integer: {
GB_ASSERT(precision != 0);
ExactValue i = {ExactValue_Integer};
bit_int_not(&i.value_integer, &v.value_integer, precision, !is_unsigned);
return i;
}
default:
goto failure;
}
// NOTE(bill): unsigned integers will be negative and will need to be
// limited to the types precision
// IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored
if (is_unsigned) {
i = i & unsigned_integer_maxs[precision/8];
}
return exact_value_i64(i);
}
case Token_Not: {
@@ -472,10 +472,10 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return;
case ExactValue_Float:
// TODO(bill): Is this good enough?
*x = exact_value_float(cast(f64)x->value_integer);
*x = exact_value_float(big_int_to_f64(&x->value_integer));
return;
case ExactValue_Complex:
*x = exact_value_complex(cast(f64)x->value_integer, 0);
*x = exact_value_complex(big_int_to_f64(&x->value_integer), 0);
return;
}
break;
@@ -513,28 +513,29 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
i64 c = 0ll;
BigInt const *a = &x.value_integer;
BigInt const *b = &y.value_integer;
BigInt c = {};
switch (op) {
case Token_Add: c = a + b; break;
case Token_Sub: c = a - b; break;
case Token_Mul: c = a * b; break;
case Token_Quo: return exact_value_float(fmod(cast(f64)a, cast(f64)b));
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
case Token_Mod: c = a % b; break;
case Token_ModMod: c = ((a % b) + b) % b; break;
case Token_And: c = a & b; break;
case Token_Or: c = a | b; break;
case Token_Xor: c = a ^ b; break;
case Token_AndNot: c = a & (~b); break;
case Token_Shl: c = a << b; break;
case Token_Shr: c = a >> b; break;
case Token_Add: big_int_add(&c, a, b); break;
case Token_Sub: big_int_sub(&c, a, b); break;
case Token_Mul: big_int_mul(&c, a, b); break;
case Token_Quo: return exact_value_float(fmod(big_int_to_f64(a), big_int_to_f64(b)));
case Token_QuoEq: big_int_quo(&c, a, b); break; // NOTE(bill): Integer division
case Token_Mod: big_int_rem(&c, a, b); break;
case Token_ModMod: big_int_euclidean_mod(&c, a, b); break;
case Token_And: big_int_and(&c, a, b); break;
case Token_Or: big_int_or(&c, a, b); break;
case Token_Xor: big_int_xor(&c, a, b); break;
case Token_AndNot: big_int_and_not(&c, a, b); break;
case Token_Shl: big_int_shl(&c, a, b); break;
case Token_Shr: big_int_shr(&c, a, b); break;
default: goto error;
}
return exact_value_i64(c);
break;
ExactValue res = {ExactValue_Integer};
res.value_integer = c;
return res;
}
case ExactValue_Float: {
@@ -638,15 +639,14 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
i32 cmp = big_int_cmp(&x.value_integer, &y.value_integer);
switch (op) {
case Token_CmpEq: return a == b;
case Token_NotEq: return a != b;
case Token_Lt: return a < b;
case Token_LtEq: return a <= b;
case Token_Gt: return a > b;
case Token_GtEq: return a >= b;
case Token_CmpEq: return cmp == 0;
case Token_NotEq: return cmp != 0;
case Token_Lt: return cmp < 0;
case Token_LtEq: return cmp <= 0;
case Token_Gt: return cmp > 0;
case Token_GtEq: return cmp >= 0;
}
break;
}

View File

@@ -4778,7 +4778,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
GB_ASSERT(is_type_integer(tv.type));
GB_ASSERT(tv.value.kind == ExactValue_Integer);
i32 src_index = cast(i32)tv.value.value_integer;
i32 src_index = cast(i32)big_int_to_i64(&tv.value.value_integer);
i32 dst_index = i-1;
irValue *src_elem = ir_emit_array_epi(proc, src, src_index);
@@ -5639,7 +5639,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
Type *selector_type = base_type(type_of_expr(se->selector));
GB_ASSERT_MSG(is_type_integer(selector_type), "%s", type_to_string(selector_type));
ExactValue val = type_and_value_of_expr(sel).value;
i64 index = val.value_integer;
i64 index = big_int_to_i64(&val.value_integer);
Selection sel = lookup_field_from_index(type, index);
GB_ASSERT(sel.entity != nullptr);

View File

@@ -65,6 +65,15 @@ void ir_write_i64(irFileBuffer *f, i64 i) {
String str = i64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1);
ir_write_string(f, str);
}
void ir_write_big_int(irFileBuffer *f, BigInt const &x) {
i64 i = 0;
if (x.neg) {
i = big_int_to_i64(&x);
} else {
i = cast(i64)big_int_to_u64(&x);
}
ir_write_i64(f, i);
}
void ir_file_write(irFileBuffer *f, void *data, isize len) {
ir_file_buffer_write(f, data, len);
@@ -587,19 +596,19 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
}
case ExactValue_Integer: {
if (is_type_pointer(type)) {
if (value.value_integer == 0) {
if (big_int_is_zero(&value.value_integer)) {
ir_write_str_lit(f, "null");
} else {
ir_write_str_lit(f, "inttoptr (");
ir_print_type(f, m, t_int);
ir_write_byte(f, ' ');
ir_write_i64(f, value.value_integer);
ir_write_big_int(f, value.value_integer);
ir_write_str_lit(f, " to ");
ir_print_type(f, m, t_rawptr);
ir_write_str_lit(f, ")");
}
} else {
ir_write_i64(f, value.value_integer);
ir_write_big_int(f, value.value_integer);
}
break;
}

View File

@@ -4,6 +4,7 @@
#include "timings.cpp"
#include "build_settings.cpp"
#include "tokenizer.cpp"
#include "big_int.cpp"
#include "exact_value.cpp"
#include "parser.hpp"
@@ -408,7 +409,7 @@ bool parse_build_flags(Array<String> args) {
}
case BuildFlag_OptimizationLevel:
GB_ASSERT(value.kind == ExactValue_Integer);
build_context.optimization_level = cast(i32)value.value_integer;
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
break;
case BuildFlag_ShowTimings:
GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -416,7 +417,7 @@ bool parse_build_flags(Array<String> args) {
break;
case BuildFlag_ThreadCount: {
GB_ASSERT(value.kind == ExactValue_Integer);
isize count = cast(isize)value.value_integer;
isize count = cast(isize)big_int_to_i64(&value.value_integer);
if (count <= 0) {
gb_printf_err("%.*s expected a positive non-zero number, got %.*s\n", LIT(name), LIT(param));
build_context.thread_count = 0;
@@ -716,6 +717,7 @@ int main(int arg_count, char **arg_ptr) {
init_string_buffer_memory();
init_global_error_collector();
global_big_int_init();
arena_init(&global_ast_arena, heap_allocator());
array_init(&library_collections, heap_allocator());

View File

@@ -3470,22 +3470,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
return s;
}
// Ast *parse_export_decl(AstFile *f) {
// CommentGroup *docs = f->lead_comment;
// Token token = expect_token(f, Token_export);
// Token file_path = expect_token_after(f, Token_String, "export");
// Ast *s = nullptr;
// if (f->curr_proc != nullptr) {
// syntax_error(token, "You cannot use 'export' within a procedure. This must be done at the file scope");
// s = ast_bad_decl(f, token, file_path);
// } else {
// s = ast_export_decl(f, token, file_path, docs, f->line_comment);
// array_add(&f->imports_and_exports, s);
// }
// expect_semicolon(f, s);
// return s;
// }
Ast *parse_foreign_decl(AstFile *f) {
CommentGroup *docs = f->lead_comment;
Token token = expect_token(f, Token_foreign);
@@ -3668,17 +3652,6 @@ Ast *parse_stmt(AstFile *f) {
Token name = expect_token(f, Token_Ident);
String tag = name.string;
// if (tag == "shared_global_scope") {
// if (f->curr_proc == nullptr) {
// f->is_global_scope = true;
// s = ast_empty_stmt(f, f->curr_token);
// } else {
// syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope");
// s = ast_bad_decl(f, token, f->curr_token);
// }
// expect_semicolon(f, s);
// return s;
// } else
if (tag == "bounds_check") {
s = parse_stmt(f);
s->stmt_state_flags |= StmtStateFlag_bounds_check;
@@ -3734,9 +3707,7 @@ Ast *parse_stmt(AstFile *f) {
return s;
}
syntax_error(token,
"Expected a statement, got '%.*s'",
LIT(token_strings[token.kind]));
syntax_error(token, "Expected a statement, got '%.*s'", LIT(token_strings[token.kind]));
fix_advance_to_next_stmt(f);
return ast_bad_stmt(f, token, f->curr_token);
}
@@ -3938,39 +3909,25 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path,
}
if (rd_err != ReadDirectory_None) {
if (pos.line != 0) {
gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column);
}
gb_mutex_lock(&global_error_collector.mutex);
defer (gb_mutex_unlock(&global_error_collector.mutex));
global_error_collector.count++;
switch (rd_err) {
case ReadDirectory_InvalidPath:
gb_printf_err("Invalid path: %.*s\n", LIT(rel_path));
return false;
case ReadDirectory_NotExists:
gb_printf_err("Path does not exist: %.*s\n", LIT(rel_path));
return false;
case ReadDirectory_NotDir:
gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(rel_path));
return false;
case ReadDirectory_Unknown:
gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(rel_path));
return false;
case ReadDirectory_Permission:
gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(rel_path));
return false;
case ReadDirectory_Empty:
gb_printf_err("Empty directory: %.*s\n", LIT(rel_path));
return false;
}
switch (rd_err) {
case ReadDirectory_InvalidPath:
error(pos, "Invalid path: %.*s", LIT(rel_path));
return false;
case ReadDirectory_NotExists:
error(pos, "Path does not exist: %.*s", LIT(rel_path));
return false;
case ReadDirectory_Permission:
error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
return false;
case ReadDirectory_NotDir:
error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path));
return false;
case ReadDirectory_Empty:
error(pos, "Empty directory: %.*s", LIT(rel_path));
return false;
case ReadDirectory_Unknown:
error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path));
return false;
}
for_array(list_index, list) {
@@ -4353,44 +4310,34 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) {
ParseFileError err = init_ast_file(file, fi->fullpath, &err_pos);
if (err != ParseFile_None) {
gb_mutex_lock(&global_error_collector.mutex);
defer (gb_mutex_unlock(&global_error_collector.mutex));
global_error_collector.count++;
if (err == ParseFile_EmptyFile) {
if (fi->fullpath == p->init_fullpath) {
gb_printf_err("Initial file is empty - %.*s\n", LIT(p->init_fullpath));
error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath));
gb_exit(1);
}
goto skip;
}
if (pos.line != 0) {
gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column);
}
gb_printf_err("Failed to parse file: %.*s\n\t", LIT(fi->name));
switch (err) {
case ParseFile_WrongExtension:
gb_printf_err("Invalid file extension: File must have the extension '.odin'");
error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name));
break;
case ParseFile_InvalidFile:
gb_printf_err("Invalid file or cannot be found");
error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name));
break;
case ParseFile_Permission:
gb_printf_err("File permissions problem");
error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name));
break;
case ParseFile_NotFound:
gb_printf_err("File cannot be found ('%.*s')", LIT(fi->fullpath));
error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath));
break;
case ParseFile_InvalidToken:
gb_printf_err("Invalid token found in file at (%td:%td)", err_pos.line, err_pos.column);
error(pos, "Failed to parse file: %.*s; invalid token found in file at (%td:%td)", LIT(fi->name), err_pos.line, err_pos.column);
break;
case ParseFile_EmptyFile:
gb_printf_err("File contains no tokens");
error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name));
break;
}
gb_printf_err("\n");
return err;
}

View File

@@ -292,6 +292,16 @@ void error(Token token, char *fmt, ...) {
va_end(va);
}
void error(TokenPos pos, char *fmt, ...) {
va_list va;
va_start(va, fmt);
Token token = {};
token.pos = pos;
error_va(token, fmt, va);
va_end(va);
}
void syntax_error(Token token, char *fmt, ...) {
va_list va;
va_start(va, fmt);