string comparisons

This commit is contained in:
gingerBill
2016-08-15 13:46:01 +01:00
parent 0f48a7d299
commit 3ed75b22a3
14 changed files with 1176 additions and 195 deletions
+9 -9
View File
@@ -242,7 +242,7 @@ void check_close_scope(Checker *c) {
}
void scope_lookup_parent_entity(Scope *s, String name, Scope **scope, Entity **entity) {
u64 key = hash_string(name);
HashKey key = hash_string(name);
for (; s != NULL; s = s->parent) {
Entity **found = map_get(&s->elements, key);
if (found) {
@@ -262,7 +262,7 @@ Entity *scope_lookup_entity(Scope *s, String name) {
}
Entity *current_scope_lookup_entity(Scope *s, String name) {
u64 key = hash_string(name);
HashKey key = hash_string(name);
Entity **found = map_get(&s->elements, key);
if (found)
return *found;
@@ -273,7 +273,7 @@ Entity *current_scope_lookup_entity(Scope *s, String name) {
Entity *scope_insert_entity(Scope *s, Entity *entity) {
String name = entity->token.string;
u64 key = hash_string(name);
HashKey key = hash_string(name);
Entity **found = map_get(&s->elements, key);
if (found)
return *found;
@@ -465,7 +465,7 @@ void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode
void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL);
GB_ASSERT(identifier->kind == AstNode_Ident);
u64 key = hash_pointer(identifier);
HashKey key = hash_pointer(identifier);
map_set(&i->definitions, key, entity);
}
@@ -484,7 +484,7 @@ void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL);
GB_ASSERT(identifier->kind == AstNode_Ident);
u64 key = hash_pointer(identifier);
HashKey key = hash_pointer(identifier);
map_set(&i->uses, key, entity);
}
@@ -648,7 +648,7 @@ void check_parsed_files(Checker *c) {
case_end;
case_ast_node(id, ImportDecl, decl);
case_ast_node(ld, LoadDecl, decl);
// NOTE(bill): ignore
case_end;
@@ -662,7 +662,7 @@ void check_parsed_files(Checker *c) {
gb_for_array(i, c->info.entities.entries) {
auto *entry = &c->info.entities.entries[i];
Entity *e = cast(Entity *)cast(uintptr)entry->key;
Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
DeclInfo *d = entry->value;
check_entity_decl(c, e, d, NULL);
}
@@ -679,8 +679,8 @@ void check_parsed_files(Checker *c) {
// Add untyped expression values
gb_for_array(i, c->info.untyped.entries) {
auto *entry = c->info.untyped.entries + i;
u64 key = entry->key;
AstNode *expr = cast(AstNode *)cast(uintptr)key;
HashKey key = entry->key;
AstNode *expr = cast(AstNode *)cast(uintptr)key.key;
ExpressionInfo *info = &entry->value;
if (info != NULL && expr != NULL) {
if (is_type_typed(info->type)) {
+6 -4
View File
@@ -41,7 +41,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
Token name_token = i->token;
// TODO(bill): is the curr_scope correct?
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type);
u64 key = hash_string(name_token.string);
HashKey key = hash_string(name_token.string);
if (map_get(&entity_map, key)) {
// TODO(bill): Scope checking already checks the declaration
error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
@@ -615,8 +615,10 @@ b32 check_is_expr_vector_index(Checker *c, AstNode *expr) {
expr = unparen_expr(expr);
if (expr->kind == AstNode_IndexExpr) {
ast_node(ie, IndexExpr, expr);
Type *t = get_base_type(type_of_expr(&c->info, ie->expr));
return is_type_vector(t);
Type *t = type_of_expr(&c->info, ie->expr);
if (t != NULL) {
return is_type_vector(get_base_type(t));
}
}
return false;
}
@@ -1057,7 +1059,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) {
u64 key = hash_pointer(e);
HashKey key = hash_pointer(e);
ExpressionInfo *found = map_get(&c->info.untyped, key);
if (found == NULL)
return;
+1 -1
View File
@@ -58,7 +58,7 @@ void ssa_gen_code(ssaGen *s) {
gb_for_array(i, info->entities.entries) {
auto *entry = &info->entities.entries[i];
Entity *e = cast(Entity *)cast(uintptr)entry->key;
Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
DeclInfo *decl = entry->value;
String name = e->token.string;
+37 -10
View File
@@ -400,7 +400,32 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
ssa_fprintf(f, "%%%d = ", value->id);
if (gb_is_between(bo->op.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
if (is_type_float(elem_type)) {
if (is_type_string(elem_type)) {
ssa_fprintf(f, "call ");
ssa_print_type(f, m->sizes, t_bool);
char *runtime_proc = "";
switch (bo->op.kind) {
case Token_CmpEq: runtime_proc = "__string_eq"; break;
case Token_NotEq: runtime_proc = "__string_ne"; break;
case Token_Lt: runtime_proc = "__string_lt"; break;
case Token_Gt: runtime_proc = "__string_gt"; break;
case Token_LtEq: runtime_proc = "__string_le"; break;
case Token_GtEq: runtime_proc = "__string_gt"; break;
}
ssa_fprintf(f, " @%s(", runtime_proc);
ssa_print_type(f, m->sizes, type);
ssa_fprintf(f, " ");
ssa_print_value(f, m, bo->left, type);
ssa_fprintf(f, ", ");
ssa_print_type(f, m->sizes, type);
ssa_fprintf(f, " ");
ssa_print_value(f, m, bo->right, type);
ssa_fprintf(f, ")\n");
return;
} else if (is_type_float(elem_type)) {
ssa_fprintf(f, "fcmp ");
switch (bo->op.kind) {
case Token_CmpEq: ssa_fprintf(f, "oeq"); break;
@@ -434,14 +459,15 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
ssa_fprintf(f, "f");
switch (bo->op.kind) {
case Token_Add: ssa_fprintf(f, "add"); break;
case Token_Sub: ssa_fprintf(f, "sub"); break;
case Token_And: ssa_fprintf(f, "and"); break;
case Token_Or: ssa_fprintf(f, "or"); break;
case Token_Xor: ssa_fprintf(f, "xor"); break;
case Token_Shl: ssa_fprintf(f, "shl"); break;
case Token_Add: ssa_fprintf(f, "add"); break;
case Token_Sub: ssa_fprintf(f, "sub"); break;
case Token_And: ssa_fprintf(f, "and"); break;
case Token_Or: ssa_fprintf(f, "or"); break;
case Token_Xor: ssa_fprintf(f, "xor"); break;
case Token_Shl: ssa_fprintf(f, "shl"); break;
case Token_Shr: ssa_fprintf(f, "lshr"); break;
case Token_Mul: ssa_fprintf(f, "mul"); break;
case Token_Mul: ssa_fprintf(f, "mul"); break;
case Token_Not: ssa_fprintf(f, "xor"); break;
case Token_AndNot: GB_PANIC("Token_AndNot Should never be called");
@@ -655,15 +681,16 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
ssa_print_encoded_local(f, make_string(".string"));
ssa_fprintf(f, " = type {i8*, ");
ssa_print_type(f, m->sizes, t_int);
ssa_fprintf(f, "} ; Basic_string\n\n");
ssa_fprintf(f, "} ; Basic_string\n");
ssa_print_encoded_local(f, make_string(".rawptr"));
ssa_fprintf(f, " = type i8* ; Basic_rawptr\n\n");
ssa_fprintf(f, "declare void @llvm.memmove.p0i8.p0i8.");
ssa_print_type(f, m->sizes, t_int);
ssa_fprintf(f, "(i8*, i8*, ");
ssa_print_type(f, m->sizes, t_int);
ssa_fprintf(f, ", i32, i1)\n\n");
ssa_fprintf(f, ", i32, i1) argmemonly nounwind \n\n");
gb_for_array(i, m->nested_type_names) {
ssaValue *v = m->nested_type_names[i];
+3 -6
View File
@@ -670,7 +670,7 @@ ssaValue *ssa_make_value_block(ssaProcedure *proc, AstNode *node, Scope *scope,
b32 ssa_is_blank_ident(AstNode *node) {
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
return are_strings_equal(i->token.string, make_string("_"));
return is_blank_ident(i->token.string);
}
return false;
}
@@ -1390,6 +1390,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
ssaValue *right = ssa_build_expr(proc, ue->expr);
return ssa_emit_arith(proc, ue->op, left, right, tv->type);
} break;
case Token_Not: // Boolean not
case Token_Xor: { // Bitwise not
// NOTE(bill): "not" `x` == `x` "xor" `-1`
ExactValue neg_one = make_exact_value_integer(-1);
@@ -1397,10 +1398,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
ssaValue *right = ssa_make_value_constant(proc->module->allocator, tv->type, neg_one);
return ssa_emit_arith(proc, ue->op, left, right, tv->type);
} break;
case Token_Not: // Boolean not
GB_PANIC("Token_Not");
return NULL;
}
case_end;
@@ -2057,7 +2054,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
String name = make_string(name_text, name_len-1);
Entity **found = map_get(&proc->module->info->definitions, hash_pointer(pd->name));
GB_ASSERT(found != NULL);
GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.token.string));
Entity *e = *found;
ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
proc->module, e->type, pd->type, pd->body, name);
+47 -23
View File
@@ -6,18 +6,41 @@
// Hasing
gb_inline u64 hashing_proc(void const *data, isize len) {
return gb_murmur64(data, len);
struct HashKey {
u64 key;
b32 is_string;
String string; // if String, s.len > 0
};
gb_inline HashKey hashing_proc(void const *data, isize len) {
HashKey h = {};
h.key = gb_murmur64(data, len);
// h.key = gb_fnv64a(data, len);
return h;
}
gb_inline u64 hash_string(String s) {
return hashing_proc(s.text, s.len);
gb_inline HashKey hash_string(String s) {
HashKey h = hashing_proc(s.text, s.len);
h.is_string = true;
h.string = s;
return h;
}
gb_inline u64 hash_pointer(void *ptr) {
uintptr u = cast(uintptr)ptr;
u64 p = cast(u64)u;
return p;
gb_inline HashKey hash_pointer(void *ptr) {
uintptr p = cast(uintptr)ptr;
HashKey h = {cast(u64)p};
return h;
}
b32 hash_key_equal(HashKey a, HashKey b) {
if (a.key == b.key) {
if (a.is_string) {
if (b.is_string) return are_strings_equal(a.string, b.string);
return false;
}
return true;
}
return false;
}
i64 next_pow2(i64 n) {
@@ -58,17 +81,17 @@ i64 next_pow2(i64 n) {
////////////////////////////////////////////////////////////////
typedef struct MapFindResult {
struct MapFindResult {
isize hash_index;
isize entry_prev;
isize entry_index;
} MapFindResult;
};
template <typename T>
struct MapEntry {
u64 key;
isize next;
T value;
HashKey key;
isize next;
T value;
};
template <typename T>
@@ -79,9 +102,9 @@ struct Map {
template <typename T> void map_init (Map<T> *h, gbAllocator a);
template <typename T> void map_destroy(Map<T> *h);
template <typename T> T * map_get (Map<T> *h, u64 key);
template <typename T> void map_set (Map<T> *h, u64 key, T value);
template <typename T> void map_remove (Map<T> *h, u64 key);
template <typename T> T * map_get (Map<T> *h, HashKey key);
template <typename T> void map_set (Map<T> *h, HashKey key, T value);
template <typename T> void map_remove (Map<T> *h, HashKey key);
template <typename T> void map_clear (Map<T> *h);
template <typename T> void map_grow (Map<T> *h);
template <typename T> void map_rehash (Map<T> *h, isize new_count);
@@ -101,7 +124,7 @@ gb_inline void map_destroy(Map<T> *h) {
}
template <typename T>
gb_internal isize map__add_entry(Map<T> *h, u64 key) {
gb_internal isize map__add_entry(Map<T> *h, HashKey key) {
MapEntry<T> e = {0};
e.key = key;
e.next = -1;
@@ -110,14 +133,15 @@ gb_internal isize map__add_entry(Map<T> *h, u64 key) {
}
template <typename T>
gb_internal MapFindResult map__find(Map<T> *h, u64 key) {
gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
MapFindResult fr = {-1, -1, -1};
if (gb_array_count(h->hashes) > 0) {
fr.hash_index = key % gb_array_count(h->hashes);
fr.hash_index = key.key % gb_array_count(h->hashes);
fr.entry_index = h->hashes[fr.hash_index];
while (fr.entry_index >= 0) {
if (h->entries[fr.entry_index].key == key)
if (hash_key_equal(h->entries[fr.entry_index].key, key)) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = h->entries[fr.entry_index].next;
}
@@ -166,7 +190,7 @@ void map_rehash(Map<T> *h, isize new_count) {
}
template <typename T>
gb_inline T *map_get(Map<T> *h, u64 key) {
gb_inline T *map_get(Map<T> *h, HashKey key) {
isize index = map__find(h, key).entry_index;
if (index >= 0)
return &h->entries[index].value;
@@ -174,7 +198,7 @@ gb_inline T *map_get(Map<T> *h, u64 key) {
}
template <typename T>
void map_set(Map<T> *h, u64 key, T value) {
void map_set(Map<T> *h, HashKey key, T value) {
isize index;
MapFindResult fr;
if (gb_array_count(h->hashes) == 0)
@@ -197,7 +221,7 @@ void map_set(Map<T> *h, u64 key, T value) {
}
template <typename T>
void map_remove(Map<T> *h, u64 key) {
void map_remove(Map<T> *h, HashKey key) {
MapFindResult fr = map__find(h, key);
if (fr.entry_index >= 0) {
if (fr.entry_prev < 0) {
+51 -49
View File
@@ -59,8 +59,8 @@ struct AstScope {
struct Parser {
String init_fullpath;
gbArray(AstFile) files;
gbArray(String) imports;
isize import_index;
gbArray(String) loads;
isize load_index;
isize total_token_count;
};
@@ -177,7 +177,7 @@ AST_NODE_KIND(_DeclBegin, struct{}) \
}) \
AST_NODE_KIND(TypeDecl, struct { Token token; AstNode *name, *type; }) \
AST_NODE_KIND(AliasDecl, struct { Token token; AstNode *name, *type; }) \
AST_NODE_KIND(ImportDecl, struct { Token token, filepath; }) \
AST_NODE_KIND(LoadDecl, struct { Token token, filepath; }) \
AST_NODE_KIND(_DeclEnd, struct{}) \
AST_NODE_KIND(_TypeBegin, struct{}) \
AST_NODE_KIND(Field, struct { \
@@ -337,8 +337,8 @@ Token ast_node_token(AstNode *node) {
return node->TypeDecl.token;
case AstNode_AliasDecl:
return node->AliasDecl.token;
case AstNode_ImportDecl:
return node->ImportDecl.token;
case AstNode_LoadDecl:
return node->LoadDecl.token;
case AstNode_Field: {
if (node->Field.name_list)
return ast_node_token(node->Field.name_list);
@@ -394,7 +394,7 @@ AstEntity *make_ast_entity(AstFile *f, Token token, AstNode *decl, AstScope *par
return entity;
}
u64 hash_token(Token t) {
HashKey hash_token(Token t) {
return hash_string(t.string);
}
@@ -766,10 +766,10 @@ gb_inline AstNode *make_alias_decl(AstFile *f, Token token, AstNode *name, AstNo
}
gb_inline AstNode *make_import_decl(AstFile *f, Token token, Token filepath) {
AstNode *result = make_node(f, AstNode_ImportDecl);
result->ImportDecl.token = token;
result->ImportDecl.filepath = filepath;
gb_inline AstNode *make_load_decl(AstFile *f, Token token, Token filepath) {
AstNode *result = make_node(f, AstNode_LoadDecl);
result->LoadDecl.token = token;
result->LoadDecl.filepath = filepath;
return result;
}
@@ -825,6 +825,13 @@ gb_inline b32 allow_token(AstFile *f, TokenKind kind) {
}
b32 is_blank_ident(String str) {
if (str.len == 1) {
return str.text[0] == '_';
}
return false;
}
gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaration, AstNode *name_list) {
for (AstNode *n = name_list; n != NULL; n = n->next) {
if (n->kind != AstNode_Ident) {
@@ -836,8 +843,7 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio
n->Ident.entity = entity;
AstEntity *insert_entity = ast_scope_insert(scope, *entity);
if (insert_entity != NULL &&
!are_strings_equal(insert_entity->token.string, make_string("_"))) {
if (insert_entity != NULL && !is_blank_ident(insert_entity->token.string)) {
ast_file_err(f, entity->token,
"There is already a previous declaration of `%.*s` in the current scope at\n"
"\t%.*s(%td:%td)",
@@ -845,6 +851,10 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio
LIT(insert_entity->token.pos.file),
insert_entity->token.pos.line,
insert_entity->token.pos.column);
gb_printf_err("Hashes\n");
gb_printf_err("%16llx - %.*s\n", hash_string(insert_entity->token.string), LIT(insert_entity->token.string));
gb_printf_err("%16llx - %.*s\n\n", hash_string(entity->token.string), LIT(entity->token.string));
}
}
}
@@ -1898,16 +1908,6 @@ AstNode *parse_stmt(AstFile *f) {
return alias_decl;
} break;
case Token_import: {
Token token = expect_token(f, Token_import);
Token filepath = expect_token(f, Token_String);
if (f->curr_scope == f->file_scope) {
return make_import_decl(f, token, filepath);
}
ast_file_err(f, token, "You cannot `import` within a procedure. This must be done at the file scope.");
return make_bad_decl(f, token, filepath);
} break;
// Operands
case Token_Identifier:
case Token_Integer:
@@ -1944,10 +1944,20 @@ AstNode *parse_stmt(AstFile *f) {
expect_token(f, Token_Semicolon);
return make_branch_stmt(f, token);
case Token_Hash:
case Token_Hash: {
s = parse_tag_stmt(f, NULL);
if (are_strings_equal(s->TagStmt.name.string, make_string("load"))) {
Token file_path = expect_token(f, Token_String);
if (f->curr_scope == f->file_scope) {
return make_load_decl(f, s->TagStmt.token, file_path);
}
ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope.");
return make_bad_decl(f, token, file_path);
}
s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument
return s;
} break;
case Token_OpenBrace: return parse_block_stmt(f);
@@ -2036,7 +2046,7 @@ void destroy_ast_file(AstFile *f) {
b32 init_parser(Parser *p) {
gb_array_init(p->files, gb_heap_allocator());
gb_array_init(p->imports, gb_heap_allocator());
gb_array_init(p->loads, gb_heap_allocator());
return true;
}
@@ -2046,24 +2056,24 @@ void destroy_parser(Parser *p) {
destroy_ast_file(&p->files[i]);
}
#if 1
gb_for_array(i, p->imports) {
// gb_free(gb_heap_allocator(), p->imports[i].text);
gb_for_array(i, p->loads) {
// gb_free(gb_heap_allocator(), p->loads[i].text);
}
#endif
gb_array_free(p->files);
gb_array_free(p->imports);
gb_array_free(p->loads);
}
// NOTE(bill): Returns true if it's added
b32 try_add_import_path(Parser *p, String import_file) {
gb_for_array(i, p->imports) {
String import = p->imports[i];
b32 try_add_load_path(Parser *p, String import_file) {
gb_for_array(i, p->loads) {
String import = p->loads[i];
if (are_strings_equal(import, import_file)) {
return false;
}
}
gb_array_append(p->imports, import_file);
gb_array_append(p->loads, import_file);
return true;
}
@@ -2076,7 +2086,7 @@ gb_global Rune illegal_import_runes[] = {
'|', ',', '<', '>', '?',
};
b32 is_import_path_valid(String path) {
b32 is_load_path_valid(String path) {
if (path.len > 0) {
u8 *start = path.text;
u8 *end = path.text + path.len;
@@ -2125,37 +2135,29 @@ void parse_file(Parser *p, AstFile *f) {
// NOTE(bill): Sanity check
ast_file_err(f, ast_node_token(node), "Only declarations are allowed at file scope");
} else {
if (node->kind == AstNode_ImportDecl) {
auto *id = &node->ImportDecl;
if (node->kind == AstNode_LoadDecl) {
auto *id = &node->LoadDecl;
String file_str = id->filepath.string;
char ext[] = ".odin";
isize ext_len = gb_size_of(ext)-1;
b32 append_ext = false;
if (!is_import_path_valid(file_str)) {
if (!is_load_path_valid(file_str)) {
ast_file_err(f, ast_node_token(node), "Invalid import path");
continue;
}
if (string_extension_position(file_str) < 0)
append_ext = true;
isize str_len = base_dir.len+file_str.len;
if (append_ext)
str_len += ext_len;
u8 *str = gb_alloc_array(gb_heap_allocator(), u8, str_len+1);
defer (gb_free(gb_heap_allocator(), str));
gb_memcopy(str, base_dir.text, base_dir.len);
gb_memcopy(str+base_dir.len, file_str.text, file_str.len);
if (append_ext)
gb_memcopy(str+base_dir.len+file_str.len, ext, ext_len+1);
str[str_len] = '\0';
char *path_str = gb_path_get_full_name(gb_heap_allocator(), cast(char *)str);
String import_file = make_string(path_str);
if (!try_add_import_path(p, import_file)) {
gb_printf_err("load path: %.*s\n", LIT(import_file));
if (!try_add_load_path(p, import_file)) {
gb_free(gb_heap_allocator(), import_file.text);
}
}
@@ -2167,11 +2169,11 @@ void parse_file(Parser *p, AstFile *f) {
ParseFileError parse_files(Parser *p, char *init_filename) {
char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename);
String init_fullpath = make_string(fullpath_str);
gb_array_append(p->imports, init_fullpath);
gb_array_append(p->loads, init_fullpath);
p->init_fullpath = init_fullpath;
gb_for_array(i, p->imports) {
String import_path = p->imports[i];
gb_for_array(i, p->loads) {
String import_path = p->loads[i];
AstFile file = {};
ParseFileError err = init_ast_file(&file, import_path);
if (err != ParseFile_None) {
+6 -1
View File
@@ -25,7 +25,12 @@ gb_inline String make_string(char *text) {
gb_inline b32 are_strings_equal(String a, String b) {
if (a.len == b.len) {
return gb_memcompare(a.text, b.text, a.len) == 0;
for (isize i = 0; i < a.len; i++) {
if (a.text[i] != b.text[i]) {
return false;
}
}
return true;
}
return false;
}
-1
View File
@@ -117,7 +117,6 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_for, "for"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
TOKEN_KIND(Token_import, "import"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_enum, "enum"), \