From be12f12c3c2872fd89021af16c2ab838277c4c4a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 00:57:10 +0100 Subject: Fix #814 --- src/check_stmt.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a3c9a529c..9ca53c4fc 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -960,9 +960,44 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand b1 = rhs; check_comparison(ctx, &a1, &b1, Token_LtEq); - add_constant_switch_case(ctx, &seen, lhs); - if (upper_op == Token_GtEq) { - add_constant_switch_case(ctx, &seen, rhs); + if (is_type_enum(x.type)) { + // TODO(bill): Fix this logic so it's fast!!! + + i64 v0 = exact_value_to_i64(lhs.value); + i64 v1 = exact_value_to_i64(rhs.value); + Operand v = {}; + v.mode = Addressing_Constant; + v.type = x.type; + v.expr = x.expr; + + Type *bt = base_type(x.type); + GB_ASSERT(bt->kind == Type_Enum); + for (i64 vi = v0; vi <= v1; vi++) { + if (upper_op != Token_GtEq && vi == v1) { + break; + } + + bool found = false; + for_array(j, bt->Enum.fields) { + Entity *f = bt->Enum.fields[j]; + GB_ASSERT(f->kind == Entity_Constant); + + i64 fv = exact_value_to_i64(f->Constant.value); + if (fv == vi) { + found = true; + break; + } + } + if (found) { + v.value = exact_value_i64(vi); + add_constant_switch_case(ctx, &seen, v); + } + } + } else { + add_constant_switch_case(ctx, &seen, lhs); + if (upper_op == Token_GtEq) { + add_constant_switch_case(ctx, &seen, rhs); + } } if (is_type_string(x.type)) { -- cgit v1.2.3 From 7b7081d60733caa996a89be2651482a2aeed8bbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:59:54 +0100 Subject: Remove old dead code --- src/check_decl.cpp | 11 --- src/check_expr.cpp | 72 ------------------- src/check_stmt.cpp | 47 ------------ src/check_type.cpp | 13 ---- src/checker.cpp | 200 --------------------------------------------------- src/checker.hpp | 1 - src/llvm_backend.cpp | 51 ------------- src/parser.hpp | 1 - src/types.cpp | 19 ----- 9 files changed, 415 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index baabe4184..5e8e79791 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -289,17 +289,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) if (decl != nullptr) { AttributeContext ac = {}; check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); - if (ac.atom_op_table != nullptr) { - Type *bt = base_type(e->type); - switch (bt->kind) { - case Type_Struct: - bt->Struct.atom_op_table = ac.atom_op_table; - break; - default: - error(e->token, "Only struct types can have custom atom operations"); - break; - } - } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8cc5aa894..fb3a51415 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7573,47 +7573,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return Expr_Expr; } - if (t->kind == Type_Struct) { - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - if (atom_op_table != nullptr) { - if (atom_op_table->op[TypeAtomOp_index_set]) { - if (c->assignment_lhs_hint == node) { - o->mode = Addressing_AtomOpAssign; - o->type = o->type; - o->expr = node; - return kind; - } - } - if (atom_op_table->op[TypeAtomOp_index_get]) { - Entity *e = atom_op_table->op[TypeAtomOp_index_get]; - if (ie->index == nullptr) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Missing index for '%s'", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - - auto args = array_make(heap_allocator(), 2); - args[0] = ie->expr; - args[1] = ie->index; - - GB_ASSERT(c->file != nullptr); - Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {}); - check_expr_base(c, o, fake_call, type_hint); - AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call}; - map_set(&c->info->atom_op_map, hash_pointer(node), entry); - o->expr = node; - return kind; - } - } - } - - i64 max_count = -1; bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); @@ -7752,37 +7711,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_soa_struct(t)) { valid = true; o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); - } else { - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - Entity *e = atom_op_table->op[TypeAtomOp_slice]; - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - - Ast *expr = se->expr; - if (o->mode == Addressing_Variable) { - expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr); - } else if (is_type_pointer(o->type)) { - // Okay - } else { - gbString str = expr_to_string(node); - error(node, "Cannot slice '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - auto args = array_make(heap_allocator(), 1); - args[0] = expr; - - - GB_ASSERT(c->file != nullptr); - Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {}); - check_expr_base(c, o, fake_call, type_hint); - AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call}; - map_set(&c->info->atom_op_map, hash_pointer(node), entry); - valid = true; - } } break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 9ca53c4fc..a954b44b6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1489,53 +1489,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { auto lhs_to_ignore = array_make(temporary_allocator(), lhs_count); isize max = gb_min(lhs_count, rhs_count); - // NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working - // correctly for assignments - for (isize i = 0; i < max; i++) { - if (lhs_operands[i].mode == Addressing_AtomOpAssign) { - Operand lhs = lhs_operands[i]; - - Type *t = base_type(lhs.type); - GB_ASSERT(t->kind == Type_Struct); - ast_node(ie, IndexExpr, unparen_expr(lhs.expr)); - - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr); - Entity *e = atom_op_table->op[TypeAtomOp_index_set]; - - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - GB_ASSERT(ctx->file != nullptr); - - - TypeAndValue tv = type_and_value_of_expr(ie->expr); - Ast *expr = ie->expr; - if (is_type_pointer(tv.type)) { - // Okay - } else if (tv.mode == Addressing_Variable) { - // NOTE(bill): Hack it to take the address instead - expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr); - } else { - continue; - } - - auto args = array_make(heap_allocator(), 3); - args[0] = expr; - args[1] = ie->index; - args[2] = rhs_operands[i].expr; - - Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {}); - Operand fake_operand = {}; - fake_operand.expr = lhs.expr; - check_expr_base(ctx, &fake_operand, fake_call, nullptr); - AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call}; - map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry); - - lhs_to_ignore[i] = true; - - } - } - for (isize i = 0; i < max; i++) { if (lhs_to_ignore[i]) { continue; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0e77792b7..cdce6dae8 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -322,19 +322,6 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t array_add(&array, e); map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array); } - - { - Type *dst_bt = base_type(named_type); - Type *src_bt = base_type(original_type); - if ((dst_bt != nullptr && src_bt != nullptr) && - (dst_bt->kind == src_bt->kind)){ - if (dst_bt->kind == Type_Struct) { - if (dst_bt->Struct.atom_op_table == nullptr) { - dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table; - } - } - } - } } Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params, diff --git a/src/checker.cpp b/src/checker.cpp index 8016f1020..21ca4c398 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2666,206 +2666,6 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { if (name == "private") { // NOTE(bill): Handled elsewhere `check_collect_value_decl` return true; - } else if (name == "index_get") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.result_count == 0) { - error(value, "'%s' attribute must return something", LIT(name)); - valid = false; - } - - if (pt->Proc.param_count < 2) { - error(value, "'%s' attribute must allow for 2 parameters", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 2) { - error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name)); - valid = false; - } - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_index_get] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; - } else if (name == "index_set") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.param_count < 3) { - error(value, "'%s' attribute must allow for 3 parameters", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 3) { - error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name)); - valid = false; - } - } - - if (pt->Proc.variadic || pt->Proc.c_vararg) { - error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); - valid = false; - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_index_set] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; - } else if (name == "slice") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.param_count < 1) { - error(value, "'%s' attribute must allow for 1 parameter", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 1) { - error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name)); - valid = false; - } - { - Entity *param = pt->Proc.params->Tuple.variables[0]; - Type *param_type = base_type(param->type); - if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) { - // okay - } else { - error(value, "'%s' attribute's first parameter must be a pointer", LIT(name)); - valid = false; - } - - } - } - - if (pt->Proc.variadic || pt->Proc.c_vararg) { - error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); - valid = false; - } - - if (pt->Proc.result_count != 1) { - error(value, "'%s' attribute must return 1 result", LIT(name)); - valid = false; - } else { - Type *rt = pt->Proc.results->Tuple.variables[0]->type; - rt = base_type(rt); - if (!is_type_slice(rt)) { - error(value, "'%s' attribute must return a slice", LIT(name)); - valid = false; - } - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_slice] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 41b6e8c42..38628ed51 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -114,7 +114,6 @@ struct AttributeContext { String deprecated_message; DeferredProcedure deferred_procedure; u32 optimization_mode; // ProcedureOptimizationMode - struct TypeAtomOpTable *atom_op_table; }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ce633b127..240e33c80 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12147,27 +12147,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return lb_addr(val); } - if (!is_type_indexable(t)) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found != nullptr) { - if (found->kind == TypeAtomOp_index_get) { - return lb_build_addr(p, found->node); - } else if (found->kind == TypeAtomOp_index_get_ptr) { - return lb_addr(lb_build_expr(p, found->node)); - } else if (found->kind == TypeAtomOp_index_set) { - lbValue ptr = lb_build_addr_ptr(p, ie->expr); - if (deref) { - ptr = lb_emit_load(p, ptr); - } - - lbAddr addr = {lbAddr_AtomOp_index_set}; - addr.addr = ptr; - addr.index_set.index = lb_build_expr(p, ie->index); - addr.index_set.node = found->node; - return addr; - } - } - } GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { @@ -12312,36 +12291,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { bool no_indices = se->low == nullptr && se->high == nullptr; - { - Type *type = base_type(type_of_expr(se->expr)); - if (type->kind == Type_Struct && !is_type_soa_struct(type)) { - TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found) { - lbValue base = lb_build_expr(p, found->node); - - Type *slice_type = base.type; - lbValue len = lb_slice_len(p, base); - if (high.value == nullptr) high = len; - - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); - } - - - lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } - } - } - } - - lbAddr addr = lb_build_addr(p, se->expr); lbValue base = lb_addr_load(p, addr); Type *type = base_type(base.type); diff --git a/src/parser.hpp b/src/parser.hpp index b6bf42f63..77d5f1b02 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -22,7 +22,6 @@ enum AddressingMode { Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check) Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable - Addressing_AtomOpAssign = 12, // Specialized for custom atom operations for assignments }; struct TypeAndValue { diff --git a/src/types.cpp b/src/types.cpp index ffdb90c03..8a78e08d1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -128,21 +128,6 @@ enum StructSoaKind { StructSoa_Dynamic = 3, }; -enum TypeAtomOpKind { - TypeAtomOp_Invalid, - - TypeAtomOp_index_get, - TypeAtomOp_index_set, - TypeAtomOp_slice, - TypeAtomOp_index_get_ptr, - - TypeAtomOp_COUNT, -}; - -struct TypeAtomOpTable { - Entity *op[TypeAtomOp_COUNT]; -}; - struct TypeStruct { Array fields; Array tags; @@ -156,8 +141,6 @@ struct TypeStruct { i64 custom_align; Entity * names; - TypeAtomOpTable *atom_op_table; - Type * soa_elem; i64 soa_count; StructSoaKind soa_kind; @@ -180,8 +163,6 @@ struct TypeUnion { Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; - TypeAtomOpTable *atom_op_table; - bool no_nil; bool maybe; bool is_polymorphic; -- cgit v1.2.3 From ce08e832f7dcdeeae37cf0e432648efa2f27f2a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:34:35 +0100 Subject: Allow `..=` alongside `..` as a "full range" operator; Update `core:odin/parser` etc --- core/odin/parser/parser.odin | 66 ++++++++++++++++++++++++++++++++++---- core/odin/tokenizer/token.odin | 2 ++ core/odin/tokenizer/tokenizer.odin | 3 ++ src/check_expr.cpp | 7 ++-- src/check_stmt.cpp | 1 + src/check_type.cpp | 1 + src/llvm_backend.cpp | 6 ++-- src/parser.cpp | 18 ++++++----- src/tokenizer.cpp | 4 +++ 9 files changed, 89 insertions(+), 19 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 160eb3a5c..661c46751 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -12,6 +12,10 @@ Parser :: struct { file: ^ast.File, tok: tokenizer.Tokenizer, + // If optional_semicolons is true, semicolons are completely as statement terminators + // different to .Insert_Semicolon in tok.flags + optional_semicolons: bool, + warn: Warning_Handler, err: Error_Handler, @@ -128,6 +132,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.line_comment = nil; } + if p.optional_semicolons { + p.tok.flags += {.Insert_Semicolon}; + } + p.file = file; tokenizer.init(&p.tok, file.src, file.fullpath, p.err); if p.tok.ch <= 0 { @@ -400,6 +408,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { if node == nil { return false; } + + if p.optional_semicolons { + return true; + } + switch n in node.derived { case ast.Empty_Stmt, ast.Block_Stmt: return true; @@ -439,14 +452,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return false; } +expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) { + if !p.optional_semicolons && .Insert_Semicolon in p.tok.flags && token.text == "\n" { + #partial switch token.kind { + case .Close_Brace: + case .Close_Paren: + case .Else: + return; + } + if is_semicolon_optional_for_node(p, s) { + return; + } + + tok := token; + tok.pos.column -= 1; + error(p, tok.pos, "expected ';', got newline"); + } +} + expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { if allow_token(p, .Semicolon) { + expect_semicolon_newline_error(p, p.prev_tok, node); return true; } prev := p.prev_tok; if prev.kind == .Semicolon { + expect_semicolon_newline_error(p, p.prev_tok, node); return true; } @@ -615,7 +648,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { cond = parse_expr(p, false); } else { init = parse_simple_stmt(p, nil); - if allow_token(p, .Semicolon) { + if parse_control_statement_semicolon_separator(p) { cond = parse_expr(p, false); } else { cond = convert_stmt_to_expr(p, init, "boolean expression"); @@ -668,6 +701,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { return if_stmt; } +parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool { + tok := peek_token(p); + if tok.kind != .Open_Brace { + return allow_token(p, .Semicolon); + } + if tok.text == ";" { + return allow_token(p, .Semicolon); + } + return false; + +} + parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if p.curr_proc == nil { error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope"); @@ -716,7 +761,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } } - if !is_range && allow_token(p, .Semicolon) { + if !is_range && parse_control_statement_semicolon_separator(p) { init = cond; cond = nil; if p.curr_tok.kind != .Semicolon { @@ -820,7 +865,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { is_type_switch = true; - } else if allow_token(p, .Semicolon) { + } else if parse_control_statement_semicolon_separator(p) { init = tag; tag = nil; if p.curr_tok.kind != .Open_Brace { @@ -831,6 +876,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } + skip_possible_newline(p); open := expect_token(p, .Open_Brace); for p.curr_tok.kind == .Case { @@ -958,6 +1004,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl defer p.in_foreign_block = prev_in_foreign_block; p.in_foreign_block = true; + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { decl := parse_foreign_block_decl(p); @@ -1287,7 +1334,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int { #partial switch kind { case .Question, .If, .When: return 1; - case .Ellipsis, .Range_Half: + case .Ellipsis, .Range_Half, .Range_Full: if !p.allow_range { return 0; } @@ -2233,6 +2280,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } body: ^ast.Stmt; + skip_possible_newline_for_literal(p); + if allow_token(p, .Undef) { body = nil; if where_token.kind != .Invalid { @@ -2405,6 +2454,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { p.expr_level = where_prev_level; } + skip_possible_newline_for_literal(p); expect_token(p, .Open_Brace); fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct); close := expect_token(p, .Close_Brace); @@ -2473,6 +2523,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { variants: [dynamic]^ast.Expr; + skip_possible_newline_for_literal(p); expect_token_after(p, .Open_Brace, "union"); for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { @@ -2503,6 +2554,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { if p.curr_tok.kind != .Open_Brace { base_type = parse_type(p); } + + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); fields := parse_elem_list(p); close := expect_token(p, .Close_Brace); @@ -2601,6 +2654,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } } + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); asm_string := parse_expr(p, false); expect_token(p, .Comma); @@ -2811,7 +2865,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a open := expect_token(p, .Open_Bracket); #partial switch p.curr_tok.kind { - case .Colon, .Ellipsis, .Range_Half: + case .Colon, .Ellipsis, .Range_Half, .Range_Full: // NOTE(bill): Do not err yet break; case: @@ -2819,7 +2873,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a } #partial switch p.curr_tok.kind { - case .Ellipsis, .Range_Half: + case .Ellipsis, .Range_Half, .Range_Full: error(p, p.curr_tok.pos, "expected a colon, not a range"); fallthrough; case .Colon: diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 1b37bae23..88908d7f8 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -107,6 +107,7 @@ Token_Kind :: enum u32 { Comma, // , Ellipsis, // .. Range_Half, // ..< + Range_Full, // ..= Back_Slash, // \ B_Operator_End, @@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string { ",", "..", "..<", + "..=", "\\", "", diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 58f546191..297f42e3d 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token { if t.ch == '<' { advance_rune(t); kind = .Range_Half; + } else if t.ch == '=' { + advance_rune(t); + kind = .Range_Full; } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fb3a51415..61cfd7d6e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5940,8 +5940,9 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu TokenKind op = Token_Lt; switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; + case Token_Ellipsis: op = Token_LtEq; break; // .. + case Token_RangeFull: op = Token_LtEq; break; // ..= + case Token_RangeHalf: op = Token_Lt; break; // ..< default: error(ie->op, "Invalid range operator"); break; } bool ok = compare_exact_values(op, a, b); @@ -5952,7 +5953,7 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu } ExactValue inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { + if (ie->op.kind != Token_RangeHalf) { inline_for_depth = exact_value_increment_one(inline_for_depth); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a954b44b6..2c9d8e00e 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -939,6 +939,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { TokenKind upper_op = Token_Invalid; switch (be->op.kind) { case Token_Ellipsis: upper_op = Token_GtEq; break; + case Token_RangeFull: upper_op = Token_GtEq; break; case Token_RangeHalf: upper_op = Token_Gt; break; default: GB_PANIC("Invalid range operator"); break; } diff --git a/src/check_type.cpp b/src/check_type.cpp index cdce6dae8..24a3e0a59 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -931,6 +931,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no switch (be->op.kind) { case Token_Ellipsis: + case Token_RangeFull: if (upper - lower >= bits) { error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7bdc8b04a..41b1e8fc9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4041,6 +4041,7 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, TokenKind op = Token_Lt; switch (node->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } @@ -4454,7 +4455,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s ExactValue start = start_expr->tav.value; ExactValue end = end_expr->tav.value; - if (op == Token_Ellipsis) { // .. [start, end] + if (op != Token_RangeHalf) { // .. [start, end] (or ..=) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_LtEq, val, end); @@ -4465,7 +4466,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s lb_build_stmt(p, rs->body); } - } else if (op == Token_RangeHalf) { // ..< [start, end) + } else { // ..< [start, end) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_Lt, val, end); @@ -4632,6 +4633,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) { TokenKind op = Token_Invalid; switch (ie->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } diff --git a/src/parser.cpp b/src/parser.cpp index 0dae732e5..2e27b8698 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1344,6 +1344,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) { bool is_token_range(TokenKind kind) { switch (kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: return true; } @@ -1574,6 +1575,10 @@ void expect_semicolon(AstFile *f, Ast *s) { return; } + if (f->curr_token.kind == Token_EOF) { + return; + } + if (s != nullptr) { bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0; if (insert_semi) { @@ -2315,7 +2320,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { f->expr_level = prev_level; } - + skip_possible_newline_for_literal(f); Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -2675,6 +2680,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { switch (f->curr_token.kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: // NOTE(bill): Do not err yet case Token_Colon: @@ -2686,6 +2692,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { switch (f->curr_token.kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: syntax_error(f->curr_token, "Expected a colon, not a range"); /* fallthrough */ @@ -2812,6 +2819,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_when: return 1; case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: if (!f->allow_range) { return 0; @@ -3926,12 +3934,6 @@ Ast *parse_return_stmt(AstFile *f) { while (f->curr_token.kind != Token_Semicolon) { Ast *arg = parse_expr(f, false); - // if (f->curr_token.kind == Token_Eq) { - // Token eq = expect_token(f, Token_Eq); - // Ast *value = parse_value(f); - // arg = ast_field_value(f, arg, value, eq); - // } - array_add(&results, arg); if (f->curr_token.kind != Token_Comma || f->curr_token.kind == Token_EOF) { @@ -4052,7 +4054,7 @@ Ast *parse_case_clause(AstFile *f, bool is_type) { } f->allow_range = prev_allow_range; f->allow_in_expr = prev_allow_in_expr; - expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + expect_token(f, Token_Colon); Array stmts = parse_stmt_list(f); return ast_case_clause(f, token, list, stmts); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 5936ac3fe..d1310b56e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -76,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \ TOKEN_KIND(Token_Period, "."), \ TOKEN_KIND(Token_Comma, ","), \ TOKEN_KIND(Token_Ellipsis, ".."), \ + TOKEN_KIND(Token_RangeFull, "..="), \ TOKEN_KIND(Token_RangeHalf, "..<"), \ TOKEN_KIND(Token_BackSlash, "\\"), \ TOKEN_KIND(Token__OperatorEnd, ""), \ @@ -1204,6 +1205,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { if (t->curr_rune == '<') { advance_to_next_rune(t); token->kind = Token_RangeHalf; + } else if (t->curr_rune == '=') { + advance_to_next_rune(t); + token->kind = Token_RangeFull; } } else if ('0' <= t->curr_rune && t->curr_rune <= '9') { scan_number_to_token(t, token, true); -- cgit v1.2.3 From 6ef96d33003d2fbdedd283ea432e70afc2f1d7ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 14:44:02 +0100 Subject: Improve untyped to typed logic for aiding the backend --- src/check_expr.cpp | 23 ++++++++++++++++++-- src/check_stmt.cpp | 6 +++++- src/llvm_backend.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 10 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4a37b2903..68f05084e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2216,6 +2216,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ return; } + if (is_type_untyped(y->type)) { + convert_to_typed(c, y, t_uint); + } + x->mode = Addressing_Value; } @@ -2886,7 +2890,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { if (token_is_comparison(be->op.kind)) { // NOTE(bill): Do nothing as the types are fine } else if (token_is_shift(be->op.kind)) { - update_expr_type(c, be->left, type, final); + update_expr_type(c, be->left, type, final); } else { update_expr_type(c, be->left, type, final); update_expr_type(c, be->right, type, final); @@ -3200,8 +3204,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { break; } - operand->type = target_type; update_expr_type(c, operand->expr, target_type, true); + operand->type = target_type; } bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) { @@ -4108,6 +4112,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } +bool is_expr_constant_zero(Ast *expr) { + GB_ASSERT(expr != nullptr); + auto v = exact_value_to_integer(expr->tav.value); + if (v.kind == ExactValue_Integer) { + return big_int_cmp_zero(&v.value_integer) == 0; + } + return false; +} + + CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); @@ -4299,7 +4313,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (o.mode == Addressing_Type && is_type_typeid(e->type)) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_expr_type(c, o.expr, t, true); } + } if (variadic) { @@ -4347,6 +4364,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (o.mode == Addressing_Type && is_type_typeid(t)) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_expr_type(c, o.expr, t, true); } } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c9d8e00e..ee60a4acd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1621,7 +1621,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } else { for (isize i = 0; i < result_count; i++) { Entity *e = pt->results->Tuple.variables[i]; - check_assignment(ctx, &operands[i], e->type, str_lit("return statement")); + Operand *o = &operands[i]; + check_assignment(ctx, o, e->type, str_lit("return statement")); + if (is_type_untyped(o->type)) { + update_expr_type(ctx, o->expr, e->type, true); + } } } case_end; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c46c4fd85..a15b6a172 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3409,6 +3409,20 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals LLVMBuildCondBr(p->builder, cv, true_block->block, false_block->block); } +bool lb_is_expr_untyped_const(Ast *expr) { + auto const &tv = type_and_value_of_expr(expr); + if (is_type_untyped(tv.type)) { + return tv.value.kind != ExactValue_Invalid; + } + return false; +} + +lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) { + GB_ASSERT(is_type_typed(t)); + auto const &tv = type_and_value_of_expr(expr); + return lb_const_value(m, t, tv.value); +} + lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block) { GB_ASSERT(cond != nullptr); GB_ASSERT(true_block != nullptr); @@ -3440,8 +3454,13 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f case_end; } - lbValue v = lb_build_expr(p, cond); - // v = lb_emit_conv(p, v, t_bool); + lbValue v = {}; + if (lb_is_expr_untyped_const(cond)) { + v = lb_expr_untyped_const_to_typed(p->module, cond, t_llvm_bool); + } else { + v = lb_build_expr(p, cond); + } + v = lb_emit_conv(p, v, t_llvm_bool); lb_emit_if(p, v, true_block, false_block); @@ -4872,6 +4891,9 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast if (done->preds.count == 0) { lb_start_block(p, rhs); + if (lb_is_expr_untyped_const(right)) { + return lb_expr_untyped_const_to_typed(m, right, type); + } return lb_build_expr(p, right); } @@ -4886,7 +4908,12 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast } lb_start_block(p, rhs); - lbValue edge = lb_build_expr(p, right); + lbValue edge = {}; + if (lb_is_expr_untyped_const(right)) { + edge = lb_expr_untyped_const_to_typed(m, right, type); + } else { + edge = lb_build_expr(p, right); + } incoming_values[done->preds.count] = edge.value; incoming_blocks[done->preds.count] = p->curr_block->block; @@ -7010,15 +7037,29 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_And: case Token_Or: case Token_Xor: - case Token_AndNot: - case Token_Shl: - case Token_Shr: { + case Token_AndNot: { Type *type = default_type(tv.type); lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); return lb_emit_arith(p, be->op.kind, left, right, type); } + case Token_Shl: + case Token_Shr: { + lbValue left, right; + Type *type = default_type(tv.type); + left = lb_build_expr(p, be->left); + + if (lb_is_expr_untyped_const(be->right)) { + // NOTE(bill): RHS shift operands can still be untyped + // Just bypass the standard lb_build_expr + right = lb_expr_untyped_const_to_typed(p->module, be->right, type); + } else { + right = lb_build_expr(p, be->right); + } + return lb_emit_arith(p, be->op.kind, left, right, type); + } + case Token_CmpEq: case Token_NotEq: case Token_Lt: @@ -11385,8 +11426,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type)); if (tv.value.kind != ExactValue_Invalid) { + // NOTE(bill): The commented out code below is just for debug purposes only + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s\n%s", type_to_string(tv.type), token_pos_to_string(expr_pos), expr_to_string(expr)); + // if (is_type_untyped(tv.type)) { + // gb_printf_err("%s %s\n", token_pos_to_string(expr_pos), expr_to_string(expr)); + // } + // NOTE(bill): Short on constant values - // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s", type_to_string(tv.type), token_pos_to_string(expr_pos)); return lb_const_value(p->module, tv.type, tv.value); } -- cgit v1.2.3 From 3ac934dd15b59be670627213643e80c6d902fe4d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:58:02 +0100 Subject: Add suggestion for unused expression on `x == y` Expression is not used: 'x == 123' Suggestion: Did you mean to do an assignment? 'x = 123;' --- src/check_stmt.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee60a4acd..a62b55ab2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1436,6 +1436,28 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); gb_string_free(expr_str); + if (operand.expr->kind == Ast_BinaryExpr) { + ast_node(be, BinaryExpr, operand.expr); + if (be->op.kind != Token_CmpEq) { + break; + } + + switch (be->left->tav.mode) { + case Addressing_Context: + case Addressing_Variable: + case Addressing_MapIndex: + case Addressing_SoaVariable: + { + gbString lhs = expr_to_string(be->left); + gbString rhs = expr_to_string(be->right); + error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs); + error_line("\t '%s = %s;'\n", lhs, rhs); + gb_string_free(rhs); + gb_string_free(lhs); + } + break; + } + } break; } -- cgit v1.2.3 From 9c54ed57924bf8ff241b0bffebaeabfa541db24c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 14:15:57 +0100 Subject: Add range-based error messages to `-verbose-errors` Example: Cannot convert '(1 + 2)' to 'untyped bool' from 'untyped integer' x := (1 + 2) * true; ^~~~~~^ --- src/check_builtin.cpp | 4 +- src/check_expr.cpp | 17 +-- src/check_stmt.cpp | 2 +- src/check_type.cpp | 10 +- src/llvm_backend.cpp | 4 +- src/parser.cpp | 158 ++++++------------------ src/parser.hpp | 6 +- src/parser_pos.cpp | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tokenizer.cpp | 64 ++++++---- 9 files changed, 429 insertions(+), 167 deletions(-) create mode 100644 src/parser_pos.cpp (limited to 'src/check_stmt.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c9b911f92..1acb9732f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -87,7 +87,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; if (name == "defined") { break; } @@ -124,7 +124,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; if (name == "location") { if (ce->args.count > 1) { error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 68f05084e..81fe3baa9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5516,7 +5516,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); - String name = bd->name; + String name = bd->name.string; if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -6191,13 +6191,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bd, BasicDirective, node); o->mode = Addressing_Constant; - if (bd->name == "file") { + String name = bd->name.string; + if (name == "file") { o->type = t_untyped_string; o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); - } else if (bd->name == "line") { + } else if (name == "line") { o->type = t_untyped_integer; o->value = exact_value_i64(bd->token.pos.line); - } else if (bd->name == "procedure") { + } else if (name == "procedure") { if (c->curr_proc_decl == nullptr) { error(node, "#procedure may only be used within procedures"); o->type = t_untyped_string; @@ -6206,7 +6207,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->type = t_untyped_string; o->value = exact_value_string(c->proc_name); } - } else if (bd->name == "caller_location") { + } else if (name == "caller_location") { init_core_source_code_location(c->checker); error(node, "#caller_location may only be used as a default argument parameter"); o->type = t_source_code_location; @@ -6373,7 +6374,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->type->ArrayType.tag != nullptr) { Ast *tag = cl->type->ArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name; + String name = tag->BasicDirective.name.string; if (name == "soa") { error(node, "#soa arrays are not supported for compound literals"); return kind; @@ -6385,7 +6386,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->elems.count > 0) { Ast *tag = cl->type->DynamicArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name; + String name = tag->BasicDirective.name.string; if (name == "soa") { error(node, "#soa arrays are not supported for compound literals"); return kind; @@ -8151,7 +8152,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { case_ast_node(bd, BasicDirective, node); str = gb_string_append_rune(str, '#'); - str = string_append_string(str, bd->name); + str = string_append_string(str, bd->name.string); case_end; case_ast_node(ud, Undef, node); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a62b55ab2..64d17a8c8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -7,7 +7,7 @@ bool is_diverging_stmt(Ast *stmt) { return false; } if (expr->CallExpr.proc->kind == Ast_BasicDirective) { - String name = expr->CallExpr.proc->BasicDirective.name; + String name = expr->CallExpr.proc->BasicDirective.name.string; return name == "panic"; } Ast *proc = unparen_expr(expr->CallExpr.proc); diff --git a/src/check_type.cpp b/src/check_type.cpp index 24a3e0a59..419904876 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1191,7 +1191,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * if (allow_caller_location && expr->kind == Ast_BasicDirective && - expr->BasicDirective.name == "caller_location") { + expr->BasicDirective.name.string == "caller_location") { init_core_source_code_location(ctx->checker); param_value.kind = ParameterValue_Location; o.type = t_source_code_location; @@ -2711,7 +2711,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t bool is_partial = false; if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "partial") { is_partial = true; } else { @@ -2745,7 +2745,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); } else if (name == "simd") { @@ -2770,7 +2770,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_slice(ctx, e, at->elem, elem); } else { @@ -2790,7 +2790,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t Type *elem = check_type(ctx, dat->elem); if (dat->tag != nullptr) { GB_ASSERT(dat->tag->kind == Ast_BasicDirective); - String name = dat->tag->BasicDirective.name; + String name = dat->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem); } else { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8bd5aaa37..08c9445bd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8887,7 +8887,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; GB_ASSERT(name == "location"); String procedure = p->entity->token.string; TokenPos pos = ast_token(ce->proc).pos; @@ -11515,7 +11515,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(bd, BasicDirective, expr); TokenPos pos = bd->token.pos; - GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name)); + GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string)); case_end; case_ast_node(i, Implicit, expr); diff --git a/src/parser.cpp b/src/parser.cpp index b8d53e724..a5180b4dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,109 +1,4 @@ -Token ast_token(Ast *node) { - switch (node->kind) { - case Ast_Ident: return node->Ident.token; - case Ast_Implicit: return node->Implicit; - case Ast_Undef: return node->Undef; - case Ast_BasicLit: return node->BasicLit.token; - case Ast_BasicDirective: return node->BasicDirective.token; - case Ast_ProcGroup: return node->ProcGroup.token; - case Ast_ProcLit: return ast_token(node->ProcLit.type); - case Ast_CompoundLit: - if (node->CompoundLit.type != nullptr) { - return ast_token(node->CompoundLit.type); - } - return node->CompoundLit.open; - - case Ast_TagExpr: return node->TagExpr.token; - case Ast_BadExpr: return node->BadExpr.begin; - case Ast_UnaryExpr: return node->UnaryExpr.op; - case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left); - case Ast_ParenExpr: return node->ParenExpr.open; - case Ast_CallExpr: return ast_token(node->CallExpr.proc); - case Ast_SelectorExpr: - if (node->SelectorExpr.selector != nullptr) { - return ast_token(node->SelectorExpr.selector); - } - return node->SelectorExpr.token; - case Ast_SelectorCallExpr: - if (node->SelectorCallExpr.expr != nullptr) { - return ast_token(node->SelectorCallExpr.expr); - } - return node->SelectorCallExpr.token; - case Ast_ImplicitSelectorExpr: - if (node->ImplicitSelectorExpr.selector != nullptr) { - return ast_token(node->ImplicitSelectorExpr.selector); - } - return node->ImplicitSelectorExpr.token; - case Ast_IndexExpr: return node->IndexExpr.open; - case Ast_SliceExpr: return node->SliceExpr.open; - case Ast_Ellipsis: return node->Ellipsis.token; - case Ast_FieldValue: return node->FieldValue.eq; - case Ast_DerefExpr: return node->DerefExpr.op; - case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); - case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); - case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); - case Ast_TypeCast: return node->TypeCast.token; - case Ast_AutoCast: return node->AutoCast.token; - case Ast_InlineAsmExpr: return node->InlineAsmExpr.token; - - case Ast_BadStmt: return node->BadStmt.begin; - case Ast_EmptyStmt: return node->EmptyStmt.token; - case Ast_ExprStmt: return ast_token(node->ExprStmt.expr); - case Ast_TagStmt: return node->TagStmt.token; - case Ast_AssignStmt: return node->AssignStmt.op; - case Ast_BlockStmt: return node->BlockStmt.open; - case Ast_IfStmt: return node->IfStmt.token; - case Ast_WhenStmt: return node->WhenStmt.token; - case Ast_ReturnStmt: return node->ReturnStmt.token; - case Ast_ForStmt: return node->ForStmt.token; - case Ast_RangeStmt: return node->RangeStmt.token; - case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token; - case Ast_CaseClause: return node->CaseClause.token; - case Ast_SwitchStmt: return node->SwitchStmt.token; - case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; - case Ast_DeferStmt: return node->DeferStmt.token; - case Ast_BranchStmt: return node->BranchStmt.token; - case Ast_UsingStmt: return node->UsingStmt.token; - - case Ast_BadDecl: return node->BadDecl.begin; - case Ast_Label: return node->Label.token; - - case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]); - case Ast_PackageDecl: return node->PackageDecl.token; - case Ast_ImportDecl: return node->ImportDecl.token; - case Ast_ForeignImportDecl: return node->ForeignImportDecl.token; - - case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token; - - case Ast_Attribute: - return node->Attribute.token; - - case Ast_Field: - if (node->Field.names.count > 0) { - return ast_token(node->Field.names[0]); - } - return ast_token(node->Field.type); - case Ast_FieldList: - return node->FieldList.token; - - case Ast_TypeidType: return node->TypeidType.token; - case Ast_HelperType: return node->HelperType.token; - case Ast_DistinctType: return node->DistinctType.token; - case Ast_PolyType: return node->PolyType.token; - case Ast_ProcType: return node->ProcType.token; - case Ast_RelativeType: return ast_token(node->RelativeType.tag); - case Ast_PointerType: return node->PointerType.token; - case Ast_ArrayType: return node->ArrayType.token; - case Ast_DynamicArrayType: return node->DynamicArrayType.token; - case Ast_StructType: return node->StructType.token; - case Ast_UnionType: return node->UnionType.token; - case Ast_EnumType: return node->EnumType.token; - case Ast_BitSetType: return node->BitSetType.token; - case Ast_MapType: return node->MapType.token; - } - - return empty_token; -} +#include "parser_pos.cpp" Token token_end_of_line(AstFile *f, Token tok) { u8 const *start = f->tokenizer.start + tok.pos.offset; @@ -474,12 +369,15 @@ Ast *clone_ast(Ast *node) { void error(Ast *node, char const *fmt, ...) { Token token = {}; + TokenPos end_pos = {}; if (node != nullptr) { token = ast_token(node); + end_pos = ast_end_pos(node); } + va_list va; va_start(va, fmt); - error_va(token.pos, fmt, va); + error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -501,16 +399,28 @@ void error_no_newline(Ast *node, char const *fmt, ...) { } void warning(Ast *node, char const *fmt, ...) { + Token token = {}; + TokenPos end_pos = {}; + if (node != nullptr) { + token = ast_token(node); + end_pos = ast_end_pos(node); + } va_list va; va_start(va, fmt); - warning_va(ast_token(node).pos, fmt, va); + warning_va(token.pos, end_pos, fmt, va); va_end(va); } void syntax_error(Ast *node, char const *fmt, ...) { + Token token = {}; + TokenPos end_pos = {}; + if (node != nullptr) { + token = ast_token(node); + end_pos = ast_end_pos(node); + } va_list va; va_start(va, fmt); - syntax_error_va(ast_token(node).pos, fmt, va); + syntax_error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -682,7 +592,7 @@ Ast *ast_basic_lit(AstFile *f, Token basic_lit) { return result; } -Ast *ast_basic_directive(AstFile *f, Token token, String name) { +Ast *ast_basic_directive(AstFile *f, Token token, Token name) { Ast *result = alloc_ast_node(f, Ast_BasicDirective); result->BasicDirective.token = token; result->BasicDirective.name = name; @@ -2042,27 +1952,27 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); } else if (name.string == "file") { - return ast_basic_directive(f, token, name.string); - } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); - } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); - } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); + return ast_basic_directive(f, token, name); + } else if (name.string == "line") { return ast_basic_directive(f, token, name); + } else if (name.string == "procedure") { return ast_basic_directive(f, token, name); + } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name); } else if (name.string == "location") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "load") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "assert") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "defined") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "config") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "soa" || name.string == "simd") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); switch (type->kind) { @@ -2074,7 +1984,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } return original_type; } else if (name.string == "partial") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); switch (type->kind) { @@ -2107,7 +2017,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } return operand; } else if (name.string == "relative") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); tag = parse_call_expr(f, tag); Ast *type = parse_type(f); return ast_relative_type(f, tag, type); @@ -4554,10 +4464,10 @@ Ast *parse_stmt(AstFile *f) { } return s; } else if (tag == "assert") { - Ast *t = ast_basic_directive(f, hash_token, tag); + Ast *t = ast_basic_directive(f, hash_token, name); return ast_expr_stmt(f, parse_call_expr(f, t)); } else if (tag == "panic") { - Ast *t = ast_basic_directive(f, hash_token, tag); + Ast *t = ast_basic_directive(f, hash_token, name); return ast_expr_stmt(f, parse_call_expr(f, t)); } else if (name.string == "force_inline" || name.string == "force_no_inline") { diff --git a/src/parser.hpp b/src/parser.hpp index 77d5f1b02..89f714aaa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -286,8 +286,8 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { Token token; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ - Token token; \ - String name; \ + Token token; \ + Token name; \ }) \ AST_KIND(Ellipsis, "ellipsis", struct { \ Token token; \ @@ -324,7 +324,7 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ - AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \ + AST_KIND(DerefExpr, "dereference expression", struct { Ast *expr; Token op; }) \ AST_KIND(SliceExpr, "slice expression", struct { \ Ast *expr; \ Token open, close; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp new file mode 100644 index 000000000..c5ad89604 --- /dev/null +++ b/src/parser_pos.cpp @@ -0,0 +1,331 @@ +Token ast_token(Ast *node) { + switch (node->kind) { + case Ast_Ident: return node->Ident.token; + case Ast_Implicit: return node->Implicit; + case Ast_Undef: return node->Undef; + case Ast_BasicLit: return node->BasicLit.token; + case Ast_BasicDirective: return node->BasicDirective.token; + case Ast_ProcGroup: return node->ProcGroup.token; + case Ast_ProcLit: return ast_token(node->ProcLit.type); + case Ast_CompoundLit: + if (node->CompoundLit.type != nullptr) { + return ast_token(node->CompoundLit.type); + } + return node->CompoundLit.open; + + case Ast_TagExpr: return node->TagExpr.token; + case Ast_BadExpr: return node->BadExpr.begin; + case Ast_UnaryExpr: return node->UnaryExpr.op; + case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left); + case Ast_ParenExpr: return node->ParenExpr.open; + case Ast_CallExpr: return ast_token(node->CallExpr.proc); + case Ast_SelectorExpr: + if (node->SelectorExpr.selector != nullptr) { + return ast_token(node->SelectorExpr.selector); + } + return node->SelectorExpr.token; + case Ast_SelectorCallExpr: + if (node->SelectorCallExpr.expr != nullptr) { + return ast_token(node->SelectorCallExpr.expr); + } + return node->SelectorCallExpr.token; + case Ast_ImplicitSelectorExpr: + if (node->ImplicitSelectorExpr.selector != nullptr) { + return ast_token(node->ImplicitSelectorExpr.selector); + } + return node->ImplicitSelectorExpr.token; + case Ast_IndexExpr: return node->IndexExpr.open; + case Ast_SliceExpr: return node->SliceExpr.open; + case Ast_Ellipsis: return node->Ellipsis.token; + case Ast_FieldValue: return node->FieldValue.eq; + case Ast_DerefExpr: return node->DerefExpr.op; + case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); + case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); + case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); + case Ast_TypeCast: return node->TypeCast.token; + case Ast_AutoCast: return node->AutoCast.token; + case Ast_InlineAsmExpr: return node->InlineAsmExpr.token; + + case Ast_BadStmt: return node->BadStmt.begin; + case Ast_EmptyStmt: return node->EmptyStmt.token; + case Ast_ExprStmt: return ast_token(node->ExprStmt.expr); + case Ast_TagStmt: return node->TagStmt.token; + case Ast_AssignStmt: return node->AssignStmt.op; + case Ast_BlockStmt: return node->BlockStmt.open; + case Ast_IfStmt: return node->IfStmt.token; + case Ast_WhenStmt: return node->WhenStmt.token; + case Ast_ReturnStmt: return node->ReturnStmt.token; + case Ast_ForStmt: return node->ForStmt.token; + case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token; + case Ast_CaseClause: return node->CaseClause.token; + case Ast_SwitchStmt: return node->SwitchStmt.token; + case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; + case Ast_DeferStmt: return node->DeferStmt.token; + case Ast_BranchStmt: return node->BranchStmt.token; + case Ast_UsingStmt: return node->UsingStmt.token; + + case Ast_BadDecl: return node->BadDecl.begin; + case Ast_Label: return node->Label.token; + + case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]); + case Ast_PackageDecl: return node->PackageDecl.token; + case Ast_ImportDecl: return node->ImportDecl.token; + case Ast_ForeignImportDecl: return node->ForeignImportDecl.token; + + case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token; + + case Ast_Attribute: + return node->Attribute.token; + + case Ast_Field: + if (node->Field.names.count > 0) { + return ast_token(node->Field.names[0]); + } + return ast_token(node->Field.type); + case Ast_FieldList: + return node->FieldList.token; + + case Ast_TypeidType: return node->TypeidType.token; + case Ast_HelperType: return node->HelperType.token; + case Ast_DistinctType: return node->DistinctType.token; + case Ast_PolyType: return node->PolyType.token; + case Ast_ProcType: return node->ProcType.token; + case Ast_RelativeType: return ast_token(node->RelativeType.tag); + case Ast_PointerType: return node->PointerType.token; + case Ast_ArrayType: return node->ArrayType.token; + case Ast_DynamicArrayType: return node->DynamicArrayType.token; + case Ast_StructType: return node->StructType.token; + case Ast_UnionType: return node->UnionType.token; + case Ast_EnumType: return node->EnumType.token; + case Ast_BitSetType: return node->BitSetType.token; + case Ast_MapType: return node->MapType.token; + } + + return empty_token; +} + +TokenPos token_pos_end(Token const &token) { + TokenPos pos = token.pos; + pos.offset += cast(i32)token.string.len; + for (isize i = 0; i < token.string.len; i++) { + // TODO(bill): This assumes ASCII + char c = token.string[i]; + if (c == '\n') { + pos.line += 1; + pos.column = 1; + } else { + pos.column += 1; + } + } + return pos; +} + +Token ast_end_token(Ast *node) { + GB_ASSERT(node != nullptr); + + switch (node->kind) { + case Ast_Ident: return node->Ident.token; + case Ast_Implicit: return node->Implicit; + case Ast_Undef: return node->Undef; + case Ast_BasicLit: return node->BasicLit.token; + case Ast_BasicDirective: return node->BasicDirective.token; + case Ast_ProcGroup: return node->ProcGroup.close; + case Ast_ProcLit: + if (node->ProcLit.body) { + return ast_end_token(node->ProcLit.body); + } + return ast_end_token(node->ProcLit.type); + case Ast_CompoundLit: + return node->CompoundLit.close; + + case Ast_BadExpr: return node->BadExpr.end; + case Ast_TagExpr: return ast_end_token(node->TagExpr.expr); + case Ast_UnaryExpr: return ast_end_token(node->UnaryExpr.expr); + case Ast_BinaryExpr: return ast_end_token(node->BinaryExpr.right); + case Ast_ParenExpr: return node->ParenExpr.close; + case Ast_CallExpr: return node->CallExpr.close; + case Ast_SelectorExpr: + return ast_end_token(node->SelectorExpr.selector); + case Ast_SelectorCallExpr: + return ast_end_token(node->SelectorCallExpr.call); + case Ast_ImplicitSelectorExpr: + return ast_end_token(node->SelectorExpr.selector); + case Ast_IndexExpr: return node->IndexExpr.close; + case Ast_SliceExpr: return node->SliceExpr.close; + case Ast_Ellipsis: + if (node->Ellipsis.expr) { + return ast_end_token(node->Ellipsis.expr); + } + return node->Ellipsis.token; + case Ast_FieldValue: return ast_end_token(node->FieldValue.value); + case Ast_DerefExpr: return node->DerefExpr.op; + case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y); + case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y); + case Ast_TypeAssertion: return ast_end_token(node->TypeAssertion.type); + case Ast_TypeCast: return ast_end_token(node->TypeCast.expr); + case Ast_AutoCast: return ast_end_token(node->AutoCast.expr); + case Ast_InlineAsmExpr: return node->InlineAsmExpr.close; + + case Ast_BadStmt: return node->BadStmt.end; + case Ast_EmptyStmt: return node->EmptyStmt.token; + case Ast_ExprStmt: return ast_end_token(node->ExprStmt.expr); + case Ast_TagStmt: return ast_end_token(node->TagStmt.stmt); + case Ast_AssignStmt: + if (node->AssignStmt.rhs.count > 0) { + return ast_end_token(node->AssignStmt.rhs[node->AssignStmt.rhs.count-1]); + } + return node->AssignStmt.op; + case Ast_BlockStmt: return node->BlockStmt.close; + case Ast_IfStmt: + if (node->IfStmt.else_stmt) { + return ast_end_token(node->IfStmt.else_stmt); + } + return ast_end_token(node->IfStmt.body); + case Ast_WhenStmt: + if (node->WhenStmt.else_stmt) { + return ast_end_token(node->WhenStmt.else_stmt); + } + return ast_end_token(node->WhenStmt.body); + case Ast_ReturnStmt: + if (node->ReturnStmt.results.count > 0) { + return ast_end_token(node->ReturnStmt.results[node->ReturnStmt.results.count-1]); + } + return node->ReturnStmt.token; + case Ast_ForStmt: return ast_end_token(node->ForStmt.body); + case Ast_RangeStmt: return ast_end_token(node->RangeStmt.body); + case Ast_UnrollRangeStmt: return ast_end_token(node->UnrollRangeStmt.body); + case Ast_CaseClause: + if (node->CaseClause.stmts.count) { + return ast_end_token(node->CaseClause.stmts[node->CaseClause.stmts.count-1]); + } else if (node->CaseClause.list.count) { + return ast_end_token(node->CaseClause.list[node->CaseClause.list.count-1]); + } + return node->CaseClause.token; + case Ast_SwitchStmt: return ast_end_token(node->SwitchStmt.body); + case Ast_TypeSwitchStmt: return ast_end_token(node->TypeSwitchStmt.body); + case Ast_DeferStmt: return ast_end_token(node->DeferStmt.stmt); + case Ast_BranchStmt: + if (node->BranchStmt.label) { + return ast_end_token(node->BranchStmt.label); + } + return node->BranchStmt.token; + case Ast_UsingStmt: + if (node->UsingStmt.list.count > 0) { + return ast_end_token(node->UsingStmt.list[node->UsingStmt.list.count-1]); + } + return node->UsingStmt.token; + + case Ast_BadDecl: return node->BadDecl.end; + case Ast_Label: + if (node->Label.name) { + return ast_end_token(node->Label.name); + } + return node->Label.token; + + case Ast_ValueDecl: + if (node->ValueDecl.values.count > 0) { + return ast_end_token(node->ValueDecl.values[node->ValueDecl.values.count-1]); + } + if (node->ValueDecl.type) { + return ast_end_token(node->ValueDecl.type); + } + if (node->ValueDecl.names.count > 0) { + return ast_end_token(node->ValueDecl.names[node->ValueDecl.names.count-1]); + } + return {}; + + case Ast_PackageDecl: return node->PackageDecl.name; + case Ast_ImportDecl: return node->ImportDecl.relpath; + case Ast_ForeignImportDecl: + if (node->ForeignImportDecl.filepaths.count > 0) { + return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]; + } + if (node->ForeignImportDecl.library_name.kind != Token_Invalid) { + return node->ForeignImportDecl.library_name; + } + return node->ForeignImportDecl.token; + + case Ast_ForeignBlockDecl: + return ast_end_token(node->ForeignBlockDecl.body); + + case Ast_Attribute: + if (node->Attribute.close.kind != Token_Invalid) { + return node->Attribute.close; + } + return ast_end_token(node->Attribute.elems[node->Attribute.elems.count-1]); + + case Ast_Field: + if (node->Field.tag.kind != Token_Invalid) { + return node->Field.tag; + } + if (node->Field.default_value) { + return ast_end_token(node->Field.default_value); + } + if (node->Field.type) { + return ast_end_token(node->Field.type); + } + return ast_end_token(node->Field.names[node->Field.names.count-1]); + case Ast_FieldList: + if (node->FieldList.list.count > 0) { + return ast_end_token(node->FieldList.list[node->FieldList.list.count-1]); + } + return node->FieldList.token; + + case Ast_TypeidType: + if (node->TypeidType.specialization) { + return ast_end_token(node->TypeidType.specialization); + } + return node->TypeidType.token; + case Ast_HelperType: return ast_end_token(node->HelperType.type); + case Ast_DistinctType: return ast_end_token(node->DistinctType.type); + case Ast_PolyType: + if (node->PolyType.specialization) { + return ast_end_token(node->PolyType.specialization); + } + return ast_end_token(node->PolyType.type); + case Ast_ProcType: + if (node->ProcType.results) { + return ast_end_token(node->ProcType.results); + } + if (node->ProcType.params) { + return ast_end_token(node->ProcType.params); + } + return node->ProcType.token; + case Ast_RelativeType: + return ast_end_token(node->RelativeType.type); + case Ast_PointerType: return ast_end_token(node->PointerType.type); + case Ast_ArrayType: return ast_end_token(node->ArrayType.elem); + case Ast_DynamicArrayType: return ast_end_token(node->DynamicArrayType.elem); + case Ast_StructType: + if (node->StructType.fields.count > 0) { + return ast_end_token(node->StructType.fields[node->StructType.fields.count-1]); + } + return node->StructType.token; + case Ast_UnionType: + if (node->UnionType.variants.count > 0) { + return ast_end_token(node->UnionType.variants[node->UnionType.variants.count-1]); + } + return node->UnionType.token; + case Ast_EnumType: + if (node->EnumType.fields.count > 0) { + return ast_end_token(node->EnumType.fields[node->EnumType.fields.count-1]); + } + if (node->EnumType.base_type) { + return ast_end_token(node->EnumType.base_type); + } + return node->EnumType.token; + case Ast_BitSetType: + if (node->BitSetType.underlying) { + return ast_end_token(node->BitSetType.underlying); + } + return ast_end_token(node->BitSetType.elem); + case Ast_MapType: return ast_end_token(node->MapType.value); + } + + return empty_token; +} + +TokenPos ast_end_pos(Ast *node) { + return token_pos_end(ast_end_token(node)); +} diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3c44aaad4..826fccc04 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -423,7 +423,7 @@ void error_out(char const *fmt, ...) { } -bool show_error_on_line(TokenPos const &pos) { +bool show_error_on_line(TokenPos const &pos, TokenPos end) { if (!show_error_line()) { return false; } @@ -435,6 +435,8 @@ bool show_error_on_line(TokenPos const &pos) { if (the_line != nullptr) { String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + // TODO(bill): This assumes ASCII + enum { MAX_LINE_LENGTH = 76, MAX_TAB_WIDTH = 8, @@ -462,15 +464,33 @@ bool show_error_on_line(TokenPos const &pos) { } error_out("\n\t"); - for (i32 i = 0; i < offset; i++) error_out(" "); - error_out("^\n"); - error_out("\n"); + for (i32 i = 0; i < offset; i++) { + error_out(" "); + } + error_out("^"); + if (end.file_id == pos.file_id) { + if (end.line > pos.line) { + for (i32 i = offset; i < line.len; i++) { + error_out("~"); + } + } else if (end.line == pos.line && end.column > pos.column) { + i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset)); + for (i32 i = 1; i < length-1; i++) { + error_out("~"); + } + if (length > 1) { + error_out("^"); + } + } + } + + error_out("\n\n"); return true; } return false; } -void error_va(TokenPos pos, char const *fmt, va_list va) { +void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it @@ -481,7 +501,7 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { error_out("%s %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -489,9 +509,9 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { } } -void warning_va(TokenPos const &pos, char const *fmt, va_list va) { +void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - error_va(pos, fmt, va); + error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -505,7 +525,7 @@ void warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -537,7 +557,7 @@ void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { } -void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { +void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it @@ -546,7 +566,7 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Error: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -557,9 +577,9 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { } } -void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { +void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - syntax_error_va(pos, fmt, va); + syntax_error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -571,7 +591,7 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } @@ -584,14 +604,14 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { void warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(token.pos, fmt, va); + warning_va(token.pos, {}, fmt, va); va_end(va); } void error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - error_va(token.pos, fmt, va); + error_va(token.pos, {}, fmt, va); va_end(va); } @@ -600,7 +620,7 @@ void error(TokenPos pos, char const *fmt, ...) { va_start(va, fmt); Token token = {}; token.pos = pos; - error_va(pos, fmt, va); + error_va(pos, {}, fmt, va); va_end(va); } @@ -615,21 +635,21 @@ void error_line(char const *fmt, ...) { void syntax_error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(token.pos, fmt, va); + syntax_error_va(token.pos, {}, fmt, va); va_end(va); } void syntax_error(TokenPos pos, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(pos, fmt, va); + syntax_error_va(pos, {}, fmt, va); va_end(va); } void syntax_warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_warning_va(token.pos, fmt, va); + syntax_warning_va(token.pos, {}, fmt, va); va_end(va); } @@ -748,7 +768,7 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) { pos.offset = cast(i32)(t->read_curr - t->start); va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; @@ -762,7 +782,7 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) { } va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; -- cgit v1.2.3 From 92abddddc5ca4be622e93856c7246159b594e9e9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 15:02:36 +0100 Subject: Be a little more correct with the temporary Ast node --- src/check_stmt.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 64d17a8c8..7772b5c97 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1537,8 +1537,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } Operand lhs = {Addressing_Invalid}; Operand rhs = {Addressing_Invalid}; - Ast binary_expr = {Ast_BinaryExpr}; - ast_node(be, BinaryExpr, &binary_expr); + Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr); + ast_node(be, BinaryExpr, binary_expr); be->op = op; be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); // NOTE(bill): Only use the first one will be used @@ -1546,7 +1546,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); + check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } -- cgit v1.2.3