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); }