From 5923470e35403f48c2551e826e756ecb5b3fd1e4 Mon Sep 17 00:00:00 2001 From: Lyla Bravo Date: Wed, 9 Dec 2020 18:51:37 -0300 Subject: [PATCH 01/22] Add Nix derivation --- default.nix | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 default.nix diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..64d20f674 --- /dev/null +++ b/default.nix @@ -0,0 +1,27 @@ +{ pkgs ? import { } }: +let + odin-unwrapped = pkgs.llvmPackages_11.stdenv.mkDerivation (rec { + name = "odin-unwrapped"; + src = ./.; + dontConfigure = true; + nativeBuildInputs = [ pkgs.git ]; + buildPhase = '' + make debug SHELL=${pkgs.llvmPackages_11.stdenv.shell} + ''; + installPhase = '' + mkdir -p $out/bin + cp odin $out/bin/odin + cp -r core $out/bin/core + ''; + }); + path = builtins.map (path: path + "/bin") (with pkgs.llvmPackages_11; [ + bintools + llvm + clang + lld + ]); +in +pkgs.writeScriptBin "odin" '' + #!${pkgs.llvmPackages_11.stdenv.shell} + PATH="${(builtins.concatStringsSep ":" path)}" exec ${odin-unwrapped}/bin/odin $@ +'' From 6f6a3f2ccf9f11977ded6b2a8f43c3a718729a27 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 9 Dec 2020 23:35:08 +0000 Subject: [PATCH 02/22] Fix typos for `-insert-semicolon` --- core/odin/parser/parser.odin | 11 ++++++----- src/parser.cpp | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 7bd20c699..cd99327be 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -545,8 +545,8 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - body = parse_block_stmt(p, true); skip_possible_newline_for_literal(p); + body = parse_block_stmt(p, true); } if allow_token(p, .Else) { @@ -621,8 +621,8 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - body = parse_block_stmt(p, false); skip_possible_newline_for_literal(p); + body = parse_block_stmt(p, false); } if allow_token(p, .Else) { @@ -630,6 +630,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { case .If: else_stmt = parse_if_stmt(p); case .Open_Brace: + skip_possible_newline_for_literal(p); else_stmt = parse_block_stmt(p, false); case .Do: expect_token(p, .Do); @@ -683,8 +684,8 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - body = parse_body(p); skip_possible_newline_for_literal(p); + body = parse_body(p); } range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end); @@ -718,8 +719,8 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - body = parse_body(p); skip_possible_newline_for_literal(p); + body = parse_body(p); } @@ -1087,8 +1088,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - body = parse_block_stmt(p, false); skip_possible_newline_for_literal(p); + body = parse_block_stmt(p, false); } if bad_stmt { diff --git a/src/parser.cpp b/src/parser.cpp index 46af47c2d..6e64ee9a8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1284,8 +1284,7 @@ bool skip_possible_newline(AstFile *f) { if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) { return false; } - Token *prev = &f->curr_token; - if (prev->kind == Token_Semicolon && prev->string == "\n") { + if (token_is_newline(f->curr_token)) { advance_token(f); return true; } @@ -1296,10 +1295,10 @@ bool skip_possible_newline_for_literal(AstFile *f) { if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) { return false; } - TokenPos curr_pos = f->curr_token.pos; - if (token_is_newline(f->curr_token)) { + Token curr = f->curr_token; + if (token_is_newline(curr)) { Token next = peek_token(f); - if (curr_pos.line+1 >= next.pos.line) { + if (curr.pos.line+1 >= next.pos.line) { switch (next.kind) { case Token_OpenBrace: case Token_else: @@ -3795,8 +3794,8 @@ Ast *parse_if_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as if condition"); } } else { - body = parse_block_stmt(f, false); skip_possible_newline_for_literal(f); + body = parse_block_stmt(f, false); } if (allow_token(f, Token_else)) { @@ -3805,6 +3804,7 @@ Ast *parse_if_stmt(AstFile *f) { else_stmt = parse_if_stmt(f); break; case Token_OpenBrace: + skip_possible_newline_for_literal(f); else_stmt = parse_block_stmt(f, false); break; case Token_do: { @@ -3851,8 +3851,8 @@ Ast *parse_when_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as when statement"); } } else { - body = parse_block_stmt(f, true); skip_possible_newline_for_literal(f); + body = parse_block_stmt(f, true); } if (allow_token(f, Token_else)) { @@ -3957,8 +3957,8 @@ Ast *parse_for_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - body = parse_block_stmt(f, false); skip_possible_newline_for_literal(f); + body = parse_block_stmt(f, false); } return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body); } @@ -3993,8 +3993,8 @@ Ast *parse_for_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - body = parse_block_stmt(f, false); skip_possible_newline_for_literal(f); + body = parse_block_stmt(f, false); } if (is_range) { @@ -4345,8 +4345,8 @@ Ast *parse_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - body = parse_block_stmt(f, false); skip_possible_newline_for_literal(f); + body = parse_block_stmt(f, false); } if (bad_stmt) { return ast_bad_stmt(f, inline_token, f->curr_token); From f64584b92a54dceb28121461f2ef4fd02c7b30de Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 9 Dec 2020 23:40:45 +0000 Subject: [PATCH 03/22] Improve -insert-semicolon rules --- core/odin/parser/parser.odin | 9 +++------ src/parser.cpp | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index a23aa6a5b..28ff64533 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -521,6 +521,7 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt { } parse_block_stmt :: proc(p: ^Parser, is_when: bool) -> ^ast.Stmt { + skip_possible_newline_for_literal(p); if !is_when && p.curr_proc == nil { error(p, p.curr_tok.pos, "you cannot use a block statement in the file scope"); } @@ -545,10 +546,10 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - skip_possible_newline_for_literal(p); body = parse_block_stmt(p, true); } + skip_possible_newline_for_literal(p); if allow_token(p, .Else) { #partial switch p.curr_tok.kind { case .When: @@ -621,18 +622,17 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - skip_possible_newline_for_literal(p); body = parse_block_stmt(p, false); } else_tok := p.curr_tok.pos; + skip_possible_newline_for_literal(p); if allow_token(p, .Else) { #partial switch p.curr_tok.kind { case .If: else_stmt = parse_if_stmt(p); case .Open_Brace: - skip_possible_newline_for_literal(p); else_stmt = parse_block_stmt(p, false); case .Do: expect_token(p, .Do); @@ -687,7 +687,6 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - skip_possible_newline_for_literal(p); body = parse_body(p); } @@ -722,7 +721,6 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - skip_possible_newline_for_literal(p); body = parse_body(p); } @@ -1094,7 +1092,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { - skip_possible_newline_for_literal(p); body = parse_block_stmt(p, false); } diff --git a/src/parser.cpp b/src/parser.cpp index 6e64ee9a8..59d02090e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3181,6 +3181,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { Ast *parse_block_stmt(AstFile *f, b32 is_when) { + skip_possible_newline_for_literal(f); if (!is_when && f->curr_proc == nullptr) { syntax_error(f->curr_token, "You cannot use a block statement in the file scope"); return ast_bad_stmt(f, f->curr_token, f->curr_token); @@ -3794,17 +3795,16 @@ Ast *parse_if_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as if condition"); } } else { - skip_possible_newline_for_literal(f); body = parse_block_stmt(f, false); } + skip_possible_newline_for_literal(f); if (allow_token(f, Token_else)) { switch (f->curr_token.kind) { case Token_if: else_stmt = parse_if_stmt(f); break; case Token_OpenBrace: - skip_possible_newline_for_literal(f); else_stmt = parse_block_stmt(f, false); break; case Token_do: { @@ -3851,10 +3851,10 @@ Ast *parse_when_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as when statement"); } } else { - skip_possible_newline_for_literal(f); body = parse_block_stmt(f, true); } + skip_possible_newline_for_literal(f); if (allow_token(f, Token_else)) { switch (f->curr_token.kind) { case Token_when: @@ -3957,7 +3957,6 @@ Ast *parse_for_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - skip_possible_newline_for_literal(f); body = parse_block_stmt(f, false); } return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body); @@ -3993,7 +3992,6 @@ Ast *parse_for_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - skip_possible_newline_for_literal(f); body = parse_block_stmt(f, false); } @@ -4345,7 +4343,6 @@ Ast *parse_stmt(AstFile *f) { syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); } } else { - skip_possible_newline_for_literal(f); body = parse_block_stmt(f, false); } if (bad_stmt) { From 60b9ef1f5de9de7cd1ba5fce782f4fc0c0d88d2b Mon Sep 17 00:00:00 2001 From: Oskar Nordquist Date: Sun, 13 Dec 2020 02:36:29 +0100 Subject: [PATCH 04/22] Fix math.factorial() --- core/math/math.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/math/math.odin b/core/math/math.odin index ee93a4d8c..ca344afeb 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -508,7 +508,7 @@ factorial :: proc(n: int) -> int { assert(n >= 0, "parameter must not be negative"); assert(n < len(table), "parameter is too large to lookup in the table"); - return 0; + return table[n]; } classify_f32 :: proc(x: f32) -> Float_Class { From cffbd2d2761f4019b82204593a55063cc0480a75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 13 Dec 2020 17:09:41 +0000 Subject: [PATCH 05/22] Add odin/parser/parse_files.odin --- core/odin/ast/ast.odin | 53 ++++++++++++++++-- core/odin/ast/clone.odin | 5 ++ core/odin/ast/file.odin | 40 -------------- core/odin/ast/walk.odin | 45 ++++++++++++++++ core/odin/parser/parse_files.odin | 89 +++++++++++++++++++++++++++++++ core/odin/parser/parser.odin | 15 +++++- 6 files changed, 200 insertions(+), 47 deletions(-) delete mode 100644 core/odin/ast/file.odin create mode 100644 core/odin/parser/parse_files.odin diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 88a5a1ee3..d51f482a3 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -35,11 +35,6 @@ Node_State_Flag :: enum { } Node_State_Flags :: distinct bit_set[Node_State_Flag]; - -Comment_Group :: struct { - list: []tokenizer.Token, -} - Node :: struct { pos: tokenizer.Pos, end: tokenizer.Pos, @@ -47,6 +42,54 @@ Node :: struct { derived: any, } +Comment_Group :: struct { + using node: Node, + list: []tokenizer.Token, +} + +Package_Kind :: enum { + Normal, + Runtime, + Init, +} + +Package :: struct { + using node: Node, + kind: Package_Kind, + id: int, + name: string, + fullpath: string, + files: map[string]^File, + + user_data: rawptr, +} + +File :: struct { + using node: Node, + id: int, + pkg: ^Package, + + fullpath: string, + src: []byte, + + docs: ^Comment_Group, + + pkg_decl: ^Package_Decl, + pkg_token: tokenizer.Token, + pkg_name: string, + + decls: [dynamic]^Stmt, + imports: [dynamic]^Import_Decl, + directive_count: int, + + comments: [dynamic]^Comment_Group, + + syntax_warning_count: int, + syntax_error_count: int, +} + + +// Base Types Expr :: struct { using expr_base: Node, diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index 155fef1f7..d210d8bd4 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node { align = ti.align; } + switch in node.derived { + case Package, File: + panic("Cannot clone this node type"); + } + res := cast(^Node)mem.alloc(size, align); src: rawptr = node; if node.derived != nil { diff --git a/core/odin/ast/file.odin b/core/odin/ast/file.odin deleted file mode 100644 index dafb10e14..000000000 --- a/core/odin/ast/file.odin +++ /dev/null @@ -1,40 +0,0 @@ -package odin_ast - -import "core:odin/tokenizer" - -Package_Kind :: enum { - Normal, - Runtime, - Init, -} - -Package :: struct { - kind: Package_Kind, - id: int, - name: string, - fullpath: string, - files: []^File, - - user_data: rawptr, -} - -File :: struct { - id: int, - pkg: ^Package, - - fullpath: string, - src: []byte, - - pkg_decl: ^Package_Decl, - pkg_token: tokenizer.Token, - pkg_name: string, - - decls: [dynamic]^Stmt, - imports: [dynamic]^Import_Decl, - directive_count: int, - - comments: [dynamic]^Comment_Group, - - syntax_warning_count: int, - syntax_error_count: int, -} diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index 09580d9a7..9abdbffdd 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -59,6 +59,18 @@ walk :: proc(v: ^Visitor, node: ^Node) { } switch n in &node.derived { + case File: + if n.docs != nil { + walk(v, n.docs); + } + walk_stmt_list(v, n.decls[:]); + case Package: + for _, f in n.files { + walk(v, f); + } + + case Comment_Group: + // empty case Bad_Expr: case Ident: case Implicit: @@ -252,29 +264,59 @@ walk :: proc(v: ^Visitor, node: ^Node) { case Bad_Decl: case Value_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); walk_expr_list(v, n.names); if n.type != nil { walk(v, n.type); } walk_expr_list(v, n.values); + if n.comment != nil { + walk(v, n.comment); + } case Package_Decl: + if n.docs != nil { + walk(v, n.docs); + } + if n.comment != nil { + walk(v, n.comment); + } case Import_Decl: + if n.docs != nil { + walk(v, n.docs); + } + if n.comment != nil { + walk(v, n.comment); + } case Foreign_Block_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); if n.foreign_library != nil { walk(v, n.foreign_library); } walk(v, n.body); case Foreign_Import_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); walk(v, n.name); + if n.comment != nil { + walk(v, n.comment); + } case Proc_Group: walk_expr_list(v, n.args); case Attribute: walk_expr_list(v, n.elems); case Field: + if n.docs != nil { + walk(v, n.docs); + } walk_expr_list(v, n.names); if n.type != nil { walk(v, n.type); @@ -282,6 +324,9 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.default_value != nil { walk(v, n.default_value); } + if n.comment != nil { + walk(v, n.comment); + } case Field_List: for x in n.list { walk(v, x); diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin new file mode 100644 index 000000000..80c29fa86 --- /dev/null +++ b/core/odin/parser/parse_files.odin @@ -0,0 +1,89 @@ +package odin_parser + +import "core:odin/tokenizer" +import "core:odin/ast" +import "core:path/filepath" +import "core:fmt" +import "core:os" +import "core:slice" + +collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) { + NO_POS :: tokenizer.Pos{}; + + pkg_path, ok := filepath.abs(path); + if !ok { + return; + } + + path_pattern := fmt.tprintf("%s/*.odin", pkg_path); + matches, err := filepath.glob(path_pattern); + defer delete(matches); + + if err != nil { + return; + } + + pkg = ast.new(ast.Package, NO_POS, NO_POS); + pkg.fullpath = pkg_path; + + for match in matches { + src: []byte; + fullpath, ok := filepath.abs(match); + if !ok { + return; + } + src, ok = os.read_entire_file(fullpath); + if !ok { + delete(fullpath); + return; + } + file := ast.new(ast.File, NO_POS, NO_POS); + file.pkg = pkg; + file.src = src; + file.fullpath = fullpath; + pkg.files[fullpath] = file; + } + + success = true; + return; +} + +parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool { + p := p; + if p == nil { + p = &Parser{}; + p^ = default_parser(); + } + + ok := true; + + files := make([]^ast.File, len(pkg.files), context.temp_allocator); + i := 0; + for _, file in pkg.files { + files[i] = file; + i += 1; + } + slice.sort(files); + + for file in files { + if !parse_file(p, file) { + ok = false; + } + if pkg.name == "" { + pkg.name = file.pkg_decl.name; + } else if pkg.name != file.pkg_decl.name { + error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name); + } + } + + return ok; +} + +parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) { + pkg, ok = collect_package(path); + if !ok { + return; + } + ok = parse_package(pkg, p); + return; +} diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 28ff64533..84835bda9 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -107,6 +107,14 @@ default_parser :: proc() -> Parser { }; } +is_package_name_reserved :: proc(name: string) -> bool { + switch name { + case "builtin", "intrinsics": + return true; + } + return false; +} + parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { zero_parser: { p.prev_tok = {}; @@ -139,8 +147,11 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { pkg_name := expect_token_after(p, .Ident, "package"); if pkg_name.kind == .Ident { - if is_blank_ident(pkg_name) { + switch name := pkg_name.text; { + case is_blank_ident(name): error(p, pkg_name.pos, "invalid package name '_'"); + case is_package_name_reserved(name), file.pkg.kind != .Runtime && name == "runtime": + error(p, pkg_name.pos, "use of reserved package name '%s'", name); } } p.file.pkg_name = pkg_name.text; @@ -276,7 +287,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro } if len(list) > 0 { - comments = new(ast.Comment_Group); + comments = ast.new(ast.Comment_Group, list[0].pos, end_pos(list[len(list)-1])); comments.list = list[:]; append(&p.file.comments, comments); } From 404c9e40ee4104120201a41e6acded3b41fbf9ae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 14:31:18 +0000 Subject: [PATCH 06/22] Update io/util.odin --- core/io/util.odin | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/core/io/util.odin b/core/io/util.odin index 2036aa570..70af13d7e 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -21,11 +21,9 @@ write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) { return write_i64(w, i64(i), base); } -@(private) Tee_Reader :: struct { r: Reader, w: Writer, - allocator: runtime.Allocator, } @(private) @@ -40,27 +38,22 @@ _tee_reader_vtable := &Stream_VTable{ } return; }, - impl_destroy = proc(s: Stream) -> Error { - t := (^Tee_Reader)(s.stream_data); - allocator := t.allocator; - free(t, allocator); - return .None; - }, }; -// tee_reader returns a Reader that writes to 'w' what it reads from 'r' +// tee_reader_init returns a Reader that writes to 'w' what it reads from 'r' // All reads from 'r' performed through it are matched with a corresponding write to 'w' // There is no internal buffering done // The write must complete before th read completes // Any error encountered whilst writing is reported as a 'read' error -// tee_reader must call io.destroy when done with -tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) { - t := new(Tee_Reader, allocator); +// tee_reader_init must call io.destroy when done with +tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := context.allocator) -> Reader { t.r, t.w = r, w; - t.allocator = allocator; + return tee_reader_to_reader(t); +} - out.stream_data = t; - out.stream_vtable = _tee_reader_vtable; +tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) { + r.stream_data = t; + r.stream_vtable = _tee_reader_vtable; return; } @@ -90,11 +83,10 @@ _limited_reader_vtable := &Stream_VTable{ }, }; -new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { - l := new(Limited_Reader); +limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { l.r = r; l.n = n; - return l; + return limited_reader_to_reader(l); } limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { @@ -118,7 +110,7 @@ Section_Reader :: struct { limit: i64, } -init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) { +section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) { s.r = r; s.off = off; s.limit = off + n; From c17d17a9b44f64cb9c65c6f0a858de073de33113 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 14:32:34 +0000 Subject: [PATCH 07/22] Remove unused procedure --- core/io/util.odin | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/io/util.odin b/core/io/util.odin index 70af13d7e..224bcd7a5 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -95,12 +95,6 @@ limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { return; } -@(private="package") -inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { - l.r = r; - l.n = n; - return limited_reader_to_reader(l); -} // Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At Section_Reader :: struct { From aa2562fe7c4f787f3210f6822f08e8887710ea4f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 14:33:32 +0000 Subject: [PATCH 08/22] Replace procedure call --- core/io/io.odin | 2 +- core/io/util.odin | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/io/io.odin b/core/io/io.odin index e33b33064..215cf5e10 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -436,7 +436,7 @@ copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err // It returns the number of bytes copied and the first error that occurred whilst copying, if any. // On return, written == n IFF err == nil copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) { - nsrc := inline_limited_reader(&Limited_Reader{}, src, n); + nsrc := limited_reader_init(&Limited_Reader{}, src, n); written, err = copy(dst, nsrc); if written == n { return n, nil; diff --git a/core/io/util.odin b/core/io/util.odin index 224bcd7a5..c8ba6c259 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -95,7 +95,6 @@ limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { return; } - // Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At Section_Reader :: struct { r: Reader_At, From f6e2d74d10d4f8ade814a0ed36bed4d98eae768c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 14:36:45 +0000 Subject: [PATCH 09/22] Keep -vet happy --- core/io/util.odin | 1 - core/odin/parser/parse_files.odin | 4 ++-- core/path/filepath/match.odin | 8 ++++---- core/path/filepath/path.odin | 3 +-- core/path/filepath/path_windows.odin | 7 ++++--- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/io/util.odin b/core/io/util.odin index c8ba6c259..796e8a36c 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -1,6 +1,5 @@ package io -import "core:runtime" import "core:strconv" write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) { diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin index 80c29fa86..99275777c 100644 --- a/core/odin/parser/parse_files.odin +++ b/core/odin/parser/parse_files.odin @@ -10,8 +10,8 @@ import "core:slice" collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) { NO_POS :: tokenizer.Pos{}; - pkg_path, ok := filepath.abs(path); - if !ok { + pkg_path, pkg_path_ok := filepath.abs(path); + if !pkg_path_ok { return; } diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 75508c449..57307934e 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -272,13 +272,13 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s } defer os.close(d); - fi, ferr := os.fstat(d); + file_info, ferr := os.fstat(d); if ferr != 0 { - os.file_info_delete(fi); + os.file_info_delete(file_info); return; } - if !fi.is_dir { - os.file_info_delete(fi); + if !file_info.is_dir { + os.file_info_delete(file_info); return; } diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index 3a11c09e8..f19a10d46 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -2,7 +2,6 @@ // To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package package filepath -import "core:os" import "core:strings" // is_separator checks whether the byte is a valid separator character @@ -265,7 +264,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> ( } buf := make([]byte, size); n := copy(buf, ".."); - for i in 0.. bool { return false; } - path := path[l:]; + path := path; + path = path[l:]; if path == "" { return false; } @@ -123,8 +124,8 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string { } assert(index == count); - for s, i in list { - s, new := strings.replace_all(s, `"`, ``, allocator); + for s0, i in list { + s, new := strings.replace_all(s0, `"`, ``, allocator); if !new { s = strings.clone(s, allocator); } From 416051f17b7e8375b2bc4823727c0f67d7ad9beb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 16:39:31 +0000 Subject: [PATCH 10/22] Fix #811 --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 15380e55c..b0012bfcc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8760,8 +8760,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 lo = exact_value_to_i64(x.value); i64 hi = exact_value_to_i64(y.value); i64 max_index = hi; - if (op.kind == Token_RangeHalf) { + if (op.kind == Token_RangeHalf) { // ..< (exclusive) hi -= 1; + } else { // .. (inclusive) + max_index += 1; } bool new_range = range_cache_add_range(&rc, lo, hi); From 82d63306c4a5ad8cd275368a4e9613d9d5315eb9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Dec 2020 16:55:19 +0000 Subject: [PATCH 11/22] Fix enumerated array index printing bug #808 --- core/fmt/fmt.odin | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 84cf4f160..463eda928 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1087,6 +1087,8 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) { et := runtime.type_info_base(enum_type); + ev := ev; + ev += runtime.Type_Info_Enum_Value(offset); #partial switch e in et.variant { case: return "", false; case runtime.Type_Info_Enum: @@ -1111,18 +1113,6 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false; } -fmt_write_i64 :: proc(w: io.Writer, i: i64, base: int = 10) { - buf: [32]byte; - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); - io.write_string(w, s); -} -fmt_write_u64 :: proc(w: io.Writer, i: u64, base: int) { - buf: [32]byte; - s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); - io.write_string(w, s); -} - - fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { if ti == nil { @@ -1211,7 +1201,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } } v := i64(i) + info.lower; - fmt_write_i64(fi.writer, v, 10); + io.write_i64(fi.writer, v, 10); commas += 1; } } @@ -1253,7 +1243,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { u <<= sa; u >>= sa; - fmt_write_u64(fi.writer, u, 10); + io.write_u64(fi.writer, u, 10); } io.write_byte(fi.writer, '}'); @@ -1312,7 +1302,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for in 0.. Date: Mon, 14 Dec 2020 22:47:21 +0000 Subject: [PATCH 14/22] Fix minor constant value declaration bug --- src/check_decl.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5234955fb..9f8f460ba 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -168,18 +168,6 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) { return; } -#if 0 - if (!is_type_constant_type(operand->type)) { - gbString type_str = type_to_string(operand->type); - error(operand->expr, "Invalid constant type: '%s'", type_str); - gb_string_free(type_str); - if (e->type == nullptr) { - e->type = t_invalid; - } - return; - } -#endif - if (e->type == nullptr) { // NOTE(bill): type inference e->type = operand->type; } @@ -388,15 +376,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, e->flags |= EntityFlag_Visited; if (type_expr) { - Type *t = check_type(ctx, type_expr); - if (!is_type_constant_type(t) && !is_type_proc(t)) { - gbString str = type_to_string(t); - error(type_expr, "Invalid constant type '%s'", str); - gb_string_free(str); - e->type = t_invalid; - return; - } - e->type = t; + e->type = check_type(ctx, type_expr); } Operand operand = {}; From 2957da538d959fe5adf3c69a2cb7a28906a577b4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Dec 2020 22:07:53 +0000 Subject: [PATCH 15/22] Add `strings.Intern` --- core/strings/intern.odin | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 core/strings/intern.odin diff --git a/core/strings/intern.odin b/core/strings/intern.odin new file mode 100644 index 000000000..def03d899 --- /dev/null +++ b/core/strings/intern.odin @@ -0,0 +1,54 @@ +package strings + +import "core:mem" + +Intern_Entry :: struct { + len: int, + str: [1]byte, // string is allocated inline with the entry to keep allocations simple +} + +Intern :: struct { + allocator: mem.Allocator, + entries: map[string]^Intern_Entry, +} + +intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) { + m.allocator = allocator; + m.entries = make(map[string]^Intern_Entry, 16, map_allocator); +} + +intern_destroy :: proc(m: ^Intern) { + for _, value in m.entries { + free(value, m.allocator); + } + delete(m.entries); +} + +intern_get :: proc(m: ^Intern, text: string) -> string { + entry := _intern_get_entry(m, text); + return #no_bounds_check string(entry.str[:entry.len]); +} +intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring { + entry := _intern_get_entry(m, text); + return cstring(&entry.str[0]); +} + +_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check { + if prev, ok := m.entries[text]; ok { + return prev; + } + if m.allocator.procedure == nil { + m.allocator = context.allocator; + } + + entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1; + new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator)); + + new_entry.len = len(text); + copy(new_entry.str[:new_entry.len], text); + new_entry.str[new_entry.len] = 0; + + key := string(new_entry.str[:new_entry.len]); + m.entries[key] = new_entry; + return new_entry; +} From 6c2b93d5193f827c86c0cdd5938ceb91a6804397 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Dec 2020 22:28:40 +0000 Subject: [PATCH 16/22] Improve text/scanner whitespace parameter to use a bit_set instead; Improve error message for `for x in y` where y is not iterable but allows `in` as an operator --- core/text/scanner/scanner.odin | 13 ++++++++----- src/check_stmt.cpp | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index 03b4bb28a..45c2a5d8f 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -61,14 +61,17 @@ Scan_Flag :: enum u32 { Scan_Comments, Skip_Comments, // if set with .Scan_Comments, comments become white space } -Scan_Flags :: bit_set[Scan_Flag; u32]; +Scan_Flags :: distinct bit_set[Scan_Flag; u32]; Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; +// Only allows for ASCII whitespace +Whitespace :: distinct bit_set['\x00'.. (tok: rune) { s.pos.line = 0; redo: for { - for s.whitespace & (1<val0 != nullptr && rs->val1 == nullptr) { + if (is_type_map(operand.type) || is_type_bit_set(operand.type)) { + gbString v = expr_to_string(rs->val0); + defer (gb_string_free(v)); + error_line("\tSuggestion: place parentheses around the expression\n"); + error_line("\t for (%s in %s) {\n", v, s); + } + } } } From 5faa560f82eba49a862ee340b3b560a96044f868 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 15 Dec 2020 23:23:07 +0000 Subject: [PATCH 17/22] Make `container.Map` have similar semantics to the built-in `map` type --- core/container/map.odin | 86 +++++++++++++++++++++++------------------ src/types.cpp | 3 ++ 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/core/container/map.odin b/core/container/map.odin index 3e6c18fba..53edae3d5 100644 --- a/core/container/map.odin +++ b/core/container/map.odin @@ -1,14 +1,18 @@ package container +import "intrinsics" +_ :: intrinsics; -Map :: struct(Value: typeid) { + +Map :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) { hash: Array(int), - entries: Array(Map_Entry(Value)), + entries: Array(Map_Entry(Key, Value)), } -Map_Entry :: struct(Value: typeid) { - key: u64, +Map_Entry :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) { + hash: uintptr, next: int, + key: Key, value: Value, } @@ -47,28 +51,28 @@ multi_map_remove_all map_init :: proc{map_init_none, map_init_cap}; -map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) { +map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) { m.hash.allocator = allocator; m.entries.allocator = allocator; } -map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) { +map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) { m.hash.allocator = allocator; m.entries.allocator = allocator; map_reserve(m, cap); } -map_delete :: proc(m: $M/Map($Value)) { +map_delete :: proc(m: $M/Map($Key, $Value)) { array_delete(m.hash); array_delete(m.entries); } -map_has :: proc(m: $M/Map($Value), key: u64) -> bool { +map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool { return _map_find_or_fail(m, key) >= 0; } -map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok { +map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok { i := _map_find_or_fail(m, key); if i < 0 { return {}, false; @@ -76,7 +80,7 @@ map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional return array_get(m.entries, i).value, true; } -map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok { +map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok { i := _map_find_or_fail(m, key); if i < 0 { return default, false; @@ -84,7 +88,7 @@ map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Va return array_get(m.entries, i).value, true; } -map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value { +map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value { i := _map_find_or_fail(m, key); if i < 0 { return nil; @@ -92,7 +96,7 @@ map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value { return array_get_ptr(m.entries, i).value; } -map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { +map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) { if array_len(m.hash) == 0 { _map_grow(m); } @@ -104,7 +108,7 @@ map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { } } -map_remove :: proc(m: ^$M/Map($Value), key: u64) { +map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) { fr := _map_find_key(m^, key); if fr.entry_index >= 0 { _map_erase(m, fr); @@ -112,7 +116,7 @@ map_remove :: proc(m: ^$M/Map($Value), key: u64) { } -map_reserve :: proc(m: ^$M/Map($Value), new_size: int) { +map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) { nm: M; map_init(&nm, m.hash.allocator); array_resize(&nm.hash, new_size); @@ -130,14 +134,14 @@ map_reserve :: proc(m: ^$M/Map($Value), new_size: int) { m^ = nm; } -map_clear :: proc(m: ^$M/Map($Value)) { +map_clear :: proc(m: ^$M/Map($Key, $Value)) { array_clear(&m.hash); array_clear(&m.entries); } -multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) { +multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Value) { i := _map_find_or_fail(m, key); if i < 0 { return nil; @@ -145,11 +149,11 @@ multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) { return array_get_ptr(m.entries, i); } -multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) { +multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) { i := e.next; for i >= 0 { it := array_get_ptr(m.entries, i); - if it.key == e.key { + if it.hash == e.hash && it.key == e.key { return it; } i = it.next; @@ -157,7 +161,7 @@ multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Ent return nil; } -multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int { +multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int { n := 0; e := multi_map_find_first(m, key); for e != nil { @@ -169,7 +173,7 @@ multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int { multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}; -multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) { +multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) { if items == nil { return; } @@ -180,7 +184,7 @@ multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) { } } -multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) { +multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) { e := multi_map_find_first(m, key); i := 0; for e != nil && i < len(items) { @@ -190,7 +194,7 @@ multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) { } } -multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value { +multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value { items: Array(Value); array_init(&items, 0); @@ -204,7 +208,7 @@ multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value { } -multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) { +multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) { if array_len(m.hash) == 0 { _map_grow(m); } @@ -216,14 +220,14 @@ multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) { } } -multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) { +multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) { fr := _map_find_entry(m, e); if fr.entry_index >= 0 { _map_erase(m, fr); } } -multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) { +multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) { for map_exist(m^, key) { map_remove(m, key); } @@ -239,9 +243,12 @@ Map_Find_Result :: struct { entry_index: int, } -_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int { - e: Map_Entry(Value); +_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) { + hasher := intrinsics.type_hasher_proc(Key); + + e: Map_Entry(Key, Value); e.key = key; + e.hash = hasher(&e.key, 0); e.next = -1; idx := array_len(m.entries); array_push(&m.entries, e); @@ -271,7 +278,7 @@ _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) { } -_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { +_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) { fr: Map_Find_Result; fr.hash_index = -1; fr.entry_prev = -1; @@ -281,11 +288,16 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { return fr; } - fr.hash_index = int(key % u64(array_len(m.hash))); + hasher := intrinsics.type_hasher_proc(Key); + + key := key; + hash := hasher(&key, 0); + + fr.hash_index = int(hash % uintptr(array_len(m.hash))); fr.entry_index = array_get(m.hash, fr.hash_index); for fr.entry_index >= 0 { it := array_get_ptr(m.entries, fr.entry_index); - if it.key == key { + if it.hash == hash && it.key == key { return fr; } fr.entry_prev = fr.entry_index; @@ -294,7 +306,7 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { return fr; } -_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result { +_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) -> Map_Find_Result { fr: Map_Find_Result; fr.hash_index = -1; fr.entry_prev = -1; @@ -304,7 +316,7 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re return fr; } - fr.hash_index = int(e.key % u64(array_len(m.hash))); + fr.hash_index = int(e.hash % uintptr(array_len(m.hash))); fr.entry_index = array_get(m.hash, fr.hash_index); for fr.entry_index >= 0 { it := array_get_ptr(m.entries, fr.entry_index); @@ -317,10 +329,10 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re return fr; } -_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int { +_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int { return _map_find_key(m, key).entry_index; } -_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int { +_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int { fr := _map_find_key(m^, key); if fr.entry_index >= 0 { return fr.entry_index; @@ -336,7 +348,7 @@ _map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int { } -_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int { +_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int { fr := _map_find_key(m^, key); i := _map_add_entry(m, key); @@ -352,12 +364,12 @@ _map_make :: proc(m: ^$M/Map($Value), key: u64) -> int { } -_map_full :: proc(m: $M/Map($Value)) -> bool { +_map_full :: proc(m: $M/Map($Key, $Value)) -> bool { // TODO(bill): Determine good max load factor return array_len(m.entries) >= (array_len(m.hash) / 4)*3; } -_map_grow :: proc(m: ^$M/Map($Value)) { +_map_grow :: proc(m: ^$M/Map($Key, $Value)) { new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate map_reserve(m, new_size); } diff --git a/src/types.cpp b/src/types.cpp index df87cb645..d79a3337a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1720,6 +1720,9 @@ TypeTuple *get_record_polymorphic_params(Type *t) { bool is_type_polymorphic(Type *t, bool or_specialized=false) { + if (t == nullptr) { + return false; + } if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) { return false; } From a31b992d2bf2f252ebede90c1849ef4a99ada666 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Dec 2020 00:25:05 +0000 Subject: [PATCH 18/22] Rename bytes/strings.odin to bytes/bytes.odin --- core/bytes/{strings.odin => bytes.odin} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/bytes/{strings.odin => bytes.odin} (100%) diff --git a/core/bytes/strings.odin b/core/bytes/bytes.odin similarity index 100% rename from core/bytes/strings.odin rename to core/bytes/bytes.odin From 1470cab842ef41b3ec61255b0366feb51a21f67f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Dec 2020 00:36:25 +0000 Subject: [PATCH 19/22] Make bytes.odin consistent with strings.odin in functionality --- core/bytes/buffer.odin | 10 +++++ core/bytes/bytes.odin | 93 ++++++++++++++++++------------------------ 2 files changed, 49 insertions(+), 54 deletions(-) diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index 61fde9605..af9c1e57f 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -35,6 +35,11 @@ buffer_init_string :: proc(b: ^Buffer, s: string) { copy(b.buf[:], s); } +buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) { + b.buf.allocator = allocator; + reserve(&b.buf, cap); + resize(&b.buf, len); +} buffer_destroy :: proc(b: ^Buffer) { delete(b.buf); @@ -283,6 +288,11 @@ buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { s.stream_vtable = _buffer_vtable; return; } +buffer_to_writer :: proc(b: ^Buffer) -> (s: io.Writer) { + s.stream_data = b; + s.stream_vtable = _buffer_vtable; + return; +} @(private) _buffer_vtable := &io.Stream_VTable{ diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin index 61ae4631a..de640421f 100644 --- a/core/bytes/bytes.odin +++ b/core/bytes/bytes.odin @@ -743,13 +743,12 @@ split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator return buf; } -/* // scrub scruvs invalid utf-8 characters and replaces them with the replacement string // Adjacent invalid bytes are only replaced once scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte { str := s; - b: Builder; - init_builder(&b, 0, len(s), allocator); + b: Buffer; + buffer_init_allocator(&b, 0, len(s), allocator); has_error := false; cursor := 0; @@ -761,11 +760,11 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> if r == utf8.RUNE_ERROR { if !has_error { has_error = true; - write(&b, origin[:cursor]); + buffer_write(&b, origin[:cursor]); } } else if has_error { has_error = false; - write(&b, replacement); + buffer_write(&b, replacement); origin = origin[cursor:]; cursor = 0; @@ -775,9 +774,8 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> str = str[w:]; } - return to_string(b); + return buffer_to_bytes(&b); } -*/ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte { @@ -795,8 +793,7 @@ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte { return buf; } -/* -expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { +expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) -> []byte { if tab_size <= 0 { panic("tab size must be positive"); } @@ -806,20 +803,20 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return nil; } - b: Builder; - init_builder(&b, allocator); - writer := to_writer(&b); + b: Buffer; + buffer_init_allocator(&b, 0, len(s), allocator); + str := s; column: int; for len(str) > 0 { - r, w := utf8.decode_rune_in_string(str); + r, w := utf8.decode_rune(str); if r == '\t' { expand := tab_size - column%tab_size; for i := 0; i < expand; i += 1 { - io.write_byte(writer, ' '); + buffer_write_byte(&b, ' '); } column += expand; @@ -830,15 +827,14 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> column += w; } - io.write_rune(writer, r); + buffer_write_rune(&b, r); } str = str[w:]; } - return to_string(b); + return buffer_to_bytes(&b); } -*/ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) { i := index(str, sep); @@ -853,11 +849,10 @@ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) { return; } -/* center_justify :: centre_justify; // NOTE(bill): Because Americans exist -// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length -centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// centre_justify returns a byte slice with a pad byte slice at boths sides if the str's rune length is smaller than length +centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -866,21 +861,18 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + write_pad_string(&b, pad, pad_len, remains/2); + buffer_write(&b, str); + write_pad_string(&b, pad, pad_len, (remains+1)/2); - write_pad_string(w, pad, pad_len, remains/2); - io.write_string(w, str); - write_pad_string(w, pad, pad_len, (remains+1)/2); - - return to_string(b); + return buffer_to_bytes(&b); } -// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length -left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// left_justify returns a byte slice with a pad byte slice at left side if the str's rune length is smaller than length +left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -889,20 +881,17 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + buffer_write(&b, str); + write_pad_string(&b, pad, pad_len, remains); - io.write_string(w, str); - write_pad_string(w, pad, pad_len, remains); - - return to_string(b); + return buffer_to_bytes(&b); } -// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length -right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// right_justify returns a byte slice with a pad byte slice at right side if the str's rune length is smaller than length +right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -911,39 +900,35 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + write_pad_string(&b, pad, pad_len, remains); + buffer_write(&b, str); - write_pad_string(w, pad, pad_len, remains); - io.write_string(w, str); - - return to_string(b); + return buffer_to_bytes(&b); } @private -write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { +write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) { repeats := remains / pad_len; for i := 0; i < repeats; i += 1 { - io.write_string(w, pad); + buffer_write(b, pad); } n := remains % pad_len; p := pad; for i := 0; i < n; i += 1 { - r, width := utf8.decode_rune_in_string(p); - io.write_rune(w, r); + r, width := utf8.decode_rune(p); + buffer_write_rune(b, r); p = p[width:]; } } -*/ // fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space From e6dfc22b8a5309e9c387587108c73b4b49ac2ca4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Dec 2020 00:47:05 +0000 Subject: [PATCH 20/22] Add `bytes.buffer_write_to` and `bytes.buffer_read_from` --- core/bytes/buffer.odin | 64 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index af9c1e57f..95b816eac 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -8,6 +8,8 @@ MIN_READ :: 512; @(private) SMALL_BUFFER_SIZE :: 64; +// A Buffer is a variable-sized buffer of bytes with a io.Stream interface +// The zero value for Buffer is an empty buffer ready to use. Buffer :: struct { buf: [dynamic]byte, off: int, @@ -281,6 +283,51 @@ buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Er return string(slice), err; } +buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) { + b.last_read = .Invalid; + if byte_count := buffer_length(b); byte_count > 0 { + m, e := io.write(w, b.buf[b.off:]); + if m > byte_count { + panic("bytes.buffer_write_to: invalid io.write count"); + } + b.off += m; + n = i64(m); + if e != nil { + err = e; + return; + } + if m != byte_count { + err = .Short_Write; + return; + } + } + buffer_reset(b); + return; +} + +buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check { + b.last_read = .Invalid; + for { + i := _buffer_grow(b, MIN_READ); + resize(&b.buf, i); + m, e := io.read(r, b.buf[i:cap(b.buf)]); + if m < 0 { + err = .Negative_Read; + return; + } + + resize(&b.buf, i+m); + n += i64(m); + if e == .EOF { + return; + } + if e != nil { + err = e; + return; + } + } + return; +} buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { @@ -288,11 +335,6 @@ buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { s.stream_vtable = _buffer_vtable; return; } -buffer_to_writer :: proc(b: ^Buffer) -> (s: io.Writer) { - s.stream_data = b; - s.stream_vtable = _buffer_vtable; - return; -} @(private) _buffer_vtable := &io.Stream_VTable{ @@ -337,9 +379,13 @@ _buffer_vtable := &io.Stream_VTable{ buffer_destroy(b); return nil; }, - - // TODO(bill): write_to and read_from - // impl_write_to = nil, - // impl_read_from = nil, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_write_to(b, w); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_read_from(b, r); + }, }; From 720f2c7c61ddfc79deba2bb29b3727b50314cafb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Dec 2020 14:23:45 +0000 Subject: [PATCH 21/22] Allow `check_expr_with_type_hint` to allow assignment of types to typeid without requiring `typeid_of` --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b0012bfcc..63b275c99 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7910,7 +7910,9 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { err_str = "used as a value"; break; case Addressing_Type: - err_str = "is not an expression but a type"; + if (t == nullptr || !is_type_typeid(t)) { + err_str = "is not an expression but a type"; + } break; case Addressing_Builtin: err_str = "must be called"; From 3558848da818dc330d139ff5d756bb9b9498b1d4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Dec 2020 17:36:59 +0000 Subject: [PATCH 22/22] Fix ir_print.cpp for typeid constants --- src/ir.cpp | 12 ++++++++++-- src/ir_print.cpp | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index f69b25494..90cce3699 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1638,6 +1638,7 @@ irValue *ir_check_compound_lit_constant(irModule *m, Ast *expr) { if (expr == nullptr) { return nullptr; } + if (expr->kind == Ast_CompoundLit) { ast_node(cl, CompoundLit, expr); for_array(i, cl->elems) { @@ -1661,6 +1662,7 @@ irValue *ir_check_compound_lit_constant(irModule *m, Ast *expr) { } } + return nullptr; } @@ -6743,7 +6745,10 @@ irValue *ir_type_info(irProcedure *proc, Type *type) { return ir_emit_array_ep(proc, ir_global_type_info_data, ir_const_i32(id)); } -irValue *ir_typeid(irModule *m, Type *type) { +u64 ir_typeid_as_integer(irModule *m, Type *type) { + if (type == nullptr) { + return 0; + } type = default_type(type); u64 id = cast(u64)ir_type_info_index(m->info, type); @@ -6808,8 +6813,11 @@ irValue *ir_typeid(irModule *m, Type *type) { data |= (reserved &~ (1ull<<1)) << 63ull; // kind } + return id; +} - return ir_value_constant(t_typeid, exact_value_u64(data)); +irValue *ir_typeid(irModule *m, Type *type) { + return ir_value_constant(t_typeid, exact_value_u64(ir_typeid_as_integer(m, type))); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a58ddbe0f..594cc57c2 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -787,6 +787,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } switch (value.kind) { + case ExactValue_Typeid: + GB_ASSERT(is_type_typeid(type)); + ir_write_u64(f, ir_typeid_as_integer(m, value.value_typeid)); + break; + case ExactValue_Bool: if (value.value_bool) { ir_write_string(f, are_types_identical(type, t_llvm_bool) ? str_lit("true") : str_lit("1"));