1
//! Comparison codegen edge cases — multi-arg chains, ratio/integer
2
//! mix, eql/equal symmetry, neq with multiple args.
3

            
4
use super::common::{compile_and_validate, wrap_with_runtime_ratio};
5

            
6
#[test]
7
1
fn eq_multi_arg_constant_fold() {
8
1
    compile_and_validate("(= 1 1 1 1)");
9
1
}
10

            
11
#[test]
12
1
fn lt_multi_arg_chain() {
13
1
    compile_and_validate("(< 1 2 3 4)");
14
1
}
15

            
16
#[test]
17
1
fn gt_descending_chain() {
18
1
    compile_and_validate("(> 4 3 2 1)");
19
1
}
20

            
21
#[test]
22
1
fn eql_with_strings() {
23
1
    compile_and_validate("(eql \"foo\" \"foo\")");
24
1
}
25

            
26
#[test]
27
1
fn eql_with_symbols() {
28
1
    compile_and_validate("(eql 'a 'a)");
29
1
}
30

            
31
#[test]
32
1
fn eql_with_nil() {
33
1
    compile_and_validate("(eql nil nil)");
34
1
}
35

            
36
#[test]
37
1
fn equal_with_ratios() {
38
1
    compile_and_validate("(equal (/ 1 2) (/ 1 2))");
39
1
}
40

            
41
#[test]
42
1
fn equal_unequal_lists() {
43
1
    compile_and_validate("(equal '(1 2) '(1 3))");
44
1
}
45

            
46
/// `EQUAL?` / `EQ?` (Scheme spellings) are documented natives + registered
47
/// builtin symbols; they must have real codegen handlers, not just exist as
48
/// names the compiler rejects. Regression for the missing `EQUAL?`/`EQ?`
49
/// NativeSpec rows.
50
#[test]
51
1
fn equal_question_alias_compiles() {
52
1
    compile_and_validate("(equal? 'a 'a)");
53
1
}
54

            
55
#[test]
56
1
fn eq_question_alias_compiles() {
57
1
    compile_and_validate("(eq? 1 1)");
58
1
}
59

            
60
/// Equality in value (stack) position — the last form of a `defun` body.
61
/// `EQL`/`EQUAL` used to carry `stack: None`, so this errored with
62
/// "cannot produce stack value". Locks in the stack handler.
63
#[test]
64
1
fn equal_in_value_position_compiles() {
65
1
    compile_and_validate("(defun same? (a b) (equal? a b)) (same? 1 1)");
66
1
}
67

            
68
/// Equality as an `and` operand drives the for-stack path; with `stack: None`
69
/// it failed. Mirrors the Metro script's `(and (equal? …) (equal? …))` shape.
70
#[test]
71
1
fn equal_as_and_operand_compiles() {
72
1
    compile_and_validate(&wrap_with_runtime_ratio("(and (equal? X X) (equal? X X))"));
73
1
}
74

            
75
#[test]
76
1
fn cmp_runtime_lhs_string_constant() {
77
    // Comparison helpers should refuse mixing — Ratio vs String.
78
    use super::common::compile_expect_error;
79
1
    let err = compile_expect_error(&wrap_with_runtime_ratio("(= X \"foo\")"));
80
1
    assert!(!err.is_empty(), "got: {err}");
81
1
}
82

            
83
#[test]
84
1
fn le_with_runtime_and_ratio_constant() {
85
1
    compile_and_validate(&wrap_with_runtime_ratio("(<= (/ 1 2) X)"));
86
1
}
87

            
88
#[test]
89
1
fn ge_with_runtime_and_ratio_constant() {
90
1
    compile_and_validate(&wrap_with_runtime_ratio("(>= (/ 1 2) X)"));
91
1
}
92

            
93
#[test]
94
1
fn cmp_eq_ratio_with_integer_literal() {
95
1
    compile_and_validate(&wrap_with_runtime_ratio("(= X (/ 5 1))"));
96
1
}
97

            
98
#[test]
99
1
fn not_runtime_chained() {
100
1
    compile_and_validate(&wrap_with_runtime_ratio("(not (not (= X 0)))"));
101
1
}