sim-lib-logic 0.1.0

SIM workspace package for sim lib logic.
Documentation
use std::sync::Arc;

use sim_kernel::{Cx, Error, Expr, Result, Shape, ShapeDoc, ShapeMatch, Symbol, Value};
use sim_shape::{AnyShape, shape_value};

use crate::clause::{is_goal_expr, parse_clause_expr};

pub fn register_logic_shapes(
    linker: &mut sim_kernel::Linker<'_>,
    cx: &mut sim_kernel::LoadCx,
) -> Result<()> {
    let _ = cx;
    for (symbol, shape) in [
        (
            Symbol::qualified("logic", "Var"),
            Arc::new(VarShape) as Arc<dyn Shape>,
        ),
        (
            Symbol::qualified("logic", "Goal"),
            Arc::new(GoalShape) as Arc<dyn Shape>,
        ),
        (
            Symbol::qualified("logic", "Clause"),
            Arc::new(ClauseShape(None)) as Arc<dyn Shape>,
        ),
        (
            Symbol::qualified("logic", "Fact"),
            Arc::new(ClauseShape(Some("fact"))) as Arc<dyn Shape>,
        ),
        (
            Symbol::qualified("logic", "Rule"),
            Arc::new(ClauseShape(Some("rule"))) as Arc<dyn Shape>,
        ),
        (
            Symbol::qualified("logic", "Answer"),
            Arc::new(AnswerShape) as Arc<dyn Shape>,
        ),
    ] {
        linker.shape_value(symbol.clone(), shape_value(symbol, shape))?;
    }
    linker.shape_value(
        Symbol::qualified("logic", "Config"),
        shape_value(Symbol::qualified("logic", "Config"), Arc::new(AnyShape)),
    )?;
    Ok(())
}

struct VarShape;
struct GoalShape;
struct ClauseShape(Option<&'static str>);
struct AnswerShape;

impl Shape for VarShape {
    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> {
        Ok(if matches!(expr, Expr::Local(_)) {
            ShapeMatch::accept(sim_kernel::MatchScore::exact(100))
        } else {
            ShapeMatch::reject("logic variable expected")
        })
    }

    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
        Ok(ShapeDoc::new("LogicVar").with_detail("?x or Expr::Local"))
    }
}

impl Shape for GoalShape {
    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> {
        Ok(if is_goal_expr(expr) {
            ShapeMatch::accept(sim_kernel::MatchScore::exact(100))
        } else {
            ShapeMatch::reject("logic goal expected")
        })
    }

    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
        Ok(ShapeDoc::new("LogicGoal").with_detail("call-shaped goal expression"))
    }
}

impl Shape for ClauseShape {
    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 clause = parse_clause_expr(crate::ClauseId(0), expr.clone());
        let accepted = match (&self.0, clause) {
            (None, Ok(_)) => true,
            (Some("fact"), Ok(clause)) => clause.body.is_empty(),
            (Some("rule"), Ok(clause)) => !clause.body.is_empty(),
            _ => false,
        };
        Ok(if accepted {
            ShapeMatch::accept(sim_kernel::MatchScore::exact(100))
        } else {
            ShapeMatch::reject("logic clause expected")
        })
    }

    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
        let name = match self.0 {
            Some("fact") => "LogicFact",
            Some("rule") => "LogicRule",
            _ => "LogicClause",
        };
        Ok(ShapeDoc::new(name))
    }
}

impl Shape for AnswerShape {
    fn check_value(&self, _cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
        Ok(
            if value
                .object()
                .downcast_ref::<sim_kernel::ShapeMatchObject>()
                .is_some()
            {
                ShapeMatch::accept(sim_kernel::MatchScore::exact(100))
            } else {
                ShapeMatch::reject("logic answer expected")
            },
        )
    }

    fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
        let value = cx.eval_expr(expr.clone())?;
        self.check_value(cx, value)
    }

    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
        Ok(ShapeDoc::new("LogicAnswer").with_detail("ShapeMatch answer"))
    }
}

pub(crate) fn require_logic_stream(value: &Value) -> Result<&crate::stream::LogicStream> {
    value
        .object()
        .downcast_ref::<crate::stream::LogicStream>()
        .ok_or(Error::TypeMismatch {
            expected: "logic stream",
            found: "non-stream",
        })
}