bugfixes and added file_stat
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 26 Aug 2022 18:59:38 +0000 (13:59 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 26 Aug 2022 18:59:38 +0000 (13:59 -0500)
core/onyx/fs.onyx
core/os/dir.onyx
core/os/file.onyx
core/string.onyx
core/wasi/wasi_fs.onyx
include/bh.h
include/parser.h
src/onyx_runtime.c
src/parser.c

index 9ba45011d0641582733022bcb092a7e05e75efa2..d98f798df627dae4190ff11dbe19bb1a436b6a02 100644 (file)
@@ -22,6 +22,7 @@ __file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError)
     __file_open_impl :: (path: str, mode: os.OpenMode, out_handle: ^FileData.Handle) -> os.FileError ---
 
     __file_close :: (fd: FileData) -> os.FileError ---
+    __file_stat   :: (path: str, stat: ^os.FileStat) -> bool ---
     __file_exists :: (path: str) -> bool ---
     __file_remove :: (path: str) -> bool ---
     __file_rename :: (old_path: str, new_path: str) -> bool ---
index 5594b588484b4fa6c6e85b6eb136644567e55e6a..6accbb32e5a177d23552d1a7f12280dcf9a92609 100644 (file)
@@ -5,20 +5,7 @@ package core.os
 Directory :: fs.DirectoryData;
 
 DirectoryEntry :: struct {
-    // Most of these types were stollen directly from
-    // man readdir(3), and the most reasonable types
-    // were used for Windows.
-    Type :: enum {
-        Unknown     :: 0x00;
-        Block       :: 0x01;
-        Char        :: 0x02;
-        Directory   :: 0x03;
-        RegularFile :: 0x04;
-        SymLink     :: 0x05;
-        Other       :: 0x06;
-    }
-
-    type        : Type;
+    type        : FileType;
     identifier  : u32;
     name_length : u32;
     name_data   : [256] u8;
index 98026b55c53a60907cce286ee945c8c413225a9d..62534da8f2b14d30bd8a3061a583c207c0c68e5b 100644 (file)
@@ -24,7 +24,28 @@ File :: struct {
     use data   : fs.FileData;
 }
 
+// Most of these types were stollen directly from
+// man readdir(3), and the most reasonable types
+// were used for Windows.
+FileType :: enum {
+    Unknown     :: 0x00;
+    Block       :: 0x01;
+    Char        :: 0x02;
+    Directory   :: 0x03;
+    RegularFile :: 0x04;
+    SymLink     :: 0x05;
+    Other       :: 0x06;
+}
+
+//
+// This is fairly minimal for right now.
+FileStat :: struct {
+    size: i64;
+    type: FileType;
+}
+
 file_exists :: fs.__file_exists
+file_stat   :: fs.__file_stat
 remove_file :: fs.__file_remove
 rename_file :: fs.__file_rename
 
@@ -102,7 +123,17 @@ with_file :: (path: str, mode := OpenMode.Read) -> Iterator(^File) {
     return .{ c, next, close_context };
 }
 
+is_file :: (path: str) -> bool {
+    s: FileStat;
+    if !file_stat(path, ^s) do return false;
+    return s.type == .RegularFile;
+}
 
+is_directory :: (path: str) -> bool {
+    s: FileStat;
+    if !file_stat(path, ^s) do return false;
+    return s.type == .Directory;
+}
 
 
 file_logger_open :: (filename: str, allocator := context.allocator) -> Logger {
index b4134f09382569f92344073f57d9ab03bd49307f..c4db484d4e8c95e1f7b56d4ecf214c3723692fd5 100644 (file)
@@ -462,3 +462,20 @@ split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
 
     return strarr[0 .. delim_count + 1];
 }
+
+//
+// Splits a string into two parts, divided by the
+// first instance of the provided character. Either
+// string can be empty if the first instance of the
+// character occurs at the very beginning or end of
+// the string, or if it does not occur at all.
+//
+bisect :: (s: str, c: u8) -> (str, str) {
+    index := index_of(s, c); 
+    if index == -1 {
+        return s, "";
+    }
+
+    return s[0 .. index], s[index+1 .. s.length];
+}
+
index 6422ef4636ebbe8c63922f518356075d1d28c07f..4b6eadb5ed49e2f37ebe9a99aca585c90aaf744b 100644 (file)
@@ -110,6 +110,28 @@ __file_close :: (file: FileData) -> os.FileError {
     return .None;
 }
 
+__file_stat :: (path: str, out: ^os.FileStat) -> bool {
+    fs: wasi.FileStat;
+
+    exists := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
+        if err == .Success {
+            exists = true;
+            out.size = ~~ fs.size;
+
+            switch fs.filetype {
+                case .RegularFile do out.type = .RegularFile;
+                case .Directory do out.type = .Directory;
+                case .SymLink do out.type = .SymLink;
+                case #default do out.type = .Unknown;
+            }
+        }
+    }
+
+    return exists;
+}
+
 __file_exists :: (path: str) -> bool {
     fs: wasi.FileStat;
 
index b9f008ddd563fa1f74bd9d9640aef2616f3b0b32..da5bbf3bd6e713004daf1a36bf120ddcec31de18 100644 (file)
@@ -359,6 +359,15 @@ typedef struct bh_file_contents {
     isize line_count;
 } bh_file_contents;
 
+
+#define BH_FILE_TYPE_DIRECTORY 3
+#define BH_FILE_TYPE_FILE      4
+#define BH_FILE_TYPE_LINK      5
+typedef struct bh_file_stats {
+    isize size;
+    u32 file_type;
+} bh_file_stats;
+
 bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
 
 bh_file_error bh_file_create(bh_file* file, char const* filename);
@@ -377,6 +386,7 @@ i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
 i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
 void bh_file_flush(bh_file* file);
 i64 bh_file_size(bh_file* file);
+b32 bh_file_stat(char const* filename, bh_file_stats* stat);
 b32 bh_file_exists(char const* filename);
 b32 bh_file_remove(char const* filename);
 char* bh_path_get_full_name(char const* filename, bh_allocator a);
@@ -1635,6 +1645,21 @@ b32 bh_file_contents_free(bh_file_contents* contents) {
     return 1;
 }
 
+b32 bh_file_stat(char const* filename, bh_file_stats* out) {
+    struct stat s;
+    if (stat(filename, &s) == -1) {
+        return 0;
+    }
+
+    out->size = s.st_size;
+
+    if ((s.st_mode & S_IFMT) == S_IFDIR) out->file_type = BH_FILE_TYPE_DIRECTORY;
+    if ((s.st_mode & S_IFMT) == S_IFREG) out->file_type = BH_FILE_TYPE_FILE;
+    if ((s.st_mode & S_IFMT) == S_IFLNK) out->file_type = BH_FILE_TYPE_LINK;
+
+    return 1;
+}
+
 b32 bh_file_exists(char const* filename) {
     struct stat s;
     return stat(filename, &s) != -1;
index 054dd7574962d99a6f228a80777977ab5bbf85ab..4c1e505a21ad6e6e0a48d2c4127da2d3aa21b0b7 100644 (file)
@@ -37,6 +37,7 @@ typedef struct OnyxParser {
     b32 hit_unexpected_token : 1;
 
     b32 parse_calls : 1;
+    b32 inside_tag  : 1;
 } OnyxParser;
 
 const char* onyx_ast_node_kind_string(AstKind kind);
index 7827e1d3f0ff6d33a47b52871c8aa2127b96d43e..8a7473e8e610bffa8095ebcfbdda55a0216016eb 100644 (file)
@@ -87,6 +87,19 @@ ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) {
     return NULL;
 }
 
+ONYX_DEF(__file_stat, (WASM_I32, 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;
+
+    results->data[0] = WASM_I32_VAL(bh_file_stat(path, ONYX_PTR(params->data[2].of.i32)));
+    return NULL;
+}
+
 ONYX_DEF(__file_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;
@@ -1333,6 +1346,7 @@ ONYX_LIBRARY {
     ONYX_FUNC(__file_open_impl)
     ONYX_FUNC(__file_close)
     ONYX_FUNC(__file_exists)
+    ONYX_FUNC(__file_stat)
     ONYX_FUNC(__file_remove)
     ONYX_FUNC(__file_seek)
     ONYX_FUNC(__file_tell)
index fae5d2b8a0205180e4ed5142150d5fe268c9b974..f41fe0939aa3f8a11dae000d3a994806dbf058b9 100644 (file)
@@ -188,7 +188,18 @@ static void expect_no_stored_tags(OnyxParser *parser) {
 }
 
 static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
-    if (bh_arr_length(parser->stored_tags) == 0) return;
+    //
+    // When inside_tag is true, no tags will be added to the element.
+    // This happens if you have a something like so,
+    //
+    // #tag "asdf"
+    // #tag handler.{ (x: i32) => x * 2 }
+    // foo :: () { ... }
+    //
+    // In this situation, the inner procedure defined in the second
+    // tag should NOT consume the "asdf" tag.
+    //
+    if (bh_arr_length(parser->stored_tags) == 0 || parser->inside_tag) return;
 
     bh_arr(AstTyped *) arr = *out_arr;
 
@@ -2054,10 +2065,14 @@ static AstStructType* parse_struct(OnyxParser* parser) {
         while (parse_possible_directive(parser, "tag")) {
             if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
 
+            parser->inside_tag = 1;
+
             do {
                 AstTyped* expr = parse_expression(parser, 0);
                 bh_arr_push(meta_tags, expr);
             } while (consume_token_if_next(parser, ','));
+
+            parser->inside_tag = 0;
         }
 
         member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
@@ -3257,8 +3272,12 @@ static void parse_top_level_statement(OnyxParser* parser) {
                 return;
             }
             else if (parse_possible_directive(parser, "tag")) {
+                parser->inside_tag = 1;
+
                 AstTyped *expr = parse_expression(parser, 0);
                 bh_arr_push(parser->stored_tags, expr);
+
+                parser->inside_tag = 0;
                 return;
             }
             else {