Interfaces now look more like procedures, with sentinels being specified using `t as T` in the body.
// @CLEANUP: Is this really necessary?
typedef struct InterfaceParam {
OnyxToken *value_token;
- OnyxToken *type_token;
+ AstType *value_type;
+ Type *type;
} InterfaceParam;
+typedef struct InterfaceSentinel {
+ OnyxToken *name;
+ AstType *type;
+} InterfaceSentinel;
+
typedef struct InterfaceConstraint {
AstTyped *expr;
AstType *expected_type_expr;
char *name;
bh_arr(InterfaceParam) params;
+ bh_arr(InterfaceSentinel) sentinels;
bh_arr(InterfaceConstraint) exprs;
Scope *scope;
union {
struct {
AstInterface * interface;
- bh_arr(AstType *) type_args;
+ bh_arr(AstTyped *) args;
Scope* scope;
bh_arr(InterfaceConstraint) exprs;
u32 expr_idx;
// an instance of a type generic polymorphic structure.
// For example, converting to an iterator can be tested with:
//
- // IsIterable :: interface (t: $T) {
+ // IsIterable :: interface (T: type_expr) {
+ // t as T;
// { t->AsIterator() } -> Iterator;
// }
//
b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint) {
AstInterface *interface = constraint->interface;
- Type* type = type_build_from_ast(context.ast_alloc, constraint->type_args[0]);
+ Type* type = type_build_from_ast(context.ast_alloc, (AstType *) constraint->args[0]);
if (!type) return 0;
if (!strcmp(interface->name, "type_is_bool")) return type_is_bool(type);
+#include "astnodes.h"
+#include "types.h"
#define BH_INTERNAL_ALLOCATOR (global_heap_allocator)
#define BH_DEBUG
#include "parser.h"
return Check_Success;
}
+CheckStatus check_interface(AstInterface *interface) {
+ bh_arr_each(InterfaceParam, param, interface->params) {
+ CHECK(type, ¶m->value_type);
+
+ param->type = type_build_from_ast(context.ast_alloc, param->value_type);
+ if (!param->type) {
+ YIELD(param->value_type->token->pos, "Waiting for interface parameter's type to be constructed.");
+ }
+ }
+
+ return Check_Success;
+}
+
CheckStatus check_interface_constraint(AstConstraint *constraint) {
if (constraint->interface->kind != Ast_Kind_Interface) {
// CLEANUP: This error message might not look totally right in some cases.
constraint->scope = scope_create(context.ast_alloc, constraint->interface->scope, constraint->token->pos);
- if (bh_arr_length(constraint->type_args) != bh_arr_length(constraint->interface->params)) {
+ if (bh_arr_length(constraint->args) != bh_arr_length(constraint->interface->params)) {
ERROR_(constraint->token->pos, "Wrong number of arguments given to interface. Expected %d, got %d.",
bh_arr_length(constraint->interface->params),
- bh_arr_length(constraint->type_args));
+ bh_arr_length(constraint->args));
}
fori (i, 0, bh_arr_length(constraint->interface->params)) {
InterfaceParam *ip = &constraint->interface->params[i];
- AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
- sentinel->token = ip->value_token;
- sentinel->type_node = constraint->type_args[i];
+ AstTyped *arg = constraint->args[i];
+ TYPE_CHECK(&arg, ip->type) {
+ ERROR_(arg->token->pos, "Mismatched type in interface construction. Expected something of type '%s', but got something of type '%s'.", type_get_name(ip->type), type_get_name(arg->type));
+ }
AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
- type_alias->token = ip->type_token;
- type_alias->alias = (AstTyped *) constraint->type_args[i];
+ type_alias->token = ip->value_token;
+ type_alias->alias = arg;
+
+ symbol_introduce(constraint->scope, ip->value_token, (AstNode *) type_alias);
+ }
+
+ fori (i, 0, bh_arr_length(constraint->interface->sentinels)) {
+ InterfaceSentinel *is = &constraint->interface->sentinels[i];
- symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
- symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
+ AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
+ sentinel->token = is->name;
+ sentinel->type_node = is->type;
+
+ symbol_introduce(constraint->scope, is->name, (AstNode *) sentinel);
}
assert(constraint->entity);
if (cc->produce_errors) {
AstConstraint *constraint = cc->constraints[i];
char constraint_map[512] = {0};
- fori (i, 0, bh_arr_length(constraint->type_args)) {
+ fori (i, 0, bh_arr_length(constraint->args)) {
+ if (!node_is_type((AstNode *) constraint->args[i])) {
+ continue;
+ }
+
if (i != 0) strncat(constraint_map, ", ", 511);
OnyxToken* symbol = constraint->interface->params[i].value_token;
token_toggle_end(symbol);
strncat(constraint_map, " is of type '", 511);
- strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
+ strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, (AstType *) constraint->args[i])), 511);
strncat(constraint_map, "'", 511);
}
case Entity_Type_Polymorph_Query: cs = check_polyquery(ent->poly_query); break;
case Entity_Type_Enum_Value: cs = check_expression(&ent->enum_value->value); break;
case Entity_Type_Process_Directive: cs = check_process_directive((AstNode *) ent->expr); break;
+ case Entity_Type_Interface: cs = check_interface(ent->interface); break;
case Entity_Type_String_Literal:
case Entity_Type_Expression:
AstConstraint* dc = (AstConstraint *) nn;
AstConstraint* sc = (AstConstraint *) node;
- dc->type_args = NULL;
- bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
+ dc->args = NULL;
+ bh_arr_new(global_heap_allocator, dc->args, bh_arr_length(sc->args));
- bh_arr_each(AstType *, type_arg, sc->type_args) {
- bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
+ bh_arr_each(AstTyped *, arg, sc->args) {
+ bh_arr_push(dc->args, (AstTyped *) ast_clone(a, (AstNode *) *arg));
}
dc->phase = Constraint_Phase_Waiting_To_Be_Queued;
write_type_node(&tmp_buffer, constraint->interface);
bh_buffer_write_string(&tmp_buffer, "(");
- bh_arr_each(AstType *, ptype_arg, constraint->type_args) {
- if (ptype_arg != constraint->type_args) {
+ bh_arr_each(AstTyped *, ptype_arg, constraint->args) {
+ if (ptype_arg != constraint->args) {
bh_buffer_write_string(&tmp_buffer, ", ");
}
- AstType *type_arg = *ptype_arg;
+ AstTyped *type_arg = *ptype_arg;
write_type_node(&tmp_buffer, type_arg);
}
// such as procedure definitions, string literals, struct definitions
// and declarations to be introduced into scopes.
+#include "astnodes.h"
#define BH_INTERNAL_ALLOCATOR (global_heap_allocator)
#include "parser.h"
InterfaceParam ip;
ip.value_token = expect_token(parser, Token_Type_Symbol);
expect_token(parser, ':');
- expect_token(parser, '$');
- ip.type_token = expect_token(parser, Token_Type_Symbol);
+ ip.value_type = parse_type(parser);
bh_arr_push(interface->params, ip);
}
bh_arr_new(global_heap_allocator, interface->exprs, 2);
+ bh_arr_new(global_heap_allocator, interface->sentinels, 2);
type_create_scope(parser, &interface->scope, interface->token);
parser->current_scope = interface->scope;
continue;
}
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, Token_Type_Keyword_As)) {
+ InterfaceSentinel sentinel;
+ sentinel.name = expect_token(parser, Token_Type_Symbol);
+ consume_token(parser);
+
+ sentinel.type = parse_type(parser);
+ bh_arr_push(interface->sentinels, sentinel);
+
+ consume_token_if_next(parser, ';');
+ continue;
+ }
+
InterfaceConstraint ic = {0};
if (parse_possible_directive(parser, "not")) {
ic.invert_condition = 1;
constraint->token = constraint->interface->token;
- bh_arr_new(global_heap_allocator, constraint->type_args, 2);
+ bh_arr_new(global_heap_allocator, constraint->args, 2);
expect_token(parser, '(');
while (!consume_token_if_next(parser, ')')) {
if (parser->hit_unexpected_token) return constraint;
- AstType* type_node = parse_type(parser);
- bh_arr_push(constraint->type_args, type_node);
+ AstTyped* type_node = parse_expression(parser, 0);
+ bh_arr_push(constraint->args, type_node);
if (parser->curr->type != ')')
expect_token(parser, ',');
constraint->interface = (AstInterface *) param->implicit_interface;
constraint->token = constraint->interface->token;
- bh_arr_new(global_heap_allocator, constraint->type_args, 1);
- bh_arr_push(constraint->type_args, (AstType *) ast_clone(context.ast_alloc, param->poly_sym));
+ bh_arr_new(global_heap_allocator, constraint->args, 1);
+ bh_arr_push(constraint->args, (AstTyped *) ast_clone(context.ast_alloc, param->poly_sym));
bh_arr_push(solidified_func.func->constraints.constraints, constraint);
}
return Symres_Success;
}
+static SymresStatus symres_interface(AstInterface* interface) {
+ bh_arr_each(InterfaceParam, param, interface->params) {
+ SYMRES(type, ¶m->value_type);
+ }
+
+ return Symres_Success;
+}
+
static SymresStatus symres_constraint(AstConstraint* constraint) {
switch (constraint->phase) {
case Constraint_Phase_Cloning_Expressions:
case Constraint_Phase_Waiting_To_Be_Queued: {
SYMRES(expression, (AstTyped **) &constraint->interface);
- bh_arr_each(AstType *, type_arg, constraint->type_args) {
- SYMRES(type, type_arg);
+ bh_arr_each(AstTyped *, arg, constraint->args) {
+ SYMRES(expression, arg);
}
return Symres_Success;
next_state = Entity_State_Finalized;
break;
+ case Entity_Type_Interface: ss = symres_interface(ent->interface); break;
+
case Entity_Type_Overloaded_Function: ss = symres_overloaded_function(ent->overloaded_function); break;
case Entity_Type_Expression: ss = symres_expression(&ent->expr); break;
case Entity_Type_Type_Alias: ss = symres_type(&ent->type_alias); break;
delete :: #match {}
#local
- Destroyable :: interface (t: $T) {
+ Destroyable :: interface (T: type_expr) {
+ t as T;
+
{ T.destroy(&t) } -> void;
}
Helper interface to test if something can be passed to
as_iter successfully.
"""
-Iterable :: interface (t: $T) {
+Iterable :: interface (T: type_expr) {
+ t as T;
{ as_iter(t) } -> Iterator;
}
}
#local
-ImplicitIterator :: interface (t: $T) {
+ImplicitIterator :: interface (T: type_expr) {
+ t as T;
+
{ t->iter_open() } -> void;
t->iter_next();
{ t->iter_close() } -> void;
as_iter :: macro (x: $T/HasAsIter) => x->as_iter();
#local
-HasAsIter :: interface (t: $T) {
+HasAsIter :: interface (T: type_expr) {
+ t as T;
+
{ t->as_iter() } -> Iterator;
}
}
}
-#local ValidKey :: interface (t: $T) {
+#local ValidKey :: interface (T: type_expr) {
// In order to use a certain type as a key in a Map, you must
// provide an implementation of core.hash.hash() for that type,
// and you must provide an operator overload for ==.
+ t as T;
+
{ hash.hash(t) } -> u32;
{ t == t } -> bool;
}
return !(p1.first == p2.first) || !(p1.second == p2.second);
}
-#local Equatable :: interface (t: $T) {
+#local Equatable :: interface (T: type_expr) {
+ t as T;
{ t == t } -> bool;
}
use core {Optional}
-#local SetValue :: interface (t: $T) {
+#local SetValue :: interface (T: type_expr) {
+ t as T;
+
{ hash.hash(t) } -> u32;
{ t == t } -> bool;
}
-#local HasEquals :: interface (t: $T) {
+#local HasEquals :: interface (T: type_expr) {
+ t as T;
{ t == t } -> bool;
}
// Interface that holds true when the type has a hash() overload defined.
// Useful in datastructure when the ability to hash is dependent on whether
// the stored type is hashable. See core/container/pair.onyx for an example.
-Hashable :: interface (t: $T) {
+Hashable :: interface (T: type_expr) {
+ t as T;
{ hash(t) } -> u32;
}
#local
-HasHashMethod :: interface (t: $T) {
+HasHashMethod :: interface (T: type_expr) {
+ t as T;
{ t->hash() } -> u32;
}
// Slice: [] $T
// Function: () -> void
-type_is_bool :: interface (t: $T) #intrinsic
-type_is_int :: interface (t: $T) #intrinsic
-type_is_float :: interface (t: $T) #intrinsic
-type_is_number :: interface (t: $T) #intrinsic
-type_is_simd :: interface (t: $T) #intrinsic
-type_is_pointer :: interface (t: $T) #intrinsic
-type_is_enum :: interface (t: $T) #intrinsic
-type_is_simple :: interface (t: $T) #intrinsic
-type_is_array :: interface (t: $T) #intrinsic
-type_is_slice :: interface (t: $T) #intrinsic
-type_is_struct :: interface (t: $T) #intrinsic
-type_is_compound :: interface (t: $T) #intrinsic
-type_is_function :: interface (t: $T) #intrinsic
+type_is_bool :: interface (T: type_expr) #intrinsic
+type_is_int :: interface (T: type_expr) #intrinsic
+type_is_float :: interface (T: type_expr) #intrinsic
+type_is_number :: interface (T: type_expr) #intrinsic
+type_is_simd :: interface (T: type_expr) #intrinsic
+type_is_pointer :: interface (T: type_expr) #intrinsic
+type_is_enum :: interface (T: type_expr) #intrinsic
+type_is_simple :: interface (T: type_expr) #intrinsic
+type_is_array :: interface (T: type_expr) #intrinsic
+type_is_slice :: interface (T: type_expr) #intrinsic
+type_is_struct :: interface (T: type_expr) #intrinsic
+type_is_compound :: interface (T: type_expr) #intrinsic
+type_is_function :: interface (T: type_expr) #intrinsic
//
#local {
- __HasEqMethod :: interface (t: $T, r: $R) { T.__eq(t, r); }
- __HasNeMethod :: interface (t: $T, r: $R) { T.__ne(t, r); }
- __HasLtMethod :: interface (t: $T, r: $R) { T.__lt(t, r); }
- __HasLeMethod :: interface (t: $T, r: $R) { T.__le(t, r); }
- __HasGtMethod :: interface (t: $T, r: $R) { T.__gt(t, r); }
- __HasGeMethod :: interface (t: $T, r: $R) { T.__ge(t, r); }
- __HasAddMethod :: interface (t: $T, r: $R) { T.__add(t, r); }
- __HasMinusMethod :: interface (t: $T, r: $R) { T.__minus(t, r); }
- __HasMulMethod :: interface (t: $T, r: $R) { T.__mul(t, r); }
- __HasDivMethod :: interface (t: $T, r: $R) { T.__div(t, r); }
- __HasModMethod :: interface (t: $T, r: $R) { T.__mod(t, r); }
- __HasAndMethod :: interface (t: $T, r: $R) { T.__and(t, r); }
- __HasOrMethod :: interface (t: $T, r: $R) { T.__or(t, r); }
- __HasShlMethod :: interface (t: $T, r: $R) { T.__shl(t, r); }
- __HasShrMethod :: interface (t: $T, r: $R) { T.__shr(t, r); }
- __HasSarMethod :: interface (t: $T, r: $R) { T.__sar(t, r); }
- __HasXorMethod :: interface (t: $T, r: $R) { T.__xor(t, r); }
- __HasBandMethod :: interface (t: $T, r: $R) { T.__band(t, r); }
- __HasBorMethod :: interface (t: $T, r: $R) { T.__bor(t, r); }
- __HasSubMethod :: interface (t: $T, r: $R) { T.__sub(t, r); }
+ __HasEqMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__eq(t, r); }
+ __HasNeMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__ne(t, r); }
+ __HasLtMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__lt(t, r); }
+ __HasLeMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__le(t, r); }
+ __HasGtMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__gt(t, r); }
+ __HasGeMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__ge(t, r); }
+ __HasAddMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__add(t, r); }
+ __HasMinusMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__minus(t, r); }
+ __HasMulMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__mul(t, r); }
+ __HasDivMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__div(t, r); }
+ __HasModMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__mod(t, r); }
+ __HasAndMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__and(t, r); }
+ __HasOrMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__or(t, r); }
+ __HasShlMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__shl(t, r); }
+ __HasShrMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__shr(t, r); }
+ __HasSarMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__sar(t, r); }
+ __HasXorMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__xor(t, r); }
+ __HasBandMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__band(t, r); }
+ __HasBorMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__bor(t, r); }
+ __HasSubMethod :: interface (T: type_expr, R: type_expr) { t as T; r as R; T.__sub(t, r); }
}
#if #defined(runtime.vars.Onyx_Enable_Operator_Methods) {
#doc "Generic procedure for turning something into a string."
as_str :: #match -> str {}
-#local HasAsStrMethod :: interface (t: $T) {
+#local HasAsStrMethod :: interface (T: type_expr) {
+ t as T;
+
{ T.as_str(t) } -> str;
}
// expressions do not type check correctly, then the set of parameters does not
// satisfy the interface.
- CanAdd :: interface (t: $T) {
+ CanAdd :: interface (T: type_expr) {
+ t as T;
+
t + t;
}
// These have to be defined out here because #operator and #match are only allowed
// as top level expressions currently.
-NumberLike :: interface (t: $T) {
+NumberLike :: interface (T: type_expr) {
+ t as T;
+
// The constraints here are a little stricter than before. This syntax
// allows you to specify the expected type of the expression. If the type
// of the expression and the type after the arrow do not match, then the
// when the are combined with overloaded procedures.
// Take this example. Here is the same CanAdd interface from before.
- CanAdd :: interface (t: $T) {
+ CanAdd :: interface (T: type_expr) {
+ t as T;
+
t + t;
}
use core {package, *}
-OverloadsEqual :: interface (t: $T) {
+OverloadsEqual :: interface (T: type_expr) {
+ t as T;
{ T.equals(t, t) } -> bool;
}
// allocating memory that isn't technically free, but there
// should be more than enough space in the allocator to not
// run into that problem... Hopefully.
- tile_data_ring := alloc.ring.make(tile_data);
+ tile_data_ring := alloc.ring.make(.{ ~~tile_data.data, 200 * sizeof TileData });
tile_allocator := alloc.ring.make_allocator(&tile_data_ring);
while !string.empty(file) {
}
-#local __HasEqMethod :: interface (t: $T) {
+#local __HasEqMethod :: interface (T: type_expr) {
+ t as T;
+
{ T.__eq(t, t) } -> bool;
}
return g(f(a));
}
- Number :: interface (t: $T) {
+ Number :: interface (T: type_expr) {
+ t as T;
+
{ t * t } -> T;
}
use core {*}
-Speak :: interface (t: $T) {
+Speak :: interface (T: type_expr) {
+ t as T;
+
{ speak(t, str.{}) } -> void;
{ yell(t, str.{}) } -> void;
speak_things(dog);
speak_things(cat);
-}
\ No newline at end of file
+}
true
false
true
+[ 1, 2, 3, 4, 5 ]
use core {println, printf}
-Hashable :: interface (t :$T) {
+Hashable :: interface (T: type_expr) {
+ t as T;
+
{ hash.to_u32(t) } -> u32;
}
macro (x: $T) where Hashable(T) {
println(hash.to_u32(x));
},
-
(x: any) {
printf("{} is not hashable!\n", x.type);
},
}
-CanCastTo :: interface (t: $T, d: $D) {
+CanCastTo :: interface (T: type_expr, D: type_expr) {
+ t as T;
+
{ cast(D) t } -> D;
}
return x + y + x * y;
}
-SemiRing :: interface (t: $T) {
+SemiRing :: interface (T: type_expr) {
+ t as T;
+
{t + t} -> T;
{t * t} -> T;
}
return (x & y) | (x ^ y);
}
-BitField :: interface (t: $T) {
+BitField :: interface (T: type_expr) {
+ t as T;
+
{ ~t } -> T;
{ t & t } -> T;
{ t | t } -> T;
return consume_inner(iterator);
}
+
+ReturnsArray :: interface (T: type_expr, N: i32) {
+ t as T;
+
+ { t->make_an_array() } -> [N] i32;
+}
+
+MakeArray :: struct {
+ member: i32;
+
+ make_an_array :: (self: &MakeArray) -> [5] i32 {
+ return .[ 1, 2, 3, 4, 5 ];
+ }
+}
+
+run_make_array :: (x: $T) where ReturnsArray(T, 5) {
+ x->make_an_array() |> println();
+}
+
main :: (args: [] cstr) {
try_hash("This is a test");
try_hash(context);
b: [10] i32;
println(cast_able(b, [] i32));
+
+ run_make_array(&MakeArray.{});
}
d.values = c;
}
-Adder :: interface(t: $T) {
- { t + t } -> T;
+Adder :: interface(T: type_expr) {
+ t as T;
+
+ { t + t } -> T;
}
add :: (a, b: [$N]$T) -> [N]T
return a + b;
}
-CanBeIndexed :: interface(t: $T) {
- t[0];
+CanBeIndexed :: interface(T: type_expr) {
+ t as T;
+
+ t[0];
}
Foo :: struct (T: type_expr) where CanBeIndexed(T) {