Lines
13.48 %
Functions
4.44 %
Branches
100 %
use super::super::context::CompileContext;
use super::super::emit::FunctionEmitter;
use super::super::expr::{compile_expr, eval_value, format_expr};
use super::comparison::bool_result;
use crate::ast::Expr;
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
pub(super) fn make_struct_instance(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 3 {
return Err(Error::Arity {
name: "MAKE-STRUCT-INSTANCE".to_string(),
expected: 3,
actual: args.len(),
});
}
let name_expr = eval_value(symbols, &args[0])?;
let fields_expr = eval_value(symbols, &args[1])?;
let values_expr = eval_value(symbols, &args[2])?;
let name = match name_expr {
Expr::String(s) => s,
Expr::Quote(inner) => match inner.as_ref() {
Expr::String(s) => s.clone(),
other => {
return Err(Error::Compile(format!(
"MAKE-STRUCT-INSTANCE: expected string for name, got quoted {}",
format_expr(other)
)));
},
"MAKE-STRUCT-INSTANCE: expected string for name, got {}",
format_expr(&other)
};
let field_names = match fields_expr {
Expr::List(elems) => elems
.iter()
.map(|e| match e {
Expr::String(s) => Ok(s.clone()),
Expr::Symbol(s) => Ok(s.clone()),
other => Err(Error::Compile(format!(
"MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got {}",
))),
})
.collect::<Result<Vec<_>>>()?,
"MAKE-STRUCT-INSTANCE: expected string or symbol for field name, got quoted {}",
other => return Err(Error::Compile(format!(
"MAKE-STRUCT-INSTANCE: expected list for fields, got quoted {}",
"MAKE-STRUCT-INSTANCE: expected list for fields, got {}",
let field_values = match values_expr {
.map(|e| match eval_value(symbols, e)? {
Expr::RuntimeValue(val) => Ok(val),
Expr::Nil => Ok(crate::runtime::Value::Nil),
Expr::Bool(b) => Ok(crate::runtime::Value::Bool(b)),
Expr::Number(n) => Ok(crate::runtime::Value::Number(n)),
Expr::String(s) => Ok(crate::runtime::Value::String(s)),
Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s)),
"MAKE-STRUCT-INSTANCE: unsupported field value type: {}",
Expr::RuntimeValue(val) => Ok(val.clone()),
Expr::Bool(b) => Ok(crate::runtime::Value::Bool(*b)),
Expr::Number(n) => Ok(crate::runtime::Value::Number(*n)),
Expr::String(s) => Ok(crate::runtime::Value::String(s.clone())),
Expr::Symbol(s) => Ok(crate::runtime::Value::Symbol(s.clone())),
"MAKE-STRUCT-INSTANCE: expected list for field values, got quoted {}",
"MAKE-STRUCT-INSTANCE: expected list for field values, got {}",
use crate::runtime::Value;
// Ensure we have the correct number of field values
if field_values.len() == field_names.len() {
let struct_value = Value::Struct {
name: name.clone(),
fields: field_values,
Ok(Expr::RuntimeValue(struct_value))
} else {
// Pad with nil values if we have fewer values than fields
let mut padded_values = field_values;
while padded_values.len() < field_names.len() {
padded_values.push(Value::Nil);
fields: padded_values,
pub(super) fn struct_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 2 {
name: "STRUCT-FIELD".to_string(),
expected: 2,
let instance_expr = eval_value(symbols, &args[0])?;
let field_expr = eval_value(symbols, &args[1])?;
let field_name = match field_expr {
Expr::String(s) => s.to_uppercase(),
Expr::Symbol(s) => s,
Expr::Symbol(s) => s.clone(),
"STRUCT-FIELD: expected string or symbol for field name, got quoted {}",
"STRUCT-FIELD: expected string or symbol for field name, got {}",
match instance_expr {
Expr::RuntimeValue(val) => {
match val {
Value::Struct { name, fields } => {
let Some(field_names) = symbols.struct_fields(&name) else {
return Ok(Expr::Nil);
let Some(index) = field_names.iter().position(|f| f == &field_name) else {
let value = fields.get(index).cloned().unwrap_or(Value::Nil);
match value {
Value::Nil => Ok(Expr::Nil),
Value::Bool(b) => Ok(Expr::Bool(b)),
Value::Number(n) => Ok(Expr::Number(n)),
Value::String(s) => Ok(Expr::String(s)),
Value::Symbol(s) => Ok(Expr::Symbol(s)),
Value::Struct { .. }
| Value::Pair(_)
| Value::Vector(_)
| Value::Closure(_) => Ok(Expr::RuntimeValue(value)),
_ => Err(Error::Compile(format!(
"STRUCT-FIELD: expected struct instance, got {}",
val.type_name()
pub(super) fn struct_p(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.is_empty() || args.len() > 2 {
name: "STRUCT-P".to_string(),
expected: 1,
let object_expr = eval_value(symbols, &args[0])?;
let type_name = if args.len() == 2 {
let type_expr = eval_value(symbols, &args[1])?;
Some(match type_expr {
"STRUCT-P: expected string or symbol for type name, got quoted {}",
"STRUCT-P: expected string or symbol for type name, got {}",
None
match object_expr {
Value::Struct { name, fields: _ } => {
if let Some(type_name) = type_name {
Ok(bool_result(name == type_name))
Ok(Expr::Bool(true))
_ => Ok(Expr::Nil),
pub(super) fn struct_set_field(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "STRUCT-SET-FIELD".to_string(),
let new_value_expr = eval_value(symbols, &args[2])?;
"STRUCT-SET-FIELD: expected string or symbol for field name, got quoted {}",
"STRUCT-SET-FIELD: expected string or symbol for field name, got {}",
Value::Struct { name, mut fields } => {
let field_names = symbols.struct_fields(&name).ok_or_else(|| {
Error::Compile(format!("STRUCT-SET-FIELD: unknown struct type {name}"))
})?;
let index = field_names
.position(|f| f == &field_name)
.ok_or_else(|| {
Error::Compile(format!(
"STRUCT-SET-FIELD: unknown field {field_name} for struct {name}"
))
let new_value = match new_value_expr {
Expr::RuntimeValue(v) => v,
Expr::Nil => Value::Nil,
Expr::Bool(b) => Value::Bool(b),
Expr::Number(n) => Value::Number(n),
Expr::String(s) => Value::String(s),
Expr::Symbol(s) => Value::Symbol(s),
"STRUCT-SET-FIELD: unsupported value type: {}",
if fields.len() <= index {
fields.resize(index + 1, Value::Nil);
fields[index] = new_value;
Ok(Expr::RuntimeValue(Value::Struct { name, fields }))
"STRUCT-SET-FIELD: expected struct instance, got {}",
pub(super) fn make_struct_runtime(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.is_empty() {
name: "MAKE-STRUCT-RUNTIME".to_string(),
// First argument is the struct name
Expr::RuntimeValue(crate::runtime::Value::String(s)) => s,
_ => {
return Err(Error::Runtime(
"MAKE-STRUCT-RUNTIME requires string name".to_string(),
));
// Remaining arguments are field values - convert back to runtime values
let mut field_values = Vec::new();
for arg in &args[1..] {
let field_expr = eval_value(symbols, arg)?;
let field_value = match field_expr {
Expr::RuntimeValue(val) => val,
Expr::Nil => crate::runtime::Value::Nil,
Expr::Bool(b) => crate::runtime::Value::Bool(b),
Expr::Number(n) => crate::runtime::Value::Number(n),
Expr::String(s) => crate::runtime::Value::String(s),
Expr::Symbol(s) => crate::runtime::Value::Symbol(s),
return Err(Error::Runtime(format!(
"Cannot convert to runtime value: {field_expr:?}"
field_values.push(field_value);
// Create the struct value
let struct_value = crate::runtime::Value::Struct {
name,
pub(super) fn compile_make_struct_instance(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let result = make_struct_instance(symbols, args)?;
compile_expr(ctx, emit, symbols, &result)
pub(super) fn compile_struct_field(
let result = struct_field(symbols, args)?;
pub(super) fn compile_struct_p(
let result = struct_p(symbols, args)?;
pub(super) fn compile_struct_set_field(
let result = struct_set_field(symbols, args)?;
pub(super) fn compile_make_struct_runtime(
let result = make_struct_runtime(symbols, args)?;