core library bugfixes; added cbindgen; packages are global now
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 20 May 2022 00:27:57 +0000 (19:27 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 20 May 2022 00:27:57 +0000 (19:27 -0500)
22 files changed:
core/alloc.onyx
core/alloc/arena.onyx
core/onyx/cbindgen.onyx [new file with mode: 0644]
core/onyx/fs.onyx
core/os/dir.onyx
core/os/process.onyx
core/runtime/common.onyx
core/runtime/info/proc_tags.onyx
core/std.onyx
include/small_windows.h
include/utils.h
scripts/c_binding.onyx [deleted file]
scripts/c_library.onyx [deleted file]
scripts/onyx-pkg.onyx
scripts/run_tests.onyx
src/builtins.c
src/onyx.c
src/onyx_runtime.c
src/parser.c
src/utils.c
tests/struct_use_pointer_member
tests/struct_use_pointer_member.onyx

index b4984f69bfae3d6aeb653455cb76ad04fd61eadf..8bbdcb7d729a530e0147bca04c4cf06e7e1bec63 100644 (file)
@@ -31,13 +31,11 @@ TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb
 heap_allocator : Allocator;
 
 // The global temp allocator, set up upon program intialization.
-#local
+#local #thread_local
 temp_state     : arena.ArenaState;
 temp_allocator : Allocator;
 
-init :: () {
-    heap.init();
-
+init_temp_allocator :: () {
     temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
     temp_allocator = as_allocator(^temp_state);
 }
index eaf8fdef041e31ded4e61cbd2095307a8d578abb..6224a442cf43a58c35fb91f652882169fb5ec281 100644 (file)
@@ -118,8 +118,9 @@ free :: (arena: ^ArenaState) {
 clear :: (arena: ^ArenaState) {
     walker := arena.first_arena.next;
     while walker != null {
+        next := walker.next;
         raw_free(arena.backing_allocator, walker);
-        walker = walker.next;
+        walker = next;
     }
 
     arena.size = sizeof rawptr;
diff --git a/core/onyx/cbindgen.onyx b/core/onyx/cbindgen.onyx
new file mode 100644 (file)
index 0000000..01ec30a
--- /dev/null
@@ -0,0 +1,371 @@
+package cbindgen
+
+//
+// How to use Onyx's cbindgen
+//
+// 1. Use generate_c_binding(config) to create a C source file for a given foreign block.
+// 2. Use compile_c_file() to compile the C source file into a native library.
+// 3. Run the build file with the --generate-foreign-info flag enabled.
+//
+// Full example:
+// #load "core/std"
+// #load "./module"
+//
+// use core
+//
+// main :: () {
+//     path := module_path(#file);
+//     c_file_path := string.concat(path, "temporary.c");
+//     success := cbindgen.generate_c_binding(.{
+//         output_file            = c_file_path,
+//         foreign_block          = something.foreign_block,
+//         cast_map               = .[],
+//         custom_implementations = .[],
+//         preamble               = .[
+// """
+// #include "..."
+// #define ...
+// // ...
+// """
+//         ],
+//     });
+//
+//     if !success do os.exit(1);
+//
+//     success = cbindgen.compile_c_file(c_file_path, string.concat(path, "library"));
+//     if !success do os.exit(1);
+//
+//     os.remove_file(c_file_path);
+// }
+//
+
+#if #defined (runtime.Generated_Foreign_Info) {
+
+#if runtime.compiler_os == .Linux {
+    #if #defined (runtime.vars.CC) {
+        Linux_Compiler :: runtime.vars.CC    
+    } else {
+        Linux_Compiler :: "/usr/bin/gcc"
+    }
+}
+
+
+use core
+use simd
+
+Cast_Mapping :: struct {
+    type: type_expr;
+    name: str;
+}
+
+Impl_Mapping :: struct {
+    name: str;
+    impl: str;
+}
+
+Binding_Config :: struct {
+    foreign_block: runtime.info.foreign_block;
+    preamble: [] str;
+    output_file: str;
+    cast_map: [] Cast_Mapping;
+    custom_implementations: [] Impl_Mapping;
+    name_map: (str) -> str = null_proc;
+}
+
+generate_c_binding :: (use binding_config: Binding_Config) -> bool {
+    wrote := false;
+    for file: os.with_file(output_file, .Write) {
+        writer := io.writer_make(file);
+        fb := runtime.info.get_foreign_block(foreign_block);
+
+        write_file_introduction(^writer, preamble, fb.module_name);
+
+        for fb.funcs {
+            for impl: custom_implementations {
+                if impl.name == it.name {
+                    io.write(^writer, impl.impl);
+                    io.write(^writer, "\n");
+                    continue continue;
+                }
+            }
+
+            write_function_body(^writer, it, cast_map, name_map);
+        }
+
+        write_library_block(^writer, fb.funcs);
+        wrote = true;
+    }
+
+    return wrote;
+}
+
+//
+// Call as module_path(#file)
+module_path :: (file_path: str) -> str {
+    out := file_path;
+    while out.count > 0 {
+        if out[out.count - 1] == #char "/" || out[out.count - 1] == #char "\\" do break;
+        out.count -= 1;
+    }
+
+    return out;
+}
+
+//
+// This is not as platform independent as I would like.
+// I don't know the best, most compatible way to compile DLLs on Windows.
+// Maybe those DLL can be shipped with the library, as most Windows is x86_64.
+//
+compile_c_file :: (
+    path: str, dest: str,
+    includes: [] str = .[],
+    libraries: [] str = .[],
+    flags := "") -> bool {
+    #if runtime.compiler_os == .Linux {
+        args: [..] str;
+        args << "-shared";
+        args << "-fPIC";
+        args << path;
+
+        args << "-I";
+        args << "/usr/share/onyx/include";
+
+        for includes {
+            args << "-I";
+            args << it;
+        }
+
+        if flags != "" {
+            for string.split(flags, #char " ") do args << it;
+        }
+
+        args << "-o";
+        args << tprintf("{}.so", dest);
+
+        for libraries {
+            args << aprintf("-l{}", it);
+        }
+
+        proc := os.process_spawn(Linux_Compiler, args);
+        defer os.process_destroy(^proc);
+
+        proc_reader := io.reader_make(^proc);
+        output := io.read_all(^proc_reader);
+        defer memory.free_slice(^output);
+
+        exit := os.process_wait(^proc);
+        if exit != .Success {
+            eprintf("Failed to compile {}\n", path);
+            eprintf("{}\n", output);
+        }
+
+        return exit == .Success;
+    }
+
+    #if runtime.compiler_os == .Windows {
+        return true;
+    }
+}
+
+#local {
+
+    write_file_introduction :: (writer: ^io.Writer, preamble: [] str, name: str) {
+        io.write_format(writer, "//\n");
+        io.write_format(writer, "// THIS FILE WAS AUTOMATICALLY GENERATED.\n");
+        io.write_format(writer, "//\n");
+
+        for text: preamble {
+            io.write_format(writer, "{}\n", text);
+        }
+
+        io.write_format(writer, """
+#define ONYX_LIBRARY_NAME {}
+#include "onyx_library.h"
+
+#define P(i, k) (params->data[i].of.k)
+
+""", name);
+    }
+
+    write_function_body :: (writer, ff, cast_map, name_map) => {
+        use runtime.info;
+
+        method_type := ff.type;
+        method_info := cast (^Type_Info_Function) get_type_info(method_type);
+        assert(method_info.kind == .Function, "Expected function type.");
+
+        io.write_format(writer, "ONYX_DEF({}, (", ff.name);
+        i := 0;
+        for method_info.parameter_types {
+            io.write(writer, type_encoding(it));
+
+            if i != method_info.parameter_types.count - 1 {
+                io.write(writer, ", ");
+            }
+            i += 1;
+        }
+
+        io.write(writer, "), (");
+        io.write(writer, type_encoding(method_info.return_type));
+        io.write(writer, ")) {\n");
+
+        method_name := ff.name;
+        if name_map != null_proc {
+            method_name = name_map(method_name);
+        }
+
+        print_body(writer, method_name, method_info, cast_map);
+
+        io.write(writer, "}\n\n");
+    }
+
+    write_library_block :: (writer, funcs) => {
+        io.write(writer, "\n\n");
+        io.write(writer, "ONYX_LIBRARY {\n");
+        for funcs {
+            io.write_format(writer, "    ONYX_FUNC({})\n", it.name);
+        }
+        io.write(writer, "    NULL\n");
+        io.write(writer, "};");
+    }
+
+    print_body :: (writer, method_name, method_info, cast_map) => {
+        use runtime.info;
+        call := io.dynamic_string_stream_make();
+        defer io.dynamic_string_stream_free(^call);
+        callw := io.writer_make(^call);
+
+        param_num := 0;
+        for method_info.parameter_types {
+            if get_type_info(it).kind == .Slice {
+                io.write_format(^callw, "ONYX_PTR(P({}, i32)), P({}, i32)", param_num, param_num + 1);
+                param_num += 1;
+
+            } elseif is_pointer(it) {
+                io.write_format(^callw, "ONYX_PTR(P({}, i32))", param_num);
+
+            } else {
+                matched := false;
+                for^ m: cast_map {
+                    if m.type == it {
+                        io.write_format(^callw, "({}) P({}, {})", m.name, param_num, type_to_wasm_type(it));
+                        matched = true;
+                        break;
+                    }
+                }
+                
+                if !matched do io.write_format(^callw, "P({}, {})", param_num, type_to_wasm_type(it));
+            }
+
+            io.write_format(^callw, ", ");
+            param_num += 1;
+        }
+
+        call_str := call->to_str();
+        wasm_return_type := type_to_wasm_type(method_info.return_type);
+        switch wasm_return_type {
+            case ""    do io.write_format(writer, "    {}({});\n", method_name, call_str[0..call_str.count-2]);
+            case "i32" do io.write_format(writer, "    results->data[0] = WASM_I32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
+            case "i64" do io.write_format(writer, "    results->data[0] = WASM_I64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
+            case "f32" do io.write_format(writer, "    results->data[0] = WASM_F32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
+            case "f64" do io.write_format(writer, "    results->data[0] = WASM_F64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
+        }
+
+        io.write_format(writer, "    return NULL;\n");
+    }
+
+    type_to_wasm_type :: (t: type_expr) -> str {
+        use runtime.info;
+
+        param_info := get_type_info(t);
+        switch param_info.kind {
+            case .Basic do switch t {
+                case bool do return "i32";
+                case i8   do return "i32";
+                case u8   do return "i32";
+                case i16  do return "i32";
+                case u16  do return "i32";
+                case i32  do return "i32";
+                case u32  do return "i32";
+                case i64  do return "i64";
+                case u64  do return "i64";
+
+                case f32  do return "f32";
+                case f64  do return "f64";
+
+                case rawptr do return "i32"; // This will have to depend on the pointer size...
+                
+                case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "v128";
+
+                case type_expr do return "i32";
+            }
+
+            case .Pointer do return "i32"; // This will also have to depend on the pointer size...
+            case .Function do assert(false, "Passing functions between wasm and c is not yet supported.");
+            case .Array do return "i32";
+            case .Slice do assert(false, "ASDFASDF");
+            case .Enum do return type_to_wasm_type((cast(^Type_Info_Enum) param_info).backing_type);
+            case .Distinct do return type_to_wasm_type((cast(^Type_Info_Distinct) param_info).base_type);
+
+            case .Struct {
+                s_info := cast(^Type_Info_Struct) param_info;
+                if s_info.members.count == 1 {
+                    return type_to_wasm_type(s_info.members[0].type);
+                }
+
+                assert(false, "Passing structures between wasm and c is not yet supported.");
+            }
+        }
+
+        return "";
+    }
+
+    type_encoding :: (t: type_expr) -> str {
+        use runtime.info;
+
+        param_info := get_type_info(t);
+        switch param_info.kind {
+            case .Basic do switch t {
+                case bool do return "WASM_I32";
+                case i8   do return "WASM_I32";
+                case u8   do return "WASM_I32";
+                case i16  do return "WASM_I32";
+                case u16  do return "WASM_I32";
+                case i32  do return "WASM_I32";
+                case u32  do return "WASM_I32";
+                case i64  do return "WASM_I64";
+                case u64  do return "WASM_I64";
+
+                case f32  do return "WASM_F32";
+                case f64  do return "WASM_F64";
+
+                case rawptr do return "WASM_I32"; // This will have to depend on the pointer size...
+                
+                case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "WASM_V128";
+
+                case type_expr do return "WASM_I32";
+            }
+
+            case .Pointer do return "WASM_I32"; // This will also have to depend on the pointer size...
+            case .Function do assert(false, "Passing functions between wasm and c is not yet supported.");
+            case .Array do return "WASM_I32";
+            case .Slice do return "WASM_I32,WASM_I32";
+            case .Enum do return type_encoding((cast(^Type_Info_Enum) param_info).backing_type);
+            case .Distinct do return type_encoding((cast(^Type_Info_Distinct) param_info).base_type);
+
+            case .Struct {
+                s_info := cast(^Type_Info_Struct) param_info;
+                if s_info.members.count == 1 {
+                    return type_encoding(s_info.members[0].type);
+                    return;
+                }
+
+                assert(false, "Passing structures between wasm and c is not yet supported.");
+            }
+        }
+
+        return "";
+    }
+}
+
+}
index ea0124c0ecc649f6eb718196d1bd97cad62d0e1c..9ba45011d0641582733022bcb092a7e05e75efa2 100644 (file)
@@ -36,6 +36,7 @@ __file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError)
     __dir_open   :: (path: str, dir: ^DirectoryData) -> bool ---
     __dir_close  :: (dir: DirectoryData) -> void ---
     __dir_read   :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool ---
+    __dir_create :: (path: str) -> bool ---
     __dir_remove :: (path: str) -> bool ---
 }
 
index b97c7878226451bee0f845ac03c6d1f304f91b34..5594b588484b4fa6c6e85b6eb136644567e55e6a 100644 (file)
@@ -40,6 +40,7 @@ dir_read :: (dir: Directory, out_entry: ^DirectoryEntry) -> bool {
     return fs.__dir_read(dir, out_entry);
 }
 
+dir_create :: fs.__dir_create
 dir_exists :: fs.__file_exists
 dir_remove :: fs.__dir_remove
-dir_rename :: fs.__file_rename
\ No newline at end of file
+dir_rename :: fs.__file_rename
index e685849b25818adf6993c8a10505f7881c3dec36..ab3e53be2bab0a5f1bc07788772d2774dc0576a6 100644 (file)
@@ -16,8 +16,8 @@ Process :: struct {
     process_handle: Handle;
 }
 
-process_spawn :: (path: str, args: [] str, non_blocking_io := false) -> Process {
-    handle := __process_spawn(path, args, non_blocking_io);
+process_spawn :: (path: str, args: [] str, non_blocking_io := false, starting_directory := "") -> Process {
+    handle := __process_spawn(path, args, non_blocking_io, starting_directory);
 
     return .{
         .{ ^process_stream_vtable },
@@ -81,7 +81,7 @@ process_destroy :: (use p: ^Process) => {
 }
 
 #foreign "onyx_runtime" {
-    __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool) -> Process.Handle ---
+    __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> Process.Handle ---
     __process_read    :: (handle: Process.Handle, buffer: [] u8) -> i32 ---
     __process_write   :: (handle: Process.Handle, buffer: [] u8) -> i32 ---
     __process_kill    :: (handle: Process.Handle) -> bool ---
index f511b7afcd9e6d391fc2ad7f844e0c76864edac3..1abf76176ea858e9542f906b3bc1091bde5e50e1 100644 (file)
@@ -28,7 +28,7 @@ __assert_handler :: (msg: str, site: CallSite) {
 __runtime_initialize :: () {
     __initialize_data_segments();
 
-    alloc.init();
+    alloc.heap.init();
     __thread_initialize();
 
     #if Multi_Threading_Enabled do thread.__initialize();
@@ -47,6 +47,8 @@ __thread_initialize :: macro () {
         memory.set(__tls_base, 0, __tls_size);
     }
 
+    alloc.init_temp_allocator();
+
     __initialize(^context);
     context.allocator = alloc.heap_allocator;
     context.temp_allocator = alloc.temp_allocator;
index 3b3112247f6b3f75eb5ac24fde51a9109d5c613a..b110725561f1420373d1fc3b1dc71af70af250df 100644 (file)
@@ -15,7 +15,7 @@ get_tags_for_procedure :: (func: $T) -> [] any {
     if get_type_info(T).kind != .Function do return .[];
 
     for tagged_procedures {
-        if it.func == func do return it.tags;
+        if (*cast(^T) ^it.func) == func do return it.tags;
     }
 
     return .[];
index c32ea1da46f93483afaf0384855bb6fddcd4de12..fe416ce73a10dc9fd7da785bb18d23815632f33c 100644 (file)
@@ -55,6 +55,7 @@ package core
 
     #load "./onyx/fs"
     #load "./onyx/cptr"
+    #load "./onyx/cbindgen"
 }
 #if runtime.runtime == .Wasi   {
     #load "./wasi/wasi"
index fe2ba6fa4182e0a168c4f549141bd40c21079a8b..28cea0bd0155711b0462e80a4b9501ab3ef9911c 100644 (file)
@@ -419,6 +419,7 @@ GB_DLL_IMPORT BOOL   WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX
 GB_DLL_IMPORT BOOL   WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists);
 GB_DLL_IMPORT BOOL   WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f);
 GB_DLL_IMPORT BOOL   WINAPI DeleteFileA     (char const *path);
+GB_DLL_IMPORT BOOL   WINAPI CreateDirectoryA(char const *path);
 GB_DLL_IMPORT BOOL   WINAPI RemoveDirectoryA(char const *path);
 GB_DLL_IMPORT BOOL   WINAPI MoveFileA       (char const *old_path, char const *new_path);
 
index 69a518d80c1a42cffc48384119470e227f108907..46ee92b68606bb18779b172cb10ed48d04b0fe4d 100644 (file)
@@ -11,7 +11,7 @@ extern bh_allocator global_heap_allocator;
 const char* onyx_ast_node_kind_string(AstKind kind);
 
 Package* package_lookup(char* package_name);
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc);
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos);
 void package_track_use_package(Package* package, Entity* entity);
 void package_reinsert_use_packages(Package* package);
 
diff --git a/scripts/c_binding.onyx b/scripts/c_binding.onyx
deleted file mode 100644 (file)
index 95e41ae..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-package c_binding
-
-use package core
-use package simd
-#local info :: package runtime.info
-
-#if !#defined((package runtime).Generated_Foreign_Info) {
-    #error "Please run this script with the '--generate-foreign-info' flag."
-}
-
-Cast_Mapping :: struct {
-    type: type_expr;
-    name: str;
-}
-
-Impl_Mapping :: struct {
-    name: str;
-    impl: str;
-}
-
-Binding_Config :: struct {
-    foreign_block: info.foreign_block;
-    preamble: [] str;
-    output_file: str;
-    cast_map: [] Cast_Mapping;
-    custom_implementations: [] Impl_Mapping;
-    name_map: (str) -> str = null_proc;
-}
-
-build_c_binding :: (use binding_config: Binding_Config) -> bool {
-    for file: os.with_file(output_file, .Write) {
-        writer := io.writer_make(file);
-        fb := info.get_foreign_block(foreign_block);
-
-        write_file_introduction(^writer, preamble, fb.module_name);
-
-        for fb.funcs {
-            for impl: custom_implementations {
-                if impl.name == it.name {
-                    io.write(^writer, impl.impl);
-                    io.write(^writer, "\n");
-                    continue continue;
-                }
-            }
-
-            write_function_body(^writer, it, cast_map, name_map);
-        }
-
-        write_library_block(^writer, fb.funcs);
-    }
-
-    return true;
-}
-
-write_file_introduction :: (writer: ^io.Writer, preamble: [] str, name: str) {
-    io.write_format(writer, "//\n");
-    io.write_format(writer, "// THIS FILE WAS AUTOMATICALLY GENERATED.\n");
-    io.write_format(writer, "//\n");
-
-    for text: preamble {
-        io.write_format(writer, "{}\n", text);
-    }
-
-    io.write_format(writer, """
-#define ONYX_LIBRARY_NAME {}
-#include "onyx_library.h"
-
-#define P(i, k) (params->data[i].of.k)
-
-""", name);
-}
-
-write_function_body :: (writer, ff, cast_map, name_map) => {
-    use info;
-
-    method_type := ff.type;
-    method_info := cast (^Type_Info_Function) get_type_info(method_type);
-    assert(method_info.kind == .Function, "Expected function type.");
-
-    io.write_format(writer, "ONYX_DEF({}, (", ff.name);
-    i := 0;
-    for method_info.parameter_types {
-        io.write(writer, type_encoding(it));
-
-        if i != method_info.parameter_types.count - 1 {
-            io.write(writer, ", ");
-        }
-        i += 1;
-    }
-
-    io.write(writer, "), (");
-    io.write(writer, type_encoding(method_info.return_type));
-    io.write(writer, ")) {\n");
-
-    method_name := ff.name;
-    if name_map != null_proc {
-        method_name = name_map(method_name);
-    }
-
-    print_body(writer, method_name, method_info, cast_map);
-
-    io.write(writer, "}\n\n");
-}
-
-write_library_block :: (writer, funcs) => {
-    io.write(writer, "\n\n");
-    io.write(writer, "ONYX_LIBRARY {\n");
-    for funcs {
-        io.write_format(writer, "    ONYX_FUNC({})\n", it.name);
-    }
-    io.write(writer, "    NULL\n");
-    io.write(writer, "};");
-}
-
-print_body :: (writer, method_name, method_info, cast_map) => {
-    use info;
-    call := io.dynamic_string_stream_make();
-    defer io.dynamic_string_stream_free(^call);
-    callw := io.writer_make(^call);
-
-    param_num := 0;
-    for method_info.parameter_types {
-        if get_type_info(it).kind == .Slice {
-            io.write_format(^callw, "ONYX_PTR(P({}, i32)), P({}, i32)", param_num, param_num + 1);
-            param_num += 1;
-
-        } elseif is_pointer(it) {
-            io.write_format(^callw, "ONYX_PTR(P({}, i32))", param_num);
-
-        } else {
-            matched := false;
-            for^ m: cast_map {
-                if m.type == it {
-                    io.write_format(^callw, "({}) P({}, {})", m.name, param_num, type_to_wasm_type(it));
-                    matched = true;
-                    break;
-                }
-            }
-            
-            if !matched do io.write_format(^callw, "P({}, {})", param_num, type_to_wasm_type(it));
-        }
-
-        io.write_format(^callw, ", ");
-        param_num += 1;
-    }
-
-    call_str := call->to_str();
-    wasm_return_type := type_to_wasm_type(method_info.return_type);
-    switch wasm_return_type {
-        case ""    do io.write_format(writer, "    {}({});\n", method_name, call_str[0..call_str.count-2]);
-        case "i32" do io.write_format(writer, "    results->data[0] = WASM_I32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
-        case "i64" do io.write_format(writer, "    results->data[0] = WASM_I64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
-        case "f32" do io.write_format(writer, "    results->data[0] = WASM_F32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
-        case "f64" do io.write_format(writer, "    results->data[0] = WASM_F64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]);
-    }
-
-    io.write_format(writer, "    return NULL;\n");
-}
-
-type_to_wasm_type :: (t: type_expr) -> str {
-    use info;
-
-    param_info := get_type_info(t);
-    switch param_info.kind {
-        case .Basic do switch t {
-            case bool do return "i32";
-            case i8   do return "i32";
-            case u8   do return "i32";
-            case i16  do return "i32";
-            case u16  do return "i32";
-            case i32  do return "i32";
-            case u32  do return "i32";
-            case i64  do return "i64";
-            case u64  do return "i64";
-
-            case f32  do return "f32";
-            case f64  do return "f64";
-
-            case rawptr do return "i32"; // This will have to depend on the pointer size...
-            
-            case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "v128";
-
-            case type_expr do return "i32";
-        }
-
-        case .Pointer do return "i32"; // This will also have to depend on the pointer size...
-        case .Function do assert(false, "Passing functions between wasm and c is not yet supported.");
-        case .Array do return "i32";
-        case .Slice do assert(false, "ASDFASDF");
-        case .Enum do return type_to_wasm_type((cast(^Type_Info_Enum) param_info).backing_type);
-        case .Distinct do return type_to_wasm_type((cast(^Type_Info_Distinct) param_info).base_type);
-
-        case .Struct {
-            s_info := cast(^Type_Info_Struct) param_info;
-            if s_info.members.count == 1 {
-                return type_to_wasm_type(s_info.members[0].type);
-            }
-
-            assert(false, "Passing structures between wasm and c is not yet supported.");
-        }
-    }
-
-    return "";
-}
-
-type_encoding :: (t: type_expr) -> str {
-    use info;
-
-    param_info := get_type_info(t);
-    switch param_info.kind {
-        case .Basic do switch t {
-            case bool do return "WASM_I32";
-            case i8   do return "WASM_I32";
-            case u8   do return "WASM_I32";
-            case i16  do return "WASM_I32";
-            case u16  do return "WASM_I32";
-            case i32  do return "WASM_I32";
-            case u32  do return "WASM_I32";
-            case i64  do return "WASM_I64";
-            case u64  do return "WASM_I64";
-
-            case f32  do return "WASM_F32";
-            case f64  do return "WASM_F64";
-
-            case rawptr do return "WASM_I32"; // This will have to depend on the pointer size...
-            
-            case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "WASM_V128";
-
-            case type_expr do return "WASM_I32";
-        }
-
-        case .Pointer do return "WASM_I32"; // This will also have to depend on the pointer size...
-        case .Function do assert(false, "Passing functions between wasm and c is not yet supported.");
-        case .Array do return "WASM_I32";
-        case .Slice do return "WASM_I32,WASM_I32";
-        case .Enum do return type_encoding((cast(^Type_Info_Enum) param_info).backing_type);
-        case .Distinct do return type_encoding((cast(^Type_Info_Distinct) param_info).base_type);
-
-        case .Struct {
-            s_info := cast(^Type_Info_Struct) param_info;
-            if s_info.members.count == 1 {
-                return type_encoding(s_info.members[0].type);
-                return;
-            }
-
-            assert(false, "Passing structures between wasm and c is not yet supported.");
-        }
-    }
-
-    return "";
-}
diff --git a/scripts/c_library.onyx b/scripts/c_library.onyx
deleted file mode 100644 (file)
index 69cef4e..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-
-#load "core/std"
-use package core
-
-runtime :: package runtime
-
-Linux_Compiler :: "/usr/bin/gcc"
-
-
-//
-// Call as module_path(#file)
-module_path :: (file_path: str) -> str {
-    out := file_path;
-    while out.count > 0 {
-        if out[out.count - 1] == #char "/" || out[out.count - 1] == #char "\\" do break;
-        out.count -= 1;
-    }
-
-    return out;
-}
-
-compile_c_file :: (path: str, dest: str, includes: [] str = .[], libraries: [] str = .[], flags := "") -> bool {
-    printf("Compiling '{}' -> '{}'.\n", path, dest);
-
-    #if runtime.compiler_os == .Linux {
-        args: [..] str;
-        args << "-shared";
-        args << "-fPIC";
-        args << path;
-
-        args << "-I";
-        args << "/usr/share/onyx/include";
-
-        for includes {
-            args << "-I";
-            args << it;
-        }
-
-        if flags != "" {
-            for string.split(flags, #char " ") do args << it;
-        }
-
-        args << "-o";
-        args << dest;
-
-        for libraries {
-            args << aprintf("-l{}", it);
-        }
-
-        proc := os.process_spawn(Linux_Compiler, args);
-        defer os.process_destroy(^proc);
-
-        proc_reader := io.reader_make(^proc);
-        output := io.read_all(^proc_reader);
-        defer memory.free_slice(^output);
-
-        exit := os.process_wait(^proc);
-        println(exit);
-        println(output);
-        return exit == .Success;
-    }
-
-    #if runtime.compiler_os == .Windows {
-        return true;
-    }
-}
\ No newline at end of file
index 3df59bd00adfbf44420ee950c49d3014e7931084..9928ccb46b113e76104b11bf3b94344ae4633156 100644 (file)
@@ -3,8 +3,8 @@
 
 Version :: SemVer.{0, 1, 1}
 
-use package core
-use package core.intrinsics.onyx {__initialize, __zero_value}
+use core
+use core.intrinsics.onyx {__initialize, __zero_value}
 
 global_arguments: struct {
     #tag "--config-file"
@@ -29,8 +29,7 @@ main :: (args: [] cstr) {
     loaded_config_file := false;
     defer if loaded_config_file do store_config_file();
 
-    info :: package runtime.info
-    command_procedures := info.get_procedures_with_tag(Command);
+    command_procedures := runtime.info.get_procedures_with_tag(Command);
     defer delete(^command_procedures);
     for command_procedures {
         if it.tag.command == command {
@@ -65,8 +64,7 @@ run_help_command :: (args: [] cstr) {
     printf("onyx-pkg version {}\n", Version);
     printf("Package dependency resolver and synchronizer for Onyx.\n\nUsage:\n");
 
-    info :: package runtime.info
-    command_procedures := info.get_procedures_with_tag(Command);
+    command_procedures := runtime.info.get_procedures_with_tag(Command);
     defer delete(^command_procedures);
     for command_procedures {
         printf("{}\n", it.tag.description);
@@ -307,12 +305,50 @@ run_sync_command :: (args: [] cstr) {
             if it.type != .Directory do continue;
 
             if !array.contains(needed_dependency_folders, it->name()) {
-                os.remove_directory(tprintf("{}/{}", config.config.lib_source_directory, it->name()));
+                package_folder := tprintf("{}/{}", config.config.lib_source_directory, it->name());
+                attempt_remove_native_library(package_folder);
+                os.remove_directory(package_folder);
             }
         }
     }
 }
 
+#tag Command.{ "rebuild", "Rebuild native library for a package", "package-or-url",
+"""
+package-or-url   Git repository name or package name on disk to remove.
+"""
+}
+run_rebuild_command :: (args: [] cstr) {
+    if args.count < 1 {
+        eprintf("Expected package name.");
+        return;
+    }
+
+    dep := string.as_str(args[0]);
+
+    folder := dep;
+    if config.dependency_folders.folders->has(dep) {
+        folder = config.dependency_folders.folders[dep];
+    }
+
+    if rebuild_native_library(folder) {
+        printf("Successfully rebuilt {}.\n", dep);
+    } else {
+        printf("Failed to rebuild {}.\n", dep);
+        os.exit(1);
+    }
+}
+
+#local {
+    #if runtime.compiler_os == .Linux {
+        native_library_suffix :: ".so"
+    }
+    #if runtime.compiler_os == .Windows {
+        native_library_suffix :: ".dll"
+    }
+}
+
+
 install_package :: (pack: Package, downgrade_if_necessary := false) -> (bool, installed_folder: str) {
     if config.dependency_folders.folders->has(pack.repo) {
         folder_name := config.dependency_folders.folders[pack.repo];
@@ -342,8 +378,9 @@ install_package :: (pack: Package, downgrade_if_necessary := false) -> (bool, in
     }
 
     assert(config.dependency_folders.folders->has(pack.repo), "");
-    folder_name := config.dependency_folders.folders[pack.repo];
-    return true, folder_name;
+    folder_name     := config.dependency_folders.folders[pack.repo];
+    install_success := run_native_library_installation(folder_name);
+    return install_success, folder_name;
 }
 
 uninstall_package :: (pack: Package) -> bool {
@@ -353,9 +390,34 @@ uninstall_package :: (pack: Package) -> bool {
 
         if os.file_exists(package_folder) {
             // Should this check if the version to be deleted is the one that is actually installed?
+            attempt_remove_native_library(package_folder);
             os.remove_directory(package_folder);
         }
+        
+        return true;
     }
+
+    return false;
+}
+
+attempt_remove_native_library :: (package_folder: str) -> bool {
+    inner_config: Config;
+    for os.with_file(tprintf("{}/onyx-pkg.ini", package_folder)) {
+        r := io.reader_make(it);
+        result, error := parse_ini_file(^r, ^inner_config);
+
+        if result != .Success do return false;
+        if string.is_empty(inner_config.native_library.library) do return false;
+
+        os.remove_file(tprintf("{}/{}{}", config.config.lib_bin_directory, inner_config.native_library.library, native_library_suffix));             
+    }
+
+    return true;
+}
+
+rebuild_native_library :: (folder: str) -> bool {
+    attempt_remove_native_library(tprintf("{}/{}", config.config.lib_source_directory, folder));
+    return run_native_library_installation(folder);
 }
 
 get_installed_version_of_package :: (package_path: str) -> SemVer {
@@ -372,6 +434,47 @@ get_installed_version_of_package :: (package_path: str) -> SemVer {
     return .{0, 0, 0};
 }
 
+run_native_library_installation :: (folder: str) -> bool {
+    inner_config: Config;
+    for os.with_file(tprintf("{}/{}/onyx-pkg.ini", config.config.lib_source_directory, folder)) {
+        r := io.reader_make(it);
+        result, error := parse_ini_file(^r, ^inner_config);
+
+        if result != .Success do return false;
+        if string.is_empty(inner_config.native_library.build_cmd) do return true;
+
+        args := string.split(inner_config.native_library.build_cmd, #char " ", context.temp_allocator);
+        cmd  := args[0];
+        args  = args[1 .. args.count];
+
+        installed_dest := tprintf("{}/{}", config.config.lib_source_directory, folder);
+        build_proc     := os.process_spawn(cmd, args, starting_directory=installed_dest);
+        build_result   := os.process_wait(^build_proc);
+
+        if build_result != .Success {
+            eprintf("Failed to build native library in {}.\n", folder);
+            return false;
+        }
+        
+        if !os.dir_exists(config.config.lib_bin_directory) {
+            if !os.dir_create(config.config.lib_bin_directory) {
+                eprintf("Failed to create native library directory, {}.\n", config.config.lib_bin_directory);
+                return false;
+            }
+        }
+
+        source_path := tprintf("{}/{}{}", installed_dest, inner_config.native_library.library, native_library_suffix);
+        dest_path   := tprintf("{}/{}{}", config.config.lib_bin_directory, inner_config.native_library.library, native_library_suffix);
+        success := os.rename_file(source_path, dest_path);
+                                  
+        if !success {
+            eprintf("Failed to move native library to final destination.\n {} -> {}\n", source_path, dest_path);
+        }
+
+        return success;
+    }
+}
+
 
 #tag conv.Custom_Parse.{parse}
 #tag conv.Custom_Format.{format}
@@ -425,9 +528,8 @@ Package :: struct {
     version: SemVer;
 }
 
-#local runtime :: package runtime
 #if runtime.compiler_os == .Linux {
-    git_path :: "/usr/bin/git"
+    git_path :: "git"
 }
 #if runtime.compiler_os == .Windows {
     git_path :: "git.exe"
@@ -452,9 +554,7 @@ Git :: struct {
             }
         }
 
-        if os.process_wait(^git_proc) == .Error {
-            eprintf("Encountered error running 'git' command.");
-        }
+        os.process_wait(^git_proc);
 
         return versions;
     }
@@ -565,6 +665,12 @@ Config :: struct {
     }
     config: Config = .{};
 
+    Native_Library :: struct {
+        build_cmd: str;
+        library:   str;
+    }
+    native_library: Native_Library;
+
     Dependencies :: struct {
         dependencies: Map(str, SemVer);
 
@@ -676,7 +782,7 @@ store_config_file :: () -> bool {
 }
 
 parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseError) {
-    info :: package runtime.info
+    info :: runtime.info
 
     line := 1;
     error :: macro (msg: str) {
@@ -743,7 +849,6 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE
 
         field_name := r->read_until(#char "=", allocator=context.temp_allocator);
         assert(r->read_byte() == #char "=", "expected =");
-        r->skip_whitespace();
 
         field := info.get_struct_member(active_item_type, string.strip_whitespace(field_name));
         target := cast(^u8) active_item_ptr + field.offset;
@@ -755,8 +860,9 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE
             // If the type is a string, then the value can be the entire line
             // of text. No need to force the quotes.
             if field.type == str {
-                *cast(^str) target = string.alloc_copy(value_string)
-                                  |> string.strip_whitespace();
+                *cast(^str) target = value_string
+                                  |> string.strip_whitespace()
+                                  |> string.alloc_copy();
 
             } else {
                 error(aprintf("Failed to parse value."));
@@ -771,7 +877,7 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE
 }
 
 write_ini_file :: (w: ^io.Writer, output: any) -> bool {
-    info :: package runtime.info
+    info :: runtime.info
 
     output_info := cast(^info.Type_Info_Struct) info.get_type_info(output.type);
     if output_info.kind != .Struct do return false;
index 0df2023c4857a48b2d394bb77c0e22a62558aac4..01020d8c99f9dcfd4c4cadd0d995009b7323c9e8 100644 (file)
@@ -5,9 +5,8 @@
 
 #load "core/std"
 
-use package core
-use package core.intrinsics.onyx { init }
-#local runtime :: package runtime
+use core
+use core.intrinsics.onyx { init }
 
 Color :: enum {
     White;
@@ -114,7 +113,7 @@ main :: (args) => {
 
     iter.parallel_for(cases, settings.threads, ^exec_context) {
         // Weird macros mean I have to forward external names
-        use package core
+        use core
         print_color :: print_color;
 
         args: [] str;
index 7f24614146d7e817d9b1867a56c0bfa60cc26ea1..0c3520e1cdb90d6e554c1a4bfaffd40bb956739c 100644 (file)
@@ -374,7 +374,7 @@ void initialize_builtins(bh_allocator a) {
         if (bsym->package == NULL)
             symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node);
         else {
-            Package* p = package_lookup_or_create(bsym->package, context.global_scope, a);
+            Package* p = package_lookup_or_create(bsym->package, context.global_scope, a, context.global_scope->created_at);
             assert(p);
 
             symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
@@ -382,7 +382,7 @@ void initialize_builtins(bh_allocator a) {
         bsym++;
     }
 
-    Package* p = package_lookup_or_create("builtin", context.global_scope, a);
+    Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at);
 
     builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
     if (builtin_string_type == NULL) {
@@ -480,7 +480,7 @@ void initalize_special_globals() {
 }
 
 void introduce_build_options(bh_allocator a) {
-    Package* p = package_lookup_or_create("runtime", context.global_scope, a);
+    Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at);
 
     AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime");
     if (Runtime_Type == NULL) {
index 1275ae23138abcaf4aa198bf04adbef7b0f6db81..c73d01efe83a0449cd595553ef46247251fce420 100644 (file)
@@ -235,7 +235,11 @@ static void context_init(CompileOptions* opts) {
     context.options = opts;
     context.cycle_detected = 0;
 
-    context.global_scope = scope_create(global_heap_allocator, NULL, (OnyxFilePos) { 0 });
+    OnyxFilePos internal_location = { 0 };
+    internal_location.filename = "<compiler internal>";
+    internal_location.line     = 1;
+    internal_location.column   = 1;
+    context.global_scope = scope_create(global_heap_allocator, NULL, internal_location);
     sh_new_arena(context.packages);
 
     // NOTE: This will be initialized upon the first call to entity_heap_insert.
index 4144d0822e4a9aad686603abb4afc2a63282b3b9..804dfc970fc71ea232440dedb3bfe612b97dd32b 100644 (file)
@@ -34,6 +34,7 @@ ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32))
     int   path_len = params->data[1].of.i32;
 
     char path[512] = {0};
+    path_len = bh_min(path_len, 511);
     strncpy(path, path_ptr, path_len);
     path[path_len] = 0;
 
@@ -77,6 +78,7 @@ ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
     int   path_len = params->data[1].of.i32;
 
     char path[512] = {0};
+    path_len = bh_min(path_len, 511);
     strncpy(path, path_ptr, path_len);
     path[path_len] = 0;
 
@@ -89,6 +91,7 @@ ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
     int   path_len = params->data[1].of.i32;
 
     char path[512] = {0};
+    path_len = bh_min(path_len, 511);
     strncpy(path, path_ptr, path_len);
     path[path_len] = 0;
 
@@ -190,6 +193,7 @@ ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
     int   old_path_len = params->data[1].of.i32;
 
     char old_path[512] = {0};
+    old_path_len = bh_min(old_path_len, 511);
     strncpy(old_path, old_path_ptr, old_path_len);
     old_path[old_path_len] = 0;
 
@@ -197,6 +201,7 @@ ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
     int   new_path_len = params->data[3].of.i32;
 
     char new_path[512] = {0};
+    new_path_len = bh_min(new_path_len, 511);
     strncpy(new_path, new_path_ptr, new_path_len);
     new_path[new_path_len] = 0;
 
@@ -220,6 +225,7 @@ ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
     int   path_len = params->data[1].of.i32;
 
     char path[512] = {0};
+    path_len = bh_min(path_len, 511);
     strncpy(path, path_ptr, path_len);
     path[path_len] = 0;
 
@@ -339,11 +345,32 @@ ONYX_DEF(__dir_close, (WASM_I64), ()) {
     return NULL;
 }
 
+ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) {
+    char *path_ptr = ONYX_PTR(params->data[0].of.i32);
+    int   path_len = params->data[1].of.i32;
+
+    char path[512] = {0};
+    path_len = bh_min(path_len, 511);
+    strncpy(path, path_ptr, path_len);
+    path[path_len] = 0;
+
+#ifdef _BH_WINDOWS
+    results->data[0] = WASM_I32_VAL(CreateDirectoryA(path, NULL));
+    return NULL;
+#endif
+
+#ifdef _BH_LINUX
+    results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0);
+    return NULL;
+#endif
+}
+
 ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) {
     char *path_ptr = ONYX_PTR(params->data[0].of.i32);
     int   path_len = params->data[1].of.i32;
 
     char path[512] = {0};
+    path_len = bh_min(path_len, 511);
     strncpy(path, path_ptr, path_len);
     path[path_len] = 0;
 
@@ -508,18 +535,27 @@ typedef struct OnyxProcess {
 #endif
 } OnyxProcess;
 
-ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
+ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) {
     char* process_str = ONYX_PTR(params->data[0].of.i32);
     i32   process_len = params->data[1].of.i32;
     i32   args_ptr    = params->data[2].of.i32;
     i32   args_len    = params->data[3].of.i32;
     b32   blocking_io = !params->data[4].of.i32;
+    char *cwd_str     = ONYX_PTR(params->data[5].of.i32);
+    i32   cwd_len     = params->data[6].of.i32;
 
     char process_path[1024];
     process_len = bh_min(1023, process_len);
     memcpy(process_path, process_str, process_len);
     process_path[process_len] = '\0';
 
+    char starting_dir[1024];
+    if (cwd_len > 0) {
+        cwd_len = bh_min(1023, cwd_len);
+        memcpy(starting_dir, cwd_str, cwd_len);
+        starting_dir[cwd_len] = '\0';
+    }
+
     OnyxProcess *process = malloc(sizeof(OnyxProcess));
     memset(process, 0, sizeof(*process));
     process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
@@ -568,7 +604,11 @@ ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (W
                     fcntl(1, F_SETFL, O_NONBLOCK);
                 }
 
-                execv(process_path, process_args);
+                if (cwd_len > 0) {
+                    chdir(starting_dir); // Switch current working directory.
+                }
+
+                execvp(process_path, process_args);
                 exit(-1);
                 break;
 
@@ -633,7 +673,12 @@ ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (W
 
         memset(&process->proc_info, 0, sizeof process->proc_info);
 
-        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, NULL, &startup, &process->proc_info);
+        char *working_dir = NULL;
+        if (cwd_len > 0) {
+            working_dir = starting_dir;
+        }
+
+        success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info);
         if (!success) {
             printf("FAILED TO CREATE PROCESS: %d\n", GetLastError());
             wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK
@@ -1201,6 +1246,7 @@ ONYX_LIBRARY {
     ONYX_FUNC(__dir_open)
     ONYX_FUNC(__dir_read)
     ONYX_FUNC(__dir_close)
+    ONYX_FUNC(__dir_create)
     ONYX_FUNC(__dir_remove)
 
     ONYX_FUNC(__spawn_thread)
index a68e574f7421026b0f37e007a82c01ffb70a6b8a..1b658e75fad5c2bb5728bc556b933d71365f21a8 100644 (file)
@@ -3309,7 +3309,7 @@ static AstPackage* parse_package_expression(OnyxParser* parser) {
 
 static Package* parse_file_package(OnyxParser* parser) {
     if (parser->curr->type != Token_Type_Keyword_Package) {
-        return package_lookup_or_create("main", context.global_scope, parser->allocator);
+        return package_lookup_or_create("main", context.global_scope, parser->allocator, parser->curr->pos);
     }
 
     AstPackage* package_node = parse_package_expression(parser);
@@ -3323,7 +3323,7 @@ static Package* parse_file_package(OnyxParser* parser) {
         token_toggle_end(*symbol);
 
         strncat(aggregate_name, (*symbol)->text, 2047);
-        Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator);
+        Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator, package_node->token->pos);
 
         AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package);
         pnode->token = *symbol;
index 611ad8de35e49dd9903f3c4f997785570012549b..79114df656e5571b135988800544dcff4df1d43a 100644 (file)
@@ -26,7 +26,7 @@ Package* package_lookup(char* package_name) {
     }
 }
 
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) {
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) {
     i32 index = shgeti(context.packages, package_name);
     if (index != -1) {
         return context.packages[index].value;
@@ -45,6 +45,14 @@ Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_al
 
         shput(context.packages, pac_name, package);
 
+        if (!charset_contains(pac_name, '.')) {
+            AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package);
+            package_node->package_name = package->name;
+            package_node->package = package;
+
+            symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node);
+        }
+
         return package;
     }
 }
@@ -110,6 +118,11 @@ b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* sym
             AstNode *node = scope->symbols[index].value;
             if (node != symbol) {
                 onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name);
+
+                if (node->token) {
+                    onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here.");
+                }
+
                 return 0;
             }
             return 1;
index d606442da5e8403144baa7e7aba17c0dd9e4d039..141715b4d4828c85dca57abe34887c0f7dc95580 100644 (file)
@@ -1,2 +1,2 @@
 Hello, I am Billy!
-Go away!! 0xEDF4
+Go away!! (^Person) -> void
index c2b8d445baffb81fec79fb1568030cc569494acf..14686b20251af1970eae1b96a77834cfdfe7a261 100644 (file)
@@ -19,7 +19,7 @@ nice_person := Person_Vtable.{
 
 mean_person := Person_Vtable.{
     greet = (use p: ^Person) {
-        printf("Go away!! {}\n", ^greet);
+        printf("Go away!! {}\n", typeof greet);
     }
 }
 
@@ -29,4 +29,4 @@ main :: (args) => {
 
     billy->greet();
     charles->greet();
-}
\ No newline at end of file
+}