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

            
9
132
pub(super) fn make_struct_instance(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
10
132
    if args.len() != 3 {
11
        return Err(Error::Arity {
12
            name: "MAKE-STRUCT-INSTANCE".to_string(),
13
            expected: 3,
14
            actual: args.len(),
15
        });
16
132
    }
17

            
18
132
    let name_expr = eval_value(symbols, &args[0])?;
19
132
    let fields_expr = eval_value(symbols, &args[1])?;
20
132
    let values_expr = eval_value(symbols, &args[2])?;
21

            
22
132
    let name = match name_expr {
23
        Expr::String(s) => s,
24
132
        Expr::Quote(inner) => match inner.as_ref() {
25
132
            Expr::String(s) => s.clone(),
26
            other => {
27
                return Err(Error::Compile(format!(
28
                    "MAKE-STRUCT-INSTANCE: expected string for name, got quoted {}",
29
                    format_expr(other)
30
                )));
31
            }
32
        },
33
        other => {
34
            return Err(Error::Compile(format!(
35
                "MAKE-STRUCT-INSTANCE: expected string for name, got {}",
36
                format_expr(&other)
37
            )));
38
        }
39
    };
40

            
41
132
    let field_names = match fields_expr {
42
        Expr::List(elems) => elems
43
            .iter()
44
            .map(|e| match e {
45
                Expr::String(s) => Ok(s.clone()),
46
                Expr::Symbol(s) => Ok(s.clone()),
47
                other => Err(Error::Compile(format!(
48
                    "MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got {}",
49
                    format_expr(other)
50
                ))),
51
            })
52
            .collect::<Result<Vec<_>>>()?,
53
132
        Expr::Quote(inner) => match inner.as_ref() {
54
132
            Expr::List(elems) => elems
55
132
                .iter()
56
484
                .map(|e| match e {
57
484
                    Expr::String(s) => Ok(s.clone()),
58
                    Expr::Symbol(s) => Ok(s.clone()),
59
                    other => Err(Error::Compile(format!(
60
                        "MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got quoted {}",
61
                        format_expr(other)
62
                    ))),
63
484
                })
64
132
                .collect::<Result<Vec<_>>>()?,
65
            other => return Err(Error::Compile(format!(
66
                "MAKE-STRUCT-INSTANCE: expected list for fields, got quoted {}",
67
                format_expr(other)
68
            ))),
69
        },
70
        other => return Err(Error::Compile(format!(
71
            "MAKE-STRUCT-INSTANCE: expected list for fields, got {}",
72
            format_expr(&other)
73
        ))),
74
    };
75

            
76
132
    let field_values = match values_expr {
77
        Expr::List(elems) => elems
78
            .iter()
79
            .map(|e| match eval_value(symbols, e)? {
80
                Expr::RuntimeValue(val) => Ok(val),
81
                Expr::Nil => Ok(crate::runtime::Value::Nil),
82
                Expr::Bool(b) => Ok(crate::runtime::Value::Bool(b)),
83
                Expr::Number(n) => Ok(crate::runtime::Value::Number(n)),
84
                Expr::String(s) => Ok(crate::runtime::Value::String(s)),
85
                Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s)),
86
                other => Err(Error::Compile(format!(
87
                    "MAKE-STRUCT-INSTANCE: unsupported field value type: {}",
88
                    format_expr(&other)
89
                ))),
90
            })
91
            .collect::<Result<Vec<_>>>()?,
92
132
        Expr::Quote(inner) => match inner.as_ref() {
93
132
            Expr::List(elems) => elems
94
132
                .iter()
95
484
                .map(|e| match e {
96
                    Expr::RuntimeValue(val) => Ok(val.clone()),
97
132
                    Expr::Nil => Ok(crate::runtime::Value::Nil),
98
                    Expr::Bool(b) => Ok(crate::runtime::Value::Bool(*b)),
99
176
                    Expr::Number(n) => Ok(crate::runtime::Value::Number(*n)),
100
176
                    Expr::String(s) => Ok(crate::runtime::Value::String(s.clone())),
101
                    Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s.clone())),
102
                    other => Err(Error::Compile(format!(
103
                        "MAKE-STRUCT-INSTANCE: unsupported field value type: {}",
104
                        format_expr(other)
105
                    ))),
106
484
                })
