aboutsummaryrefslogtreecommitdiffstats
path: root/tests/expr_test.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-16 15:49:07 +0200
committeruser <user@clank>2026-08-16 15:49:07 +0200
commit7cd9de2c15f5b00b99ea8d974a4c73f0c2d4f972 (patch)
tree476b659e7f5b54b0511b1b5e4dd9a559fa7230bf /tests/expr_test.c
parentsl0ppty: zero strength is not a pass (diff)
downloadslosh-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.c35
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);