From 9ba2a6d02cab3feff9d70f7bb9c2e8eb72bc5533 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Jul 2016 01:04:57 +0100 Subject: [PATCH] Slices and slice expressions --- build.bat | 1 + src/checker/checker.cpp | 6 + src/checker/expression.cpp | 249 ++++++++++++++++++++++++++++++++----- src/checker/statements.cpp | 12 +- src/checker/type.cpp | 33 ++++- src/parser.cpp | 59 ++++++++- src/test.odin | 13 +- src/tokenizer.cpp | 2 +- 8 files changed, 327 insertions(+), 48 deletions(-) diff --git a/build.bat b/build.bat index f43d9d9c9..07eee7b82 100644 --- a/build.bat +++ b/build.bat @@ -24,6 +24,7 @@ set compiler_warnings= ^ -wd4505 -wd4512 -wd4550 ^ set compiler_includes= ^ + rem -I"C:\Program Files\LLVM\include" set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib ^ diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 85a491804..4136be912 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -71,6 +71,9 @@ enum BuiltinProcedureId { BuiltinProcedure_offset_of, BuiltinProcedure_offset_of_val, BuiltinProcedure_static_assert, + BuiltinProcedure_len, + BuiltinProcedure_cap, + BuiltinProcedure_copy, BuiltinProcedure_print, BuiltinProcedure_println, @@ -111,6 +114,9 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { {STR_LIT("offset_of"), 2, false, Expression_Expression}, {STR_LIT("offset_of_val"), 1, false, Expression_Expression}, {STR_LIT("static_assert"), 1, false, Expression_Statement}, + {STR_LIT("len"), 1, false, Expression_Expression}, + {STR_LIT("cap"), 1, false, Expression_Expression}, + {STR_LIT("copy"), 2, false, Expression_Expression}, {STR_LIT("print"), 1, true, Expression_Statement}, {STR_LIT("println"), 1, true, Expression_Statement}, }; diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp index aa4e86f95..fbb213dfa 100644 --- a/src/checker/expression.cpp +++ b/src/checker/expression.cpp @@ -252,8 +252,9 @@ Type *check_type_expression_extra(Checker *c, AstNode *expression, Type *named_t set_base_type(named_type, t); return t; } else { - print_checker_error(c, ast_node_token(expression), "Empty array size"); - return NULL; + Type *t = make_type_slice(check_type(c, expression->array_type.element)); + set_base_type(named_type, t); + return t; } break; @@ -335,12 +336,17 @@ Type *check_type(Checker *c, AstNode *expression, Type *named_type) { case AstNode_ParenExpression: return check_type(c, expression->paren_expression.expression, named_type); - case AstNode_ArrayType: - type = make_type_array(check_type(c, expression->array_type.element), - check_array_count(c, expression->array_type.count)); - set_base_type(named_type, type); + case AstNode_ArrayType: { + if (expression->array_type.count != NULL) { + type = make_type_array(check_type(c, expression->array_type.element), + check_array_count(c, expression->array_type.count)); + set_base_type(named_type, type); + } else { + type = make_type_slice(check_type(c, expression->array_type.element)); + set_base_type(named_type, type); + } goto end; - break; + } break; case AstNode_StructType: { type = make_type_structure(); @@ -860,35 +866,43 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) { operand->type = target_type; } -b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound_checks) { +b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) { Operand operand = {Addressing_Invalid}; check_expression(c, &operand, index_value); - if (operand.mode == Addressing_Invalid) + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; return false; + } convert_to_typed(c, &operand, &basic_types[Basic_int]); - if (operand.mode == Addressing_Invalid) + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; return false; + } if (!is_type_integer(operand.type)) { gbString expr_str = expression_to_string(operand.expression); print_checker_error(c, ast_node_token(operand.expression), "Index `%s` must be an integer", expr_str); gb_string_free(expr_str); + if (value) *value = 0; return false; } if (operand.mode == Addressing_Constant) { - if (bound_checks && max_count > 0) { // NOTE(bill): Do array bound checking + if (max_count >= 0) { // NOTE(bill): Do array bound checking i64 i = value_to_integer(operand.value).value_integer; if (i < 0) { gbString expr_str = expression_to_string(operand.expression); print_checker_error(c, ast_node_token(operand.expression), "Index `%s` cannot be a negative value", expr_str); gb_string_free(expr_str); + if (value) *value = 0; return false; } + if (value) *value = i; + if (i >= max_count) { gbString expr_str = expression_to_string(operand.expression); print_checker_error(c, ast_node_token(operand.expression), @@ -896,10 +910,13 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound gb_string_free(expr_str); return false; } + + return true; } } // NOTE(bill): It's alright :D + if (value) *value = -1; return true; } @@ -920,10 +937,6 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { return f; } } - } else { - // TODO(bill): Array.count - // TODO(bill): Array.elements - // TODO(bill): Or should these be functions? } return NULL; @@ -960,7 +973,6 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) { } - b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { GB_ASSERT(call->kind == AstNode_CallExpression); auto *ce = &call->call_expression; @@ -972,10 +984,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (ce->arg_list_count > bp->arg_count && !bp->variadic) err = "Too many"; if (err) { - gbString call_str = expression_to_string(call); - defer (gb_string_free(call_str)); - print_checker_error(c, ce->close, "`%s` arguments for `%s`, expected %td, got %td", - err, call_str, bp->arg_count, ce->arg_list_count); + print_checker_error(c, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", + err, LIT(call->call_expression.proc->identifier.token.string), + bp->arg_count, ce->arg_list_count); return false; } } @@ -1044,7 +1055,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) print_checker_error(c, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`"); return false; } - if (field_arg->kind != AstNode_Identifier) { + if (field_arg == NULL || + field_arg->kind != AstNode_Identifier) { print_checker_error(c, ast_node_token(field_arg), "Expected an identifier for field argument"); return false; } @@ -1055,7 +1067,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (entity == NULL) { gbString type_str = type_to_string(type); print_checker_error(c, ast_node_token(ce->arg_list), - "`%s` has no field named `%s`", type_str, LIT(field_arg->identifier.token.string)); + "`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string)); return false; } @@ -1089,7 +1101,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (entity == NULL) { gbString type_str = type_to_string(type); print_checker_error(c, ast_node_token(arg), - "`%s` has no field named `%s`", type_str, LIT(s->selector->identifier.token.string)); + "`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string)); return false; } @@ -1099,6 +1111,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; case BuiltinProcedure_static_assert: + // static_assert :: proc(cond: bool) + if (operand->mode != Addressing_Constant || !is_type_boolean(operand->type)) { gbString str = expression_to_string(ce->arg_list); @@ -1116,6 +1130,92 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; + case BuiltinProcedure_len: + case BuiltinProcedure_cap: { + Type *t = get_base_type(operand->type); + + AddressingMode mode = Addressing_Invalid; + Value value = {}; + + switch (t->kind) { + case Type_Basic: + if (id == BuiltinProcedure_len) { + if (is_type_string(t)) { + if (operand->mode == Addressing_Constant) { + mode = Addressing_Constant; + value = make_value_integer(operand->value.value_string.len); + } else { + mode = Addressing_Value; + } + } + } + break; + + case Type_Array: + mode = Addressing_Constant; + value = make_value_integer(t->array.count); + break; + + case Type_Slice: + mode = Addressing_Value; + break; + } + + if (mode == Addressing_Invalid) { + gbString str = expression_to_string(operand->expression); + print_checker_error(c, ast_node_token(operand->expression), + "Invalid expression `%s` for `%.*s`", + str, LIT(bp->name)); + gb_string_free(str); + return false; + } + + operand->mode = mode; + operand->type = &basic_types[Basic_int]; + operand->value = value; + + } break; + + case BuiltinProcedure_copy: { + // copy :: proc(x, y: []Type) -> int + Type *dest_type = NULL, *src_type = NULL; + Type *d = get_base_type(operand->type); + if (d->kind == Type_Slice) + dest_type = d->slice.element; + + Operand op = {}; + check_expression(c, &op, ce->arg_list->next); + if (op.mode == Addressing_Invalid) + return false; + Type *s = get_base_type(op.type); + if (s->kind == Type_Slice) + src_type = s->slice.element; + + if (dest_type == NULL || src_type == NULL) { + print_checker_error(c, ast_node_token(call), "`copy` only expects slices as arguments"); + return false; + } + + if (!are_types_identical(dest_type, src_type)) { + gbString d_arg = expression_to_string(ce->arg_list); + gbString s_arg = expression_to_string(ce->arg_list->next); + gbString d_str = type_to_string(dest_type); + gbString s_str = type_to_string(src_type); + defer (gb_string_free(d_arg)); + defer (gb_string_free(s_arg)); + defer (gb_string_free(d_str)); + defer (gb_string_free(s_str)); + print_checker_error(c, ast_node_token(call), + "Arguments to `copy`, %s, %s, have different element types: %s vs %s", + d_arg, s_arg, d_str, s_str); + return false; + } + + operand->type = &basic_types[Basic_int]; // Returns number of elements copied + operand->mode = Addressing_Value; + } break; + + case BuiltinProcedure_print: case BuiltinProcedure_println: { for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) { @@ -1170,7 +1270,6 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode operand->mode = Addressing_Value; check_not_tuple(c, operand); check_assignment(c, operand, sig_params[param_index]->type, make_string("argument")); - } if (i < tuple->variable_count && param_index == param_count) { @@ -1179,7 +1278,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode } } - if (param_index < param_count) + if (param_index >= param_count) break; } @@ -1392,8 +1491,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr goto error; b32 valid = false; - b32 bound_checks = false; - i64 max_count = 0; + i64 max_count = -1; Type *t = get_base_type(operand->type); switch (t->kind) { case Type_Basic: @@ -1401,7 +1499,6 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr valid = true; if (operand->mode == Addressing_Constant) { max_count = operand->value.value_string.len; - bound_checks = true; } operand->mode = Addressing_Value; operand->type = &basic_types[Basic_u8]; @@ -1411,16 +1508,19 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr case Type_Array: valid = true; max_count = t->array.count; - bound_checks = max_count > 0; if (operand->mode != Addressing_Variable) operand->mode = Addressing_Value; operand->type = t->array.element; break; + case Type_Slice: + valid = true; + operand->type = t->slice.element; + operand->mode = Addressing_Variable; + break; + case Type_Pointer: valid = true; - bound_checks = false; - max_count = 0; operand->mode = Addressing_Variable; operand->type = get_base_type(t->pointer.element); break; @@ -1442,7 +1542,92 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr goto error; } - check_index_value(c, expression->index_expression.value, max_count, bound_checks); + check_index_value(c, expression->index_expression.value, max_count, NULL); + } break; + + + case AstNode_SliceExpression: { + auto *se = &expression->slice_expression; + check_expression(c, operand, se->expression); + if (operand->mode == Addressing_Invalid) + goto error; + + b32 valid = false; + i64 max_count = -1; + Type *t = get_base_type(operand->type); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t)) { + valid = true; + if (operand->mode == Addressing_Constant) { + max_count = operand->value.value_string.len; + } + operand->mode = Addressing_Value; + } + break; + + case Type_Array: + valid = true; + max_count = t->array.count; + if (operand->mode != Addressing_Variable) { + gbString str = expression_to_string(expression); + print_checker_error(c, ast_node_token(expression), "Cannot slice array `%s`, value is not addressable", str); + gb_string_free(str); + goto error; + } + operand->type = make_type_slice(t->array.element); + operand->mode = Addressing_Value; + break; + + case Type_Slice: + valid = true; + operand->mode = Addressing_Value; + break; + + case Type_Pointer: + valid = true; + operand->type = make_type_slice(get_base_type(t->pointer.element)); + operand->mode = Addressing_Value; + break; + } + + if (!valid) { + gbString str = expression_to_string(operand->expression); + print_checker_error(c, ast_node_token(operand->expression), + "Cannot slice `%s`", str); + gb_string_free(str); + goto error; + } + + i64 indices[3] = {}; + AstNode *nodes[3] = {se->low, se->high, se->max}; + for (isize i = 0; i < gb_count_of(nodes); i++) { + AstNode *node = nodes[i]; + i64 index = max_count; + if (node != NULL) { + i64 capacity = -1; + if (max_count >= 0) + capacity = max_count; + i64 j = 0; + if (check_index_value(c, node, capacity, &j)) { + index = j; + } + } else if (i == 0) { + index = 0; + } + indices[i] = index; + } + + for (isize i = 0; i < gb_count_of(indices); i++) { + i64 a = indices[i]; + for (isize j = i+1; j < gb_count_of(indices); j++) { + i64 b = indices[j]; + if (a > b && b >= 0) { + print_checker_error(c, se->close, "Invalid slice indices: [%td > %td]", a, b); + } + } + } + } break; case AstNode_CastExpression: { diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp index 036b096c9..15a0904a6 100644 --- a/src/checker/statements.cpp +++ b/src/checker/statements.cpp @@ -38,8 +38,14 @@ b32 check_assignable_to(Checker *c, Operand *operand, Type *type) { if (sb->kind == Type_Array && tb->kind == Type_Array) { if (are_types_identical(sb->array.element, tb->array.element)) { - if (tb->array.count == 0) // NOTE(bill): Not static size - return true; + return sb->array.count == tb->array.count; + } + } + + if ((sb->kind == Type_Array || sb->kind == Type_Slice) && + tb->kind == Type_Slice) { + if (are_types_identical(sb->array.element, tb->slice.element)) { + return true; } } @@ -233,7 +239,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in if (i < lhs_count) { if (lhs[i]->type == NULL) print_checker_error(c, lhs[i]->token, "Too few values on the right hand side of the declaration"); - } else if (rhs != NULL) { + } else if (rhs != NULL) { print_checker_error(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration"); } } diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 684e0651c..46957601e 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -55,6 +55,7 @@ enum TypeKind { Type_Basic, Type_Array, + Type_Slice, Type_Structure, Type_Pointer, Type_Named, @@ -71,6 +72,9 @@ struct Type { Type *element; i64 count; } array; + struct { + Type *element; + } slice; struct { // Theses are arrays Entity **fields; // Entity_Variable @@ -111,6 +115,7 @@ void set_base_type(Type *t, Type *base) { } +// TODO(bill): Remove heap allocation Type *alloc_type(TypeKind kind) { Type *t = gb_alloc_item(gb_heap_allocator(), Type); t->kind = kind; @@ -123,6 +128,7 @@ Type *make_type_basic(BasicType basic) { t->basic = basic; return t; } + Type *make_type_array(Type *element, i64 count) { Type *t = alloc_type(Type_Array); t->array.element = element; @@ -130,6 +136,12 @@ Type *make_type_array(Type *element, i64 count) { return t; } +Type *make_type_slice(Type *element) { + Type *t = alloc_type(Type_Slice); + t->array.element = element; + return t; +} + Type *make_type_structure(void) { Type *t = alloc_type(Type_Structure); return t; @@ -166,7 +178,7 @@ Type *make_type_procedure(Scope *scope, Type *params, isize params_count, Type * -#define STR_LIT(x) {cast(u8 *)x, gb_size_of(x)-1} +#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1} gb_global Type basic_types[] = { {Type_Basic, {Basic_Invalid, 0, STR_LIT("invalid type")}}, {Type_Basic, {Basic_bool, BasicFlag_Boolean, STR_LIT("bool")}}, @@ -483,10 +495,11 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { } i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) { - GB_ASSERT(t->kind == Type_Structure); - type_set_offsets(s, allocator, t); - if (gb_is_between(index, 0, t->structure.field_count-1)) { - return t->structure.offsets[index]; + if (t->kind == Type_Structure) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->structure.field_count-1)) { + return t->structure.offsets[index]; + } } return 0; } @@ -501,6 +514,7 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_Basic: str = gb_string_append_length(str, type->basic.name.text, type->basic.name.len); break; + case Type_Array: if (type->array.count >= 0) { str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count)); @@ -509,12 +523,18 @@ gbString write_type_to_string(gbString str, Type *type) { } str = write_type_to_string(str, type->array.element); break; + + case Type_Slice: + str = gb_string_appendc(str, "[]"); + str = write_type_to_string(str, type->array.element); + break; + case Type_Structure: { str = gb_string_appendc(str, "struct{"); for (isize i = 0; i < type->structure.field_count; i++) { Entity *f = type->structure.fields[i]; GB_ASSERT(f->kind == Entity_Variable); - if (i < type->structure.field_count-1) + if (i > 0) str = gb_string_appendc(str, "; "); str = gb_string_append_length(str, f->token.string.text, f->token.string.len); str = gb_string_appendc(str, ": "); @@ -564,6 +584,7 @@ gbString write_type_to_string(gbString str, Type *type) { } break; } + return str; } diff --git a/src/parser.cpp b/src/parser.cpp index d42790099..367303ade 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -47,6 +47,7 @@ AstNode__ExpressionBegin, AstNode_CallExpression, AstNode_SelectorExpression, AstNode_IndexExpression, + AstNode_SliceExpression, AstNode_CastExpression, AstNode_DereferenceExpression, AstNode__ExpressionEnd, @@ -117,7 +118,7 @@ struct AstNode { struct { Token op; AstNode *left, *right; } binary_expression; struct { AstNode *expression; Token open, close; } paren_expression; struct { Token token; AstNode *operand, *selector; } selector_expression; - struct { AstNode *expression, *value; Token open, close; } index_expression; + struct { AstNode *expression, *value; Token open, close; } index_expression; struct { Token token; AstNode *type_expression, *operand; } cast_expression; struct { AstNode *proc, *arg_list; @@ -125,6 +126,12 @@ struct AstNode { Token open, close; } call_expression; struct { Token op; AstNode *operand; } dereference_expression; + struct { + AstNode *expression; + Token open, close; + AstNode *low, *high, *max; + b32 triple_indexed; // [(1st):2nd:3rd] + } slice_expression; struct { Token begin, end; } bad_statement; struct { Token token; } empty_statement; @@ -271,6 +278,8 @@ Token ast_node_token(AstNode *node) { return ast_node_token(node->selector_expression.selector); case AstNode_IndexExpression: return node->index_expression.open; + case AstNode_SliceExpression: + return node->slice_expression.open; case AstNode_CastExpression: return node->cast_expression.token; case AstNode_DereferenceExpression: @@ -439,6 +448,19 @@ gb_inline AstNode *make_index_expression(Parser *p, AstNode *expression, AstNode return result; } + +gb_inline AstNode *make_slice_expression(Parser *p, AstNode *expression, Token open, Token close, AstNode *low, AstNode *high, AstNode *max, b32 triple_indexed) { + AstNode *result = make_node(p, AstNode_SliceExpression); + result->slice_expression.expression = expression; + result->slice_expression.open = open; + result->slice_expression.close = close; + result->slice_expression.low = low; + result->slice_expression.high = high; + result->slice_expression.max = max; + result->slice_expression.triple_indexed = triple_indexed; + return result; +} + gb_inline AstNode *make_cast_expression(Parser *p, Token token, AstNode *type_expression, AstNode *operand) { AstNode *result = make_node(p, AstNode_CastExpression); result->cast_expression.token = token; @@ -865,14 +887,43 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) { if (lhs) { // TODO(bill): Handle this } - AstNode *value; Token open, close; + AstNode *indices[3] = {}; open = expect_token(p, Token_OpenBracket); - value = parse_expression(p, false); + if (p->cursor[0].kind != Token_Colon) + indices[0] = parse_expression(p, false); + isize colon_count = 0; + Token colons[2] = {}; + + while (p->cursor[0].kind == Token_Colon && colon_count < 2) { + colons[colon_count++] = p->cursor[0]; + next_token(p); + if (p->cursor[0].kind != Token_Colon && + p->cursor[0].kind != Token_CloseBracket && + p->cursor[0].kind != Token_EOF) { + indices[colon_count] = parse_expression(p, false); + } + } close = expect_token(p, Token_CloseBracket); - operand = make_index_expression(p, operand, value, open, close); + if (colon_count == 0) { + operand = make_index_expression(p, operand, indices[0], open, close); + } else { + b32 triple_indexed = false; + if (colon_count == 2) { + triple_indexed = true; + if (indices[1] == NULL) { + print_parse_error(p, colons[0], "Second index is required in a triple indexed slice"); + indices[1] = make_bad_expression(p, colons[0], colons[1]); + } + if (indices[2] == NULL) { + print_parse_error(p, colons[1], "Third index is required in a triple indexed slice"); + indices[2] = make_bad_expression(p, colons[1], close); + } + } + operand = make_slice_expression(p, operand, open, close, indices[0], indices[1], indices[2], triple_indexed); + } } break; case Token_Pointer: // Deference diff --git a/src/test.odin b/src/test.odin index c20299578..eeeacb45d 100644 --- a/src/test.odin +++ b/src/test.odin @@ -1,9 +1,18 @@ -type float: f32; +type Vec2: struct { + x, y: f32; +} + + +print_string_array :: proc(args: []string) { + args[0] = ""; +} main :: proc() { thing :: proc(n: int) -> int, f32 { return n*n, 13.37; } - _, _ := 1, 2; + thang :: proc(a: int, b: f32, s: string) { + } + } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 38a6f54fe..073d13e50 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -195,7 +195,7 @@ char const *TOKEN_STRINGS[] = { "_KeywordBegin", "type", "proc", - "switch", + "match", "break", "continue", "fallthrough",