107
132
                .collect::<Result<Vec<_>>>()?,
108
            other => {
109
                return Err(Error::Compile(format!(
110
                    "MAKE-STRUCT-INSTANCE: expected list for field values, got quoted {}",
111
                    format_expr(other)
112
                )));
113
            }
114
        },
115
        other => {
116
            return Err(Error::Compile(format!(
117
                "MAKE-STRUCT-INSTANCE: expected list for field values, got {}",
118
                format_expr(&other)
119
            )));
120
        }
121
    };
122

            
123
    use crate::runtime::Value;
124

            
125
    // Ensure we have the correct number of field values
126
132
    if field_values.len() == field_names.len() {
127
132
        let struct_value = Value::Struct {
128
132
            name: name.clone(),
129
132
            fields: field_values,
130
132
        };
131
132
        Ok(Expr::RuntimeValue(struct_value))
132
    } else {
133
        // Pad with nil values if we have fewer values than fields
134
        let mut padded_values = field_values;
135
        while padded_values.len() < field_names.len() {
136
            padded_values.push(Value::Nil);
137
        }
138
        let struct_value = Value::Struct {
139
            name: name.clone(),
140
            fields: padded_values,
141
        };
142
        Ok(Expr::RuntimeValue(struct_value))
143
    }
144
132
}
145

            
146
pub(super) fn struct_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
147
    if args.len() != 2 {
148
        return Err(Error::Arity {
149
            name: "STRUCT-FIELD".to_string(),
150
            expected: 2,
151
            actual: args.len(),
152
        });
153
    }
154

            
155
    let instance_expr = eval_value(symbols, &args[0])?;
156
    let field_expr = eval_value(symbols, &args[1])?;
157

            
158
    let field_name = match field_expr {
159
        Expr::String(s) => s.to_uppercase(),
160
        Expr::Symbol(s) => s,
161
        Expr::Quote(inner) => match inner.as_ref() {
162
            Expr::Symbol(s) => s.clone(),
163
            other => {
164
                return Err(Error::Compile(format!(
165
                    "STRUCT-FIELD: expected string or symbol for field name, got quoted {}",
166
                    format_expr(other)
167
                )));
168
            }
169
        },
170
        other => {
171
            return Err(Error::Compile(format!(
172
                "STRUCT-FIELD: expected string or symbol for field name, got {}",
173
                format_expr(&other)
174
            )));
175
        }
176
    };
177

            
178
    match instance_expr {
179
        Expr::RuntimeValue(val) => {
180
            use crate::runtime::Value;
181
            match val {
182
                Value::Struct { name, fields } => {
183
                    let Some(field_names) = symbols.struct_fields(&name) else {
184
                        return Ok(Expr::Nil);
185
                    };
186
                    let Some(index) = field_names.iter().position(|f| f == &field_name) else {
187
                        return Ok(Expr::Nil);
188
                    };
189
                    let value = fields.get(index).cloned().unwrap_or(Value::Nil);
190
                    match value {
191
                        Value::Nil => Ok(Expr::Nil),
192
                        Value::Bool(b) => Ok(Expr::Bool(b)),
193
                        Value::Number(n) => Ok(Expr::Number(n)),
194
                        Value::String(s) => Ok(Expr::String(s)),
195
                        Value::Symbol(s) => Ok(Expr::Symbol(s)),
196
                        Value::Struct { .. }
197
                        | Value::Pair(_)
198
                        | Value::Vector(_)
199
                        | Value::Closure(_) => Ok(Expr::RuntimeValue(value)),
200
                    }
201
                }
202
                _ => Err(Error::Compile(format!(
203
                    "STRUCT-FIELD: expected struct instance, got {}",
204
                    val.type_name()
205
                ))),
206
            }
207
        }
208
        other => Err(Error::Compile(format!(
209
            "STRUCT-FIELD: expected struct instance, got {}",
210
            format_expr(&other)
211
        ))),
212
    }
213
}
214

            
215
pub(super) fn struct_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
216
    if args.is_empty() || args.len() > 2 {
217
        return Err(Error::Arity {
218
            name: "STRUCT-P".to_string(),
219
            expected: 1,
220
            actual: args.len(),
221
        });
222
    }
223

            
224
    let object_expr = eval_value(symbols, &args[0])?;
