1
use super::super::context::CompileContext;
2
use super::super::emit::FunctionEmitter;
3
use super::super::expr::{compile_expr, compile_for_stack, eval_value, format_expr};
4
use super::NativeSpec;
5
use crate::ast::{Expr, WasmType};
6
use crate::error::{Error, Result};
7
use crate::runtime::SymbolTable;
8

            
9
pub(super) const NATIVES: &[NativeSpec] = &[
10
    NativeSpec {
11
        name: "UPCASE-STRING",
12
        eval: upcase_string,
13
        stack: None,
14
        effect: Some(compile_upcase_string),
15
    },
16
    NativeSpec {
17
        name: "STRING=",
18
        eval: string_eq,
19
        stack: Some(compile_string_eq_to_stack),
20
        effect: None,
21
    },
22
];
23

            
24
340
pub(super) fn upcase_string(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
25
340
    if args.len() != 1 {
26
68
        return Err(Error::Arity {
27
68
            name: "UPCASE-STRING".to_string(),
28
68
            expected: 1,
29
68
            actual: args.len(),
30
68
        });
31
272
    }
32

            
33
272
    let arg = eval_value(symbols, &args[0])?;
34
272
    match arg {
35
204
        Expr::String(s) => Ok(Expr::String(s.to_uppercase())),
36
68
        other => Err(Error::Compile(format!(
37
68
            "UPCASE-STRING: expected string, got {}",
38
68
            format_expr(&other)
39
68
        ))),
40
    }
41
340
}
42

            
43
272
pub(super) fn compile_upcase_string(
44
272
    ctx: &mut CompileContext,
45
272
    emit: &mut FunctionEmitter,
46
272
    symbols: &mut SymbolTable,
47
272
    args: &[Expr],
48
272
) -> Result<()> {
49
272
    let result = upcase_string(symbols, args)?;
50
136
    compile_expr(ctx, emit, symbols, &result)
51
272
}
52

            
53
1236
pub(super) fn string_eq(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
54
1236
    if args.len() != 2 {
55
        return Err(Error::Arity {
56
            name: "STRING=".to_string(),
57
            expected: 2,
58
            actual: args.len(),
59
        });
60
1236
    }
61
1236
    let a = eval_value(symbols, &args[0])?;
62
1236
    let b = eval_value(symbols, &args[1])?;
63

            
64
    // Constant fold if both are compile-time strings
65
1236
    if let (Expr::String(sa), Expr::String(sb)) = (&a, &b) {
66
        return Ok(Expr::Bool(sa == sb));
67
1236
    }
68

            
69
1236
    Ok(Expr::WasmRuntime(WasmType::I32))
70
1236
}
71

            
72
1916
pub(super) fn compile_string_eq_to_stack(
73
1916
    ctx: &mut CompileContext,
74
1916
    emit: &mut FunctionEmitter,
75
1916
    symbols: &mut SymbolTable,
76
1916
    args: &[Expr],
77
1916
) -> Result<WasmType> {
78
1916
    if args.len() != 2 {
79
68
        return Err(Error::Arity {
80
68
            name: "STRING=".to_string(),
81
68
            expected: 2,
82
68
            actual: args.len(),
83
68
        });
84
1848
    }
85
1848
    let a = eval_value(symbols, &args[0])?;
86
1848
    let b = eval_value(symbols, &args[1])?;
87

            
88
    // Constant fold
89
1848
    if let (Expr::String(sa), Expr::String(sb)) = (&a, &b) {
90
136
        emit.i32_const(i32::from(sa == sb));
91
136
        return Ok(WasmType::I32);
92
1712
    }
93

            
94
    // Compile both args to stack as StringRef
95
1712
    compile_string_arg_to_stack(ctx, emit, symbols, &args[0], &a)?;
96
1508
    compile_string_arg_to_stack(ctx, emit, symbols, &args[1], &b)?;
97
1508
    emit.call(ctx.ids.string_eq);
98
1508
    Ok(WasmType::I32)
99
1916
}
100

            
101
3220
fn compile_string_arg_to_stack(
102
3220
    ctx: &mut CompileContext,
103
3220
    emit: &mut FunctionEmitter,
104
3220
    symbols: &mut SymbolTable,
105
3220
    arg: &Expr,
106
3220
    resolved: &Expr,
107
3220
) -> Result<()> {
108
3220
    if let Expr::String(s) = resolved {
109
1508
        let data_idx = ctx.add_data(s.as_bytes())?;
110
1508
        emit.i32_const(0);
111
1508
        emit.i32_const(s.len() as i32);
112
1508
        emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
113
    } else {
114
1712
        let ty = compile_for_stack(ctx, emit, symbols, arg)?;
115
1712
        if ty != WasmType::StringRef {
116
204
            return Err(Error::Compile(format!(
117
204
                "STRING= arguments must be strings, got {ty}"
118
204
            )));
119
1508
        }
120
    }
121
3016
    Ok(())
122
3220
}