spec: bitwise operations (bitwise-and/or/xor/not, arithmetic-shift, bit-count, integer-length)
OCaml: land/lor/lxor/lnot/lsl/asr in sx_primitives.ml JS: & | ^ ~ << >> with Kernighan popcount and Math.clz32 for integer-length spec/primitives.sx: stdlib.bitwise module with 7 entries 26 tests, 158 assertions, all pass OCaml+JS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1309,6 +1309,27 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
|
|||||||
return NIL;
|
return NIL;
|
||||||
};
|
};
|
||||||
''',
|
''',
|
||||||
|
|
||||||
|
"stdlib.bitwise": '''
|
||||||
|
// stdlib.bitwise
|
||||||
|
PRIMITIVES["bitwise-and"] = function(a, b) { return (a & b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-or"] = function(a, b) { return (a | b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-xor"] = function(a, b) { return (a ^ b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-not"] = function(a) { return ~a; };
|
||||||
|
PRIMITIVES["arithmetic-shift"] = function(a, count) {
|
||||||
|
return count >= 0 ? (a << count) | 0 : a >> (-count);
|
||||||
|
};
|
||||||
|
PRIMITIVES["bit-count"] = function(a) {
|
||||||
|
var n = Math.abs(a) >>> 0;
|
||||||
|
n = n - ((n >> 1) & 0x55555555);
|
||||||
|
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
|
||||||
|
return (((n + (n >> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
||||||
|
};
|
||||||
|
PRIMITIVES["integer-length"] = function(a) {
|
||||||
|
if (a === 0) return 0;
|
||||||
|
return 32 - Math.clz32(Math.abs(a));
|
||||||
|
};
|
||||||
|
''',
|
||||||
}
|
}
|
||||||
# Modules to include by default (all)
|
# Modules to include by default (all)
|
||||||
_ALL_JS_MODULES = list(PRIMITIVES_JS_MODULES.keys())
|
_ALL_JS_MODULES = list(PRIMITIVES_JS_MODULES.keys())
|
||||||
|
|||||||
@@ -2007,4 +2007,49 @@ let () =
|
|||||||
| [rx] ->
|
| [rx] ->
|
||||||
let (_, _, flags) = regex_of_value rx in
|
let (_, _, flags) = regex_of_value rx in
|
||||||
String flags
|
String flags
|
||||||
| _ -> raise (Eval_error "regex-flags: (regex)"))
|
| _ -> raise (Eval_error "regex-flags: (regex)"));
|
||||||
|
|
||||||
|
(* Bitwise operations *)
|
||||||
|
register "bitwise-and" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a; Integer b] -> Integer (a land b)
|
||||||
|
| _ -> raise (Eval_error "bitwise-and: expected (integer integer)"));
|
||||||
|
register "bitwise-or" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a; Integer b] -> Integer (a lor b)
|
||||||
|
| _ -> raise (Eval_error "bitwise-or: expected (integer integer)"));
|
||||||
|
register "bitwise-xor" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a; Integer b] -> Integer (a lxor b)
|
||||||
|
| _ -> raise (Eval_error "bitwise-xor: expected (integer integer)"));
|
||||||
|
register "bitwise-not" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a] -> Integer (lnot a)
|
||||||
|
| _ -> raise (Eval_error "bitwise-not: expected (integer)"));
|
||||||
|
register "arithmetic-shift" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a; Integer count] ->
|
||||||
|
Integer (if count >= 0 then a lsl count else a asr (-count))
|
||||||
|
| _ -> raise (Eval_error "arithmetic-shift: expected (integer integer)"));
|
||||||
|
register "bit-count" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a] ->
|
||||||
|
let n = ref (abs a) in
|
||||||
|
let c = ref 0 in
|
||||||
|
while !n <> 0 do
|
||||||
|
c := !c + (!n land 1);
|
||||||
|
n := !n lsr 1
|
||||||
|
done;
|
||||||
|
Integer !c
|
||||||
|
| _ -> raise (Eval_error "bit-count: expected (integer)"));
|
||||||
|
register "integer-length" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Integer a] ->
|
||||||
|
let n = ref (abs a) in
|
||||||
|
let bits = ref 0 in
|
||||||
|
while !n <> 0 do
|
||||||
|
incr bits;
|
||||||
|
n := !n lsr 1
|
||||||
|
done;
|
||||||
|
Integer !bits
|
||||||
|
| _ -> raise (Eval_error "integer-length: expected (integer)"))
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||||
var SX_VERSION = "2026-04-26T18:15:33Z";
|
var SX_VERSION = "2026-04-26T19:02:22Z";
|
||||||
|
|
||||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||||
@@ -697,6 +697,26 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// stdlib.bitwise
|
||||||
|
PRIMITIVES["bitwise-and"] = function(a, b) { return (a & b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-or"] = function(a, b) { return (a | b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-xor"] = function(a, b) { return (a ^ b) | 0; };
|
||||||
|
PRIMITIVES["bitwise-not"] = function(a) { return ~a; };
|
||||||
|
PRIMITIVES["arithmetic-shift"] = function(a, count) {
|
||||||
|
return count >= 0 ? (a << count) | 0 : a >> (-count);
|
||||||
|
};
|
||||||
|
PRIMITIVES["bit-count"] = function(a) {
|
||||||
|
var n = Math.abs(a) >>> 0;
|
||||||
|
n = n - ((n >> 1) & 0x55555555);
|
||||||
|
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
|
||||||
|
return (((n + (n >> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
||||||
|
};
|
||||||
|
PRIMITIVES["integer-length"] = function(a) {
|
||||||
|
if (a === 0) return 0;
|
||||||
|
return 32 - Math.clz32(Math.abs(a));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
function isPrimitive(name) { return name in PRIMITIVES; }
|
function isPrimitive(name) { return name in PRIMITIVES; }
|
||||||
function getPrimitive(name) { return PRIMITIVES[name]; }
|
function getPrimitive(name) { return PRIMITIVES[name]; }
|
||||||
|
|
||||||
|
|||||||
@@ -805,3 +805,47 @@
|
|||||||
:doc "Create a new empty mutable string buffer for O(1) amortised append.")
|
:doc "Create a new empty mutable string buffer for O(1) amortised append.")
|
||||||
|
|
||||||
(define-module :stdlib.coroutines)
|
(define-module :stdlib.coroutines)
|
||||||
|
|
||||||
|
(define-module :stdlib.bitwise)
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"bitwise-and"
|
||||||
|
:params (((a :as number) (b :as number)))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Bitwise AND of two integers.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"bitwise-or"
|
||||||
|
:params (((a :as number) (b :as number)))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Bitwise OR of two integers.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"bitwise-xor"
|
||||||
|
:params (((a :as number) (b :as number)))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Bitwise XOR of two integers.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"bitwise-not"
|
||||||
|
:params ((a :as number))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Bitwise NOT (one's complement) of an integer.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"arithmetic-shift"
|
||||||
|
:params (((a :as number) (count :as number)))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Arithmetic shift: left if count > 0, right if count < 0.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"bit-count"
|
||||||
|
:params ((a :as number))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Count set bits (popcount) in a non-negative integer.")
|
||||||
|
|
||||||
|
(define-primitive
|
||||||
|
"integer-length"
|
||||||
|
:params ((a :as number))
|
||||||
|
:returns "number"
|
||||||
|
:doc "Number of bits needed to represent integer a (excluding sign).")
|
||||||
|
|||||||
157
spec/tests/test-bitwise.sx
Normal file
157
spec/tests/test-bitwise.sx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
(defsuite
|
||||||
|
"bitwise-operations"
|
||||||
|
(deftest
|
||||||
|
"bitwise-and basic"
|
||||||
|
(do
|
||||||
|
(assert= 0 (bitwise-and 0 0))
|
||||||
|
(assert= 1 (bitwise-and 3 1))
|
||||||
|
(assert= 0 (bitwise-and 5 2))
|
||||||
|
(assert= 4 (bitwise-and 12 6))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-and identity and zero"
|
||||||
|
(do
|
||||||
|
(assert= 255 (bitwise-and 255 255))
|
||||||
|
(assert= 0 (bitwise-and 255 0))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-or basic"
|
||||||
|
(do
|
||||||
|
(assert= 0 (bitwise-or 0 0))
|
||||||
|
(assert= 3 (bitwise-or 1 2))
|
||||||
|
(assert= 7 (bitwise-or 5 3))
|
||||||
|
(assert= 15 (bitwise-or 9 6))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-or identity"
|
||||||
|
(do
|
||||||
|
(assert= 255 (bitwise-or 255 0))
|
||||||
|
(assert= 255 (bitwise-or 0 255))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-xor basic"
|
||||||
|
(do
|
||||||
|
(assert= 0 (bitwise-xor 0 0))
|
||||||
|
(assert= 3 (bitwise-xor 1 2))
|
||||||
|
(assert= 6 (bitwise-xor 3 5))
|
||||||
|
(assert= 0 (bitwise-xor 255 255))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-xor toggle bits"
|
||||||
|
(do
|
||||||
|
(assert= 14 (bitwise-xor 10 4))
|
||||||
|
(assert= 10 (bitwise-xor 14 4))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-not zero"
|
||||||
|
(do (assert= -1 (bitwise-not 0))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-not positive"
|
||||||
|
(do
|
||||||
|
(assert= -2 (bitwise-not 1))
|
||||||
|
(assert= -5 (bitwise-not 4))
|
||||||
|
(assert= -256 (bitwise-not 255))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-not negative"
|
||||||
|
(do
|
||||||
|
(assert= 0 (bitwise-not -1))
|
||||||
|
(assert= 1 (bitwise-not -2))
|
||||||
|
(assert= 4 (bitwise-not -5))))
|
||||||
|
(deftest
|
||||||
|
"bitwise-not double negation"
|
||||||
|
(do
|
||||||
|
(assert= 42 (bitwise-not (bitwise-not 42)))
|
||||||
|
(assert= 0 (bitwise-not (bitwise-not 0)))))
|
||||||
|
(deftest
|
||||||
|
"arithmetic-shift left"
|
||||||
|
(do
|
||||||
|
(assert= 2 (arithmetic-shift 1 1))
|
||||||
|
(assert= 4 (arithmetic-shift 1 2))
|
||||||
|
(assert= 16 (arithmetic-shift 1 4))
|
||||||
|
(assert= 8 (arithmetic-shift 2 2))))
|
||||||
|
(deftest
|
||||||
|
"arithmetic-shift right"
|
||||||
|
(do
|
||||||
|
(assert= 1 (arithmetic-shift 2 -1))
|
||||||
|
(assert= 1 (arithmetic-shift 4 -2))
|
||||||
|
(assert= 5 (arithmetic-shift 10 -1))
|
||||||
|
(assert= 2 (arithmetic-shift 16 -3))))
|
||||||
|
(deftest
|
||||||
|
"arithmetic-shift by zero"
|
||||||
|
(do
|
||||||
|
(assert= 42 (arithmetic-shift 42 0))
|
||||||
|
(assert= 0 (arithmetic-shift 0 5))))
|
||||||
|
(deftest
|
||||||
|
"arithmetic-shift negative value right preserves sign"
|
||||||
|
(do
|
||||||
|
(assert= -1 (arithmetic-shift -1 -1))
|
||||||
|
(assert= -2 (arithmetic-shift -4 -1))))
|
||||||
|
(deftest
|
||||||
|
"bit-count zero"
|
||||||
|
(do (assert= 0 (bit-count 0))))
|
||||||
|
(deftest
|
||||||
|
"bit-count powers of two"
|
||||||
|
(do
|
||||||
|
(assert= 1 (bit-count 1))
|
||||||
|
(assert= 1 (bit-count 2))
|
||||||
|
(assert= 1 (bit-count 4))
|
||||||
|
(assert= 1 (bit-count 128))))
|
||||||
|
(deftest
|
||||||
|
"bit-count all-ones values"
|
||||||
|
(do
|
||||||
|
(assert= 8 (bit-count 255))
|
||||||
|
(assert= 4 (bit-count 15))
|
||||||
|
(assert= 2 (bit-count 3))))
|
||||||
|
(deftest
|
||||||
|
"bit-count mixed"
|
||||||
|
(do
|
||||||
|
(assert= 3 (bit-count 7))
|
||||||
|
(assert= 2 (bit-count 5))
|
||||||
|
(assert= 3 (bit-count 11))
|
||||||
|
(assert= 4 (bit-count 30))))
|
||||||
|
(deftest
|
||||||
|
"integer-length zero"
|
||||||
|
(do (assert= 0 (integer-length 0))))
|
||||||
|
(deftest
|
||||||
|
"integer-length powers of two"
|
||||||
|
(do
|
||||||
|
(assert= 1 (integer-length 1))
|
||||||
|
(assert= 2 (integer-length 2))
|
||||||
|
(assert= 3 (integer-length 4))
|
||||||
|
(assert= 4 (integer-length 8))
|
||||||
|
(assert= 8 (integer-length 128))))
|
||||||
|
(deftest
|
||||||
|
"integer-length non-powers"
|
||||||
|
(do
|
||||||
|
(assert= 2 (integer-length 3))
|
||||||
|
(assert= 3 (integer-length 5))
|
||||||
|
(assert= 3 (integer-length 7))
|
||||||
|
(assert= 8 (integer-length 255))
|
||||||
|
(assert= 9 (integer-length 256))))
|
||||||
|
(deftest
|
||||||
|
"bitwise ops compose"
|
||||||
|
(do
|
||||||
|
(assert=
|
||||||
|
5
|
||||||
|
(bitwise-and
|
||||||
|
(bitwise-or 5 3)
|
||||||
|
(bitwise-xor 7 2)))
|
||||||
|
(assert= 0 (bitwise-and 170 85))))
|
||||||
|
(deftest
|
||||||
|
"arithmetic-shift round-trip"
|
||||||
|
(do
|
||||||
|
(assert=
|
||||||
|
10
|
||||||
|
(arithmetic-shift (arithmetic-shift 10 3) -3))))
|
||||||
|
(deftest
|
||||||
|
"extract bits with mask"
|
||||||
|
(do
|
||||||
|
(let
|
||||||
|
((x 52))
|
||||||
|
(assert=
|
||||||
|
5
|
||||||
|
(bitwise-and (arithmetic-shift x -2) 7)))))
|
||||||
|
(deftest
|
||||||
|
"clear low bits with bitwise-not mask"
|
||||||
|
(do
|
||||||
|
(assert= 252 (bitwise-and 255 (bitwise-not 3)))))
|
||||||
|
(deftest
|
||||||
|
"integer-length after shift"
|
||||||
|
(do
|
||||||
|
(assert=
|
||||||
|
4
|
||||||
|
(integer-length (arithmetic-shift 1 3))))))
|
||||||
Reference in New Issue
Block a user