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::NativeSpec;
5
use super::comparison::bool_result;
6
use crate::ast::Expr;
7
use crate::error::{Error, Result};
8
use crate::runtime::SymbolTable;
9

            
10
pub(super) const NATIVES: &[NativeSpec] = &[
11
    NativeSpec {
12
        name: "MAKE-STRUCT-INSTANCE",
13
        eval: make_struct_instance,
14
        stack: None,
15
        effect: Some(compile_make_struct_instance),
16
    },
17
    NativeSpec {
18
        name: "STRUCT-FIELD",
19
        eval: struct_field,
20
        stack: None,
21
        effect: Some(compile_struct_field),
22
    },
23
    NativeSpec {
24
        name: "STRUCT-P",
25
        eval: struct_p,
26
        stack: None,
27
        effect: Some(compile_struct_p),
28
    },
29
    NativeSpec {
30
        name: "STRUCT-SET-FIELD",
31
        eval: struct_set_field,
32
        stack: None,
33
        effect: Some(compile_struct_set_field),
34
    },
35
    NativeSpec {
36
        name: "MAKE-STRUCT-RUNTIME",
37
        eval: make_struct_runtime,
38
        stack: None,
39
        effect: Some(compile_make_struct_runtime),
40
    },
41
];
42

            
43
1136
pub(super) fn make_struct_instance(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
44
1136
    if args.len() != 3 {
45
        return Err(Error::Arity {
46
            name: "MAKE-STRUCT-INSTANCE".to_string(),
47
            expected: 3,
48
            actual: args.len(),
49
        });
50
1136
    }
51

            
52
1136
    let name_expr = eval_value(symbols, &args[0])?;
53
1136
    let fields_expr = eval_value(symbols, &args[1])?;
54
1136
    let values_expr = eval_value(symbols, &args[2])?;
55

            
56
1136
    let name = match name_expr {
57
710
        Expr::String(s) => s,
58
355
        Expr::Quote(inner) => match inner.as_ref() {
59
284
            Expr::String(s) => s.clone(),
60
71
            other => {
61
71
                return Err(Error::Compile(format!(
62
71
                    "MAKE-STRUCT-INSTANCE: expected string for name, got quoted {}",
63
71
                    format_expr(other)
64
71
                )));
65
            }
66
        },
67
71
        other => {
68
71
            return Err(Error::Compile(format!(
69
71
                "MAKE-STRUCT-INSTANCE: expected string for name, got {}",
70
71
                format_expr(&other)
71
71
            )));
72
        }
73
    };
74

            
75
994
    let field_names = match fields_expr {
76
        Expr::List(elems) => elems
77
            .iter()
78
            .map(|e| match e {
79
                Expr::String(s) => Ok(s.clone()),
80
                Expr::Symbol(s) => Ok(s.clone()),
81
                other => Err(Error::Compile(format!(
82
                    "MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got {}",
83
                    format_expr(other)
84
                ))),
85
            })
86
            .collect::<Result<Vec<_>>>()?,
87
923
        Expr::Quote(inner) => match inner.as_ref() {
88
923
            Expr::List(elems) => elems
89
923
                .iter()
90
1775
                .map(|e| match e {
91
781
                    Expr::String(s) => Ok(s.clone()),
92
852
                    Expr::Symbol(s) => Ok(s.clone()),
93
142
                    other => Err(Error::Compile(format!(
94
142
                        "MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got quoted {}",
95
142
                        format_expr(other)
96
142
                    ))),
97
1775
                })
98
923
                .collect::<Result<Vec<_>>>()?,
99
            other => return Err(Error::Compile(format!(
100
                "MAKE-STRUCT-INSTANCE: expected list for fields, got quoted {}",
101
                format_expr(other)
102
            ))),
103
        },
104
71
        other => return Err(Error::Compile(format!(
105
71
            "MAKE-STRUCT-INSTANCE: expected list for fields, got {}",
106
71
            format_expr(&other)
107
71
        ))),
108
    };
109

            
110
781
    let field_values = match values_expr {
111
        Expr::List(elems) => elems
112
            .iter()
113
            .map(|e| match eval_value(symbols, e)? {
114
                Expr::RuntimeValue(val) => Ok(val),
115
                Expr::Nil => Ok(crate::runtime::Value::Nil),
116
                Expr::Bool(b) => Ok(crate::runtime::Value::Bool(b)),
117
                Expr::Number(n) => Ok(crate::runtime::Value::Number(n)),
118
                Expr::String(s) => Ok(crate::runtime::Value::String(s)),
119
                Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s)),
120
                other => Err(Error::Compile(format!(
121
                    "MAKE-STRUCT-INSTANCE: unsupported field value type: {}",
122
                    format_expr(&other)
123
                ))),
124
            })