225
    let type_name = if args.len() == 2 {
226
        let type_expr = eval_value(symbols, &args[1])?;
227
        Some(match type_expr {
228
            Expr::String(s) => s.to_uppercase(),
229
            Expr::Symbol(s) => s,
230
            Expr::Quote(inner) => match inner.as_ref() {
231
                Expr::Symbol(s) => s.clone(),
232
                other => {
233
                    return Err(Error::Compile(format!(
234
                        "STRUCT-P: expected string or symbol for type name, got quoted {}",
235
                        format_expr(other)
236
                    )));
237
                }
238
            },
239
            other => {
240
                return Err(Error::Compile(format!(
241
                    "STRUCT-P: expected string or symbol for type name, got {}",
242
                    format_expr(&other)
243
                )));
244
            }
245
        })
246
    } else {
247
        None
248
    };
249

            
250
    match object_expr {
251
        Expr::RuntimeValue(val) => {
252
            use crate::runtime::Value;
253
            match val {
254
                Value::Struct { name, fields: _ } => {
255
                    if let Some(type_name) = type_name {
256
                        Ok(bool_result(name == type_name))
257
                    } else {
258
                        Ok(Expr::Bool(true))
259
                    }
260
                }
261
                _ => Ok(Expr::Nil),
262
            }
263
        }
264
        _ => Ok(Expr::Nil),
265
    }
266
}
267

            
268
44
pub(super) fn struct_set_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
269
44
    if args.len() != 3 {
270
        return Err(Error::Arity {
271
            name: "STRUCT-SET-FIELD".to_string(),
272
            expected: 3,
273
            actual: args.len(),
274
        });
275
44
    }
276

            
277
44
    let instance_expr = eval_value(symbols, &args[0])?;
278
44
    let field_expr = eval_value(symbols, &args[1])?;
279
44
    let new_value_expr = eval_value(symbols, &args[2])?;
280

            
281
44
    let field_name = match field_expr {
282
        Expr::String(s) => s.to_uppercase(),
283
        Expr::Symbol(s) => s,
284
44
        Expr::Quote(inner) => match inner.as_ref() {
285
            Expr::Symbol(s) => s.clone(),
286
44
            other => {
287
44
                return Err(Error::Compile(format!(
288
44
                    "STRUCT-SET-FIELD: expected string or symbol for field name, got quoted {}",
289
44
                    format_expr(other)
290
44
                )));
291
            }
292
        },
293
        other => {
294
            return Err(Error::Compile(format!(
295
                "STRUCT-SET-FIELD: expected string or symbol for field name, got {}",
296
                format_expr(&other)
297
            )));
298
        }
299
    };
300

            
301
    match instance_expr {
302
        Expr::RuntimeValue(val) => {
303
            use crate::runtime::Value;
304
            match val {
305
                Value::Struct { name, mut fields } => {
306
                    let field_names = symbols.struct_fields(&name).ok_or_else(|| {
307
                        Error::Compile(format!("STRUCT-SET-FIELD: unknown struct type {name}"))
308
                    })?;
309
                    let index = field_names
310
                        .iter()
311
                        .position(|f| f == &field_name)
312
                        .ok_or_else(|| {
313
                            Error::Compile(format!(
314
                                "STRUCT-SET-FIELD: unknown field {field_name} for struct {name}"
315
                            ))
316
                        })?;
317
                    let new_value = match new_value_expr {
318
                        Expr::RuntimeValue(v) => v,
319
                        Expr::Nil => Value::Nil,
320
                        Expr::Bool(b) => Value::Bool(b),
321
                        Expr::Number(n) => Value::Number(n),
322
                        Expr::String(s) => Value::String(s),
323
                        Expr::Symbol(s) => Value::Symbol(s),
324
                        other => {
325
                            return Err(Error::Compile(format!(
326
                                "STRUCT-SET-FIELD: unsupported value type: {}",
327
                                format_expr(&other)
328
                            )));
329
                        }
330
                    };
331
                    if fields.len() <= index {
332
                        fields.resize(index + 1, Value::Nil);
333
                    }
334
                    fields[index] = new_value;
335
                    Ok(Expr::RuntimeValue(Value::Struct { name, fields }))
336
                }
337
                _ => Err(Error::Compile(format!(
338
                    "STRUCT-SET-FIELD: expected struct instance, got {}",
339
                    val.type_name()
340
                ))),
341
            }
342
        }
343
        other => Err(Error::Compile(format!(
344
            "STRUCT-SET-FIELD: expected struct instance, got {}",
345
            format_expr(&other)
346
        ))),
347
    }
