Skip to main content

atmos/os_lib/aura/builtins/
compare_list.rs

1// 分割: aura/builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ6)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use super::*;
4
5pub(crate) fn values_equal(a: &Value, b: &Value) -> bool {
6    match (a, b) {
7        (Value::Num(n1), Value::Num(n2)) => n1 == n2,
8        (Value::Str(s1), Value::Str(s2)) => s1 == s2,
9        (Value::Symbol(s1), Value::Symbol(s2)) => s1 == s2,
10        (Value::Nil, Value::Nil) => true,
11        (Value::Ratio(r1), Value::Ratio(r2)) => r1 == r2,
12        (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
13        (Value::Complex(c1), Value::Complex(c2)) => c1 == c2,
14        // DictやListの比較は省略(必要なら追加)
15        _ => false,
16    }
17}
18
19pub(crate) fn values_cmp(a: &Value, b: &Value) -> Option<core::cmp::Ordering> {
20    match (a, b) {
21        (Value::Num(n1), Value::Num(n2)) => n1.partial_cmp(n2),
22        (Value::Str(s1), Value::Str(s2)) => s1.partial_cmp(s2),
23        (Value::Ratio(r1), Value::Ratio(r2)) => {
24            let left = r1.num.mul(&r2.den);
25            let right = r2.num.mul(&r1.den);
26            left.partial_cmp(&right)
27        }
28        (Value::Num(n1), Value::Ratio(r2)) => {
29            let left = n1.mul(&r2.den);
30            let right = r2.num.clone();
31            left.partial_cmp(&right)
32        }
33        (Value::Ratio(r1), Value::Num(n2)) => {
34            let left = r1.num.clone();
35            let right = n2.mul(&r1.den);
36            left.partial_cmp(&right)
37        }
38        _ => None,
39    }
40}
41
42pub(crate) fn builtin_eq(env: &mut Env, args: &[AST]) -> Result<Value, String> {
43    let vals = eval_args(env, args)?;
44    if vals.len() < 2 {
45        return Ok(Value::Bool(true));
46    }
47    for i in 0..vals.len() - 1 {
48        if !values_equal(&vals[i], &vals[i + 1]) {
49            return Ok(Value::Bool(false));
50        }
51    }
52    Ok(Value::Bool(true))
53}
54
55pub(crate) fn builtin_neq(env: &mut Env, args: &[AST]) -> Result<Value, String> {
56    let vals = eval_args(env, args)?;
57    if vals.len() < 2 {
58        return Ok(Value::Bool(false));
59    }
60    for i in 0..vals.len() - 1 {
61        if values_equal(&vals[i], &vals[i + 1]) {
62            return Ok(Value::Bool(false));
63        }
64    }
65    Ok(Value::Bool(true))
66}
67
68pub(crate) fn builtin_lt(env: &mut Env, args: &[AST]) -> Result<Value, String> {
69    let vals = eval_args(env, args)?;
70    if vals.len() < 2 {
71        return Ok(Value::Bool(true));
72    }
73    for i in 0..vals.len() - 1 {
74        match values_cmp(&vals[i], &vals[i + 1]) {
75            Some(core::cmp::Ordering::Less) => {}
76            _ => return Ok(Value::Bool(false)),
77        }
78    }
79    Ok(Value::Bool(true))
80}
81
82pub(crate) fn builtin_le(env: &mut Env, args: &[AST]) -> Result<Value, String> {
83    let vals = eval_args(env, args)?;
84    if vals.len() < 2 {
85        return Ok(Value::Bool(true));
86    }
87    for i in 0..vals.len() - 1 {
88        match values_cmp(&vals[i], &vals[i + 1]) {
89            Some(core::cmp::Ordering::Less) | Some(core::cmp::Ordering::Equal) => {}
90            _ => return Ok(Value::Bool(false)),
91        }
92    }
93    Ok(Value::Bool(true))
94}
95
96pub(crate) fn builtin_gt(env: &mut Env, args: &[AST]) -> Result<Value, String> {
97    let vals = eval_args(env, args)?;
98    if vals.len() < 2 {
99        return Ok(Value::Bool(true));
100    }
101    for i in 0..vals.len() - 1 {
102        match values_cmp(&vals[i], &vals[i + 1]) {
103            Some(core::cmp::Ordering::Greater) => {}
104            _ => return Ok(Value::Bool(false)),
105        }
106    }
107    Ok(Value::Bool(true))
108}
109
110pub(crate) fn builtin_ge(env: &mut Env, args: &[AST]) -> Result<Value, String> {
111    let vals = eval_args(env, args)?;
112    if vals.len() < 2 {
113        return Ok(Value::Bool(true));
114    }
115    for i in 0..vals.len() - 1 {
116        match values_cmp(&vals[i], &vals[i + 1]) {
117            Some(core::cmp::Ordering::Greater) | Some(core::cmp::Ordering::Equal) => {}
118            _ => return Ok(Value::Bool(false)),
119        }
120    }
121    Ok(Value::Bool(true))
122}
123
124pub(crate) fn builtin_and(env: &mut Env, args: &[AST]) -> Result<Value, String> {
125    if args.is_empty() {
126        return Ok(Value::Bool(true));
127    }
128    let mut last_val = Value::Bool(true);
129    for arg in args {
130        let evaluated = eval(env, arg)?;
131        let val = force_eval(env, evaluated)?;
132        if !super::super::eval::is_truthy(&val)? {
133            return Ok(val);
134        }
135        last_val = val;
136    }
137    Ok(last_val)
138}
139
140pub(crate) fn builtin_or(env: &mut Env, args: &[AST]) -> Result<Value, String> {
141    if args.is_empty() {
142        return Ok(Value::Bool(false));
143    }
144    let mut last_val = Value::Bool(false);
145    for arg in args {
146        let evaluated = eval(env, arg)?;
147        let val = force_eval(env, evaluated)?;
148        if super::super::eval::is_truthy(&val)? {
149            return Ok(val);
150        }
151        last_val = val;
152    }
153    Ok(last_val)
154}
155
156pub(crate) fn builtin_not(env: &mut Env, args: &[AST]) -> Result<Value, String> {
157    let vals = eval_args(env, args)?;
158    if vals.len() != 1 {
159        return Err(String::from("not requires exactly 1 argument"));
160    }
161    if super::super::eval::is_truthy(&vals[0])? {
162        Ok(Value::Bool(false))
163    } else {
164        Ok(Value::Bool(true))
165    }
166}
167
168pub(crate) fn builtin_xor(env: &mut Env, args: &[AST]) -> Result<Value, String> {
169    let vals = eval_args(env, args)?;
170    let mut count = 0;
171    for val in vals {
172        if super::super::eval::is_truthy(&val)? {
173            count += 1;
174        }
175    }
176    if count % 2 == 1 {
177        Ok(Value::Bool(true))
178    } else {
179        Ok(Value::Bool(false))
180    }
181}
182
183pub(crate) fn builtin_lambda(env: &mut Env, args: &[AST]) -> Result<Value, String> {
184    if args.len() != 2 {
185        return Err(String::from(
186            "lambda requires exactly 2 arguments: lambda((args...) body)",
187        ));
188    }
189
190    let mut arg_names = Vec::new();
191    if let AST::List(arg_list) = &args[0] {
192        for arg in arg_list {
193            if let AST::Symbol(sym) = arg {
194                arg_names.push(sym.clone());
195            } else {
196                return Err(String::from("lambda arguments must be symbols"));
197            }
198        }
199    } else {
200        return Err(String::from(
201            "lambda first argument must be a list of symbols",
202        ));
203    }
204
205    let body_ast = args[1].clone();
206    let closure_env = Box::new(env.clone());
207
208    Ok(Value::Lambda(arg_names, body_ast, closure_env))
209}
210
211pub(crate) fn builtin_while(env: &mut Env, args: &[AST]) -> Result<Value, String> {
212    if args.len() < 2 {
213        return Err(String::from(
214            "while requires at least 2 arguments: while(cond, body...)",
215        ));
216    }
217    let cond_ast = &args[0];
218    let body_asts = &args[1..];
219    let mut last_val = Value::Nil;
220
221    loop {
222        crate::kernel::scheduler::yield_now();
223        let eval_cond = eval(env, cond_ast)?;
224        let cond_val = force_eval(env, eval_cond)?;
225        if !super::super::eval::is_truthy(&cond_val)? {
226            break;
227        }
228        for body_ast in body_asts {
229            let eval_body = eval(env, body_ast)?;
230            last_val = force_eval(env, eval_body)?;
231        }
232    }
233    Ok(last_val)
234}
235
236pub(crate) fn builtin_for(env: &mut Env, args: &[AST]) -> Result<Value, String> {
237    if args.len() < 3 {
238        return Err(String::from(
239            "for requires at least 3 arguments: for(var, list, body...)",
240        ));
241    }
242    let var_name = if let AST::Symbol(sym) = &args[0] {
243        sym.clone()
244    } else {
245        return Err(String::from("for first argument must be a variable symbol"));
246    };
247
248    let eval_list = eval(env, &args[1])?;
249    let list_val = force_eval(env, eval_list)?;
250    let body_asts = &args[2..];
251    let mut last_val = Value::Nil;
252
253    if let Value::List(items) = list_val {
254        for item in items {
255            crate::kernel::scheduler::yield_now();
256            env.set(var_name.clone(), item);
257            for body_ast in body_asts {
258                let eval_body = eval(env, body_ast)?;
259                last_val = force_eval(env, eval_body)?;
260            }
261        }
262    } else {
263        return Err(String::from("for second argument must evaluate to a list"));
264    }
265    Ok(last_val)
266}
267
268pub(crate) fn builtin_list(env: &mut Env, args: &[AST]) -> Result<Value, String> {
269    let mut items = Vec::new();
270    for arg in args {
271        let eval_arg = eval(env, arg)?;
272        items.push(force_eval(env, eval_arg)?);
273    }
274    Ok(Value::List(items))
275}
276
277pub(crate) fn builtin_cons(env: &mut Env, args: &[AST]) -> Result<Value, String> {
278    if args.len() != 2 {
279        return Err(String::from("cons requires exactly 2 arguments"));
280    }
281    let eval_head = eval(env, &args[0])?;
282    let head = force_eval(env, eval_head)?;
283    let eval_tail = eval(env, &args[1])?;
284    let tail = force_eval(env, eval_tail)?;
285
286    if let Value::List(mut list) = tail {
287        list.insert(0, head);
288        Ok(Value::List(list))
289    } else {
290        Err(String::from("cons second argument must be a list"))
291    }
292}
293
294pub(crate) fn builtin_car(env: &mut Env, args: &[AST]) -> Result<Value, String> {
295    if args.len() != 1 {
296        return Err(String::from("car requires exactly 1 argument"));
297    }
298    let eval_arg = eval(env, &args[0])?;
299    let val = force_eval(env, eval_arg)?;
300    if let Value::List(list) = val {
301        if list.is_empty() {
302            Ok(Value::Nil)
303        } else {
304            Ok(list[0].clone())
305        }
306    } else {
307        Err(String::from("car argument must be a list"))
308    }
309}
310
311pub(crate) fn builtin_cdr(env: &mut Env, args: &[AST]) -> Result<Value, String> {
312    if args.len() != 1 {
313        return Err(String::from("cdr requires exactly 1 argument"));
314    }
315    let eval_arg = eval(env, &args[0])?;
316    let val = force_eval(env, eval_arg)?;
317    if let Value::List(list) = val {
318        if list.is_empty() {
319            Ok(Value::List(Vec::new()))
320        } else {
321            Ok(Value::List(list[1..].to_vec()))
322        }
323    } else {
324        Err(String::from("cdr argument must be a list"))
325    }
326}
327
328pub(crate) fn builtin_map(env: &mut Env, args: &[AST]) -> Result<Value, String> {
329    if args.len() != 2 {
330        return Err(String::from("map requires exactly 2 arguments"));
331    }
332    let eval_func = eval(env, &args[0])?;
333    let func_val = force_eval(env, eval_func)?;
334    let eval_list = eval(env, &args[1])?;
335    let list_val = force_eval(env, eval_list)?;
336
337    if let Value::Stream(_) = &list_val {
338        return Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
339            super::super::eval::StreamCore::Map {
340                source: Box::new(list_val.clone()),
341                func: Box::new(func_val),
342                env: Box::new(env.clone()),
343            },
344        ))));
345    }
346
347    let items = if let Value::List(items) = list_val {
348        items
349    } else {
350        return Err(String::from("map second argument must be a list or stream"));
351    };
352
353    let mut result = Vec::new();
354    match func_val {
355        Value::Lambda(arg_names, body_ast, closure_env) => {
356            if arg_names.len() != 1 {
357                return Err(String::from("map function must take exactly 1 argument"));
358            }
359            for item in items {
360                let mut local_env = Env::with_parent(*closure_env.clone());
361                local_env.set(arg_names[0].clone(), item);
362                let evaluated = eval(&mut local_env, &body_ast)?;
363                let res = force_eval(&mut local_env, evaluated)?;
364                result.push(res);
365            }
366        }
367        _ => return Err(String::from("map first argument must be a lambda function")),
368    }
369    Ok(Value::List(result))
370}
371
372pub(crate) fn builtin_filter(env: &mut Env, args: &[AST]) -> Result<Value, String> {
373    if args.len() != 2 {
374        return Err(String::from("filter requires exactly 2 arguments"));
375    }
376    let eval_func = eval(env, &args[0])?;
377    let func_val = force_eval(env, eval_func)?;
378    let eval_list = eval(env, &args[1])?;
379    let list_val = force_eval(env, eval_list)?;
380
381    if let Value::Stream(_) = &list_val {
382        return Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
383            super::super::eval::StreamCore::Filter {
384                source: Box::new(list_val.clone()),
385                func: Box::new(func_val),
386                env: Box::new(env.clone()),
387            },
388        ))));
389    }
390
391    let items = if let Value::List(items) = list_val {
392        items
393    } else {
394        return Err(String::from(
395            "filter second argument must be a list or stream",
396        ));
397    };
398
399    let mut result = Vec::new();
400    match func_val {
401        Value::Lambda(arg_names, body_ast, closure_env) => {
402            if arg_names.len() != 1 {
403                return Err(String::from("filter function must take exactly 1 argument"));
404            }
405            for item in items {
406                let mut local_env = Env::with_parent(*closure_env.clone());
407                local_env.set(arg_names[0].clone(), item.clone());
408                let evaluated = eval(&mut local_env, &body_ast)?;
409                let res = force_eval(&mut local_env, evaluated)?;
410                if super::super::eval::is_truthy(&res)? {
411                    result.push(item);
412                }
413            }
414        }
415        _ => {
416            return Err(String::from(
417                "filter first argument must be a lambda function",
418            ))
419        }
420    }
421    Ok(Value::List(result))
422}
423
424pub(crate) fn builtin_range(env: &mut Env, args: &[AST]) -> Result<Value, String> {
425    if args.is_empty() || args.len() > 3 {
426        return Err(String::from("range requires 1 to 3 arguments"));
427    }
428    let eval_0 = eval(env, &args[0])?;
429    let start_val = force_eval(env, eval_0)?;
430    let start = if let Value::Num(n) = start_val {
431        n
432    } else {
433        return Err(String::from("range arguments must be numbers"));
434    };
435
436    let mut end = None;
437    if args.len() >= 2 {
438        let eval_1 = eval(env, &args[1])?;
439        let end_val = force_eval(env, eval_1)?;
440        if let Value::Num(n) = end_val {
441            end = Some(n);
442        } else {
443            return Err(String::from("range arguments must be numbers"));
444        }
445    }
446
447    let mut step = BigInt::one();
448    if args.len() == 3 {
449        let eval_2 = eval(env, &args[2])?;
450        let step_val = force_eval(env, eval_2)?;
451        if let Value::Num(n) = step_val {
452            step = n;
453        } else {
454            return Err(String::from("range arguments must be numbers"));
455        }
456    }
457
458    Ok(Value::Stream(alloc::rc::Rc::new(core::cell::RefCell::new(
459        super::super::eval::StreamCore::Range {
460            current: start,
461            end,
462            step,
463        },
464    ))))
465}
466
467pub(crate) fn builtin_take(env: &mut Env, args: &[AST]) -> Result<Value, String> {
468    if args.len() != 2 {
469        return Err(String::from("take requires exactly 2 arguments"));
470    }
471    let eval_0 = eval(env, &args[0])?;
472    let count_val = force_eval(env, eval_0)?;
473    let count = if let Value::Num(n) = count_val {
474        n.to_i64().unwrap_or(0).max(0) as usize
475    } else {
476        return Err(String::from("take count must be a number"));
477    };
478
479    let eval_1 = eval(env, &args[1])?;
480    let stream_val = force_eval(env, eval_1)?;
481
482    match stream_val {
483        Value::Stream(rc) => {
484            let mut result = Vec::new();
485            for _ in 0..count {
486                let next_opt = rc.borrow_mut().next()?;
487                if let Some(val) = next_opt {
488                    result.push(val);
489                } else {
490                    break;
491                }
492            }
493            Ok(Value::List(result))
494        }
495        Value::List(l) => {
496            let n = count.min(l.len());
497            Ok(Value::List(l[..n].to_vec()))
498        }
499        _ => Err(String::from(
500            "take second argument must be a stream or list",
501        )),
502    }
503}
504
505pub(crate) fn builtin_to_list(env: &mut Env, args: &[AST]) -> Result<Value, String> {
506    if args.len() != 1 {
507        return Err(String::from("to-list requires exactly 1 argument"));
508    }
509    let eval_0 = eval(env, &args[0])?;
510    let stream_val = force_eval(env, eval_0)?;
511
512    match stream_val {
513        Value::Stream(rc) => {
514            let mut result = Vec::new();
515            loop {
516                let next_opt = rc.borrow_mut().next()?;
517                if let Some(val) = next_opt {
518                    result.push(val);
519                } else {
520                    break;
521                }
522            }
523            Ok(Value::List(result))
524        }
525        Value::List(_) => Ok(stream_val),
526        _ => Err(String::from("to-list argument must be a stream or list")),
527    }
528}
529
530pub(crate) fn builtin_fold(env: &mut Env, args: &[AST]) -> Result<Value, String> {
531    if args.len() != 3 {
532        return Err(String::from(
533            "fold requires exactly 3 arguments: fold(fn, init, list)",
534        ));
535    }
536    let eval_func = eval(env, &args[0])?;
537    let func_val = force_eval(env, eval_func)?;
538    let eval_acc = eval(env, &args[1])?;
539    let mut acc = force_eval(env, eval_acc)?;
540    let eval_list = eval(env, &args[2])?;
541    let list_val = force_eval(env, eval_list)?;
542
543    let items = if let Value::List(items) = list_val {
544        items
545    } else {
546        return Err(String::from("fold third argument must be a list"));
547    };
548
549    match func_val {
550        Value::Lambda(arg_names, body_ast, closure_env) => {
551            if arg_names.len() != 2 {
552                return Err(String::from(
553                    "fold function must take exactly 2 arguments (acc, item)",
554                ));
555            }
556            for item in items {
557                let mut local_env = Env::with_parent(*closure_env.clone());
558                local_env.set(arg_names[0].clone(), acc);
559                local_env.set(arg_names[1].clone(), item);
560                let eval_body = eval(&mut local_env, &body_ast)?;
561                acc = force_eval(&mut local_env, eval_body)?;
562            }
563        }
564        _ => {
565            return Err(String::from(
566                "fold first argument must be a lambda function",
567            ))
568        }
569    }
570    Ok(acc)
571}
572
573pub(crate) fn builtin_print(env: &mut Env, args: &[AST]) -> Result<Value, String> {
574    let mut output = String::new();
575    for (i, arg) in args.iter().enumerate() {
576        let val = eval(env, arg)?;
577        let val = force_eval(env, val)?;
578        if i > 0 {
579            output.push(' ');
580        }
581        output.push_str(&val.to_string());
582    }
583    Ok(Value::Str(output))
584}
585
586pub(crate) fn builtin_complex(env: &mut Env, args: &[AST]) -> Result<Value, String> {
587    let vals = eval_args(env, args)?;
588    if vals.len() != 2 {
589        return Err(String::from("complex requires exactly 2 arguments"));
590    }
591
592    let real = match &vals[0] {
593        Value::Num(n) => Ratio::from_bigint(n.clone()),
594        Value::Ratio(r) => r.clone(),
595        _ => return Err(String::from("complex first argument must be a number")),
596    };
597
598    let imag = match &vals[1] {
599        Value::Num(n) => Ratio::from_bigint(n.clone()),
600        Value::Ratio(r) => r.clone(),
601        _ => return Err(String::from("complex second argument must be a number")),
602    };
603
604    Ok(Value::Complex(Complex::new(real, imag)))
605}
606
607pub(crate) fn builtin_get(env: &mut Env, args: &[AST]) -> Result<Value, String> {
608    let vals = eval_args(env, args)?;
609    if vals.len() != 2 {
610        return Err(String::from("get requires exactly 2 arguments (dict, key)"));
611    }
612
613    if let Value::Dict(dict) = &vals[0] {
614        let key_str = match &vals[1] {
615            Value::Str(s) => s.clone(),
616            Value::Symbol(s) => s.clone(),
617            _ => return Err(String::from("get key must be a string or symbol")),
618        };
619
620        if let Some(val) = dict.get(&key_str) {
621            Ok(val.clone())
622        } else {
623            Ok(Value::Nil)
624        }
625    } else {
626        Err(String::from("get first argument must be a dict"))
627    }
628}
629
630pub(crate) fn builtin_set(env: &mut Env, args: &[AST]) -> Result<Value, String> {
631    if args.len() == 2 {
632        // 変数・関数のバインド(旧 def)
633        if let AST::Symbol(sym) = &args[0] {
634            let val = eval(env, &args[1])?;
635            let forced = force_eval(env, val)?;
636            env.set(sym.clone(), forced.clone());
637            Ok(forced)
638        } else {
639            Err(String::from(
640                "First argument to set/func must be a symbol when binding",
641            ))
642        }
643    } else if args.len() == 3 {
644        // 辞書の更新
645        let vals = eval_args(env, args)?;
646        if let Value::Dict(dict) = &vals[0] {
647            let key_str = match &vals[1] {
648                Value::Str(s) => s.clone(),
649                Value::Symbol(s) => s.clone(),
650                _ => return Err(String::from("set key must be a string or symbol")),
651            };
652            let mut new_dict = dict.clone();
653            new_dict.insert(key_str, vals[2].clone());
654            Ok(Value::Dict(new_dict))
655        } else {
656            Err(String::from(
657                "set first argument must be a dict when updating",
658            ))
659        }
660    } else {
661        Err(String::from("set/func requires exactly 2 or 3 arguments"))
662    }
663}
664
665pub(crate) fn builtin_length(env: &mut Env, args: &[AST]) -> Result<Value, String> {
666    let vals = eval_args(env, args)?;
667    if vals.len() != 1 {
668        return Err(String::from("length requires exactly 1 argument"));
669    }
670    match &vals[0] {
671        Value::List(l) => Ok(Value::Num(BigInt::from_i64(l.len() as i64))),
672        Value::Str(s) => Ok(Value::Num(BigInt::from_i64(s.chars().count() as i64))),
673        Value::Dict(d) => Ok(Value::Num(BigInt::from_i64(d.len() as i64))),
674        _ => Err(String::from("length requires a list, string, or dict")),
675    }
676}
677
678pub(crate) fn builtin_type(env: &mut Env, args: &[AST]) -> Result<Value, String> {
679    let vals = eval_args(env, args)?;
680    if vals.len() != 1 {
681        return Err(String::from("type requires exactly 1 argument"));
682    }
683    let type_str = match &vals[0] {
684        Value::Num(_) => "num",
685        Value::Ratio(_) => "ratio",
686        Value::Complex(_) => "complex",
687        Value::Str(_) => "str",
688        Value::Bool(_) => "bool",
689        Value::List(_) => "list",
690        Value::Dict(_) => "dict",
691        Value::Symbol(_) => "symbol",
692        Value::Nil => "nil",
693        Value::Stream(_) => "stream",
694        Value::BuiltinFunc(_) | Value::Lambda(_, _, _) => "func",
695        Value::Thunk(_, _) => "thunk",
696    };
697    Ok(Value::Str(String::from(type_str)))
698}
699
700pub(crate) fn builtin_odd(env: &mut Env, args: &[AST]) -> Result<Value, String> {
701    let vals = eval_args(env, args)?;
702    if vals.len() != 1 {
703        return Err(String::from("odd requires exactly 1 argument"));
704    }
705    let is_odd = match &vals[0] {
706        Value::Num(n) => {
707            if n.digits.is_empty() {
708                false
709            } else {
710                n.digits[0] % 2 != 0
711            }
712        }
713        Value::Ratio(r) if r.den == BigInt::one() => {
714            if r.num.digits.is_empty() {
715                false
716            } else {
717                r.num.digits[0] % 2 != 0
718            }
719        }
720        _ => return Err(String::from("odd requires an integer")),
721    };
722    Ok(Value::Bool(is_odd))
723}
724
725pub(crate) fn builtin_even(env: &mut Env, args: &[AST]) -> Result<Value, String> {
726    let vals = eval_args(env, args)?;
727    if vals.len() != 1 {
728        return Err(String::from("even requires exactly 1 argument"));
729    }
730    let is_even = match &vals[0] {
731        Value::Num(n) => {
732            if n.digits.is_empty() {
733                true
734            } else {
735                n.digits[0] % 2 == 0
736            }
737        }
738        Value::Ratio(r) if r.den == BigInt::one() => {
739            if r.num.digits.is_empty() {
740                true
741            } else {
742                r.num.digits[0] % 2 == 0
743            }
744        }
745        _ => return Err(String::from("even requires an integer")),
746    };
747    Ok(Value::Bool(is_even))
748}
749
750// ----------------------------------------------------
751// 階乗・ビット演算・定数
752// ----------------------------------------------------
753