use crate::parsing::ast::{
ArithmeticComputation, CalendarPeriodUnit, ComparisonComputation, DateCalendarKind,
DateRelativeKind, MathematicalComputation, PrimitiveKind, VetoExpression,
};
use crate::planning::semantics::{
Expression, ExpressionKind, SemanticConversionTarget, TypedLiteral,
};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShowBranch {
pub condition: Option<ShowExpression>,
pub result: ShowExpression,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShowRule {
pub lemma_type: crate::planning::semantics::LemmaType,
pub path: Vec<crate::planning::semantics::PathSegment>,
pub branches: Vec<ShowBranch>,
pub depends_on_rules: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ShowConversionTarget {
Type(PrimitiveKind),
Unit { unit_name: String },
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ShowExpression {
Literal(Box<TypedLiteral>),
Data {
name: String,
},
Rule {
name: String,
},
And {
left: Box<ShowExpression>,
right: Box<ShowExpression>,
},
Not {
operand: Box<ShowExpression>,
},
Arithmetic {
op: ArithmeticComputation,
left: Box<ShowExpression>,
right: Box<ShowExpression>,
},
Comparison {
op: ComparisonComputation,
left: Box<ShowExpression>,
right: Box<ShowExpression>,
},
UnitConversion {
operand: Box<ShowExpression>,
target: ShowConversionTarget,
},
Math {
op: MathematicalComputation,
operand: Box<ShowExpression>,
},
Veto {
message: Option<String>,
},
Now,
DateRelative {
kind: DateRelativeKind,
operand: Box<ShowExpression>,
},
DateCalendar {
kind: DateCalendarKind,
unit: CalendarPeriodUnit,
operand: Box<ShowExpression>,
},
RangeLiteral {
from: Box<ShowExpression>,
to: Box<ShowExpression>,
},
PastFutureRange {
kind: DateRelativeKind,
operand: Box<ShowExpression>,
},
RangeContainment {
value: Box<ShowExpression>,
range: Box<ShowExpression>,
},
IsVeto {
operand: Box<ShowExpression>,
},
}
pub fn show_expression_from(
expression: &Expression,
show_keys: &HashSet<String>,
) -> ShowExpression {
match &expression.kind {
ExpressionKind::Literal(typed) => ShowExpression::Literal(Box::new(typed.as_ref().clone())),
ExpressionKind::DataPath(path) => ShowExpression::Data {
name: path.input_key(),
},
ExpressionKind::RulePath(path) => {
let key = path.input_key();
if !show_keys.contains(&key) {
panic!("BUG: rule leaf '{key}' not in Show.rules keys");
}
ShowExpression::Rule { name: key }
}
ExpressionKind::LogicalAnd(left, right) => ShowExpression::And {
left: Box::new(show_expression_from(left, show_keys)),
right: Box::new(show_expression_from(right, show_keys)),
},
ExpressionKind::LogicalNegation(operand, _) => ShowExpression::Not {
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::Arithmetic(left, op, right) => ShowExpression::Arithmetic {
op: op.clone(),
left: Box::new(show_expression_from(left, show_keys)),
right: Box::new(show_expression_from(right, show_keys)),
},
ExpressionKind::Comparison(left, op, right) => ShowExpression::Comparison {
op: op.clone(),
left: Box::new(show_expression_from(left, show_keys)),
right: Box::new(show_expression_from(right, show_keys)),
},
ExpressionKind::UnitConversion(operand, target) => ShowExpression::UnitConversion {
operand: Box::new(show_expression_from(operand, show_keys)),
target: conversion_target_from(target),
},
ExpressionKind::MathematicalComputation(op, operand) => ShowExpression::Math {
op: op.clone(),
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::Veto(VetoExpression { message }) => ShowExpression::Veto {
message: message.clone(),
},
ExpressionKind::Now => ShowExpression::Now,
ExpressionKind::DateRelative(kind, operand) => ShowExpression::DateRelative {
kind: *kind,
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::DateCalendar(kind, unit, operand) => ShowExpression::DateCalendar {
kind: *kind,
unit: *unit,
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::RangeLiteral(from, to) => ShowExpression::RangeLiteral {
from: Box::new(show_expression_from(from, show_keys)),
to: Box::new(show_expression_from(to, show_keys)),
},
ExpressionKind::PastFutureRange(kind, operand) => ShowExpression::PastFutureRange {
kind: *kind,
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::RangeContainment(value, range) => ShowExpression::RangeContainment {
value: Box::new(show_expression_from(value, show_keys)),
range: Box::new(show_expression_from(range, show_keys)),
},
ExpressionKind::ResultIsVeto(operand) => ShowExpression::IsVeto {
operand: Box::new(show_expression_from(operand, show_keys)),
},
ExpressionKind::Piecewise(_) => {
panic!("BUG: piecewise inside a rule branch")
}
}
}
pub fn show_branches_from(
branches: &[(Option<Expression>, Expression)],
show_keys: &HashSet<String>,
) -> Vec<ShowBranch> {
branches
.iter()
.map(|(condition, result)| ShowBranch {
condition: condition
.as_ref()
.map(|expression| show_expression_from(expression, show_keys)),
result: show_expression_from(result, show_keys),
})
.collect()
}
pub fn project_depends_on_rules(
deps: &std::collections::BTreeSet<crate::planning::semantics::RulePath>,
show_keys: &HashSet<String>,
) -> Vec<String> {
let mut out = Vec::new();
for path in deps {
let key = path.input_key();
if !show_keys.contains(&key) {
panic!("BUG: depends_on_rules entry '{key}' missing from Show.rules keys");
}
out.push(key);
}
out
}
fn conversion_target_from(target: &SemanticConversionTarget) -> ShowConversionTarget {
match target {
SemanticConversionTarget::Type(kind) => ShowConversionTarget::Type(*kind),
SemanticConversionTarget::Unit { unit_name, .. } => ShowConversionTarget::Unit {
unit_name: unit_name.clone(),
},
}
}