348
44
}
349

            
350
pub(super) fn make_struct_runtime(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
351
    if args.is_empty() {
352
        return Err(Error::Arity {
353
            name: "MAKE-STRUCT-RUNTIME".to_string(),
354
            expected: 1,
355
            actual: args.len(),
356
        });
357
    }
358

            
359
    // First argument is the struct name
360
    let name_expr = eval_value(symbols, &args[0])?;
361
    let name = match name_expr {
362
        Expr::String(s) => s,
363
        Expr::RuntimeValue(crate::runtime::Value::String(s)) => s,
364
        _ => {
365
            return Err(Error::Runtime(
366
                "MAKE-STRUCT-RUNTIME requires string name".to_string(),
367
            ));
368
        }
369
    };
370

            
371
    // Remaining arguments are field values - convert back to runtime values
372
    let mut field_values = Vec::new();
373
    for arg in &args[1..] {
374
        let field_expr = eval_value(symbols, arg)?;
375
        let field_value = match field_expr {
376
            Expr::RuntimeValue(val) => val,
377
            Expr::Nil => crate::runtime::Value::Nil,
378
            Expr::Bool(b) => crate::runtime::Value::Bool(b),
379
            Expr::Number(n) => crate::runtime::Value::Number(n),
380
            Expr::String(s) => crate::runtime::Value::String(s),
381
            Expr::Symbol(s) => crate::runtime::Value::Symbol(s),
382
            _ => {
383
                return Err(Error::Runtime(format!(
384
                    "Cannot convert to runtime value: {field_expr:?}"
385
                )));
386
            }
387
        };
388
        field_values.push(field_value);
389
    }
390

            
391
    // Create the struct value
392
    let struct_value = crate::runtime::Value::Struct {
393
        name,
394
        fields: field_values,
395
    };
396

            
397
    Ok(Expr::RuntimeValue(struct_value))
398
}
399

            
400
pub(super) fn compile_make_struct_instance(
401
    ctx: &mut CompileContext,
402
    emit: &mut FunctionEmitter,
403
    symbols: &mut SymbolTable,
404
    args: &[Expr],
405
) -> Result<()> {
406
    if args.len() != 3 {
407
        return Err(Error::Arity {
408
            name: "MAKE-STRUCT-INSTANCE".to_string(),
409
            expected: 3,
410
            actual: args.len(),
411
        });
412
    }
413
    let result = make_struct_instance(symbols, args)?;
414
    compile_expr(ctx, emit, symbols, &result)
415
}
416

            
417
pub(super) fn compile_struct_field(
418
    ctx: &mut CompileContext,
419
    emit: &mut FunctionEmitter,
420
    symbols: &mut SymbolTable,
421
    args: &[Expr],
422
) -> Result<()> {
423
    let result = struct_field(symbols, args)?;
424
    compile_expr(ctx, emit, symbols, &result)
425
}
426

            
427
pub(super) fn compile_struct_p(
428
    ctx: &mut CompileContext,
429
    emit: &mut FunctionEmitter,
430
    symbols: &mut SymbolTable,
431
    args: &[Expr],
432
) -> Result<()> {
433
    let result = struct_p(symbols, args)?;
434
    compile_expr(ctx, emit, symbols, &result)
435
}
436

            
437
pub(super) fn compile_struct_set_field(
438
    ctx: &mut CompileContext,
439
    emit: &mut FunctionEmitter,
440
    symbols: &mut SymbolTable,
441
    args: &[Expr],
442
) -> Result<()> {
443
    let result = struct_set_field(symbols, args)?;
444
    compile_expr(ctx, emit, symbols, &result)
445
}
446

            
447
pub(super) fn compile_make_struct_runtime(
448
    ctx: &mut CompileContext,
449
    emit: &mut FunctionEmitter,
450
    symbols: &mut SymbolTable,
451
    args: &[Expr],
452
) -> Result<()> {
453
    let result = make_struct_runtime(symbols, args)?;
454
    compile_expr(ctx, emit, symbols, &result)
455
}