diff --git a/src/README.md b/src/README.md index 2bc4a45f..2e4eca19 100644 --- a/src/README.md +++ b/src/README.md @@ -365,6 +365,8 @@ A fancier message can be created with `thrF(message, …)` with printf-like (but %R a B object of a number or string (string is printed without quotes or escaping) %H the shape of a B object %2H a shape, passed in by a ur & usz* +%0H the shape of a B object, or "atom" if the input is an atom, or "unit array" if it's a rank-0 array +%02H a shape, passed in by a ur & usz*, or "unit array" if the rank is 0 %B a B object, formatted by •Repr (be very very careful to not give a potentially large object, which'd lead to unreadably long messages!) %% "%" ``` diff --git a/src/builtins/select.c b/src/builtins/select.c index fa577519..7f5326b9 100644 --- a/src/builtins/select.c +++ b/src/builtins/select.c @@ -998,9 +998,8 @@ B select_ucw(B t, B o, B w, B x) { ur xr = RNK(x); ur wr = RNK(w); - if (isAtm(rep)) thrF("𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected %2H, got atom)", xr-1, SH(x)+1); - bool ok = xr+wr == RNK(rep)+1 && eqShPart(SH(w),SH(rep),wr) && eqShPart(SH(x)+1,SH(rep)+wr,xr-1); - if (!ok) thrF("𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected %2H, got %H)", xr-1, SH(x)+1, rep); + bool ok = isArr(rep) && xr+wr == RNK(rep)+1 && eqShPart(SH(w),SH(rep),wr) && eqShPart(SH(x)+1,SH(rep)+wr,xr-1); + if (!ok) thrF("𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected %02H, got %0H)", xr-1, SH(x)+1, rep); usz csz = arr_csz(x); if (csz == 0) { decG(rep); decG(w); return x; } diff --git a/src/builtins/sfns.c b/src/builtins/sfns.c index bf841de7..e677011c 100644 --- a/src/builtins/sfns.c +++ b/src/builtins/sfns.c @@ -1401,8 +1401,7 @@ B pick_ucw(B t, B o, B w, B x) { w = num_squeeze(mut_fcd(r, w)); B rep = isArr(o)? incG(o) : c1(o, C2(select, incG(w), C1(shape, incG(x)))); // error messages will need to get more non-trivial for deeper mismatches - if (isAtm(rep)) thrM("𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got atom)"); - if (!eqShape(w, rep)) thrF("𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected %H, got %H)", w, rep); + if (isAtm(rep) || !eqShape(w, rep)) thrF("𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected %0H, got %0H)", w, rep); return select_replace(U'⊑', w, x, rep, wia, xia, 1); } decG(w); diff --git a/src/core/stuff.c b/src/core/stuff.c index 3f7ac1e6..685f27fe 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -236,22 +236,36 @@ NOINLINE B do_fmt(B s, char* p, va_list a) { s = appendRaw(s, bqn_repr(inc(b))); break; } - case '2': - case 'H': { + case 'H': case '0': case '2': { + bool a2 = false; + bool a0 = false; + while (true) { + if (c=='H') break; + else if (c=='2') a2 = true; + else if (c=='0') a0 = true; + else fatal("Invalid format string following [H02]"); + c = *p++; + } ur r; usz* sh; - if (c=='2') { - if ('H' != *p++) fatal("Invalid format string: expected H after %2"); + bool atom; + if (a2) { r = va_arg(a, int); sh = va_arg(a, usz*); + atom = false; } else { B o = va_arg(a, B); - r = isArr(o)? RNK(o) : 0; - sh = isArr(o)? SH(o) : NULL; + atom = isAtm(o); + r = atom? 0 : RNK(o); + sh = atom? NULL : SH(o); } - if (r==0) AU("⟨⟩"); - else if (r==1) AFMT("⟨%s⟩", sh[0]); - else { + if (r==0) { + if (a0 && atom) A8("atom"); + else if (a0 && !atom) A8("unit array"); + else AU("⟨⟩"); + } else if (r==1) { + AFMT("⟨%s⟩", sh[0]); + } else { for (i32 i = 0; i < r; i++) { if(i) AU("‿"); AFMT("%s", sh[i]); diff --git a/test/cases/under.bqn b/test/cases/under.bqn index 9e0e3127..24bd0059 100644 --- a/test/cases/under.bqn +++ b/test/cases/under.bqn @@ -37,6 +37,8 @@ !"𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨2⟩, got ⟨3⟩)" % 10‿20‿30⌾(⟨1‿2,2‿1⟩⊸⊑) 4‿4⥊↕16 !"𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got atom)" % 1⌾((<⋈2)⊸⊑) ↕10 !"𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got atom)" % 1⌾((<1‿2)⊸⊑) 3‿3⥊↕9 +!"𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨1⟩, got atom)" % 1⌾((⋈1‿2)⊸⊑) 3‿3⥊↕9 +!"𝔽⌾(nested⊸⊑)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨1⟩, got unit array)" % (<1)⌾((⋈1‿2)⊸⊑) 3‿3⥊↕9 !"⁼: Inverse not found" % 1⌾((<⋈⋈2)⊸⊑) ↕10 !"⁼: Inverse not found" % 1⌾((<⋈1‿2)⊸⊑) 3‿3⥊↕9 !"⁼: Inverse not found" % (<1)⌾((<⟨1‿2,2‿1⟩)⊸⊑) 3‿3⥊↕9 @@ -82,10 +84,10 @@ n←500 ⋄ a←↕n ⋄ i←(-n)+↕2×n ⋄ r←⌽(2×n)⥊a ⋄ ! (⌽a) ≡ 100⊸+⌾(1‿2‿¯4⊸⊏) ↕10 %% 0‿101‿102‿3‿4‿5‿106‿7‿8‿9 ⟨10⊸+⌾(1‿2⊸⊏) 0↓a←↕4, a⟩ %% ⟨0‿11‿12‿3,0‿1‿2‿3⟩ %USE eqvar ⋄ 0‿0‿1‿1 {𝕨⌾(1‿2‿4‿5⊸⊏) 𝕩} _eqvar 1‿0‿1‿0‿1‿0‿1‿1‿0 %% 1‿0‿0‿0‿1‿1‿1‿1‿0 -!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨⟩, got ⟨3⟩)" % 10‿20‿30⌾(2‿3⊸⊏) ↕10 -!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨⟩, got ⟨3⟩)" % 1⊸∾⌾(2‿3⊸⊏) ↕10 +!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got ⟨3⟩)" % 10‿20‿30⌾(2‿3⊸⊏) ↕10 +!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got ⟨3⟩)" % 1⊸∾⌾(2‿3⊸⊏) ↕10 !"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨10⟩, got 2‿10)" % %USE evar ⋄ 1⊸↓⌾(2‿3‿4⊸⊏)_evar 10‿10⥊0 -!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected ⟨⟩, got atom)" % %USE evar ⋄ ⟨0⟩ {1⌾(𝕨⊸⊏)𝕩}_evar ⟨0⟩ +!"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected unit array, got atom)" % %USE evar ⋄ ⟨0⟩ {1⌾(𝕨⊸⊏)𝕩}_evar ⟨0⟩ !"𝔽⌾(a⊸⊏)𝕩: 𝔽 must return an array with the same shape as its input (expected 3‿4, got atom)" % %USE evar ⋄ ⟨0⟩ {1⌾(𝕨⊸⊏)𝕩}_evar 2‿3‿4⥊3 !"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4⌾(1‿1⊸⊏) ↕10 !"𝔽⌾(a⊸⊏): Incompatible result elements" % 3‿4⌾(1‿¯9⊸⊏) ↕10