feat: add bitwise integer primitives
sha256 and png codegen in real Clojure programs bottom out in bit ops; clonim had none. Adds the JVM-semantics set over int64: shift counts mask to 0..63, bit-shift-right is arithmetic, unsigned-bit-shift-right is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
399bfb7 parent: c735c3d added
examples/bitops.clj +10 -0 | new file mode 100644 | ||
| @@ -0,0 +1,10 @@ | ||
| 1 | +(ns bitops) | |
| 2 | + | |
| 3 | +(println (bit-and 12 10) (bit-or 12 10) (bit-xor 12 10)) ; 8 14 6 | |
| 4 | +(println (bit-and-not 12 10) (bit-not 0) (bit-not 5)) ; 4 -1 -6 | |
| 5 | +(println (bit-and 255 170 15) (bit-or 1 2 4) (bit-xor 1 2 4)) ; variadic: 10 7 7 | |
| 6 | +(println (bit-shift-left 1 10) (bit-shift-right 1024 3)) ; 1024 128 | |
| 7 | +(println (bit-shift-right -8 1) (unsigned-bit-shift-right -1 60)) ; -4 15 | |
| 8 | +(println (bit-test 5 0) (bit-test 5 1)) ; true false | |
| 9 | +(println (bit-set 0 4) (bit-clear 31 0) (bit-flip 5 1)) ; 16 30 7 | |
| 10 | +(println (reduce bit-xor [1 2 4 8])) ; as a value: 15 | |
| new file mode 100644 | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | +(ns bitops) | ||
| 2 | + | ||
| 3 | +(println (bit-and 12 10) (bit-or 12 10) (bit-xor 12 10)) ; 8 14 6 | ||
| 4 | +(println (bit-and-not 12 10) (bit-not 0) (bit-not 5)) ; 4 -1 -6 | ||
| 5 | +(println (bit-and 255 170 15) (bit-or 1 2 4) (bit-xor 1 2 4)) ; variadic: 10 7 7 | ||
| 6 | +(println (bit-shift-left 1 10) (bit-shift-right 1024 3)) ; 1024 128 | ||
| 7 | +(println (bit-shift-right -8 1) (unsigned-bit-shift-right -1 60)) ; -4 15 | ||
| 8 | +(println (bit-test 5 0) (bit-test 5 1)) ; true false | ||
| 9 | +(println (bit-set 0 4) (bit-clear 31 0) (bit-flip 5 1)) ; 16 30 7 | ||
| 10 | +(println (reduce bit-xor [1 2 4 8])) ; as a value: 15 | ||
modified
src/core.nim +29 -0 | @@ -419,6 +419,35 @@ proc registerCore*() = | ||
| 419 | 419 | var r = irem(x, y) |
| 420 | 420 | if r != 0 and ((r < 0) != (y < 0)): r += y |
| 421 | 421 | mkInt(r) |
| 422 | + # ---- bitwise (int64, JVM semantics: shift counts mask to 0..63) | |
| 423 | + def "bit-and", proc (a: openArray[Value]): Value = | |
| 424 | + result = mkInt(intOf(a[0])) | |
| 425 | + for i in 1 ..< a.len: result = mkInt(result.i and intOf(a[i])) | |
| 426 | + def "bit-or", proc (a: openArray[Value]): Value = | |
| 427 | + result = mkInt(intOf(a[0])) | |
| 428 | + for i in 1 ..< a.len: result = mkInt(result.i or intOf(a[i])) | |
| 429 | + def "bit-xor", proc (a: openArray[Value]): Value = | |
| 430 | + result = mkInt(intOf(a[0])) | |
| 431 | + for i in 1 ..< a.len: result = mkInt(result.i xor intOf(a[i])) | |
| 432 | + def "bit-and-not", proc (a: openArray[Value]): Value = | |
| 433 | + result = mkInt(intOf(a[0])) | |
| 434 | + for i in 1 ..< a.len: result = mkInt(result.i and not intOf(a[i])) | |
| 435 | + def "bit-not", proc (a: openArray[Value]): Value = mkInt(not intOf(a[0])) | |
| 436 | + def "bit-shift-left", proc (a: openArray[Value]): Value = | |
| 437 | + mkInt(intOf(a[0]) shl (intOf(a[1]) and 63)) | |
| 438 | + def "bit-shift-right", proc (a: openArray[Value]): Value = | |
| 439 | + # arithmetic: the sign bit is replicated, as on the JVM | |
| 440 | + mkInt(ashr(intOf(a[0]), intOf(a[1]) and 63)) | |
| 441 | + def "unsigned-bit-shift-right", proc (a: openArray[Value]): Value = | |
| 442 | + mkInt(cast[int64](cast[uint64](intOf(a[0])) shr uint64(intOf(a[1]) and 63))) | |
| 443 | + def "bit-test", proc (a: openArray[Value]): Value = | |
| 444 | + mkBool((ashr(intOf(a[0]), intOf(a[1]) and 63) and 1) != 0) | |
| 445 | + def "bit-set", proc (a: openArray[Value]): Value = | |
| 446 | + mkInt(intOf(a[0]) or (1'i64 shl (intOf(a[1]) and 63))) | |
| 447 | + def "bit-clear", proc (a: openArray[Value]): Value = | |
| 448 | + mkInt(intOf(a[0]) and not (1'i64 shl (intOf(a[1]) and 63))) | |
| 449 | + def "bit-flip", proc (a: openArray[Value]): Value = | |
| 450 | + mkInt(intOf(a[0]) xor (1'i64 shl (intOf(a[1]) and 63))) | |
| 422 | 451 | def "inc", proc (a: openArray[Value]): Value = |
| 423 | 452 | (if a[0].kind == kFloat: mkFloat(a[0].f + 1.0) else: mkInt(a[0].i + 1)) |
| 424 | 453 | def "dec", proc (a: openArray[Value]): Value = |
| @@ -419,6 +419,35 @@ proc registerCore*() = | |||
| 419 | var r = irem(x, y) | 419 | var r = irem(x, y) |
| 420 | if r != 0 and ((r < 0) != (y < 0)): r += y | 420 | if r != 0 and ((r < 0) != (y < 0)): r += y |
| 421 | mkInt(r) | 421 | mkInt(r) |
| 422 | + # ---- bitwise (int64, JVM semantics: shift counts mask to 0..63) | ||
| 423 | + def "bit-and", proc (a: openArray[Value]): Value = | ||
| 424 | + result = mkInt(intOf(a[0])) | ||
| 425 | + for i in 1 ..< a.len: result = mkInt(result.i and intOf(a[i])) | ||
| 426 | + def "bit-or", proc (a: openArray[Value]): Value = | ||
| 427 | + result = mkInt(intOf(a[0])) | ||
| 428 | + for i in 1 ..< a.len: result = mkInt(result.i or intOf(a[i])) | ||
| 429 | + def "bit-xor", proc (a: openArray[Value]): Value = | ||
| 430 | + result = mkInt(intOf(a[0])) | ||
| 431 | + for i in 1 ..< a.len: result = mkInt(result.i xor intOf(a[i])) | ||
| 432 | + def "bit-and-not", proc (a: openArray[Value]): Value = | ||
| 433 | + result = mkInt(intOf(a[0])) | ||
| 434 | + for i in 1 ..< a.len: result = mkInt(result.i and not intOf(a[i])) | ||
| 435 | + def "bit-not", proc (a: openArray[Value]): Value = mkInt(not intOf(a[0])) | ||
| 436 | + def "bit-shift-left", proc (a: openArray[Value]): Value = | ||
| 437 | + mkInt(intOf(a[0]) shl (intOf(a[1]) and 63)) | ||
| 438 | + def "bit-shift-right", proc (a: openArray[Value]): Value = | ||
| 439 | + # arithmetic: the sign bit is replicated, as on the JVM | ||
| 440 | + mkInt(ashr(intOf(a[0]), intOf(a[1]) and 63)) | ||
| 441 | + def "unsigned-bit-shift-right", proc (a: openArray[Value]): Value = | ||
| 442 | + mkInt(cast[int64](cast[uint64](intOf(a[0])) shr uint64(intOf(a[1]) and 63))) | ||
| 443 | + def "bit-test", proc (a: openArray[Value]): Value = | ||
| 444 | + mkBool((ashr(intOf(a[0]), intOf(a[1]) and 63) and 1) != 0) | ||
| 445 | + def "bit-set", proc (a: openArray[Value]): Value = | ||
| 446 | + mkInt(intOf(a[0]) or (1'i64 shl (intOf(a[1]) and 63))) | ||
| 447 | + def "bit-clear", proc (a: openArray[Value]): Value = | ||
| 448 | + mkInt(intOf(a[0]) and not (1'i64 shl (intOf(a[1]) and 63))) | ||
| 449 | + def "bit-flip", proc (a: openArray[Value]): Value = | ||
| 450 | + mkInt(intOf(a[0]) xor (1'i64 shl (intOf(a[1]) and 63))) | ||
| 422 | def "inc", proc (a: openArray[Value]): Value = | 451 | def "inc", proc (a: openArray[Value]): Value = |
| 423 | (if a[0].kind == kFloat: mkFloat(a[0].f + 1.0) else: mkInt(a[0].i + 1)) | 452 | (if a[0].kind == kFloat: mkFloat(a[0].f + 1.0) else: mkInt(a[0].i + 1)) |
| 424 | def "dec", proc (a: openArray[Value]): Value = | 453 | def "dec", proc (a: openArray[Value]): Value = |
added
tests/bitops.expected +8 -0 | new file mode 100644 | ||
| @@ -0,0 +1,8 @@ | ||
| 1 | +8 14 6 | |
| 2 | +4 -1 -6 | |
| 3 | +10 7 7 | |
| 4 | +1024 128 | |
| 5 | +-4 15 | |
| 6 | +true false | |
| 7 | +16 30 7 | |
| 8 | +15 | |
| new file mode 100644 | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | +8 14 6 | ||
| 2 | +4 -1 -6 | ||
| 3 | +10 7 7 | ||
| 4 | +1024 128 | ||
| 5 | +-4 15 | ||
| 6 | +true false | ||
| 7 | +16 30 7 | ||
| 8 | +15 | ||