diff options
| author | user <user@clank> | 2026-08-16 15:49:07 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-16 15:49:07 +0200 |
| commit | 7cd9de2c15f5b00b99ea8d974a4c73f0c2d4f972 (patch) | |
| tree | 476b659e7f5b54b0511b1b5e4dd9a559fa7230bf /tests/expr_test.c | |
| parent | sl0ppty: zero strength is not a pass (diff) | |
| download | slosh-7cd9de2c15f5b00b99ea8d974a4c73f0c2d4f972.tar.gz slosh-7cd9de2c15f5b00b99ea8d974a4c73f0c2d4f972.tar.xz slosh-7cd9de2c15f5b00b99ea8d974a4c73f0c2d4f972.zip | |
sl0ppty: bits
`&` `|` `^` `~` `<<` `>>`, on the 32-bit value the VM already used. There was
nothing: `x & 7` answered `unexpected trailing input at offset 2` and
`(x ^ y) % 16 < 2` answered `expected ) at offset 3`, which is a fair
description of a parser that had never heard of either.
`^` is the reason to bother. `(x ^ y) & 1` is the cheapest interesting texture
there is, and the language could express a sweep, a ramp and a comparison but
not one bit of pattern without `%` gymnastics.
**Bits bind tighter than comparison, which is not what C does.** `x & 7 == 0`
means `x & (7 == 0)` in C -- a precedence Ritchie put on record as a mistake,
kept for a decade of compatibility that a shader expression in a config file
does not have. Here it asks what it looks like it asks. Among themselves the
usual `&` then `^` then `|`; shifts stay where C has them, just under `+ -`.
`&&` and `||` still parse as themselves: the bitwise rungs decline the first
character when a second one follows it.
Semantics are JavaScript's, deliberately and to the letter -- the shift count
masked to five bits (`1 << 33` is 2), `>>` keeping the sign (`-8 >> 1` is -4).
Not taste: `contrib/shadertoy.html` reimplements this language in JS so the
preview can run in a browser, and `tests/test_shadertoy.py` compares the two on
every expression it can think of. Anything C leaves implementation-defined had
to be pinned to what JS would show, so the signed right shift is written out in
the VM rather than left to the compiler.
Verified: 22 new cases in `tests/expr_test.c` covering the operators, both
precedence questions, the sign, and the masked count; the preview learned the
same six operators and the same ladder, and the cross-check now agrees with the
compiler on 564 evaluations, including generated ones that reach for bits. Then
seven bitwise chains in a real pane -- an XOR texture, a drifting one, a stepped
pulse that walks eight shades of `t >> 7 & 7` and needs no `abs`.
Diffstat (limited to 'tests/expr_test.c')
| -rw-r--r-- | tests/expr_test.c | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/tests/expr_test.c b/tests/expr_test.c index fb191b8..abb3335 100644 --- a/tests/expr_test.c +++ b/tests/expr_test.c @@ -71,6 +71,41 @@ int main(void) { /* The reason comparisons return numbers: masking is how you write a rule. */ eq("(3 < 4) * 200", 200); + printf("\n-- bitwise, on the 32-bit value\n"); + eq("12 & 10", 8); + eq("12 | 10", 14); + eq("12 ^ 10", 6); + eq("~0", -1); + eq("~12", -13); + eq("1 << 4", 16); + eq("48 >> 2", 12); + /* `>>` keeps the sign, so it halves a negative rather than turning it into a + * large positive. Written out in the VM because C leaves it + * implementation-defined and JavaScript does not -- and the browser preview is + * JavaScript. */ + eq("0 - 8 >> 1", -4); + eq("0 - 1 >> 28", -1); + /* The count is masked to five bits, which is what JS does with it: a shift + * nobody meant is defined rather than undefined. */ + eq("1 << 33", 2); + eq("1 << 32", 1); + eq("8 >> 33", 4); + /* Bits bind tighter than comparison, unlike C, where `12 & 8 == 8` means + * `12 & (8 == 8)` and answers 0. Ritchie's own regret; not worth inheriting. */ + eq("12 & 8 == 8", 1); + eq("5 ^ 3 == 6", 1); + /* And among themselves: & then ^ then |, as everywhere else. */ + eq("1 | 6 & 4", 5); + eq("1 ^ 6 & 4", 5); + eq("3 & 1 | 4 & 4", 5); + /* `&&` and `||` still parse as themselves, one character longer. */ + eq("1 & 3 && 2 & 1", 0); + eq("(1 & 3) && (2 & 2)", 1); + eq("0 | 0 || 1 & 1", 1); + /* The XOR texture, which is the reason to want any of this. */ + eq("x ^ y", 7); + eq("(x ^ y) & 1", 1); + printf("\n-- variables\n"); eq("x", 3); eq("y", 4); |
