Skip to main content

atmos/os_lib/js/interp/
operators.rs

1// 分割: interp.rs の `impl Interp` から機械的に移動(2026-07-16 リファクタ フェーズ5)。
2// ロジック不変。メソッド可視性のみ pub(crate) へ昇格(別モジュールの impl Interp
3// からの self 呼び出しのため)。impl ブロックは型 Interp に自動で結合する。
4use super::*;
5
6impl Interp {
7    pub(crate) fn eval_unary(
8        &mut self,
9        op: &UnaryOp,
10        expr: &Expression,
11        scope: &Rc<RefCell<Scope>>,
12        this: &Value,
13    ) -> EvalResult {
14        // typeof は未定義識別子でも例外を出さない。
15        if let UnaryOp::TypeOf = op {
16            if let Expression::Identifier(name) = expr {
17                if scope_get(scope, name).is_none() {
18                    return Ok(Value::str("undefined"));
19                }
20            }
21        }
22        // delete はオペランドの「値」ではなく参照(プロパティの持ち主+キー)を必要とするため、
23        // 他の単項演算子と違って `expr` を先に評価してはいけない。
24        if let UnaryOp::Delete = op {
25            return self.eval_delete(expr, scope, this);
26        }
27        let v = self.eval(expr, scope, this)?;
28        Ok(match op {
29            UnaryOp::Neg => match &v {
30                Value::BigInt(b) => Value::bigint(b.neg()),
31                _ => Value::Number(-v.to_number()),
32            },
33            UnaryOp::Pos => {
34                // 単項 + は BigInt に対しては TypeError(ECMAScript 仕様)。
35                if matches!(v, Value::BigInt(_)) {
36                    return Err(self.throw("Cannot convert a BigInt to a number"));
37                }
38                Value::Number(v.to_number())
39            }
40            UnaryOp::Not => Value::Bool(!v.truthy()),
41            UnaryOp::BitNot => match &v {
42                Value::BigInt(b) => Value::bigint(b.bitnot()),
43                _ => Value::Number(!(to_i32(v.to_number())) as f64),
44            },
45            UnaryOp::TypeOf => Value::str(v.type_of()),
46            UnaryOp::Void => Value::Undefined,
47            // 上の早期 return で必ず処理済み(このアームには到達しない)。
48            UnaryOp::Delete => Value::Bool(true),
49        })
50    }
51
52    /// `delete expr` の実装。`expr` がプロパティアクセス(`obj.x`/`obj[x]`)なら
53    /// 実際にプロパティを取り除いて `true` を返す。それ以外(変数・リテラル等)は
54    /// 仕様上ほぼ no-op のため副作用なく `true` を返す(configurable:false 相当の
55    /// 「削除できず false」ケースはこの処理系にはプロパティ属性の概念が無いため非対応)。
56    pub(crate) fn eval_delete(
57        &mut self,
58        expr: &Expression,
59        scope: &Rc<RefCell<Scope>>,
60        this: &Value,
61    ) -> EvalResult {
62        match expr {
63            Expression::Member {
64                object, property, ..
65            } => {
66                let obj = self.eval(object, scope, this)?;
67                self.delete_property(&obj, property)
68            }
69            Expression::Index { object, index, .. } => {
70                let obj = self.eval(object, scope, this)?;
71                let key = self.eval(index, scope, this)?;
72                let k = to_property_key(&key);
73                self.delete_property(&obj, &k)
74            }
75            _ => Ok(Value::Bool(true)),
76        }
77    }
78
79    /// `delete obj.prop`/`delete obj[key]` の共通実装(`Member`/`Index` 両方から使う)。
80    /// 以前は `Member` 経由でしか `dataset`/`style`/`storage` Host プロキシの特別扱いが
81    /// 効かず、`delete element.style['color']` のような `Index` 経由の等価な書き方だと
82    /// 何も削除されないバグがあった。
83    pub(crate) fn delete_property(&mut self, obj: &Value, property: &str) -> EvalResult {
84        if let Value::Object(o) = obj {
85            // `Proxy`: `deleteProperty` トラップがあれば呼ぶ。無ければ target にフォワード
86            // (`get`/`set`/`has` と同じ「target フォワードのみ」方針)。以前はここが
87            // 未対応で、`delete proxyInstance.prop` がトラップ呼び出しはおろか target への
88            // フォワードすら行わず、常に何も削除しないまま無条件に `true` を返す
89            // サイレントバグだった。
90            let proxy = match &o.borrow().kind {
91                ObjKind::Proxy { target, handler } => Some((target.clone(), handler.clone())),
92                _ => None,
93            };
94            if let Some((target, handler)) = proxy {
95                let trap = handler.borrow().props.get("deleteProperty").cloned();
96                if let Some(trap) =
97                    trap.filter(|t| matches!(t, Value::Object(f) if f.borrow().is_callable()))
98                {
99                    let args = [Value::Object(target), Value::str(property)];
100                    let res = self.call_value(&trap, Value::Object(handler), &args)?;
101                    return Ok(Value::Bool(res.truthy()));
102                }
103                return self.delete_property(&Value::Object(target), property);
104            }
105            // `delete element.dataset.foo`(データ属性削除)は `dataset:` Host
106            // プロキシが `.props` を持たず、実データは DOM ノードの属性側にあるため、
107            // 汎用の props.remove では何も起きない黙殺バグだった。
108            let dataset_idx = match &o.borrow().kind {
109                ObjKind::Host(t) if t.starts_with("dataset:") => {
110                    t.strip_prefix("dataset:").and_then(|s| s.parse::<usize>().ok())
111                }
112                _ => None,
113            };
114            if let Some(idx) = dataset_idx {
115                let attr = camel_to_data_attr(property);
116                self.dom.borrow_mut().remove_attr(idx, &attr);
117                return Ok(Value::Bool(true));
118            }
119            // `delete element.style.color` も同じ種類のバグ(`style:` Host
120            // プロキシ自身は `.props` を持たず、実データは DOM ノードのスタイル
121            // 表側にある)。`set_style` の空文字列は削除として扱われる仕様を再利用する。
122            let style_idx = match &o.borrow().kind {
123                ObjKind::Host(t) if t.starts_with("style:") => {
124                    t.strip_prefix("style:").and_then(|s| s.parse::<usize>().ok())
125                }
126                _ => None,
127            };
128            if let Some(idx) = style_idx {
129                self.dom.borrow_mut().set_style(idx, property, "");
130                return Ok(Value::Bool(true));
131            }
132            // `delete localStorage.foo`/`delete sessionStorage.foo` も同じ種類の
133            // バグ(`storage:` Host プロキシ自身は `.props` を持たない)。
134            let storage_tag = match &o.borrow().kind {
135                ObjKind::Host(t) if t.starts_with("storage:") => Some(t.clone()),
136                _ => None,
137            };
138            if let Some(tag) = storage_tag {
139                super::super::builtins::storage_remove_prop(&tag, property);
140                return Ok(Value::Bool(true));
141            }
142            // 個別プロパティ単位の configurable 属性チェック。false の場合は削除不可 (false)。
143            if let Some(attr) = o.borrow().attrs.get(property) {
144                if !attr.configurable {
145                    return Ok(Value::Bool(false));
146                }
147            }
148            // 凍結・封印済みオブジェクトはプロパティを削除できない。
149            if o.borrow().frozen || o.borrow().sealed {
150                return Ok(Value::Bool(false));
151            }
152            let is_array = matches!(&o.borrow().kind, ObjKind::Array(_));
153            if is_array {
154                // 配列要素の delete は詰め直さず「穴」を残す(仕様どおり)。
155                // この処理系は疎配列を表現できないため undefined で近似する
156                // (`[1,,3]` の穴あきリテラルと同じ扱い)。
157                if let Ok(idx) = property.parse::<usize>() {
158                    if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
159                        if idx < items.len() {
160                            items[idx] = Value::Undefined;
161                        }
162                    }
163                }
164            } else {
165                let mut b = o.borrow_mut();
166                if let Some(attr) = b.attrs.get(property) {
167                    if !attr.configurable {
168                        return Ok(Value::Bool(false));
169                    }
170                }
171                b.props.shift_remove(property);
172                b.accessors.shift_remove(property);
173                b.attrs.shift_remove(property);
174            }
175        }
176        Ok(Value::Bool(true))
177    }
178
179    pub(crate) fn eval_update(
180        &mut self,
181        op: &str,
182        prefix: bool,
183        target: &Expression,
184        scope: &Rc<RefCell<Scope>>,
185        this: &Value,
186    ) -> EvalResult {
187        let old = self.eval(target, scope, this)?.to_number();
188        let new = if op == "++" { old + 1.0 } else { old - 1.0 };
189        self.assign_to(target, Value::Number(new), scope, this)?;
190        Ok(Value::Number(if prefix { new } else { old }))
191    }
192
193    pub(crate) fn eval_assign(
194        &mut self,
195        op: &str,
196        target: &Expression,
197        value: &Expression,
198        scope: &Rc<RefCell<Scope>>,
199        this: &Value,
200    ) -> EvalResult {
201        // `&&=`/`||=`/`??=`(ES2021 論理代入演算子)は短絡評価が仕様上の要件で、
202        // 条件次第では右辺を一切評価してはいけない(例: `obj.cache ??= expensive()` は
203        // `cache` が既に非 null/undefined なら `expensive()` を呼んではならない)。
204        // 以前は関数の先頭で `value` を無条件に評価してから分岐していたため、右辺に
205        // 副作用がある場合に常に実行されてしまう仕様違反のバグだった。他の複合代入
206        // (`+=` 等)はそのような短絡要件が無いため、先に分岐して別経路で処理する。
207        match op {
208            "&&=" => {
209                let cur = self.eval(target, scope, this)?;
210                return if cur.truthy() {
211                    let v = self.eval(value, scope, this)?;
212                    self.assign_to(target, v.clone(), scope, this)?;
213                    Ok(v)
214                } else {
215                    Ok(cur)
216                };
217            }
218            "||=" => {
219                let cur = self.eval(target, scope, this)?;
220                return if !cur.truthy() {
221                    let v = self.eval(value, scope, this)?;
222                    self.assign_to(target, v.clone(), scope, this)?;
223                    Ok(v)
224                } else {
225                    Ok(cur)
226                };
227            }
228            "??=" => {
229                let cur = self.eval(target, scope, this)?;
230                return if matches!(cur, Value::Undefined | Value::Null) {
231                    let v = self.eval(value, scope, this)?;
232                    self.assign_to(target, v.clone(), scope, this)?;
233                    Ok(v)
234                } else {
235                    Ok(cur)
236                };
237            }
238            _ => {}
239        }
240        let rhs = self.eval(value, scope, this)?;
241        let final_val = if op == "=" {
242            rhs
243        } else {
244            let cur = self.eval(target, scope, this)?;
245            let bop = match op {
246                "+=" => BinaryOp::Add,
247                "-=" => BinaryOp::Sub,
248                "*=" => BinaryOp::Mul,
249                "/=" => BinaryOp::Div,
250                "%=" => BinaryOp::Mod,
251                "**=" => BinaryOp::Pow,
252                "&=" => BinaryOp::BitAnd,
253                "|=" => BinaryOp::BitOr,
254                "^=" => BinaryOp::BitXor,
255                "<<=" => BinaryOp::Shl,
256                ">>=" => BinaryOp::Shr,
257                ">>>=" => BinaryOp::UShr,
258                _ => BinaryOp::Add,
259            };
260            self.eval_binary(&bop, cur, rhs)?
261        };
262        self.assign_to(target, final_val.clone(), scope, this)?;
263        Ok(final_val)
264    }
265
266    /// 代入先(識別子・メンバ・添字)へ値を書き込む。
267    pub(crate) fn assign_to(
268        &mut self,
269        target: &Expression,
270        val: Value,
271        scope: &Rc<RefCell<Scope>>,
272        this: &Value,
273    ) -> Result<(), Value> {
274        match target {
275            Expression::Identifier(name) => {
276                if !scope_assign(scope, name, val.clone()) {
277                    // sloppy mode: 未宣言はグローバルに作る。
278                    let mut s = scope.clone();
279                    loop {
280                        let p = s.borrow().parent.clone();
281                        match p {
282                            Some(pp) => s = pp,
283                            None => break,
284                        }
285                    }
286                    scope_declare(&s, name, val);
287                }
288                Ok(())
289            }
290            Expression::Member {
291                object, property, ..
292            } => {
293                let obj = self.eval(object, scope, this)?;
294                self.set_property(&obj, property, val);
295                Ok(())
296            }
297            Expression::Index { object, index, .. } => {
298                let obj = self.eval(object, scope, this)?;
299                let key = self.eval(index, scope, this)?;
300                self.set_property(&obj, &to_property_key(&key), val);
301                Ok(())
302            }
303            _ => Ok(()),
304        }
305    }
306
307    pub fn eval_binary(&mut self, op: &BinaryOp, l: Value, r: Value) -> EvalResult {
308        // どちらかが BigInt のときは専用処理(混在の算術は TypeError)。
309        if matches!(l, Value::BigInt(_)) || matches!(r, Value::BigInt(_)) {
310            return self.eval_binary_bigint(op, l, r);
311        }
312        Ok(match op {
313            BinaryOp::Add => {
314                // どちらかが文字列/オブジェクトなら連結。
315                let l_str = matches!(l, Value::Str(_) | Value::Object(_));
316                let r_str = matches!(r, Value::Str(_) | Value::Object(_));
317                if l_str || r_str {
318                    Value::str(format!("{}{}", l.to_js_string(), r.to_js_string()))
319                } else {
320                    Value::Number(l.to_number() + r.to_number())
321                }
322            }
323            BinaryOp::Sub => Value::Number(l.to_number() - r.to_number()),
324            BinaryOp::Mul => Value::Number(l.to_number() * r.to_number()),
325            BinaryOp::Div => Value::Number(l.to_number() / r.to_number()),
326            BinaryOp::Mod => {
327                let a = l.to_number();
328                let b = r.to_number();
329                Value::Number(if b == 0.0 { f64::NAN } else { a % b })
330            }
331            BinaryOp::Pow => Value::Number(powf(l.to_number(), r.to_number())),
332            BinaryOp::Eq => Value::Bool(loose_eq(&l, &r)),
333            BinaryOp::NotEq => Value::Bool(!loose_eq(&l, &r)),
334            BinaryOp::StrictEq => Value::Bool(l.strict_eq(&r)),
335            BinaryOp::StrictNotEq => Value::Bool(!l.strict_eq(&r)),
336            BinaryOp::Lt => cmp(&l, &r, |o| o == core::cmp::Ordering::Less),
337            BinaryOp::Gt => cmp(&l, &r, |o| o == core::cmp::Ordering::Greater),
338            BinaryOp::LtEq => cmp(&l, &r, |o| o != core::cmp::Ordering::Greater),
339            BinaryOp::GtEq => cmp(&l, &r, |o| o != core::cmp::Ordering::Less),
340            BinaryOp::BitAnd => {
341                Value::Number((to_i32(l.to_number()) & to_i32(r.to_number())) as f64)
342            }
343            BinaryOp::BitOr => {
344                Value::Number((to_i32(l.to_number()) | to_i32(r.to_number())) as f64)
345            }
346            BinaryOp::BitXor => {
347                Value::Number((to_i32(l.to_number()) ^ to_i32(r.to_number())) as f64)
348            }
349            BinaryOp::Shl => Value::Number(
350                (to_i32(l.to_number()).wrapping_shl(to_u32(r.to_number()) & 31)) as f64,
351            ),
352            BinaryOp::Shr => Value::Number(
353                (to_i32(l.to_number()).wrapping_shr(to_u32(r.to_number()) & 31)) as f64,
354            ),
355            BinaryOp::UShr => Value::Number(
356                ((to_u32(l.to_number())).wrapping_shr(to_u32(r.to_number()) & 31)) as f64,
357            ),
358            BinaryOp::In => {
359                let key = l.to_js_string();
360                match &r {
361                    Value::Object(o) => {
362                        // Proxy: has トラップがあれば呼ぶ。無ければ target にフォワード。
363                        let proxy = match &o.borrow().kind {
364                            ObjKind::Proxy { target, handler } => {
365                                Some((target.clone(), handler.clone()))
366                            }
367                            _ => None,
368                        };
369                        if let Some((target, handler)) = proxy {
370                            let trap = handler.borrow().props.get("has").cloned();
371                            if let Some(trap) = trap.filter(
372                                |t| matches!(t, Value::Object(f) if f.borrow().is_callable()),
373                            ) {
374                                let args = [Value::Object(target), Value::str(&key)];
375                                let res = self.call_value(&trap, Value::Object(handler), &args)?;
376                                return Ok(Value::Bool(res.truthy()));
377                            }
378                            return self.eval_binary(&BinaryOp::In, l, Value::Object(target));
379                        }
380                        Value::Bool(self.has_property_chain(o, &key))
381                    }
382                    _ => Value::Bool(false),
383                }
384            }
385            BinaryOp::InstanceOf => self.instance_of(&l, &r)?,
386        })
387    }
388
389    /// DOM ノードのタグ名または種別から、動的に対応する `HTMLXxxElement.prototype`
390    /// (または `HTMLElement.prototype` / `Element.prototype` / `Node.prototype`)を解決する。
391    pub(crate) fn get_dom_node_proto(&self, node_idx: usize) -> Option<ObjRef> {
392        let tag_or_text = {
393            let dom = self.dom.borrow();
394            dom.nodes.get(node_idx).map(|n| (n.tag.to_lowercase(), n.is_text))
395        }?;
396
397        let (tag, is_text) = tag_or_text;
398        let cls_name = if is_text {
399            "Node"
400        } else {
401            match tag.as_str() {
402                "input" => "HTMLInputElement",
403                "form" => "HTMLFormElement",
404                "a" => "HTMLAnchorElement",
405                "img" => "HTMLImageElement",
406                "button" => "HTMLButtonElement",
407                "select" => "HTMLSelectElement",
408                "option" => "HTMLOptionElement",
409                "textarea" => "HTMLTextAreaElement",
410                "div" => "HTMLDivElement",
411                "span" => "HTMLSpanElement",
412                "script" => "HTMLScriptElement",
413                "style" => "HTMLStyleElement",
414                "iframe" => "HTMLIFrameElement",
415                "canvas" => "HTMLCanvasElement",
416                "p" => "HTMLParagraphElement",
417                "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "HTMLHeadingElement",
418                "table" => "HTMLTableElement",
419                "tr" => "HTMLTableRowElement",
420                "td" | "th" => "HTMLTableCellElement",
421                "ul" | "ol" => "HTMLUListElement",
422                "li" => "HTMLLIElement",
423                "template" => "HTMLTemplateElement",
424                _ => "HTMLUnknownElement",
425            }
426        };
427
428        let ctor_val = super::scope_get(&self.global, cls_name)?;
429        if let Value::Object(ctor_obj) = ctor_val {
430            if let Some(Value::Object(proto)) = ctor_obj.borrow().props.get("prototype") {
431                return Some(proto.clone());
432            }
433        }
434        None
435    }
436
437    /// `l instanceof r`: `r.prototype` を起点に `l` のプロトタイプ連鎖を辿って一致を探す
438    /// (OrdinaryHasInstance の簡略実装。`Symbol.hasInstance` によるカスタマイズは非対応)。
439    pub(crate) fn instance_of(&mut self, l: &Value, r: &Value) -> EvalResult {
440        let ctor = match r {
441            Value::Object(o) if o.borrow().is_callable() => o.clone(),
442            _ => return Err(self.throw("Right-hand side of 'instanceof' is not callable")),
443        };
444        // `class Foo { static [Symbol.hasInstance](x){...} }` によるカスタム
445        // `instanceof` 判定(ES2015)が丸ごと未対応で、常にプロトタイプ連鎖の照合に
446        // 決め打ちされていたバグ。`Symbol.hasInstance` が定義されていれば仕様どおり
447        // そちらを最優先する(無ければ従来のプロトタイプ連鎖判定にフォールバック)。
448        let has_instance = ctor.borrow().props.get("Symbol(Symbol.hasInstance)").cloned();
449        if let Some(f) = has_instance {
450            if matches!(&f, Value::Object(fo) if fo.borrow().is_callable()) {
451                let result = self.call_value(&f, Value::Object(ctor), core::slice::from_ref(l))?;
452                return Ok(Value::Bool(result.truthy()));
453            }
454        }
455        let proto = match ctor.borrow().props.get("prototype") {
456            Some(Value::Object(p)) => p.clone(),
457            _ => return Ok(Value::Bool(false)),
458        };
459        let mut cur = match l {
460            Value::Object(o) => {
461                let p = o.borrow().proto.clone();
462                if p.is_none() {
463                    if let ObjKind::DomElement(idx) = o.borrow().kind {
464                        self.get_dom_node_proto(idx)
465                    } else {
466                        None
467                    }
468                } else {
469                    p
470                }
471            }
472            _ => None,
473        };
474        let mut guard = 0;
475        while let Some(p) = cur {
476            if Rc::ptr_eq(&p, &proto) {
477                return Ok(Value::Bool(true));
478            }
479            cur = p.borrow().proto.clone();
480            guard += 1;
481            if guard > 1000 {
482                break;
483            }
484        }
485        Ok(Value::Bool(false))
486    }
487
488    /// BigInt が絡む二項演算。BigInt 同士は任意精度演算、BigInt と Number の
489    /// 混在は算術・ビット演算で TypeError。等価・大小比較は数値として許容する。
490    pub(crate) fn eval_binary_bigint(&mut self, op: &BinaryOp, l: Value, r: Value) -> EvalResult {
491        use super::super::bigint::BigInt;
492        // 文字列連結は BigInt でも許容("x"+1n → "x1")。
493        if matches!(op, BinaryOp::Add) && (matches!(l, Value::Str(_)) || matches!(r, Value::Str(_)))
494        {
495            return Ok(Value::str(format!(
496                "{}{}",
497                l.to_js_string(),
498                r.to_js_string()
499            )));
500        }
501
502        // 等価系: 型混在でも数値的に比較する(=== は型も一致が必要)。
503        match op {
504            BinaryOp::StrictEq => return Ok(Value::Bool(l.strict_eq(&r))),
505            BinaryOp::StrictNotEq => return Ok(Value::Bool(!l.strict_eq(&r))),
506            BinaryOp::Eq => return Ok(Value::Bool(self.bigint_loose_eq(&l, &r))),
507            BinaryOp::NotEq => return Ok(Value::Bool(!self.bigint_loose_eq(&l, &r))),
508            BinaryOp::Lt | BinaryOp::Gt | BinaryOp::LtEq | BinaryOp::GtEq => {
509                return Ok(self.bigint_compare(op, &l, &r));
510            }
511            BinaryOp::In | BinaryOp::InstanceOf => {
512                return Ok(Value::Bool(false));
513            }
514            _ => {}
515        }
516
517        // ここから先は算術・ビット演算。両辺とも BigInt でなければ TypeError。
518        let (a, b) = match (&l, &r) {
519            (Value::BigInt(a), Value::BigInt(b)) => (a.clone(), b.clone()),
520            _ => {
521                return Err(
522                    self.throw("Cannot mix BigInt and other types, use explicit conversions")
523                );
524            }
525        };
526        let result: BigInt = match op {
527            BinaryOp::Add => a.add(&b),
528            BinaryOp::Sub => a.sub(&b),
529            BinaryOp::Mul => a.mul(&b),
530            BinaryOp::Div => match a.div(&b) {
531                Some(v) => v,
532                None => return Err(self.throw("Division by zero")),
533            },
534            BinaryOp::Mod => match a.rem(&b) {
535                Some(v) => v,
536                None => return Err(self.throw("Division by zero")),
537            },
538            BinaryOp::Pow => match a.pow(&b) {
539                Some(v) => v,
540                None => return Err(self.throw("Exponent must be non-negative")),
541            },
542            BinaryOp::BitAnd => a.bitand(&b),
543            BinaryOp::BitOr => a.bitor(&b),
544            BinaryOp::BitXor => a.bitxor(&b),
545            BinaryOp::Shl => {
546                // シフト量は i64 に収まる範囲のみ対応(過大シフトはエラー)。
547                match shift_amount(&b) {
548                    Some(n) => a.shl(n),
549                    None => return Err(self.throw("BigInt shift amount out of range")),
550                }
551            }
552            BinaryOp::Shr => match shift_amount(&b) {
553                Some(n) => a.shr(n),
554                None => return Err(self.throw("BigInt shift amount out of range")),
555            },
556            BinaryOp::UShr => {
557                // BigInt に符号なし右シフト >>> は存在しない(TypeError)。
558                return Err(self.throw("BigInts have no unsigned right shift, use >> instead"));
559            }
560            _ => return Err(self.throw("Unsupported BigInt operation")),
561        };
562        Ok(Value::bigint(result))
563    }
564
565    /// BigInt を含む緩い等価比較(BigInt↔Number/String を数値として比較)。
566    pub(crate) fn bigint_loose_eq(&self, l: &Value, r: &Value) -> bool {
567        use core::cmp::Ordering;
568        match (l, r) {
569            (Value::BigInt(a), Value::BigInt(b)) => a.cmp(b) == Ordering::Equal,
570            (Value::BigInt(a), Value::Number(n)) | (Value::Number(n), Value::BigInt(a)) => {
571                n.is_finite() && a.to_f64() == *n
572            }
573            (Value::BigInt(a), Value::Str(s)) | (Value::Str(s), Value::BigInt(a)) => {
574                match super::super::bigint::BigInt::parse_str(s) {
575                    Some(bi) => a.cmp(&bi) == Ordering::Equal,
576                    None => false,
577                }
578            }
579            (Value::BigInt(a), Value::Bool(bb)) | (Value::Bool(bb), Value::BigInt(a)) => {
580                a.to_f64() == if *bb { 1.0 } else { 0.0 }
581            }
582            _ => false,
583        }
584    }
585
586    /// BigInt を含む大小比較(BigInt↔Number は f64 経由)。
587    pub(crate) fn bigint_compare(&self, op: &BinaryOp, l: &Value, r: &Value) -> Value {
588        use core::cmp::Ordering;
589        let ord: Option<Ordering> = match (l, r) {
590            (Value::BigInt(a), Value::BigInt(b)) => Some(a.cmp(b)),
591            (Value::BigInt(a), _) => {
592                let x = a.to_f64();
593                let y = r.to_number();
594                cmp_f64(x, y)
595            }
596            (_, Value::BigInt(b)) => {
597                let x = l.to_number();
598                let y = b.to_f64();
599                cmp_f64(x, y)
600            }
601            _ => None,
602        };
603        let ord = match ord {
604            Some(o) => o,
605            None => return Value::Bool(false),
606        };
607        let res = match op {
608            BinaryOp::Lt => ord == Ordering::Less,
609            BinaryOp::Gt => ord == Ordering::Greater,
610            BinaryOp::LtEq => ord != Ordering::Greater,
611            BinaryOp::GtEq => ord != Ordering::Less,
612            _ => false,
613        };
614        Value::Bool(res)
615    }
616
617}