tessellate-core 0.1.0

Compiler and deterministic runtime for the Tess rule language
use crate::source::{SourceFile, Span, Spanned};
use serde::{Deserialize, Serialize};

pub type Name = String;

#[derive(Clone, Debug)]
pub struct Program {
    pub source: SourceFile,
    pub module: Spanned<Name>,
    /// Other partial-module files that belong to this program.
    ///
    /// Imports are resolved by project-aware hosts such as the Tess CLI. The
    /// compiler itself receives the declarations from every reachable file as
    /// one program, so name resolution remains intentionally module-wide.
    pub imports: Vec<ImportDecl>,
    /// Set only by a project-aware loader after every import is included.
    pub imports_resolved: bool,
    pub declarations: Vec<Declaration>,
}

#[derive(Clone, Debug)]
pub struct ImportDecl {
    pub path: Spanned<String>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub enum Declaration {
    Enum(EnumDecl),
    Entity(EntityDecl),
    State(StateDecl),
    Derive(DeriveDecl),
    Decision(DecisionDecl),
    Action(ActionDecl),
    Transition(TransitionDecl),
    Source(SourceDecl),
    Rule(RuleDecl),
    Case(CaseDecl),
    Invariant(InvariantDecl),
    Trace(TraceDecl),
}

impl Declaration {
    #[must_use]
    pub fn name(&self) -> &Spanned<Name> {
        match self {
            Self::Enum(value) => &value.name,
            Self::Entity(value) => &value.name,
            Self::State(value) => &value.name,
            Self::Derive(value) => &value.name,
            Self::Decision(value) => &value.name,
            Self::Action(value) => &value.name,
            Self::Transition(value) => &value.name,
            Self::Source(value) => &value.name,
            Self::Rule(value) => &value.name,
            Self::Case(value) => &value.name,
            Self::Invariant(value) => &value.name,
            Self::Trace(value) => &value.name,
        }
    }