125
            .collect::<Result<Vec<_>>>()?,
126
710
        Expr::Quote(inner) => match inner.as_ref() {
127
710
            Expr::List(elems) => elems
128
710
                .iter()
129
1420
                .map(|e| match e {
130
                    Expr::RuntimeValue(val) => Ok(val.clone()),
131
213
                    Expr::Nil => Ok(crate::runtime::Value::Nil),
132
71
                    Expr::Bool(b) => Ok(crate::runtime::Value::Bool(*b)),
133
852
                    Expr::Number(n) => Ok(crate::runtime::Value::Number(*n)),
134
284
                    Expr::String(s) => Ok(crate::runtime::Value::String(s.clone())),
135
                    Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s.clone())),
136
                    other => Err(Error::Compile(format!(
137
                        "MAKE-STRUCT-INSTANCE: unsupported field value type: {}",
138
                        format_expr(other)
139
                    ))),
140
1420
                })
141
710
                .collect::<Result<Vec<_>>>()?,
142
            other => {
143
                return Err(Error::Compile(format!(
144
                    "MAKE-STRUCT-INSTANCE: expected list for field values, got quoted {}",
145
                    format_expr(other)
146
                )));
147
            }
148
        },
149
71
        other => {
150
71
            return Err(Error::Compile(format!(
151
71
                "MAKE-STRUCT-INSTANCE: expected list for field values, got {}",
152
71
                format_expr(&other)
153
71
            )));
154
        }
155
    };
156

            
157
    use crate::runtime::Value;
158

            
159
    // Ensure we have the correct number of field values
160
710
    if field_values.len() == field_names.len() {
161
639
        let struct_value = Value::Struct {
162
639
            name: name.clone(),
163
639
            fields: field_values,
164
639
        };
165
639
        Ok(Expr::RuntimeValue(struct_value))
166
    } else {
167
        // Pad with nil values if we have fewer values than fields
168
71
        let mut padded_values = field_values;
169
142
        while padded_values.len() < field_names.len() {
170
71
            padded_values.push(Value::Nil);
171
71
        }
172
71
        let struct_value = Value::Struct {
173
71
            name: name.clone(),
174
71
            fields: padded_values,
175
71
        };
176
71
        Ok(Expr::RuntimeValue(struct_value))
177
    }
178
1136
}
179

            
180
213
fn field_name_from_expr(form: &str, expr: &Expr) -> Result<String> {
181
213
    match expr {
182
142
        Expr::String(s) => Ok(s.to_uppercase()),
183
        Expr::Symbol(s) => Ok(s.clone()),
184
71
        Expr::Quote(inner) => match inner.as_ref() {
185
            Expr::Symbol(s) => Ok(s.clone()),
186
71
            Expr::String(s) => Ok(s.to_uppercase()),
187
            other => Err(Error::Compile(format!(
188
                "{form}: expected string or symbol for field name, got quoted {}",
189
                format_expr(other)
190
            ))),
191
        },
192
        other => Err(Error::Compile(format!(
193
            "{form}: expected string or symbol for field name, got {}",
194
            format_expr(other)
195
        ))),
196
    }
197
213
}
198

            
199
142
pub(super) fn struct_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
200
142
    if args.len() != 2 {
201
71
        return Err(Error::Arity {
202
71
            name: "STRUCT-FIELD".to_string(),
203
71
            expected: 2,
204
71
            actual: args.len(),
205
71
        });
206
71
    }
207

            
208
71
    let instance_expr = eval_value(symbols, &args[0])?;
209
71
    let field_expr = eval_value(symbols, &args[1])?;
210

            
211
71
    let field_name = field_name_from_expr("STRUCT-FIELD", &field_expr)?;
