Lines
67.53 %
Functions
30.91 %
Branches
100 %
use crate::ast::{Expr, LambdaParams};
use crate::error::{Error, Result};
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
use super::super::context::CompileContext;
use super::super::emit::FunctionEmitter;
use super::super::expr::{compile_expr, eval_value, format_expr};
pub(super) fn compile_defstruct(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let result = defstruct(symbols, args)?;
compile_expr(ctx, emit, symbols, &result)
}
pub(super) fn compile_setf(
let result = setf(symbols, args)?;
pub(super) fn defstruct(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.is_empty() {
return Err(Error::Compile(
"DEFSTRUCT requires a structure name".to_string(),
));
let name = args[0].as_symbol().ok_or_else(|| {
Error::Compile(format!(
"DEFSTRUCT: expected structure name, got {:?}",
args[0]
))
})?;
let fields: Vec<String> = args[1..]
.iter()
.map(|arg| {
arg.as_symbol()
.map(std::string::ToString::to_string)
.ok_or_else(|| {
Error::Compile(format!("DEFSTRUCT: expected field name, got {arg:?}"))
})
.collect::<Result<_>>()?;
symbols.define_struct_fields(name.to_string(), fields.clone());
let constructor_name = format!("MAKE-{name}");
let constructor_params = LambdaParams {
required: Vec::new(),
optional: Vec::new(),
rest: None,
key: fields.iter().map(|field| (field.clone(), None)).collect(),
aux: Vec::new(),
};
let constructor_body = {
let field_values: Vec<Expr> = fields
.map(|field| Expr::Symbol(field.clone()))
.collect();
Expr::List(vec![
Expr::Symbol("MAKE-STRUCT-INSTANCE".to_string()),
Expr::Quote(Box::new(Expr::String(name.to_string()))),
Expr::Quote(Box::new(Expr::List(
fields.iter().map(|f| Expr::String(f.clone())).collect(),
))),
Expr::List(
vec![Expr::Symbol("LIST".to_string())]
.into_iter()
.chain(field_values)
.collect(),
),
])
let constructor = Expr::Lambda(constructor_params, Box::new(constructor_body));
symbols.define(
Symbol::new(&constructor_name, SymbolKind::Function).with_function(constructor.clone()),
);
for field in &fields {
let accessor_name = format!("{name}-{field}");
let accessor_params = LambdaParams::simple(vec!["INSTANCE".to_string()]);
let accessor_body = Expr::List(vec![
Expr::Symbol("STRUCT-FIELD".to_string()),
Expr::Symbol("INSTANCE".to_string()),
Expr::Quote(Box::new(Expr::String(field.clone()))),
]);
let accessor = Expr::Lambda(accessor_params, Box::new(accessor_body));
symbols.define(Symbol::new(&accessor_name, SymbolKind::Function).with_function(accessor));
let setf_accessor_name = format!("(SETF {accessor_name})");
let setf_params =
LambdaParams::simple(vec!["INSTANCE".to_string(), "NEW-VALUE".to_string()]);
let setf_body = Expr::List(vec![
Expr::Symbol("STRUCT-SET-FIELD".to_string()),
Expr::Symbol("NEW-VALUE".to_string()),
let setf_function = Expr::Lambda(setf_params, Box::new(setf_body));
Symbol::new(&setf_accessor_name, SymbolKind::Function).with_function(setf_function),
let predicate_name = format!("{name}-P");
let predicate_params = LambdaParams::simple(vec!["OBJECT".to_string()]);
let predicate_body = Expr::List(vec![
Expr::Symbol("STRUCT-P".to_string()),
Expr::Symbol("OBJECT".to_string()),
let predicate = Expr::Lambda(predicate_params, Box::new(predicate_body));
symbols.define(Symbol::new(&predicate_name, SymbolKind::Function).with_function(predicate));
Ok(Expr::Quote(Box::new(Expr::Symbol(name.to_string()))))
pub(super) fn setf(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if !args.len().is_multiple_of(2) {
"SETF requires an even number of arguments (place value pairs)".to_string(),
return Ok(Expr::Nil);
let mut last_value = Expr::Nil;
for pair in args.chunks(2) {
let place = &pair[0];
let value = eval_value(symbols, &pair[1])?;
last_value = set_place(symbols, place, value)?;
Ok(last_value)
pub(in crate::compiler) fn set_place(
place: &Expr,
value: Expr,
) -> Result<Expr> {
match place {
Expr::Symbol(name) => {
if let Some(sym) = symbols.lookup_mut(name) {
sym.set_value(value.clone());
} else {
symbols.define(Symbol::new(name, SymbolKind::Variable).with_value(value.clone()));
Ok(value)
Expr::List(exprs) if !exprs.is_empty() => {
if let Expr::Symbol(func_name) = &exprs[0] {
let setf_name = format!("(SETF {func_name})");
if symbols.contains(&setf_name) {
let mut setf_args = exprs[1..].to_vec();
setf_args.push(value.clone());
let call_args: Vec<Expr> = [Expr::Symbol(setf_name)]
.chain(setf_args)
super::super::expr::call(symbols, &call_args)
Err(Error::Compile(format!(
"No setf method defined for accessor '{func_name}'"
)))
Err(Error::Compile(
"Invalid place in SETF - expected function call".to_string(),
_ => Err(Error::Compile(format!(
"Invalid place in SETF: {}",
format_expr(place)