    #[must_use]
    pub fn span(&self) -> Span {
        match self {
            Self::Enum(value) => value.span,
            Self::Entity(value) => value.span,
            Self::State(value) => value.span,
            Self::Derive(value) => value.span,
            Self::Decision(value) => value.span,
            Self::Action(value) => value.span,
            Self::Transition(value) => value.span,
            Self::Source(value) => value.span,
            Self::Rule(value) => value.span,
            Self::Case(value) => value.span,
            Self::Invariant(value) => value.span,
            Self::Trace(value) => value.span,
        }
    }
}

#[derive(Clone, Debug)]
pub struct EnumDecl {
    pub name: Spanned<Name>,
    pub variants: Vec<Spanned<Name>>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct EntityDecl {
    pub name: Spanned<Name>,
    pub fields: Vec<FieldDecl>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct StateDecl {
    pub name: Spanned<Name>,
    pub fields: Vec<FieldDecl>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct FieldDecl {
    pub name: Spanned<Name>,
    pub ty: TypeRef,
    pub range: Option<RangeConstraint>,
    pub domain: Option<DomainConstraint>,
    pub optional: bool,
    pub span: Span,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TypeRef {
    Bool,
    Int,
    Decimal,
    String,
    Date,
    Duration,
    Named(Name),
    Unknown,
}

#[derive(Clone, Debug)]
pub struct RangeConstraint {
    pub start: NumericLiteral,
    pub end: NumericLiteral,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct DomainConstraint {
    pub values: Vec<Expr>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct Parameter {
    pub ty: Spanned<Name>,
    pub name: Spanned<Name>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct DeriveDecl {
    pub name: Spanned<Name>,
    pub parameters: Vec<Parameter>,
    pub return_type: Spanned<TypeRef>,
    pub expression: Expr,
    pub span: Span,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Cardinality {
    #[default]
    ExactlyOne,
    ZeroOrOne,
    Many,
}

#[derive(Clone, Debug)]
pub struct DecisionDecl {
    pub name: Spanned<Name>,
    pub parameters: Vec<Parameter>,
    pub return_type: Spanned<TypeRef>,
    pub cardinality: Cardinality,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct ActionDecl {
    pub name: Spanned<Name>,
    pub parameters: Vec<Parameter>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct TransitionDecl {
    pub name: Spanned<Name>,
    pub parameters: Vec<Parameter>,
    pub action: Spanned<Name>,
    pub action_arguments: Vec<Expr>,
    pub condition: Expr,
    pub updates: Vec<StateUpdate>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct StateUpdate {
    pub receiver: Spanned<Name>,
    pub field: Spanned<Name>,
    pub value: Expr,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct SourceDecl {
    pub name: Spanned<Name>,
    pub title: Spanned<String>,
    pub section: Option<Spanned<String>>,
    pub version: Option<Spanned<String>>,
    pub uri: Option<Spanned<String>>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub enum SourceRef {
    Named(Spanned<Name>),
    Inline(Spanned<String>),
}

impl SourceRef {
    #[must_use]
    pub const fn span(&self) -> Span {
        match self {
            Self::Named(value) | Self::Inline(value) => value.span,
        }
    }
}

#[derive(Clone, Debug)]
pub struct RuleDecl {
    pub name: Spanned<Name>,
    pub parameters: Vec<Parameter>,
    pub condition: Expr,
    pub effects: Vec<Effect>,
    pub sources: Vec<SourceRef>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub enum Effect {
    Decide {
        decision: Spanned<Name>,
        arguments: Vec<Expr>,
        value: Expr,
        span: Span,
    },
    Override {
        rule: Spanned<Name>,
        span: Span,
    },
}

#[derive(Clone, Debug)]
pub struct CaseDecl {
    pub name: Spanned<Name>,
    pub bindings: Vec<BindingDecl>,
    pub expectations: Vec<Expectation>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct BindingDecl {
    pub name: Spanned<Name>,
    pub entity: Spanned<Name>,
    pub fields: Vec<FieldValue>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct FieldValue {
    pub name: Spanned<Name>,
    pub value: Expr,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct Expectation {
    pub decision: Spanned<Name>,
    pub arguments: Vec<Expr>,
    pub operator: CompareOp,
    pub value: Expr,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub struct InvariantDecl {
    pub name: Spanned<Name>,
    pub quantifier: InvariantQuantifier,
    pub variables: Vec<Parameter>,
    pub assertion: InvariantAssertion,
    pub span: Span,
}

/// How an invariant assertion ranges over its entity bindings.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvariantQuantifier {
    /// Every binding must satisfy the assertion.
    #[default]
    All,
    /// At least one binding must satisfy the assertion.
    Some,
}

#[derive(Clone, Debug)]
pub struct TraceDecl {
    pub name: Spanned<Name>,
    pub variable: Parameter,
    pub initial: Expr,
    pub always: Vec<Expr>,
    pub terminal: Option<Expr>,
    pub no_dead_ends: bool,
    pub within: Spanned<usize>,
    pub span: Span,
}

#[derive(Clone, Debug)]
pub enum InvariantAssertion {
    Cardinality {
        cardinality: Cardinality,
        decision: Spanned<Name>,
        arguments: Vec<Expr>,
        span: Span,
    },
    Implication {
        condition: Expr,
        expectation: Expectation,
        span: Span,
    },
}

#[derive(Clone, Debug)]
pub struct Expr {
    pub kind: ExprKind,
    pub span: Span,
}

impl Expr {
    #[must_use]
    pub const fn new(kind: ExprKind, span: Span) -> Self {
        Self { kind, span }
    }
}

#[derive(Clone, Debug)]
pub enum ExprKind {
    Literal(Literal),
    Name(Name),
    Field {
        receiver: Box<Expr>,
        field: Spanned<Name>,
    },
    Call {
        callee: Spanned<Name>,
        arguments: Vec<Expr>,
    },
    Unary {
        operator: UnaryOp,
        operand: Box<Expr>,
    },
    Binary {
        left: Box<Expr>,
        operator: BinaryOp,
        right: Box<Expr>,
    },
}

#[derive(Clone, Debug)]
pub enum Literal {
    Bool(bool),
    Number(NumericLiteral),
    String(String),
    Unknown,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NumericLiteral {
    Int(i64),
    Decimal(String),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
    Not,
    Negate,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryOp {
    Or,
    And,
    Equal,
    NotEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Add,
    Subtract,
    Multiply,
    Divide,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CompareOp {
    Equal,
    NotEqual,
}