From 25e9b9bc87a5b4fa14fc7d47ca3077849ee5648d Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Fri, 2 Sep 2016 14:14:12 +0100 Subject: [PATCH] min, max, abs --- examples/basic.odin | 2 +- examples/demo.odin | 23 ++---- examples/file.odin | 10 +-- examples/math.odin | 13 +--- examples/runtime.odin | 45 +++++++---- src/checker/checker.cpp | 8 ++ src/checker/expr.cpp | 167 ++++++++++++++++++++++++++++++++++++++-- src/codegen/ssa.cpp | 32 +++++++- src/gb/gb.h | 4 + src/parser.cpp | 78 +++++++++++++++++++ src/tokenizer.cpp | 8 ++ 11 files changed, 335 insertions(+), 55 deletions(-) diff --git a/examples/basic.odin b/examples/basic.odin index 96282ccd8..26c522d4c 100644 --- a/examples/basic.odin +++ b/examples/basic.odin @@ -3,7 +3,7 @@ #load "file.odin" print_string :: proc(s: string) { - file_write(file_get_standard(FileStandard.OUTPUT), s as []byte) + file_write(file_get_standard(File_Standard.OUTPUT), s as []byte) } byte_reverse :: proc(b: []byte) { diff --git a/examples/demo.odin b/examples/demo.odin index 492974e3c..4d2344185 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -4,24 +4,17 @@ main :: proc() { + print_int(min(1, 2)); nl() + print_int(max(1, 2)); nl() + print_int(abs(-1337)); nl() + + a, b, c := 1, 2, -1337 + print_int(min(a, b)); nl() + print_int(max(a, b)); nl() + print_int(abs(c) as int); nl() - match x := "1"; x { - case "1": - print_string("1!\n") - case "2": - print_string("2!\n") - if true { - break - } - case "3", "4": - print_string("3 or 4!\n") - fallthrough - default: - print_string("default!\n") - } nl() - /* Vec3 :: type struct { x, y, z: f32 } Entity :: type struct { diff --git a/examples/file.odin b/examples/file.odin index 5b3aa9f8c..7ee8770e0 100644 --- a/examples/file.odin +++ b/examples/file.odin @@ -34,7 +34,7 @@ file_write :: proc(f: ^File, buf: []byte) -> bool { return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0 } -FileStandard :: type enum { +File_Standard :: type enum { INPUT, OUTPUT, ERROR, @@ -42,12 +42,12 @@ FileStandard :: type enum { } __std_file_set := false; -__std_files: [FileStandard.COUNT as int]File; +__std_files: [File_Standard.COUNT as int]File; -file_get_standard :: proc(std: FileStandard) -> ^File { - // using FileStandard; +file_get_standard :: proc(std: File_Standard) -> ^File { + // using File_Standard; if (!__std_file_set) { - using FileStandard + using File_Standard __std_files[INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE) __std_files[OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE) __std_files[ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE) diff --git a/examples/math.odin b/examples/math.odin index ee0245d4b..d10bcd5d4 100644 --- a/examples/math.odin +++ b/examples/math.odin @@ -29,17 +29,10 @@ fsqrt :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32" fsin :: proc(x: f32) -> f32 #foreign "llvm.sin.f32" fcos :: proc(x: f32) -> f32 #foreign "llvm.cos.f32" flerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t } -fclamp :: proc(x, lower, upper: f32) -> f32 { return fmin(fmax(x, lower), upper) } +fclamp :: proc(x, lower, upper: f32) -> f32 { return min(max(x, lower), upper) } fclamp01 :: proc(x: f32) -> f32 { return fclamp(x, 0, 1) } -fabs :: proc(x: f32) -> f32 { if x < 0 { x = -x } return x } fsign :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 } -fmin :: proc(a, b: f32) -> f32 { if a < b { return a } return b } -fmax :: proc(a, b: f32) -> f32 { if a > b { return a } return b } -fmin3 :: proc(a, b, c: f32) -> f32 { return fmin(fmin(a, b), c) } -fmax3 :: proc(a, b, c: f32) -> f32 { return fmax(fmax(a, b), c) } - - copy_sign :: proc(x, y: f32) -> f32 { ix := x transmute u32 iy := y transmute u32 @@ -76,8 +69,8 @@ remainder :: proc(x, y: f32) -> f32 { } fmod :: proc(x, y: f32) -> f32 { - y = fabs(y) - result := remainder(fabs(x), y) + y = abs(y) + result := remainder(abs(x), y) if fsign(result) < 0 { result += y } diff --git a/examples/runtime.odin b/examples/runtime.odin index 6663819d4..a512a9b8e 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -11,8 +11,15 @@ heap_free :: proc(ptr: rawptr) { } +memory_zero :: proc(data: rawptr, len: int) { + d := slice_ptr(data as ^byte, len) + for i := 0; i < len; i++ { + d[i] = 0 + } +} + memory_compare :: proc(dst, src: rawptr, len: int) -> int { - s1, s2: ^u8 = dst, src + s1, s2: ^byte = dst, src for i := 0; i < len; i++ { a := ptr_offset(s1, i)^ b := ptr_offset(s2, i)^ @@ -31,7 +38,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline { v128b :: type {4}u32 static_assert(align_of(v128b) == 16) - d, s: ^u8 = dst, src + d, s: ^byte = dst, src for ; s as uint % 16 != 0 && n != 0; n-- { d^ = s^ @@ -158,7 +165,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline { } memory_move :: proc(dst, src: rawptr, n: int) #inline { - d, s: ^u8 = dst, src + d, s: ^byte = dst, src if d == s { return } @@ -264,7 +271,7 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > -AllocationMode :: type enum { +Allocation_Mode :: type enum { ALLOC, DEALLOC, DEALLOC_ALL, @@ -273,12 +280,12 @@ AllocationMode :: type enum { -AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode, - size, alignment: int, - old_memory: rawptr, old_size: int, flags: u64) -> rawptr +Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64) -> rawptr Allocator :: type struct { - procedure: AllocatorProc; + procedure: Allocator_Proc; data: rawptr } @@ -309,18 +316,18 @@ alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_AL alloc_align :: proc(size, alignment: int) -> rawptr #inline { __check_context() a := context.allocator - return a.procedure(a.data, AllocationMode.ALLOC, size, alignment, null, 0, 0) + return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0) } dealloc :: proc(ptr: rawptr) #inline { __check_context() a := context.allocator - _ = a.procedure(a.data, AllocationMode.DEALLOC, 0, 0, ptr, 0, 0) + _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0) } dealloc_all :: proc(ptr: rawptr) #inline { __check_context() a := context.allocator - _ = a.procedure(a.data, AllocationMode.DEALLOC_ALL, 0, 0, ptr, 0, 0) + _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0) } @@ -328,7 +335,7 @@ resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { r resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline { __check_context() a := context.allocator - return a.procedure(a.data, AllocationMode.RESIZE, new_size, alignment, ptr, old_size, 0) + return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0) } @@ -355,19 +362,25 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: if new_memory == null { return null } - _ = copy(slice_ptr(new_memory as ^u8, new_size), slice_ptr(old_memory as ^u8, old_size)) + min_size := old_size; + if min_size > new_size { + min_size = new_size; + } + memory_copy(new_memory, old_memory, min_size); dealloc(old_memory) return new_memory } -__default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode, +__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr { - using AllocationMode + using Allocation_Mode match mode { case ALLOC: - return heap_alloc(size) + data := heap_alloc(size) + memory_zero(data, size) + return data case RESIZE: return default_resize_align(old_memory, old_size, size, alignment) case DEALLOC: diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 25bef96de..8dfce197c 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -145,6 +145,10 @@ enum BuiltinProcId { BuiltinProc_ptr_sub, BuiltinProc_slice_ptr, + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + BuiltinProc_Count, }; struct BuiltinProc { @@ -178,6 +182,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, + + {STR_LIT("min"), 2, false, Expr_Expr}, + {STR_LIT("max"), 2, false, Expr_Expr}, + {STR_LIT("abs"), 1, false, Expr_Expr}, }; struct CheckerContext { diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 39fa66e3d..36f3f50fc 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -878,10 +878,11 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac } else if (is_type_string(type)) { return in_value.kind == ExactValue_String; } else if (is_type_integer(type)) { - if (in_value.kind != ExactValue_Integer) + ExactValue v = exact_value_to_integer(in_value); + if (v.kind != ExactValue_Integer) return false; - if (out_value) *out_value = in_value; - i64 i = in_value.value_integer; + if (out_value) *out_value = v; + i64 i = v.value_integer; i64 s = 8*type_size_of(c->sizes, c->allocator, type); u64 umax = ~0ull; if (s < 64) { @@ -1282,7 +1283,6 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { Type *base_type = get_base_type(type); if (is_const_expr && is_type_constant_type(base_type)) { - if (base_type->kind == Type_Basic) { if (check_value_is_expressible(c, x->value, base_type, &x->value)) { can_convert = true; @@ -1295,10 +1295,12 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { if (!can_convert) { gbString expr_str = expr_to_string(x->expr); - gbString type_str = type_to_string(type); + gbString to_type = type_to_string(type); + gbString from_type = type_to_string(x->type); defer (gb_string_free(expr_str)); - defer (gb_string_free(type_str)); - error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s`", expr_str, type_str); + defer (gb_string_free(to_type)); + defer (gb_string_free(from_type)); + error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); x->mode = Addressing_Invalid; return; @@ -2342,6 +2344,157 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; } break; + case BuiltinProc_min: { + // min :: proc(a, b: comparable) -> comparable + Type *type = get_base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + return false; + } + + AstNode *other_arg = ce->arg_list->next; + Operand a = *operand; + Operand b = {}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) + return false; + if (!is_type_comparable(b.type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(b.type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + return false; + } + + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token lt = {Token_Lt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(lt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + if (!are_types_identical(operand->type, b.type)) { + gbString type_a = type_to_string(a.type); + gbString type_b = type_to_string(b.type); + defer (gb_string_free(type_a)); + defer (gb_string_free(type_b)); + error(&c->error_collector, ast_node_token(call), + "Mismatched types to `min`, `%s` vs `%s`", + type_a, type_b); + return false; + } + } + + } break; + + case BuiltinProc_max: { + // min :: proc(a, b: comparable) -> comparable + Type *type = get_base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + return false; + } + + AstNode *other_arg = ce->arg_list->next; + Operand a = *operand; + Operand b = {}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) + return false; + if (!is_type_comparable(b.type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(b.type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + return false; + } + + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token gt = {Token_Gt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(gt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + if (!are_types_identical(operand->type, b.type)) { + gbString type_a = type_to_string(a.type); + gbString type_b = type_to_string(b.type); + defer (gb_string_free(type_a)); + defer (gb_string_free(type_b)); + error(&c->error_collector, ast_node_token(call), + "Mismatched types to `max`, `%s` vs `%s`", + type_a, type_b); + return false; + } + } + + } break; + + case BuiltinProc_abs: { + // abs :: proc(n: numeric) -> numeric + Type *type = get_base_type(operand->type); + if (!is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(&c->error_collector, ast_node_token(call), + "Expected a numeric type to `abs`, got `%s`", + type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + switch (operand->value.kind) { + case ExactValue_Integer: + operand->value.value_integer = gb_abs(operand->value.value_integer); + break; + case ExactValue_Float: + operand->value.value_float = gb_abs(operand->value.value_float); + break; + default: + GB_PANIC("Invalid numeric constant"); + break; + } + } else { + operand->mode = Addressing_Value; + } + + operand->type = type; + } break; + } return true; diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index b2eb6c6a4..077a4f218 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1569,7 +1569,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case_ast_node(i, Ident, expr); Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr)); if (e->kind == Entity_Builtin) { - GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin"); + GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`", LIT(builtin_procs[e->Builtin.id].name)); return NULL; } @@ -2068,6 +2068,36 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int), cap); return ssa_emit_load(proc, slice); } break; + + case BuiltinProc_min: { + ssaValue *x = ssa_build_expr(proc, ce->arg_list); + ssaValue *y = ssa_build_expr(proc, ce->arg_list->next); + Type *t = get_base_type(ssa_type(x)); + Token lt = {Token_Lt}; + ssaValue *cond = ssa_emit_comp(proc, lt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_max: { + ssaValue *x = ssa_build_expr(proc, ce->arg_list); + ssaValue *y = ssa_build_expr(proc, ce->arg_list->next); + Type *t = get_base_type(ssa_type(x)); + Token gt = {Token_Gt}; + ssaValue *cond = ssa_emit_comp(proc, gt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_abs: { + Token lt = {Token_Lt}; + Token sub = {Token_Sub}; + + ssaValue *x = ssa_build_expr(proc, ce->arg_list); + Type *t = ssa_type(x); + + ssaValue *neg_x = ssa_emit_arith(proc, sub, v_zero, x, t); + ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero); + return ssa_emit_select(proc, cond, neg_x, x); + } break; } } } diff --git a/src/gb/gb.h b/src/gb/gb.h index 94671e1fd..fb2681b34 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -702,6 +702,10 @@ extern "C++" { #define gb_is_between(x, lower, upper) (((x) >= (lower)) && ((x) <= (upper))) #endif +#ifndef gb_abs +#define gb_abs(x) ((x) < 0 ? -(x) : (x)) +#endif + /* NOTE(bill): Very useful bit setting */ #ifndef GB_MASK_SET #define GB_MASK_SET(var, set, mask) do { \ diff --git a/src/parser.cpp b/src/parser.cpp index 4a5a8d63e..478e1377a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,6 +164,20 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \ AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \ + AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ + Token string; \ + AstNode *operand; \ + }) \ + AST_NODE_KIND(AsmStmt, "assembly statement", struct { \ + Token token; \ + b32 is_volatile; \ + Token open, close; \ + Token code_string; \ + AstNode *output_list; \ + AstNode *input_list; \ + AstNode *clobber_list; \ + isize output_count, input_count, clobber_count; \ + }) \ \ AST_NODE_KIND(_ComplexStmtEnd, "", struct{}) \ AST_NODE_KIND(_StmtEnd, "", struct{}) \ @@ -348,6 +362,8 @@ Token ast_node_token(AstNode *node) { return node->BranchStmt.token; case AstNode_UsingStmt: return node->UsingStmt.token; + case AstNode_AsmStmt: + return node->AsmStmt.token; case AstNode_BadDecl: return node->BadDecl.begin; case AstNode_VarDecl: @@ -684,6 +700,35 @@ gb_inline AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) { return result; } +gb_inline AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) { + AstNode *result = make_node(f, AstNode_AsmOperand); + result->AsmOperand.string = string; + result->AsmOperand.operand = operand; + return result; + +} + +gb_inline AstNode *make_asm_stmt(AstFile *f, Token token, b32 is_volatile, Token open, Token close, Token code_string, + AstNode *output_list, AstNode *input_list, AstNode *clobber_list, + isize output_count, isize input_count, isize clobber_count) { + AstNode *result = make_node(f, AstNode_AsmStmt); + result->AsmStmt.token = token; + result->AsmStmt.is_volatile = is_volatile; + result->AsmStmt.open = open; + result->AsmStmt.close = close; + result->AsmStmt.code_string = code_string; + result->AsmStmt.output_list = output_list; + result->AsmStmt.input_list = input_list; + result->AsmStmt.clobber_list = clobber_list; + result->AsmStmt.output_count = output_count; + result->AsmStmt.input_count = input_count; + result->AsmStmt.clobber_count = clobber_count; + return result; +} + + + + gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) { AstNode *result = make_node(f, AstNode_BadDecl); @@ -2219,6 +2264,37 @@ AstNode *parse_defer_stmt(AstFile *f) { return make_defer_stmt(f, token, statement); } +AstNode *parse_asm_stmt(AstFile *f) { + Token token = expect_token(f, Token_asm); + b32 is_volatile = false; + if (allow_token(f, Token_volatile)) { + is_volatile = true; + } + Token open, close, code_string; + open = expect_token(f, Token_OpenBrace); + code_string = expect_token(f, Token_String); + AstNode *output_list = NULL; + AstNode *input_list = NULL; + AstNode *clobber_list = NULL; + isize output_count = 0; + isize input_count = 0; + isize clobber_count = 0; + + // TODO(bill): Finish asm statement and determine syntax + + // if (f->cursor[0].kind != Token_CloseBrace) { + // expect_token(f, Token_Colon); + // } + + close = expect_token(f, Token_CloseBrace); + + return make_asm_stmt(f, token, is_volatile, open, close, code_string, + output_list, input_list, clobber_list, + output_count, input_count, clobber_count); + +} + + AstNode *parse_stmt(AstFile *f) { AstNode *s = NULL; @@ -2246,6 +2322,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_for: return parse_for_stmt(f); case Token_match: return parse_match_stmt(f); case Token_defer: return parse_defer_stmt(f); + case Token_asm: return parse_asm_stmt(f); case Token_break: case Token_continue: @@ -2255,6 +2332,7 @@ AstNode *parse_stmt(AstFile *f) { expect_semicolon_after_stmt(f, s); return s; + case Token_using: { AstNode *node = NULL; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 9d9351923..23ba6abd2 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -78,6 +78,7 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \ TOKEN_KIND(Token_Period, "."), \ TOKEN_KIND(Token_Comma, ","), \ TOKEN_KIND(Token_Ellipsis, ".."), \ + TOKEN_KIND(Token_RangeExclusive, "..<"), \ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \ \ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ @@ -93,6 +94,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ TOKEN_KIND(Token_for, "for"), \ + TOKEN_KIND(Token_range, "range"), \ TOKEN_KIND(Token_defer, "defer"), \ TOKEN_KIND(Token_return, "return"), \ TOKEN_KIND(Token_struct, "struct"), \ @@ -100,6 +102,8 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_raw_union, "raw_union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_using, "using"), \ + TOKEN_KIND(Token_asm, "asm"), \ + TOKEN_KIND(Token_volatile, "volatile"), \ TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \ TOKEN_KIND(Token_Count, "") @@ -711,6 +715,10 @@ Token tokenizer_get_token(Tokenizer *t) { } else if (t->curr_rune == '.') { // Could be an ellipsis advance_to_next_rune(t); token.kind = Token_Ellipsis; + if (t->curr_rune == '<') { + advance_to_next_rune(t); + token.kind = Token_RangeExclusive; + } } break;