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

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

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

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

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

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

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

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

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

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