Skip to main content

aver/types/
bool.rs

1/// Bool namespace — logical combinators.
2///
3/// Methods:
4///   Bool.or(a, b)   → Bool  — logical OR
5///   Bool.and(a, b)  → Bool  — logical AND
6///   Bool.not(a)     → Bool  — logical NOT
7///
8/// No effects required.
9use std::collections::HashMap;
10use std::rc::Rc;
11
12use crate::nan_value::{Arena, NanValue};
13use crate::value::{RuntimeError, Value};
14
15pub fn register(global: &mut HashMap<String, Value>) {
16    let mut members = HashMap::new();
17    for method in &["or", "and", "not"] {
18        members.insert(
19            method.to_string(),
20            Value::Builtin(format!("Bool.{}", method)),
21        );
22    }
23    global.insert(
24        "Bool".to_string(),
25        Value::Namespace {
26            name: "Bool".to_string(),
27            members,
28        },
29    );
30}
31
32pub fn effects(_name: &str) -> &'static [&'static str] {
33    &[]
34}
35
36pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
37    match name {
38        "Bool.or" => Some(bool_or(args)),
39        "Bool.and" => Some(bool_and(args)),
40        "Bool.not" => Some(bool_not(args)),
41        _ => None,
42    }
43}
44
45fn bool_or(args: &[Value]) -> Result<Value, RuntimeError> {
46    let [a, b] = args else {
47        return Err(RuntimeError::Error(format!(
48            "Bool.or() takes 2 arguments, got {}",
49            args.len()
50        )));
51    };
52    let Value::Bool(a) = a else {
53        return Err(RuntimeError::Error(
54            "Bool.or: first argument must be a Bool".to_string(),
55        ));
56    };
57    let Value::Bool(b) = b else {
58        return Err(RuntimeError::Error(
59            "Bool.or: second argument must be a Bool".to_string(),
60        ));
61    };
62    Ok(Value::Bool(*a || *b))
63}
64
65fn bool_and(args: &[Value]) -> Result<Value, RuntimeError> {
66    let [a, b] = args else {
67        return Err(RuntimeError::Error(format!(
68            "Bool.and() takes 2 arguments, got {}",
69            args.len()
70        )));
71    };
72    let Value::Bool(a) = a else {
73        return Err(RuntimeError::Error(
74            "Bool.and: first argument must be a Bool".to_string(),
75        ));
76    };
77    let Value::Bool(b) = b else {
78        return Err(RuntimeError::Error(
79            "Bool.and: second argument must be a Bool".to_string(),
80        ));
81    };
82    Ok(Value::Bool(*a && *b))
83}
84
85fn bool_not(args: &[Value]) -> Result<Value, RuntimeError> {
86    let [a] = args else {
87        return Err(RuntimeError::Error(format!(
88            "Bool.not() takes 1 argument, got {}",
89            args.len()
90        )));
91    };
92    let Value::Bool(a) = a else {
93        return Err(RuntimeError::Error(
94            "Bool.not: argument must be a Bool".to_string(),
95        ));
96    };
97    Ok(Value::Bool(!*a))
98}
99
100// ─── NanValue-native API ─────────────────────────────────────────────────────
101
102pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
103    let methods = &["or", "and", "not"];
104    let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
105    for method in methods {
106        let idx = arena.push_builtin(&format!("Bool.{}", method));
107        members.push((Rc::from(*method), NanValue::new_builtin(idx)));
108    }
109    let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
110        name: Rc::from("Bool"),
111        members,
112    });
113    global.insert("Bool".to_string(), NanValue::new_namespace(ns_idx));
114}
115
116pub fn call_nv(
117    name: &str,
118    args: &[NanValue],
119    _arena: &mut Arena,
120) -> Option<Result<NanValue, RuntimeError>> {
121    match name {
122        "Bool.or" => Some(bool_or_nv(args)),
123        "Bool.and" => Some(bool_and_nv(args)),
124        "Bool.not" => Some(bool_not_nv(args)),
125        _ => None,
126    }
127}
128
129fn bool_or_nv(args: &[NanValue]) -> Result<NanValue, RuntimeError> {
130    if args.len() != 2 {
131        return Err(RuntimeError::Error(format!(
132            "Bool.or() takes 2 arguments, got {}",
133            args.len()
134        )));
135    }
136    if !args[0].is_bool() {
137        return Err(RuntimeError::Error(
138            "Bool.or: first argument must be a Bool".to_string(),
139        ));
140    }
141    if !args[1].is_bool() {
142        return Err(RuntimeError::Error(
143            "Bool.or: second argument must be a Bool".to_string(),
144        ));
145    }
146    Ok(NanValue::new_bool(args[0].as_bool() || args[1].as_bool()))
147}
148
149fn bool_and_nv(args: &[NanValue]) -> Result<NanValue, RuntimeError> {
150    if args.len() != 2 {
151        return Err(RuntimeError::Error(format!(
152            "Bool.and() takes 2 arguments, got {}",
153            args.len()
154        )));
155    }
156    if !args[0].is_bool() {
157        return Err(RuntimeError::Error(
158            "Bool.and: first argument must be a Bool".to_string(),
159        ));
160    }
161    if !args[1].is_bool() {
162        return Err(RuntimeError::Error(
163            "Bool.and: second argument must be a Bool".to_string(),
164        ));
165    }
166    Ok(NanValue::new_bool(args[0].as_bool() && args[1].as_bool()))
167}
168
169fn bool_not_nv(args: &[NanValue]) -> Result<NanValue, RuntimeError> {
170    if args.len() != 1 {
171        return Err(RuntimeError::Error(format!(
172            "Bool.not() takes 1 argument, got {}",
173            args.len()
174        )));
175    }
176    if !args[0].is_bool() {
177        return Err(RuntimeError::Error(
178            "Bool.not: argument must be a Bool".to_string(),
179        ));
180    }
181    Ok(NanValue::new_bool(!args[0].as_bool()))
182}