use std::sync::Arc;
use sim_kernel::{Cx, Error, Expr, Result, Value};
use crate::base::{Shape, ShapeDoc, ShapeMatch};
pub trait ShapeExprParser: Send + Sync {
fn label(&self) -> &str;
fn is_effectful(&self) -> bool {
false
}
fn parse_expr(&self, source: &str) -> Result<Expr>;
}
pub struct PrattShape {
parser: Arc<dyn ShapeExprParser>,
inner: Arc<dyn Shape>,
}
impl PrattShape {
pub fn new(parser: Arc<dyn ShapeExprParser>, inner: Arc<dyn Shape>) -> Self {
Self { parser, inner }
}
pub fn parser(&self) -> &Arc<dyn ShapeExprParser> {
&self.parser
}
pub fn inner(&self) -> &Arc<dyn Shape> {
&self.inner
}
}
impl Shape for PrattShape {
fn is_effectful(&self) -> bool {
self.parser.is_effectful() || self.inner.is_effectful()
}
fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
let expr = value.object().as_expr(cx)?;
self.check_expr(cx, &expr)
}
fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
let source = match expr {
Expr::String(text) => text.as_str(),
_ => {
return Ok(ShapeMatch::reject(
"expected string expression for Pratt parse",
));
}
};
let parsed = match self.parser.parse_expr(source) {
Ok(parsed) => parsed,
Err(Error::Eval(message)) => return Ok(ShapeMatch::reject(message)),
Err(other) => return Err(other),
};
self.inner.check_expr(cx, &parsed)
}
fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
let inner = self.inner.describe(cx)?;
Ok(ShapeDoc::new("pratt shape")
.with_detail(self.parser.label().to_owned())
.with_detail(inner.name))
}
}