212

            
213
71
    match instance_expr {
214
71
        Expr::RuntimeValue(val) => {
215
            use crate::runtime::Value;
216
71
            match val {
217
71
                Value::Struct { name, fields } => {
218
71
                    let Some(field_names) = symbols.struct_fields(&name) else {
219
71
                        return Ok(Expr::Nil);
220
                    };
221
                    let Some(index) = field_names.iter().position(|f| f == &field_name) else {
222
                        return Ok(Expr::Nil);
223
                    };
224
                    let value = fields.get(index).cloned().unwrap_or(Value::Nil);
225
                    match value {
226
                        Value::Nil => Ok(Expr::Nil),
227
                        Value::Bool(b) => Ok(Expr::Bool(b)),
228
                        Value::Number(n) => Ok(Expr::Number(n)),
229
                        Value::String(s) => Ok(Expr::String(s)),
230
                        Value::Symbol(s) => Ok(Expr::Symbol(s)),
231
                        Value::Bytes(b) => Ok(Expr::Bytes(b)),
232
                        Value::Struct { .. }
233
                        | Value::Pair(_)
234
                        | Value::Vector(_)
235
                        | Value::Closure(_)
236
                        | Value::Commodity { .. } => Ok(Expr::RuntimeValue(value)),
237
                    }
238
                }
239
                _ => Err(Error::Compile(format!(
240
                    "STRUCT-FIELD: expected struct instance, got {}",
241
                    val.type_name()
242
                ))),
243
            }
244
        }
245
        other => Err(Error::Compile(format!(
246
            "STRUCT-FIELD: expected struct instance, got {}",
247
            format_expr(&other)
248
        ))),
249
    }
250
142
}
251

            
252
213
pub(super) fn struct_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
253
213
    if args.is_empty() || args.len() > 2 {
254
71
        return Err(Error::Arity {
255
71
            name: "STRUCT-P".to_string(),
256
71
            expected: 1,
257
71
            actual: args.len(),
258
71
        });
259
142
    }
260

            
261
142
    let object_expr = eval_value(symbols, &args[0])?;
262
142
    let type_name = if args.len() == 2 {
263
        let type_expr = eval_value(symbols, &args[1])?;
264
        Some(match type_expr {
265
            Expr::String(s) => s.to_uppercase(),
266
            Expr::Symbol(s) => s,
267
            Expr::Quote(inner) => match inner.as_ref() {
268
                Expr::Symbol(s) => s.clone(),
269
                other => {
270
                    return Err(Error::Compile(format!(
271
                        "STRUCT-P: expected string or symbol for type name, got quoted {}",
272
                        format_expr(other)
273
                    )));
274
                }
275
            },
276
            other => {
277
                return Err(Error::Compile(format!(
278
                    "STRUCT-P: expected string or symbol for type name, got {}",
279
                    format_expr(&other)
280
                )));
281
            }
282
        })
283
    } else {
284
142
        None
285
    };
286

            
287
142
    match object_expr {
288
71
        Expr::RuntimeValue(val) => {
289
            use crate::runtime::Value;
290
71
            match val {
291
71
                Value::Struct { name, fields: _ } => {
292
71
                    if let Some(type_name) = type_name {
293
                        Ok(bool_result(name == type_name))
294
                    } else {
295
71
                        Ok(Expr::Bool(true))
296
                    }
297
                }
298
                _ => Ok(Expr::Nil),
299
            }
300
        }
301
71
        _ => Ok(Expr::Nil),
302
    }
303
213
}
304

            
305
213
pub(super) fn struct_set_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
306
213
    if args.len() != 3 {
307
71
        return Err(Error::Arity {
308
71
            name: "STRUCT-SET-FIELD".to_string(),
309
71
            expected: 3,
310
71
            actual: args.len(),
311
71
        });
312
142
    }
313

            
314
142
    let instance_expr = eval_value(symbols, &args[0])?;
315
142
    let field_expr = eval_value(symbols, &args[1])?;
316
142
    let new_value_expr = eval_value(symbols, &args[2])?;
317

            
318
142
    let field_name = field_name_from_expr("STRUCT-SET-FIELD", &field_expr)?;
