use std::sync::Arc;
use sim_kernel::{Cx, Error, Expr, Result, ShapeId, Symbol, Value};
use crate::ExprKind;
use crate::base::{Shape, ShapeMatch};
use crate::primitives::{
AnyShape, CaptureShape, ClassShape, ExprKindShape, FieldShape, FieldSpec, ListShape,
NumberValueShape,
};
pub fn parse_shape_expr(expr: &Expr) -> Result<Arc<dyn Shape>> {
let Some(_guard) = crate::recursion::DepthGuard::enter() else {
return Err(Error::Eval(
"shape grammar nesting exceeds the recursion budget".to_owned(),
));
};
match expr {
Expr::Symbol(symbol) => Ok(match symbol.name.as_ref() {
"Any" if symbol.namespace.is_none() => Arc::new(AnyShape),
"Number" if symbol.namespace.is_none() => {
Arc::new(ExprKindShape::new(ExprKind::Number))
}
"Number" if symbol.namespace.as_deref() == Some("core") => Arc::new(NumberValueShape),
"String" if symbol.namespace.is_none() => {
Arc::new(ExprKindShape::new(ExprKind::String))
}
"Bool" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Bool)),
"Symbol" if symbol.namespace.is_none() => {
Arc::new(ExprKindShape::new(ExprKind::Symbol))
}
"Map" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Map)),
"List" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::List)),
"Nil" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Nil)),
_ => Arc::new(ClassShape::new(symbol.clone())),
}),
Expr::List(items) => parse_shape_list(items),
other => Err(Error::Eval(format!(
"cannot build shape from expression kind {:?}",
other
))),
}
}
fn parse_shape_list(items: &[Expr]) -> Result<Arc<dyn Shape>> {
let Some(Expr::Symbol(head)) = items.first() else {
let items = items
.iter()
.map(parse_shape_expr)
.collect::<Result<Vec<_>>>()?;
return Ok(Arc::new(ListShape::new(items)));
};
if head.namespace.is_none() && head.name.as_ref() == "capture" && items.len() == 3 {
let Expr::Symbol(name) = &items[1] else {
return Err(Error::Eval("capture name must be a symbol".to_owned()));
};
return Ok(Arc::new(CaptureShape::new(
name.clone(),
parse_shape_expr(&items[2])?,
)));
}
if head.namespace.is_none() && head.name.as_ref() == "fields" {
let specs = items
.iter()
.skip(1)
.map(parse_field_spec_expr)
.collect::<Result<Vec<_>>>()?;
return Ok(Arc::new(FieldShape::anonymous(specs)));
}
let items = items
.iter()
.map(parse_shape_expr)
.collect::<Result<Vec<_>>>()?;
Ok(Arc::new(ListShape::new(items)))
}
fn parse_field_spec_expr(expr: &Expr) -> Result<FieldSpec> {
let Expr::List(items) = expr else {
return Err(Error::Eval("field shape must be a list".to_owned()));
};
let [Expr::Symbol(name), shape] = items.as_slice() else {
return Err(Error::Eval(
"field shape must be of the form (:field Shape)".to_owned(),
));
};
Ok(FieldSpec::required(
normalize_field_symbol(name),
parse_shape_expr(shape)?,
))
}
fn normalize_field_symbol(symbol: &Symbol) -> Symbol {
if symbol.namespace.is_none()
&& let Some(stripped) = symbol.name.strip_prefix(':')
{
return Symbol::new(stripped.to_owned());
}
symbol.clone()
}
pub fn check_shape_on_expr(shape: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
shape.check_expr(cx, expr)
}
pub fn check_shape_on_value(shape: &dyn Shape, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
shape.check_value(cx, value)
}
pub fn shape_error(expected: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<Error> {
let matched = expected.check_expr(cx, expr)?;
if matched.accepted {
Err(Error::HostError(
"shape_error called for an accepted shape".to_owned(),
))
} else {
Ok(Error::WrongShape {
expected: expected.id().unwrap_or(ShapeId(0)),
diagnostics: matched.diagnostics,
})
}
}