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
1088
pub(super) fn make_struct_instance(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
44
1088
    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
1088
    }
51

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

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

            
75
952
    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
884
        Expr::Quote(inner) => match inner.as_ref() {
88
884
            Expr::List(elems) => elems
89
884
                .iter()
90
1700
                .map(|e| match e {
91
748
                    Expr::String(s) => Ok(s.clone()),
92
816
                    Expr::Symbol(s) => Ok(s.clone()),
93
136
                    other => Err(Error::Compile(format!(
94
136
                        "MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got quoted {}",
95
136
                        format_expr(other)
96
136
                    ))),
97
1700
                })
98
884
                .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
68
        other => return Err(Error::Compile(format!(
105
68
            "MAKE-STRUCT-INSTANCE: expected list for fields, got {}",
106
68
            format_expr(&other)
107
68
        ))),
108
    };
109

            
110
748
    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
680
        Expr::Quote(inner) => match inner.as_ref() {
127
680
            Expr::List(elems) => elems
128
680
                .iter()
129
1360
                .map(|e| match e {
130
                    Expr::RuntimeValue(val) => Ok(val.clone()),
131
204
                    Expr::Nil => Ok(crate::runtime::Value::Nil),
132
68
                    Expr::Bool(b) => Ok(crate::runtime::Value::Bool(*b)),
133
816
                    Expr::Number(n) => Ok(crate::runtime::Value::Number(*n)),
134
272
                    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
1360
                })
141
680
                .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
68
        other => {
150
68
            return Err(Error::Compile(format!(
151
68
                "MAKE-STRUCT-INSTANCE: expected list for field values, got {}",
152
68
                format_expr(&other)
153
68
            )));
154
        }
155
    };
156

            
157
    use crate::runtime::Value;
158

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

            
180
204
fn field_name_from_expr(form: &str, expr: &Expr) -> Result<String> {
181
204
    match expr {
182
136
        Expr::String(s) => Ok(s.to_uppercase()),
183
        Expr::Symbol(s) => Ok(s.clone()),
184
68
        Expr::Quote(inner) => match inner.as_ref() {
185
            Expr::Symbol(s) => Ok(s.clone()),
186
68
            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
204
}
198

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

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

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

            
213
68
    match instance_expr {
214
68
        Expr::RuntimeValue(val) => {
215
            use crate::runtime::Value;
216
68
            match val {
217
68
                Value::Struct { name, fields } => {
218
68
                    let Some(field_names) = symbols.struct_fields(&name) else {
219
68
                        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
136
}
251

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

            
261
136
    let object_expr = eval_value(symbols, &args[0])?;
262
136
    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
136
        None
285
    };
286

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

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

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

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

            
320
136
    match instance_expr {
321
136
        Expr::RuntimeValue(val) => {
322
            use crate::runtime::Value;
323
136
            match val {
324
136
                Value::Struct { name, mut fields } => {
325
136
                    let field_names = symbols.struct_fields(&name).ok_or_else(|| {
326
68
                        Error::Compile(format!("STRUCT-SET-FIELD: unknown struct type {name}"))
327
68
                    })?;
328
68
                    let index = field_names
329
68
                        .iter()
330
68
                        .position(|f| f == &field_name)
331
68
                        .ok_or_else(|| {
332
                            Error::Compile(format!(
333
                                "STRUCT-SET-FIELD: unknown field {field_name} for struct {name}"
334
                            ))
335
                        })?;
336
68
                    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
68
                        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
68
                    if fields.len() <= index {
351
                        fields.resize(index + 1, Value::Nil);
352
68
                    }
353
68
                    fields[index] = new_value;
354
68
                    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
204
}
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
748
pub(super) fn compile_make_struct_instance(
420
748
    ctx: &mut CompileContext,
421
748
    emit: &mut FunctionEmitter,
422
748
    symbols: &mut SymbolTable,
423
748
    args: &[Expr],
424
748
) -> Result<()> {
425
748
    if args.len() != 3 {
426
68
        return Err(Error::Arity {
427
68
            name: "MAKE-STRUCT-INSTANCE".to_string(),
428
68
            expected: 3,
429
68
            actual: args.len(),
430
68
        });
431
680
    }
432
680
    let result = make_struct_instance(symbols, args)?;
433
272
    compile_expr(ctx, emit, symbols, &result)
434
748
}
435

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

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

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