319

            
320
142
    match instance_expr {
321
142
        Expr::RuntimeValue(val) => {
322
            use crate::runtime::Value;
323
142
            match val {
324
142
                Value::Struct { name, mut fields } => {
325
142
                    let field_names = symbols.struct_fields(&name).ok_or_else(|| {
326
71
                        Error::Compile(format!("STRUCT-SET-FIELD: unknown struct type {name}"))
327
71
                    })?;
328
71
                    let index = field_names
329
71
                        .iter()
330
71
                        .position(|f| f == &field_name)
331
71
                        .ok_or_else(|| {
332
                            Error::Compile(format!(
333
                                "STRUCT-SET-FIELD: unknown field {field_name} for struct {name}"
334
                            ))
335
                        })?;
336
71
                    let new_value = match new_value_expr {
337
                        Expr::RuntimeValue(v) => v,
338
                        Expr::Nil => Value::Nil,
339
                        Expr::Bool(b) => Value::Bool(b),
340
                        Expr::Number(n) => Value::Number(n),
341
71
                        Expr::String(s) => Value::String(s),
342
                        Expr::Symbol(s) => Value::Symbol(s),
343
                        other => {
344
                            return Err(Error::Compile(format!(
345
                                "STRUCT-SET-FIELD: unsupported value type: {}",
346
                                format_expr(&other)
347
                            )));
348
                        }
349
                    };
350
71
                    if fields.len() <= index {
351
                        fields.resize(index + 1, Value::Nil);
352
71
                    }
353
71
                    fields[index] = new_value;
354
71
                    Ok(Expr::RuntimeValue(Value::Struct { name, fields }))
355
                }
356
                _ => Err(Error::Compile(format!(
357
                    "STRUCT-SET-FIELD: expected struct instance, got {}",
358
                    val.type_name()
359
                ))),
360
            }
361
        }
362
        other => Err(Error::Compile(format!(
363
            "STRUCT-SET-FIELD: expected struct instance, got {}",
364
            format_expr(&other)
365
        ))),
366
    }
367
213
}
368

            
369
pub(super) fn make_struct_runtime(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
370
    if args.is_empty() {
371
        return Err(Error::Arity {
372
            name: "MAKE-STRUCT-RUNTIME".to_string(),
373
            expected: 1,
374
            actual: args.len(),
375
        });
376
    }
377

            
378
    // First argument is the struct name
379
    let name_expr = eval_value(symbols, &args[0])?;
380
    let name = match name_expr {
381
        Expr::String(s) => s,
382
        Expr::RuntimeValue(crate::runtime::Value::String(s)) => s,
383
        _ => {
384
            return Err(Error::Runtime(
385
                "MAKE-STRUCT-RUNTIME requires string name".to_string(),
386
            ));
387
        }
388
    };
389

            
390
    // Remaining arguments are field values - convert back to runtime values
391
    let mut field_values = Vec::new();
392
    for arg in &args[1..] {
393
        let field_expr = eval_value(symbols, arg)?;
394
        let field_value = match field_expr {
395
            Expr::RuntimeValue(val) => val,
396
            Expr::Nil => crate::runtime::Value::Nil,
397
            Expr::Bool(b) => crate::runtime::Value::Bool(b),
398
            Expr::Number(n) => crate::runtime::Value::Number(n),
399
            Expr::String(s) => crate::runtime::Value::String(s),
400
            Expr::Symbol(s) => crate::runtime::Value::Symbol(s),
401
            _ => {
402
                return Err(Error::Runtime(format!(
403
                    "Cannot convert to runtime value: {field_expr:?}"
404
                )));
405
            }
406
        };
407
        field_values.push(field_value);
408
    }
409

            
410
    // Create the struct value
411
    let struct_value = crate::runtime::Value::Struct {
412
        name,
413
        fields: field_values,
414
    };
415

            
416
    Ok(Expr::RuntimeValue(struct_value))
417
}
418

            
419
781
pub(super) fn compile_make_struct_instance(
420
781
    ctx: &mut CompileContext,
421
781
    emit: &mut FunctionEmitter,
422
781
    symbols: &mut SymbolTable,
423
781
    args: &[Expr],
424
781
) -> Result<()> {
425
781
    if args.len() != 3 {
426
71
        return Err(Error::Arity {
427
71
            name: "MAKE-STRUCT-INSTANCE".to_string(),
428
71
            expected: 3,
429
71
            actual: args.len(),
430
71
        });
431
710
    }
432
710
    let result = make_struct_instance(symbols, args)?;
433
284
    compile_expr(ctx, emit, symbols, &result)
434
781
}
435

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

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

            
456
142
pub(super) fn compile_struct_set_field(
457
142
    ctx: &mut CompileContext,
458
142
    emit: &mut FunctionEmitter,
459
142
    symbols: &mut SymbolTable,
460
142
    args: &[Expr],
461
142
) -> Result<()> {
462
142
    let result = struct_set_field(symbols, args)?;
463
    compile_expr(ctx, emit, symbols, &result)
464
142
}
465

            
466
pub(super) fn compile_make_struct_runtime(
467
    ctx: &mut CompileContext,
468
    emit: &mut FunctionEmitter,
469
    symbols: &mut SymbolTable,
470
    args: &[Expr],
471
) -> Result<()> {
472
    let result = make_struct_runtime(symbols, args)?;
473
    compile_expr(ctx, emit, symbols, &result)
474
}