From 789064140b7bad6147c6bfee8daedd18465f2af4 Mon Sep 17 00:00:00 2001 From: dzaima Date: Sun, 26 Nov 2023 15:30:12 +0200 Subject: [PATCH] use isStr more, include builtin name in bad path messages --- src/builtins/sysfn.c | 67 +++++++++++++++++++------------------------ src/core/stuff.h | 1 + src/ffi.c | 2 +- src/load.c | 1 - src/main.c | 2 +- src/utils/file.c | 33 +++++++-------------- src/utils/file.h | 3 +- src/vm.c | 20 ++++++------- test/cases/ffi.bqn | 1 + test/cases/system.bqn | 38 +++++++++++++++++++++--- 10 files changed, 90 insertions(+), 78 deletions(-) diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index 4ca18c71..8bebbc9c 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -270,20 +270,13 @@ B show_c1(B t, B x) { return x; } -NOINLINE bool isStr(B x) { - if (isAtm(x) || RNK(x)!=1) return false; - if (elChr(TI(x,elType))) return true; - usz ia = IA(x); SGetU(x) - for (usz i = 0; i < ia; i++) if (!isC32(GetU(x,i))) return false; - return true; -} NOINLINE B vfyStr(B x, char* name, char* arg) { if (!isStr(x)) thrF("%U: %U must be a string", name, arg); return x; } B cdPath; -static B args_path(B* fullpath, B w, char* name) { // consumes w, returns args, writes to fullpath +static NOINLINE B args_path(B* fullpath, B w, char* name) { // consumes w, returns args, writes to fullpath if (!isArr(w) || RNK(w)!=1 || IA(w)>3) thrF("%U: 𝕨 must be a vector with at most 3 items, but had shape %H", name, w); usz ia = IA(w); SGet(w) @@ -721,28 +714,28 @@ B currentError_c1(B t, B x) { thrM("•CurrentError: No errors as error catching static NFnDesc* fileAtDesc; B fileAt_c1(B d, B x) { - return path_rel(nfn_objU(d), x); + return path_rel(nfn_objU(d), x, "•file.At"); } B fileAt_c2(B d, B w, B x) { - vfyStr(w,"(file).At","𝕨"); - B r = path_rel(w, x); + vfyStr(w,"•file.At","𝕨"); + B r = path_rel(w, x, "•file.At"); dec(w); return r; } static NFnDesc* fCharsDesc; B fchars_c1(B d, B x) { - return path_chars(path_rel(nfn_objU(d), x)); + return path_chars(path_rel(nfn_objU(d), x, "•file.Chars")); } B fchars_c2(B d, B w, B x) { if (isAtm(x) || RNK(x)!=1) thrM("•file.Chars: 𝕩 must be a list of characters"); - B p = path_rel(nfn_objU(d), w); + B p = path_rel(nfn_objU(d), w, "•file.Chars"); path_wChars(incG(p), x); dec(x); return p; } static NFnDesc* fBytesDesc; B fbytes_c1(B d, B x) { - I8Arr* tf = path_bytes(path_rel(nfn_objU(d), x)); + I8Arr* tf = path_bytes(path_rel(nfn_objU(d), x, "•file.Bytes")); usz ia = PIA(tf); u8* rp; B r = m_c8arrv(&rp, ia); COPY_TO(rp, el_i8, 0, taga(tf), 0, ia); @@ -751,14 +744,14 @@ B fbytes_c1(B d, B x) { } B fbytes_c2(B d, B w, B x) { if (isAtm(x) || RNK(x)!=1) thrM("•file.Bytes: 𝕩 must be a list"); - B p = path_rel(nfn_objU(d), w); + B p = path_rel(nfn_objU(d), w, "•file.Bytes"); path_wBytes(incG(p), x); dec(x); return p; } static NFnDesc* fLinesDesc; B flines_c1(B d, B x) { - return path_lines(path_rel(nfn_objU(d), x)); + return path_lines(path_rel(nfn_objU(d), x, "•file.Lines")); } B flines_c2(B d, B w, B x) { if (isAtm(x) || RNK(x)!=1) thrM("•file.Lines: 𝕩 must be a list"); @@ -773,7 +766,7 @@ B flines_c2(B d, B w, B x) { s = vec_addN(s, m_c32('\n')); } dec(x); - B p = path_rel(nfn_objU(d), w); + B p = path_rel(nfn_objU(d), w, "•file.Lines"); path_wChars(incG(p), s); decG(s); return p; @@ -783,7 +776,7 @@ static NFnDesc* importDesc; B import_c2(B d, B w, B x) { - return bqn_execFile(path_rel(nfn_objU(d), x), w); + return bqn_execFile(path_rel(nfn_objU(d), x, "•Import"), w); } // defined in fns.c @@ -798,7 +791,7 @@ B import_c1(B d, B x) { importKeyList = emptyHVec(); importValList = emptyHVec(); } - B path = path_abs(path_rel(nfn_objU(d), x)); + B path = path_abs(path_rel(nfn_objU(d), x, "•Import")); i32 prevIdx = getPrevImport(path); if (prevIdx>=0) { @@ -847,56 +840,56 @@ static NFnDesc* renameDesc; static NFnDesc* removeDesc; B list_c1(B d, B x) { - return path_list(path_rel(nfn_objU(d), x)); + return path_list(path_rel(nfn_objU(d), x, "•file.List")); } B createdir_c1(B d, B x) { - B p = path_rel(nfn_objU(d), x); + B p = path_rel(nfn_objU(d), x, "•file.CreateDir"); if (dir_create(p)) return p; - thrM("(file).CreateDir: Failed to create directory"); + thrM("•file.CreateDir: Failed to create directory"); } B realpath_c1(B d, B x) { - return path_abs(path_rel(nfn_objU(d), x)); + return path_abs(path_rel(nfn_objU(d), x, "•file.RealPath")); } B rename_c2(B d, B w, B x) { d = nfn_objU(d); - B p = path_rel(d, w); - if (path_rename(path_rel(d, x), p)) return p; - thrM("(file).Rename: Failed to rename file"); + B p = path_rel(d, w, "•file.Rename"); + if (path_rename(path_rel(d, x, "•file.Rename"), p)) return p; + thrM("•file.Rename: Failed to rename file"); } B remove_c1(B d, B x) { - if (path_remove(path_rel(nfn_objU(d), x))) return m_i32(1); - thrM("(file).Remove: Failed to remove file"); + if (path_remove(path_rel(nfn_objU(d), x, "•file.Remove"))) return m_i32(1); + thrM("•file.Remove: Failed to remove file"); } B ftype_c1(B d, B x) { - char ty = path_type(path_rel(nfn_objU(d), x)); + char ty = path_type(path_rel(nfn_objU(d), x, "•file.Type")); if (ty==0) thrM("•file.Type: Error while accessing file"); return m_c32(ty); } -B fcreated_c1 (B d, B x) { return path_info(path_rel(nfn_objU(d), x), 0); } -B faccessed_c1(B d, B x) { return path_info(path_rel(nfn_objU(d), x), 1); } -B fmodified_c1(B d, B x) { return path_info(path_rel(nfn_objU(d), x), 2); } -B fsize_c1 (B d, B x) { return path_info(path_rel(nfn_objU(d), x), 3); } +B fcreated_c1 (B d, B x) { return path_info(path_rel(nfn_objU(d), x, "•file.Created"), 0); } +B faccessed_c1(B d, B x) { return path_info(path_rel(nfn_objU(d), x, "•file.Accessed"), 1); } +B fmodified_c1(B d, B x) { return path_info(path_rel(nfn_objU(d), x, "•file.Modified"), 2); } +B fsize_c1 (B d, B x) { return path_info(path_rel(nfn_objU(d), x, "•file.Size"), 3); } B fexists_c1(B d, B x) { - char ty = path_type(path_rel(nfn_objU(d), x)); + char ty = path_type(path_rel(nfn_objU(d), x, "•file.Exists")); return m_f64(ty!=0); } B fName_c1(B t, B x) { - if (isAtm(x) || RNK(x)!=1) thrM("•file.Name: Argument must be a string"); + if (!isStr(x)) thrM("•file.Name: Argument must be a string"); return path_name(x); } B fParent_c1(B t, B x) { - if (isAtm(x) || RNK(x)!=1) thrM("•file.Parent: Argument must be a string"); + if (!isStr(x)) thrM("•file.Parent: Argument must be a string"); return path_parent(x); } B mapBytes_c1(B d, B x) { - return mmap_file(path_rel(nfn_objU(d), x)); + return mmap_file(path_rel(nfn_objU(d), x, "•file.MapBytes")); } B unixTime_c1(B t, B x) { diff --git a/src/core/stuff.h b/src/core/stuff.h index 2df6f6d5..6d45e56a 100644 --- a/src/core/stuff.h +++ b/src/core/stuff.h @@ -228,6 +228,7 @@ char* pm1_repr(u8 u); char* pm2_repr(u8 u); char* eltype_repr(u8 u); bool isPureFn(B x); // doesn't consume +bool isStr(B x); // doesn't consume; returns if x is a rank 1 array of characters (includes any empty array) B bqn_merge(B x, u32 type); // consumes B any_squeeze(B x); // consumes; accepts any array, returns one with the smallest type (doesn't recurse!) diff --git a/src/ffi.c b/src/ffi.c index c746867e..6958ed43 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -1011,7 +1011,7 @@ B ffiload_c2(B t, B w, B x) { char* ws = NULL; if (w.u != m_c32(0).u) { - w = path_rel(nfn_objU(t), w); + w = path_rel(nfn_objU(t), w, "•FFI"); ws = toCStr(w); } void* dl = dlopen(ws, RTLD_NOW); diff --git a/src/load.c b/src/load.c index 32c06101..779581f0 100644 --- a/src/load.c +++ b/src/load.c @@ -235,7 +235,6 @@ B bqn_exec(B str, B path, B args) { // consumes all return evalFunBlockConsume(bqn_comp(str, path, args)); } -bool isStr(B x); B str_all, str_none; void init_comp(B* new_re, B* prev_re, B prim, B sys) { if (q_N(prim)) { diff --git a/src/main.c b/src/main.c index bd8c9e14..93ed98f0 100644 --- a/src/main.c +++ b/src/main.c @@ -491,7 +491,7 @@ static NOINLINE i64 readInt(char** p) { } static NOINLINE B path_rel_dec(B base, B rel) { - B res = path_rel(base, rel); + B res = path_rel(base, rel, "main.c"); dec(base); return res; } diff --git a/src/utils/file.c b/src/utils/file.c index a90f257e..c2182ad2 100644 --- a/src/utils/file.c +++ b/src/utils/file.c @@ -115,23 +115,14 @@ B path_lines(B path) { // consumes; TODO rewrite this, it's horrible } -static NOINLINE void guaranteeStr(B x) { // assumes x is an array - if (elChr(TI(x,elType))) return; - usz xia = IA(x); - SGetU(x) - for (usz i = 0; i < xia; i++) if (!isC32(GetU(x, i))) thrM("Path wasn't a list of characters"); -} -B path_rel(B base, B rel) { // consumes rel; assumes base is a char vector or bi_N +B path_rel(B base, B rel, char* name) { assert(isArr(base) || q_N(base)); - if (!isArr(rel) || RNK(rel)!=1) thrM("Path wasn't a list of characters"); - SGetU(rel) + if (!isStr(rel)) thrF("%U: Path must be a list of characters", name); usz ria = IA(rel); - if (RNK(rel)!=1) thrM("Path wasn't a list of characters"); - guaranteeStr(rel); if (ria>0 && isAbsolutePath(rel)) return rel; - if (q_N(base)) thrM("Using relative path with no absolute base path known"); + if (q_N(base)) thrM("%U: Using relative path with no absolute base path known"); if (ria==0) { dec(rel); return incG(base); } usz bia = IA(base); if (bia==0) return rel; @@ -141,17 +132,17 @@ B path_rel(B base, B rel) { // consumes rel; assumes base is a char vector or bi usz ri = 0; for (usz i = 0; i < bia-(has?1:0); i++) rp[ri++] = o2cG(GetU(base, i)); rp[ri++] = PREFERRED_SEP; + SGetU(rel) for (usz i = 0; i < ria; i++) rp[ri++] = o2cG(GetU(rel, i)); dec(rel); return r; } B path_parent(B path) { - assert(isArr(path)); - SGetU(path) + assert(isStr(path)); usz pia = IA(path); - if (pia==0) thrM("Empty file path"); - guaranteeStr(path); + if (pia==0) thrM("•file.Parent: Path must be non-empty"); + SGetU(path) for (i64 i = (i64)pia-2; i >= 0; i--) { if (isPathSep(o2cG(GetU(path, i)))) return taga(arr_shVec(TI(path,slice)(path, 0, i+1))); } @@ -162,10 +153,9 @@ B path_parent(B path) { } B path_name(B path) { assert(isArr(path)); - SGetU(path) usz pia = IA(path); - if (pia==0) thrM("Empty file path"); - guaranteeStr(path); + if (pia==0) thrM("•file.Name: Path must be non-empty"); + SGetU(path) for (i64 i = (i64)pia-1; i >= 0; i--) { if (isPathSep(o2cG(GetU(path, i)))) { if (i == pia-1) thrF("File path ended with a slash: \"%R\"", path); @@ -176,6 +166,7 @@ B path_name(B path) { } B path_abs(B path) { + assert(isStr(path)); #if WASM return path; // lazy #else @@ -334,9 +325,7 @@ void mmap_init(void) { // use default canStore } #else -B mmap_file(B path) { - thrM("CBQN was compiled without •file.MapBytes support"); -} +B mmap_file(B path) { thrM("CBQN was compiled without •file.MapBytes support"); } void mmap_init() { } #endif diff --git a/src/utils/file.h b/src/utils/file.h index f515d1b0..f3bf1041 100644 --- a/src/utils/file.h +++ b/src/utils/file.h @@ -1,7 +1,8 @@ #pragma once #include "utf.h" +// unless otherwise specified, these assume that paths are strings -B path_rel(B base, B rel); // consumes rel; assumes base is a char vector or bi_N +B path_rel(B base, B rel, char* name); // consumes rel; assumes base is a string or bi_N, throws if !isStr(rel) B path_parent(B path); // consumes; returns the containing directory, with trailing slash B path_name(B path); // consumes; returns filename from a path B path_abs(B path); // consumes; returns absolute version of the path; propagates bi_N diff --git a/src/vm.c b/src/vm.c index 69830a1f..45850b30 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1667,18 +1667,16 @@ void unwindCompiler() { #endif } +NOINLINE bool isStr(B x) { + if (isAtm(x) || RNK(x)!=1) return false; + if (elChr(TI(x,elType))) return true; + usz ia = IA(x); SGetU(x) + for (usz i = 0; i < ia; i++) if (!isC32(GetU(x,i))) return false; + return true; +} NOINLINE void printErrMsg(B msg) { - if (isArr(msg)) { - if (!elChr(TI(msg,elType))) { - SGetU(msg) - usz msgLen = IA(msg); - for (usz i = 0; i < msgLen; i++) if (!isC32(GetU(msg,i))) goto base; - } - fprintsB(stderr, msg); - return; - } - base: - fprintI(stderr, msg); + if (isStr(msg)) fprintsB(stderr, msg); + else fprintI(stderr, msg); } diff --git a/test/cases/ffi.bqn b/test/cases/ffi.bqn index d8f9b27c..84c5e342 100644 --- a/test/cases/ffi.bqn +++ b/test/cases/ffi.bqn @@ -5,6 +5,7 @@ # bad •FFI invocation # generally weird +!"•FFI: Path must be a list of characters" % 1‿2 •FFI ""‿"bqn_init" "file that doesn't exist" •FFI⎊'e' ""‿"abort" %% 'e' @ •FFI⎊'e' ""‿"bqn_this symbol doesn't exist" %% 'e' !"FFI: Type must be a string" % @•FFI "hello" diff --git a/test/cases/system.bqn b/test/cases/system.bqn index 85eece1f..d25322e5 100644 --- a/test/cases/system.bqn +++ b/test/cases/system.bqn @@ -86,6 +86,8 @@ f←•ReBQN{primitives⇐⋈'÷'‿- ⋄ system⇐⟨"primitives", "foo"‿⋈, •file.Exists "/dev/file that doesn't exist" %% 0 !"Couldn't read file ""/dev/file that doesn't exist""" % •FChars "/dev/file that doesn't exist" ! (•path∾"/") ≡ •file.Parent •file.At "hi" +!"•file.Parent: Path must be non-empty" % •file.Parent "" +!"•file.Name: Path must be non-empty" % •file.Name "" !"•file.Lines: 𝕩 must be a list" % "testfile.bqn" •FLines 2‿2⥊<"abcd" !"•file.Lines: 𝕩 must be a list" % "testfile.bqn" •file.Lines <"abcd" @@ -96,8 +98,6 @@ f←•ReBQN{primitives⇐⋈'÷'‿- ⋄ system⇐⟨"primitives", "foo"‿⋈, !"•file.Chars: 𝕩 must be a list of characters" % "testfile.bqn" •FChars <'a' !"Expected character" % "testfile.bqn" •FBytes ⋈"abcd" !"•file.Bytes: 𝕩 must be a list" % "testfile.bqn" •file.Bytes <'a' -!"Path wasn't a list of characters" % 'a' •FChars "hi" -!"Path wasn't a list of characters" % (4‿2⥊"testfile.bqn") •file.Chars "hi" !"Expected character" % "badwrite"•FChars ""‿"" !"Expected character" % "badwrite"•FLines ⟨⟨"foo"⟩⟩ !"•file.Chars: 𝕩 must be a list of characters" % "badwrite"•FChars @ @@ -116,11 +116,11 @@ f←•ReBQN{primitives⇐⋈'÷'‿- ⋄ system⇐⟨"primitives", "foo"‿⋈, •FLines "testfile.bqn" %% "abc"‿"def𝕩" "testfile3B.bqn" •file.Rename "testfile3.bqn" -!"(file).Rename: Failed to rename file" % "testfile3B.bqn" •file.Rename "testfile.bqn" +!"•file.Rename: Failed to rename file" % "testfile3B.bqn" •file.Rename "testfile.bqn" •file.Exists¨ "testfile.bqn"‿"nontestfile.bqn"‿"testfile3.bqn"‿"testfile3B.bqn" %% 1‿0‿0‿1 ¯22↑•file.Createdir "testdirNested" %% "/testDir/testdirNested" ¯8↑•file.RealPath "testdirNested/.././" %% "/testDir" -!"(file).CreateDir: Failed to create directory" % •file.Createdir "testdirNested" +!"•file.CreateDir: Failed to create directory" % •file.Createdir "testdirNested" "testfile.bqn"‿"testfile2.bqn"‿"testfile3.bqn"‿"testfile3B.bqn"‿"testdirNested" ∊ •file.List "." %% 1‿1‿0‿1‿1 "testfile.bqn"‿"testfile2.bqn"‿"testfile3.bqn"‿"testfile3B.bqn"‿"testdirNested" ∊ •file.List •file.path %% 1‿1‿0‿1‿1 @@ -132,6 +132,36 @@ f←•ReBQN{primitives⇐⋈'÷'‿- ⋄ system⇐⟨"primitives", "foo"‿⋈, !"Failed to resolve ""/dev/file that doesn't exist"": No such file or directory" % •file.RealPath "/dev/file that doesn't exist" !"Failed to resolve ""/dev/file that doesn't exist"": No such file or directory" % •Import "/dev/file that doesn't exist" +# bad paths +!"•file.At: Path must be a list of characters" % •file.At 1‿2 +!"•file.At: Path must be a list of characters" % •file.At 1‿2 +!"•file.At: Path must be a list of characters" % "foo" •file.At 1‿2 +!"•file.At: 𝕨 must be a string" % 1‿2 •file.At "foo" +!"•file.List: Path must be a list of characters" % •file.List 1‿2 +!"•file.Bytes: Path must be a list of characters" % •file.Bytes 1‿2 +!"•file.Chars: Path must be a list of characters" % •file.Chars 1‿2 +!"•file.Lines: Path must be a list of characters" % •file.Lines 1‿2 +!"•file.Bytes: Path must be a list of characters" % 1‿2 •FBytes "abc" +!"•file.Chars: Path must be a list of characters" % 1‿2 •FChars "abc" +!"•file.Lines: Path must be a list of characters" % 1‿2 •FLines "abc"‿"def" +!"•file.Type: Path must be a list of characters" % •file.Type 1‿2 +!"•file.Exists: Path must be a list of characters" % •file.Exists 1‿2 +!"•file.Name: Argument must be a string" % •file.Name 1‿2 +!"•file.Parent: Argument must be a string" % •file.Parent 1‿2 +!"•file.MapBytes: Path must be a list of characters" % •file.MapBytes 1‿2 +!"•file.CreateDir: Path must be a list of characters" % •file.CreateDir 1‿2 +!"•file.RealPath: Path must be a list of characters" % •file.RealPath 1‿2 +!"•file.Rename: Path must be a list of characters" % "foo" •file.Rename 1‿2 +!"•file.Rename: Path must be a list of characters" % 1‿2 •file.Rename "foo" +!"•file.Remove: Path must be a list of characters" % •file.Remove 1‿2 +!"•file.Created: Path must be a list of characters" % •file.Created 1‿2 +!"•file.Modified: Path must be a list of characters" % •file.Modified 1‿2 +!"•file.Accessed: Path must be a list of characters" % •file.Accessed 1‿2 +!"•file.Size: Path must be a list of characters" % •file.Size 1‿2 +!"•file.Size: Path must be a list of characters" % •file.Size 2‿2⥊"abcd" + + + # some of •term !"Expected character" % •term.OutRaw 2⥊{⇐} !"•term.ErrRaw: 𝕩 must be a list" % •term.ErrRaw 2‿2⥊0