Skip to main content

bock_core/primitives/
bool.rs

1//! Bool primitive type methods and trait implementations.
2
3use bock_interp::{BockString, BuiltinRegistry, RuntimeError, TypeTag, Value};
4
5/// Register all Bool methods and trait implementations.
6pub fn register(registry: &mut BuiltinRegistry) {
7    // ── Comparable trait ─────────────────────────────────────────────────
8    registry.register(TypeTag::Bool, "compare", bool_compare);
9
10    // ── Equatable trait ──────────────────────────────────────────────────
11    registry.register(TypeTag::Bool, "equals", bool_equals);
12
13    // ── Hashable trait ───────────────────────────────────────────────────
14    registry.register(TypeTag::Bool, "hash_code", bool_hash_code);
15
16    // ── Displayable trait ────────────────────────────────────────────────
17    registry.register(TypeTag::Bool, "display", bool_display);
18
19    // ── Type-specific methods ────────────────────────────────────────────
20    registry.register(TypeTag::Bool, "negate", bool_negate);
21    registry.register(TypeTag::Bool, "to_int", bool_to_int);
22}
23
24// ─── Helpers ──────────────────────────────────────────────────────────────────
25
26fn expect_bool(args: &[Value], pos: usize, method: &str) -> Result<bool, RuntimeError> {
27    match args.get(pos) {
28        Some(Value::Bool(v)) => Ok(*v),
29        Some(other) => Err(RuntimeError::TypeError(format!(
30            "Bool.{method} expects Bool, got {other}"
31        ))),
32        None => Err(RuntimeError::ArityMismatch {
33            expected: pos + 1,
34            got: args.len(),
35        }),
36    }
37}
38
39// ─── Comparable ───────────────────────────────────────────────────────────────
40
41fn bool_compare(args: &[Value]) -> Result<Value, RuntimeError> {
42    let a = expect_bool(args, 0, "compare")?;
43    let b = expect_bool(args, 1, "compare")?;
44    Ok(Value::Int(a.cmp(&b) as i64))
45}
46
47// ─── Equatable ────────────────────────────────────────────────────────────────
48
49fn bool_equals(args: &[Value]) -> Result<Value, RuntimeError> {
50    let a = expect_bool(args, 0, "equals")?;
51    let b = expect_bool(args, 1, "equals")?;
52    Ok(Value::Bool(a == b))
53}
54
55// ─── Hashable ─────────────────────────────────────────────────────────────────
56
57fn bool_hash_code(args: &[Value]) -> Result<Value, RuntimeError> {
58    use std::hash::{Hash, Hasher};
59    let a = expect_bool(args, 0, "hash_code")?;
60    let mut hasher = std::collections::hash_map::DefaultHasher::new();
61    a.hash(&mut hasher);
62    Ok(Value::Int(hasher.finish() as i64))
63}
64
65// ─── Displayable ──────────────────────────────────────────────────────────────
66
67fn bool_display(args: &[Value]) -> Result<Value, RuntimeError> {
68    let a = expect_bool(args, 0, "display")?;
69    Ok(Value::String(BockString::new(if a {
70        "true"
71    } else {
72        "false"
73    })))
74}
75
76// ─── Type-specific methods ────────────────────────────────────────────────────
77
78fn bool_negate(args: &[Value]) -> Result<Value, RuntimeError> {
79    let a = expect_bool(args, 0, "negate")?;
80    Ok(Value::Bool(!a))
81}
82
83fn bool_to_int(args: &[Value]) -> Result<Value, RuntimeError> {
84    let a = expect_bool(args, 0, "to_int")?;
85    Ok(Value::Int(if a { 1 } else { 0 }))
86}
87
88// ─── Tests ────────────────────────────────────────────────────────────────────
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    fn reg() -> BuiltinRegistry {
95        let mut r = BuiltinRegistry::new();
96        register(&mut r);
97        r
98    }
99
100    #[test]
101    fn compare_false_less_than_true() {
102        let r = reg();
103        let result = r.call(
104            TypeTag::Bool,
105            "compare",
106            &[Value::Bool(false), Value::Bool(true)],
107        );
108        assert_eq!(result.unwrap().unwrap(), Value::Int(-1));
109    }
110
111    #[test]
112    fn equals_true() {
113        let r = reg();
114        let result = r.call(
115            TypeTag::Bool,
116            "equals",
117            &[Value::Bool(true), Value::Bool(true)],
118        );
119        assert_eq!(result.unwrap().unwrap(), Value::Bool(true));
120    }
121
122    #[test]
123    fn equals_false() {
124        let r = reg();
125        let result = r.call(
126            TypeTag::Bool,
127            "equals",
128            &[Value::Bool(true), Value::Bool(false)],
129        );
130        assert_eq!(result.unwrap().unwrap(), Value::Bool(false));
131    }
132
133    #[test]
134    fn display_true() {
135        let r = reg();
136        let result = r.call(TypeTag::Bool, "display", &[Value::Bool(true)]);
137        assert_eq!(
138            result.unwrap().unwrap(),
139            Value::String(BockString::new("true"))
140        );
141    }
142
143    #[test]
144    fn display_false() {
145        let r = reg();
146        let result = r.call(TypeTag::Bool, "display", &[Value::Bool(false)]);
147        assert_eq!(
148            result.unwrap().unwrap(),
149            Value::String(BockString::new("false"))
150        );
151    }
152
153    #[test]
154    fn negate_ok() {
155        let r = reg();
156        let result = r.call(TypeTag::Bool, "negate", &[Value::Bool(true)]);
157        assert_eq!(result.unwrap().unwrap(), Value::Bool(false));
158    }
159
160    #[test]
161    fn to_int_ok() {
162        let r = reg();
163        let true_result = r.call(TypeTag::Bool, "to_int", &[Value::Bool(true)]);
164        assert_eq!(true_result.unwrap().unwrap(), Value::Int(1));
165        let false_result = r.call(TypeTag::Bool, "to_int", &[Value::Bool(false)]);
166        assert_eq!(false_result.unwrap().unwrap(), Value::Int(0));
167    }
168
169    #[test]
170    fn hash_code_deterministic() {
171        let r = reg();
172        let h1 = r
173            .call(TypeTag::Bool, "hash_code", &[Value::Bool(true)])
174            .unwrap()
175            .unwrap();
176        let h2 = r
177            .call(TypeTag::Bool, "hash_code", &[Value::Bool(true)])
178            .unwrap()
179            .unwrap();
180        assert_eq!(h1, h2);
181    }
182}