mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Add odin/parser/parse_files.odin
This commit is contained in:
+48
-5
@@ -35,11 +35,6 @@ Node_State_Flag :: enum {
|
|||||||
}
|
}
|
||||||
Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
||||||
|
|
||||||
|
|
||||||
Comment_Group :: struct {
|
|
||||||
list: []tokenizer.Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
Node :: struct {
|
Node :: struct {
|
||||||
pos: tokenizer.Pos,
|
pos: tokenizer.Pos,
|
||||||
end: tokenizer.Pos,
|
end: tokenizer.Pos,
|
||||||
@@ -47,6 +42,54 @@ Node :: struct {
|
|||||||
derived: any,
|
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 {
|
Expr :: struct {
|
||||||
using expr_base: Node,
|
using expr_base: Node,
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
|||||||
align = ti.align;
|
align = ti.align;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch in node.derived {
|
||||||
|
case Package, File:
|
||||||
|
panic("Cannot clone this node type");
|
||||||
|
}
|
||||||
|
|
||||||
res := cast(^Node)mem.alloc(size, align);
|
res := cast(^Node)mem.alloc(size, align);
|
||||||
src: rawptr = node;
|
src: rawptr = node;
|
||||||
if node.derived != nil {
|
if node.derived != nil {
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
@@ -59,6 +59,18 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch n in &node.derived {
|
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 Bad_Expr:
|
||||||
case Ident:
|
case Ident:
|
||||||
case Implicit:
|
case Implicit:
|
||||||
@@ -252,29 +264,59 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
|
|
||||||
case Bad_Decl:
|
case Bad_Decl:
|
||||||
case Value_Decl:
|
case Value_Decl:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
walk_attribute_list(v, n.attributes[:]);
|
walk_attribute_list(v, n.attributes[:]);
|
||||||
walk_expr_list(v, n.names);
|
walk_expr_list(v, n.names);
|
||||||
if n.type != nil {
|
if n.type != nil {
|
||||||
walk(v, n.type);
|
walk(v, n.type);
|
||||||
}
|
}
|
||||||
walk_expr_list(v, n.values);
|
walk_expr_list(v, n.values);
|
||||||
|
if n.comment != nil {
|
||||||
|
walk(v, n.comment);
|
||||||
|
}
|
||||||
case Package_Decl:
|
case Package_Decl:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
|
if n.comment != nil {
|
||||||
|
walk(v, n.comment);
|
||||||
|
}
|
||||||
case Import_Decl:
|
case Import_Decl:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
|
if n.comment != nil {
|
||||||
|
walk(v, n.comment);
|
||||||
|
}
|
||||||
case Foreign_Block_Decl:
|
case Foreign_Block_Decl:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
walk_attribute_list(v, n.attributes[:]);
|
walk_attribute_list(v, n.attributes[:]);
|
||||||
if n.foreign_library != nil {
|
if n.foreign_library != nil {
|
||||||
walk(v, n.foreign_library);
|
walk(v, n.foreign_library);
|
||||||
}
|
}
|
||||||
walk(v, n.body);
|
walk(v, n.body);
|
||||||
case Foreign_Import_Decl:
|
case Foreign_Import_Decl:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
walk_attribute_list(v, n.attributes[:]);
|
walk_attribute_list(v, n.attributes[:]);
|
||||||
walk(v, n.name);
|
walk(v, n.name);
|
||||||
|
if n.comment != nil {
|
||||||
|
walk(v, n.comment);
|
||||||
|
}
|
||||||
|
|
||||||
case Proc_Group:
|
case Proc_Group:
|
||||||
walk_expr_list(v, n.args);
|
walk_expr_list(v, n.args);
|
||||||
case Attribute:
|
case Attribute:
|
||||||
walk_expr_list(v, n.elems);
|
walk_expr_list(v, n.elems);
|
||||||
case Field:
|
case Field:
|
||||||
|
if n.docs != nil {
|
||||||
|
walk(v, n.docs);
|
||||||
|
}
|
||||||
walk_expr_list(v, n.names);
|
walk_expr_list(v, n.names);
|
||||||
if n.type != nil {
|
if n.type != nil {
|
||||||
walk(v, n.type);
|
walk(v, n.type);
|
||||||
@@ -282,6 +324,9 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
|||||||
if n.default_value != nil {
|
if n.default_value != nil {
|
||||||
walk(v, n.default_value);
|
walk(v, n.default_value);
|
||||||
}
|
}
|
||||||
|
if n.comment != nil {
|
||||||
|
walk(v, n.comment);
|
||||||
|
}
|
||||||
case Field_List:
|
case Field_List:
|
||||||
for x in n.list {
|
for x in n.list {
|
||||||
walk(v, x);
|
walk(v, x);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 {
|
parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
||||||
zero_parser: {
|
zero_parser: {
|
||||||
p.prev_tok = {};
|
p.prev_tok = {};
|
||||||
@@ -139,8 +147,11 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
|||||||
|
|
||||||
pkg_name := expect_token_after(p, .Ident, "package");
|
pkg_name := expect_token_after(p, .Ident, "package");
|
||||||
if pkg_name.kind == .Ident {
|
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 '_'");
|
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;
|
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 {
|
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[:];
|
comments.list = list[:];
|
||||||
append(&p.file.comments, comments);
|
append(&p.file.comments, comments);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user