mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-02 20:58:15 +00:00
Begin transition to Jai-like syntax
This commit is contained in:
+95
-111
@@ -1075,36 +1075,33 @@ void init_preload(Checker *c) {
|
||||
c->done_preload = true;
|
||||
}
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueSpec *s, AstNodeValueSpec *init);
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d);
|
||||
|
||||
#include "expr.c"
|
||||
#include "decl.c"
|
||||
#include "stmt.c"
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueSpec *s, AstNodeValueSpec *init) {
|
||||
isize lhs = s->names.count;
|
||||
isize rhs = s->values.count;
|
||||
if (init != NULL) {
|
||||
rhs = init->values.count;
|
||||
}
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||
isize lhs = d->names.count;
|
||||
isize rhs = d->values.count;
|
||||
|
||||
if (init == NULL && rhs == 0) {
|
||||
if (s->type == NULL) {
|
||||
error_node(s->names.e[0], "Missing type or initial expression");
|
||||
if (rhs == 0) {
|
||||
if (d->type == NULL) {
|
||||
error_node(d->names.e[0], "Missing type or initial expression");
|
||||
return false;
|
||||
}
|
||||
} else if (lhs < rhs) {
|
||||
if (lhs < s->values.count) {
|
||||
AstNode *n = s->values.e[lhs];
|
||||
if (lhs < d->values.count) {
|
||||
AstNode *n = d->values.e[lhs];
|
||||
gbString str = expr_to_string(n);
|
||||
error_node(n, "Extra initial expression `%s`", str);
|
||||
gb_string_free(str);
|
||||
} else {
|
||||
error_node(s->names.e[0], "Extra initial expression");
|
||||
error_node(d->names.e[0], "Extra initial expression");
|
||||
}
|
||||
return false;
|
||||
} else if (lhs > rhs && (init != NULL || rhs != 1)) {
|
||||
AstNode *n = s->names.e[rhs];
|
||||
} else if (lhs > rhs && rhs != 1) {
|
||||
AstNode *n = d->names.e[rhs];
|
||||
gbString str = expr_to_string(n);
|
||||
error_node(n, "Missing expression for `%s`", str);
|
||||
gb_string_free(str);
|
||||
@@ -1160,15 +1157,95 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As
|
||||
switch (decl->kind) {
|
||||
case_ast_node(bd, BadDecl, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (vd->is_var) {
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), parent_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vd->values.count) {
|
||||
value = vd->values.e[i];
|
||||
}
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vd->values.count) {
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, parent_scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_decl = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(gd, GenericDecl, decl);
|
||||
if (!parent_scope->is_file) {
|
||||
// NOTE(bill): Within a procedure, variables must be in order
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNodeValueSpec empty_spec_ = {0}, *empty_spec = &empty_spec_;
|
||||
AstNodeValueSpec *prev_spec = NULL;
|
||||
|
||||
for_array(iota, gd->specs) {
|
||||
AstNode *spec = gd->specs.e[iota];
|
||||
switch (spec->kind) {
|
||||
@@ -1183,99 +1260,6 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As
|
||||
DelayedDecl di = {parent_scope, spec};
|
||||
array_add(&c->delayed_imports, di);
|
||||
case_end;
|
||||
case_ast_node(vs, ValueSpec, spec);
|
||||
switch (vs->keyword) {
|
||||
case Token_var: {
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vs->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vs->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), parent_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vs->type;
|
||||
di->init_expr = vs->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vs->values.count) {
|
||||
value = vs->values.e[i];
|
||||
}
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vs->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = gd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vs, NULL);
|
||||
} break;
|
||||
|
||||
case Token_const: {
|
||||
if (vs->type != NULL || vs->values.count > 0) {
|
||||
prev_spec = vs;
|
||||
} else if (prev_spec == NULL) {
|
||||
prev_spec = empty_spec;
|
||||
}
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < prev_spec->values.count) {
|
||||
init = prev_spec->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, parent_scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
ExactValue v_iota = make_exact_value_integer(iota);
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_decl = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, v_iota);
|
||||
d->type_expr = prev_spec->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vs, prev_spec);
|
||||
} break;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ts, TypeSpec, spec);
|
||||
if (ts->name->kind != AstNode_Ident) {
|
||||
|
||||
+11
-11
@@ -107,14 +107,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
void check_var_spec_node(Checker *c, AstNodeValueSpec *vs) {
|
||||
GB_ASSERT(vs->keyword == Token_var);
|
||||
isize entity_count = vs->names.count;
|
||||
void check_var_decl_node(Checker *c, AstNodeValueDecl *vd) {
|
||||
GB_ASSERT(vd->is_var == true);
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
Entity *entity = NULL;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
Token token = name->Ident;
|
||||
@@ -145,8 +145,8 @@ void check_var_spec_node(Checker *c, AstNodeValueSpec *vs) {
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vs->type) {
|
||||
init_type = check_type_extra(c, vs->type, NULL);
|
||||
if (vd->type) {
|
||||
init_type = check_type_extra(c, vd->type, NULL);
|
||||
if (init_type == NULL) {
|
||||
init_type = t_invalid;
|
||||
}
|
||||
@@ -165,12 +165,12 @@ void check_var_spec_node(Checker *c, AstNodeValueSpec *vs) {
|
||||
e->type = init_type;
|
||||
}
|
||||
|
||||
check_arity_match(c, vs, NULL);
|
||||
check_init_variables(c, entities, entity_count, vs->values, str_lit("variable declaration"));
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
for_array(i, vs->names) {
|
||||
for_array(i, vd->names) {
|
||||
if (entities[i] != NULL) {
|
||||
add_entity(c, c->context.scope, vs->names.e[i], entities[i]);
|
||||
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-59
@@ -85,71 +85,58 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
// Will be handled later
|
||||
case_end;
|
||||
case_ast_node(gd, GenericDecl, node);
|
||||
AstNodeValueSpec empty_spec_ = {0}, *empty_spec = &empty_spec_;
|
||||
AstNodeValueSpec *last = NULL;
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (vd->is_var) {
|
||||
// NOTE(bill): Handled later
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vd->values.count) {
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_decl = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
|
||||
DelayedEntity delay = {name, e, d};
|
||||
array_add(delayed_entities, delay);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(gd, GenericDecl, node);
|
||||
for_array(iota, gd->specs) {
|
||||
AstNode *spec = gd->specs.e[iota];
|
||||
switch (spec->kind) {
|
||||
case_ast_node(bd, BadDecl, spec);
|
||||
case_end;
|
||||
case_ast_node(vs, ValueSpec, spec);
|
||||
switch (vs->keyword) {
|
||||
case Token_var:
|
||||
break;
|
||||
|
||||
case Token_const: {
|
||||
if (vs->type != NULL || vs->values.count > 0) {
|
||||
last = vs;
|
||||
} else if (last == NULL) {
|
||||
last = empty_spec;
|
||||
}
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vs->values.count) {
|
||||
init = vs->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
ExactValue v_iota = make_exact_value_integer(iota);
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_decl = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, v_iota);
|
||||
d->type_expr = vs->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
|
||||
DelayedEntity delay = {name, e, d};
|
||||
array_add(delayed_entities, delay);
|
||||
}
|
||||
|
||||
check_arity_match(c, vs, last);
|
||||
} break;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ts, TypeSpec, spec);
|
||||
if (ts->name->kind != AstNode_Ident) {
|
||||
error_node(ts->name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[ts->name->kind]));
|
||||
|
||||
+102
-111
@@ -1026,48 +1026,45 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(gd, GenericDecl, us->node);
|
||||
for_array(spec_index, gd->specs) {
|
||||
AstNode *spec = gd->specs.e[spec_index];
|
||||
switch (spec->kind) {
|
||||
case_ast_node(vs, ValueSpec, spec);
|
||||
if (vs->keyword != Token_var) {
|
||||
error_node(spec, "`using` can only be applied to a variable declaration");
|
||||
return;
|
||||
}
|
||||
case_ast_node(vd, ValueDecl, us->node);
|
||||
if (!vd->is_var) {
|
||||
error_node(us->node, "`using` can only be applied to a variable declaration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vs->names.count > 1 && vs->type != NULL) {
|
||||
error(us->token, "`using` can only be applied to one variable of the same type");
|
||||
}
|
||||
if (vd->names.count > 1 && vd->type != NULL) {
|
||||
error(us->token, "`using` can only be applied to one variable of the same type");
|
||||
}
|
||||
|
||||
check_var_spec_node(c, vs);
|
||||
check_var_decl_node(c, vd);
|
||||
|
||||
for_array(name_index, vs->names) {
|
||||
AstNode *item = vs->names.e[name_index];
|
||||
ast_node(i, Ident, item);
|
||||
String name = i->string;
|
||||
Entity *e = scope_lookup_entity(c->context.scope, name);
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
for_array(name_index, vd->names) {
|
||||
AstNode *item = vd->names.e[name_index];
|
||||
if (item->kind != AstNode_Ident) {
|
||||
// TODO(bill): Handle error here???
|
||||
continue;
|
||||
}
|
||||
ast_node(i, Ident, item);
|
||||
String name = i->string;
|
||||
Entity *e = scope_lookup_entity(c->context.scope, name);
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
} else {
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
@@ -1096,7 +1093,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (vd->is_var) {
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
Entity *entity = NULL;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
Token token = name->Ident;
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
if (str_ne(str, str_lit("_"))) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error_node(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vd->type) {
|
||||
init_type = check_type_extra(c, vd->type, NULL);
|
||||
if (init_type == NULL) {
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
check_arity_match(c, vd);
|
||||
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
for_array(i, vd->names) {
|
||||
if (entities[i] != NULL) {
|
||||
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NOTE(bill): Handled elsewhere
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(gd, GenericDecl, node);
|
||||
for_array(spec_index, gd->specs) {
|
||||
@@ -1104,81 +1170,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
switch (spec->kind) {
|
||||
case_ast_node(bd, BadDecl, spec);
|
||||
case_end;
|
||||
case_ast_node(vs, ValueSpec, spec);
|
||||
switch (vs->keyword) {
|
||||
case Token_var: {
|
||||
isize entity_count = vs->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
Entity *entity = NULL;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
Token token = name->Ident;
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
if (str_ne(str, str_lit("_"))) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error_node(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vs->type) {
|
||||
init_type = check_type_extra(c, vs->type, NULL);
|
||||
if (init_type == NULL) {
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
check_arity_match(c, vs, NULL);
|
||||
|
||||
check_init_variables(c, entities, entity_count, vs->values, str_lit("variable declaration"));
|
||||
|
||||
for_array(i, vs->names) {
|
||||
if (entities[i] != NULL) {
|
||||
add_entity(c, c->context.scope, vs->names.e[i], entities[i]);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_const:
|
||||
// NOTE(bill): Handled elsewhere
|
||||
break;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ts, TypeSpec, spec);
|
||||
// NOTE(bill): Handled elsewhere
|
||||
case_end;
|
||||
|
||||
+117
-67
@@ -251,12 +251,6 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
AST_NODE_KIND(_ComplexStmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_SpecBegin, "", i32) \
|
||||
AST_NODE_KIND(ValueSpec, "value specification", struct { \
|
||||
TokenKind keyword; \
|
||||
AstNodeArray names; \
|
||||
AstNode * type; \
|
||||
AstNodeArray values; \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeSpec, "type specification", struct { \
|
||||
AstNode *name; \
|
||||
AstNode *type; \
|
||||
@@ -280,6 +274,13 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
u64 tags; \
|
||||
bool is_using; \
|
||||
}) \
|
||||
AST_NODE_KIND(ValueDecl, "value declaration", struct { \
|
||||
bool is_var; \
|
||||
AstNodeArray names; \
|
||||
AstNode * type; \
|
||||
AstNodeArray values; \
|
||||
u64 tags; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
|
||||
AstNode *name; \
|
||||
AstNode *type; \
|
||||
@@ -498,14 +499,14 @@ Token ast_node_token(AstNode *node) {
|
||||
return node->BadDecl.begin;
|
||||
case AstNode_GenericDecl:
|
||||
return node->GenericDecl.token;
|
||||
case AstNode_ValueDecl:
|
||||
return ast_node_token(node->ValueDecl.names.e[0]);
|
||||
case AstNode_ProcDecl:
|
||||
return ast_node_token(node->ProcDecl.name);
|
||||
|
||||
case AstNode_ForeignLibrary:
|
||||
return node->ForeignLibrary.token;
|
||||
|
||||
case AstNode_ValueSpec:
|
||||
return ast_node_token(node->ValueSpec.names.e[0]);
|
||||
case AstNode_TypeSpec:
|
||||
return ast_node_token(node->TypeSpec.name);
|
||||
case AstNode_ImportSpec:
|
||||
@@ -1076,12 +1077,12 @@ AstNode *make_generic_decl(AstFile *f, Token token, Token open, Token close, Ast
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *make_value_spec(AstFile *f, TokenKind keyword, AstNodeArray names, AstNode *type, AstNodeArray values) {
|
||||
AstNode *result = make_node(f, AstNode_ValueSpec);
|
||||
result->ValueSpec.keyword = keyword;
|
||||
result->ValueSpec.names = names;
|
||||
result->ValueSpec.type = type;
|
||||
result->ValueSpec.values = values;
|
||||
AstNode *make_value_decl(AstFile *f, bool is_var, AstNodeArray names, AstNode *type, AstNodeArray values) {
|
||||
AstNode *result = make_node(f, AstNode_ValueDecl);
|
||||
result->ValueDecl.is_var = is_var;
|
||||
result->ValueDecl.names = names;
|
||||
result->ValueDecl.type = type;
|
||||
result->ValueDecl.values = values;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1210,8 +1211,8 @@ void fix_advance_to_next_stmt(AstFile *f) {
|
||||
case Token_Semicolon:
|
||||
return;
|
||||
|
||||
case Token_var:
|
||||
case Token_const:
|
||||
// case Token_var:
|
||||
// case Token_const:
|
||||
case Token_type:
|
||||
case Token_proc:
|
||||
case Token_import:
|
||||
@@ -2139,42 +2140,42 @@ AstNode *parse_generic_decl(AstFile *f, TokenKind keyword, ParserSpecProc spec_p
|
||||
return make_generic_decl(f, token, open, close, specs, 0, false);
|
||||
}
|
||||
|
||||
PARSE_SPEC_PROC(parse_value_spec) {
|
||||
AstNodeArray names = parse_identfier_list(f);
|
||||
parse_check_name_list_for_reserves(f, names);
|
||||
AstNode *type = parse_type_attempt(f);
|
||||
AstNodeArray values = {0};
|
||||
// PARSE_SPEC_PROC(parse_value_spec) {
|
||||
// AstNodeArray names = parse_identfier_list(f);
|
||||
// parse_check_name_list_for_reserves(f, names);
|
||||
// AstNode *type = parse_type_attempt(f);
|
||||
// AstNodeArray values = {0};
|
||||
|
||||
if (allow_token(f, Token_Eq)) {
|
||||
values = parse_rhs_expr_list(f);
|
||||
}
|
||||
// if (allow_token(f, Token_Eq)) {
|
||||
// values = parse_rhs_expr_list(f);
|
||||
// }
|
||||
|
||||
if (values.count > names.count) {
|
||||
syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
|
||||
}
|
||||
// if (values.count > names.count) {
|
||||
// syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
|
||||
// }
|
||||
|
||||
switch (keyword) {
|
||||
case Token_var:
|
||||
if (type == NULL && values.count == 0 && names.count > 0) {
|
||||
syntax_error(f->curr_token, "Missing type or initialization");
|
||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
break;
|
||||
case Token_const:
|
||||
if (values.count == 0 && (index == 0 || type != NULL)) {
|
||||
syntax_error(f->curr_token, "Missing constant value");
|
||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// switch (keyword) {
|
||||
// case Token_var:
|
||||
// if (type == NULL && values.count == 0 && names.count > 0) {
|
||||
// syntax_error(f->curr_token, "Missing type or initialization");
|
||||
// return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
// }
|
||||
// break;
|
||||
// case Token_const:
|
||||
// if (values.count == 0 && (index == 0 || type != NULL)) {
|
||||
// syntax_error(f->curr_token, "Missing constant value");
|
||||
// return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
|
||||
// TODO(bill): Fix this so it does not require it
|
||||
if (values.e == NULL) {
|
||||
values = make_ast_node_array(f);
|
||||
}
|
||||
// // TODO(bill): Fix this so it does not require it
|
||||
// if (values.e == NULL) {
|
||||
// values = make_ast_node_array(f);
|
||||
// }
|
||||
|
||||
return make_value_spec(f, keyword, names, type, values);
|
||||
}
|
||||
// return make_value_spec(f, keyword, names, type, values);
|
||||
// }
|
||||
PARSE_SPEC_PROC(parse_type_spec) {
|
||||
AstNode *name = parse_identifier(f);
|
||||
AstNode *type = parse_type(f);
|
||||
@@ -2239,9 +2240,9 @@ PARSE_SPEC_PROC(parse_include_spec) {
|
||||
|
||||
AstNode *parse_decl(AstFile *f) {
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_var:
|
||||
case Token_const:
|
||||
return parse_generic_decl(f, f->curr_token.kind, parse_value_spec);
|
||||
// case Token_var:
|
||||
// case Token_const:
|
||||
// return parse_generic_decl(f, f->curr_token.kind, parse_value_spec);
|
||||
|
||||
case Token_type:
|
||||
return parse_generic_decl(f, f->curr_token.kind, parse_type_spec);
|
||||
@@ -2266,12 +2267,7 @@ AstNode *parse_decl(AstFile *f) {
|
||||
|
||||
|
||||
AstNode *parse_simple_stmt(AstFile *f) {
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_var:
|
||||
case Token_const:
|
||||
return parse_decl(f);
|
||||
}
|
||||
|
||||
Token start_token = f->curr_token;
|
||||
AstNodeArray lhs = parse_lhs_expr_list(f);
|
||||
Token token = f->curr_token;
|
||||
switch (token.kind) {
|
||||
@@ -2302,6 +2298,61 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
||||
}
|
||||
return make_assign_stmt(f, token, lhs, rhs);
|
||||
} break;
|
||||
|
||||
case Token_Colon: {
|
||||
parse_check_name_list_for_reserves(f, lhs);
|
||||
|
||||
AstNode *type = NULL;
|
||||
AstNodeArray values = {0};
|
||||
bool is_mutable = true;
|
||||
|
||||
if (allow_token(f, Token_Colon)) {
|
||||
if (!allow_token(f, Token_type)) {
|
||||
type = parse_type_attempt(f);
|
||||
}
|
||||
} else if (f->curr_token.kind != Token_Eq &&
|
||||
f->curr_token.kind != Token_Semicolon) {
|
||||
syntax_error(f->curr_token, "Expected a type separator `:` or `=`");
|
||||
}
|
||||
|
||||
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_Colon:
|
||||
is_mutable = false;
|
||||
/*fallthrough*/
|
||||
case Token_Eq:
|
||||
next_token(f);
|
||||
values = parse_rhs_expr_list(f);
|
||||
if (values.count > lhs.count) {
|
||||
syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
|
||||
} else if (values.count < lhs.count && !is_mutable) {
|
||||
syntax_error(f->curr_token, "All constant declarations must be defined");
|
||||
} else if (values.count == 0) {
|
||||
syntax_error(f->curr_token, "Expected an expression for this declaration");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_mutable) {
|
||||
if (type == NULL && values.count == 0) {
|
||||
syntax_error(f->curr_token, "Missing variable type or initialization");
|
||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
} else {
|
||||
if (type == NULL && values.count == 0 && lhs.count > 0) {
|
||||
syntax_error(f->curr_token, "Missing constant value");
|
||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
}
|
||||
|
||||
if (values.e == NULL) {
|
||||
values = make_ast_node_array(f);
|
||||
}
|
||||
|
||||
AstNodeArray specs = {0};
|
||||
array_init_reserve(&specs, heap_allocator(), 1);
|
||||
return make_value_decl(f, is_mutable, lhs, type, values);
|
||||
} break;
|
||||
}
|
||||
|
||||
if (lhs.count > 1) {
|
||||
@@ -3025,8 +3076,8 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
expect_semicolon(f, s);
|
||||
return s;
|
||||
|
||||
case Token_var:
|
||||
case Token_const:
|
||||
// case Token_var:
|
||||
// case Token_const:
|
||||
case Token_proc:
|
||||
case Token_type:
|
||||
case Token_import:
|
||||
@@ -3063,14 +3114,12 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
while (e->kind == AstNode_SelectorExpr) {
|
||||
e = unparen_expr(e->SelectorExpr.selector);
|
||||
}
|
||||
if (e->kind == AstNode_Ident) {
|
||||
if (e->kind == AstNode_Ident) {
|
||||
valid = true;
|
||||
}
|
||||
} break;
|
||||
case AstNode_GenericDecl:
|
||||
if (node->GenericDecl.token.kind == Token_var) {
|
||||
valid = true;
|
||||
}
|
||||
case AstNode_ValueDecl:
|
||||
valid = node->ValueDecl.is_var;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3153,8 +3202,8 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
return s;
|
||||
} else if (str_eq(tag, str_lit("thread_local"))) {
|
||||
AstNode *decl = parse_simple_stmt(f);
|
||||
if (decl->kind == AstNode_GenericDecl &&
|
||||
decl->GenericDecl.token.kind != Token_var) {
|
||||
if (decl->kind != AstNode_ValueDecl &&
|
||||
!decl->ValueDecl.is_var) {
|
||||
syntax_error(token, "#thread_local may only be applied to variable declarations");
|
||||
return make_bad_decl(f, token, ast_node_token(decl));
|
||||
}
|
||||
@@ -3162,8 +3211,9 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
syntax_error(token, "#thread_local is only allowed at the file scope");
|
||||
return make_bad_decl(f, token, ast_node_token(decl));
|
||||
}
|
||||
GB_ASSERT(decl->kind == AstNode_GenericDecl);
|
||||
decl->GenericDecl.tags |= VarDeclTag_thread_local;
|
||||
|
||||
GB_ASSERT(decl->kind == AstNode_ValueDecl);
|
||||
decl->ValueDecl.tags |= VarDeclTag_thread_local;
|
||||
return decl;
|
||||
} else if (str_eq(tag, str_lit("bounds_check"))) {
|
||||
s = parse_stmt(f);
|
||||
|
||||
@@ -3924,71 +3924,67 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
|
||||
ssa_build_when_stmt(proc, ws);
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (vd->is_var) {
|
||||
ssaModule *m = proc->module;
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
|
||||
|
||||
if (vd->values.count == 0) { // declared and zero-initialized
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, true);
|
||||
}
|
||||
}
|
||||
} else { // Tuple(s)
|
||||
Array(ssaAddr) lvals;
|
||||
ssaValueArray inits;
|
||||
array_init_reserve(&lvals, m->tmp_allocator, vd->names.count);
|
||||
array_init_reserve(&inits, m->tmp_allocator, vd->names.count);
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
}
|
||||
|
||||
array_add(&lvals, lval);
|
||||
}
|
||||
|
||||
for_array(i, vd->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vd->values.e[i]);
|
||||
Type *t = ssa_type(init);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
ssaValue *v = ssa_emit_struct_ev(proc, init, i);
|
||||
array_add(&inits, v);
|
||||
}
|
||||
} else {
|
||||
array_add(&inits, init);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for_array(i, inits) {
|
||||
if (lvals.e[i].addr == NULL) {
|
||||
continue;
|
||||
}
|
||||
ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i]));
|
||||
ssa_addr_store(proc, lvals.e[i], v);
|
||||
}
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(gd, GenericDecl, node);
|
||||
for_array(spec_index, gd->specs) {
|
||||
AstNode *spec = gd->specs.e[spec_index];
|
||||
switch (spec->kind) {
|
||||
case_ast_node(vs, ValueSpec, spec);
|
||||
switch (vs->keyword) {
|
||||
case Token_const:
|
||||
break;
|
||||
case Token_var: {
|
||||
ssaModule *m = proc->module;
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
|
||||
|
||||
if (vs->values.count == 0) { // declared and zero-initialized
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, true);
|
||||
}
|
||||
}
|
||||
} else { // Tuple(s)
|
||||
Array(ssaAddr) lvals;
|
||||
ssaValueArray inits;
|
||||
array_init_reserve(&lvals, m->tmp_allocator, vs->names.count);
|
||||
array_init_reserve(&inits, m->tmp_allocator, vs->names.count);
|
||||
|
||||
for_array(i, vs->names) {
|
||||
AstNode *name = vs->names.e[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
}
|
||||
|
||||
array_add(&lvals, lval);
|
||||
}
|
||||
|
||||
for_array(i, vs->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vs->values.e[i]);
|
||||
Type *t = ssa_type(init);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
ssaValue *v = ssa_emit_struct_ev(proc, init, i);
|
||||
array_add(&inits, v);
|
||||
}
|
||||
} else {
|
||||
array_add(&inits, init);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for_array(i, inits) {
|
||||
if (lvals.e[i].addr == NULL) {
|
||||
continue;
|
||||
}
|
||||
ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i]));
|
||||
ssa_addr_store(proc, lvals.e[i], v);
|
||||
}
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
} break;
|
||||
}
|
||||
case_end;
|
||||
case_ast_node(ts, TypeSpec, spec);
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
|
||||
+2
-2
@@ -84,8 +84,8 @@ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_type, "type"), \
|
||||
TOKEN_KIND(Token_proc, "proc"), \
|
||||
TOKEN_KIND(Token_var, "var"), \
|
||||
TOKEN_KIND(Token_const, "const"), \
|
||||
/* TOKEN_KIND(Token_var, "var"), */\
|
||||
/* TOKEN_KIND(Token_const, "const"), */\
|
||||
TOKEN_KIND(Token_import, "import"), \
|
||||
TOKEN_KIND(Token_include, "include"), \
|
||||
TOKEN_KIND(Token_macro, "macro"), \
|
||||
|
||||
Reference in New Issue
Block a user