use std::{collections::HashMap, fmt::Debug, ops::Deref};
use crate::{
script::{
compile::{Compile, Compiler},
figure::{Figure, MathString, Style},
parser::{FromProperty, Parse, PropertyValue},
Error,
},
span,
};
use super::{
AnyExpr, Bundle, Circle, CloneWithNode, CompileContext, Displayed, Dummy, Expr, Line, Point,
PointCollection, Properties, Scalar, Unknown,
};
pub trait Node: Debug {
fn set_display(&mut self, display: bool);
fn get_display(&self) -> bool;
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure);
fn build_unboxed(self, compiler: &mut Compiler, figure: &mut Figure)
where
Self: Sized,
{
Box::new(self).build(compiler, figure);
}
}
pub trait FromExpr<T: Displayed>: Node + Sized {
#[must_use]
fn from_expr(expr: &Expr<T>, display: Properties, context: &CompileContext) -> Self;
}
#[derive(Debug, Clone, Copy)]
pub struct MaybeUnset<T> {
value: T,
set: bool,
}
impl<T> MaybeUnset<T> {
pub fn new(default: T) -> Self {
Self {
value: default,
set: false,
}
}
pub fn new_or(default: T, value: Option<T>) -> Self {
let set = value.is_some();
Self {
value: value.unwrap_or(default),
set,
}
}
pub fn is_set(&self) -> bool {
self.set
}
pub fn set(&mut self, value: T) {
self.value = value;
self.set = true;
}
pub fn try_set(&mut self, value: Option<T>) {
if let Some(value) = value {
self.value = value;
self.set = true;
}
}
pub fn set_if_unset(&mut self, value: T) -> bool {
let set = !self.set;
if set {
self.set(value);
}
set
}
pub fn try_set_if_unset(&mut self, value: Option<T>) -> bool {
let set = !self.set && value.is_some();
if let Some(value) = value {
if set {
self.set(value);
}
}
set
}
#[must_use]
pub fn get(&self) -> &T {
&self.value
}
pub fn try_get(&self) -> Option<&T> {
if self.set {
Some(self.get())
} else {
None
}
}
pub fn unwrap(self) -> T {
self.value
}
#[must_use]
pub fn map<U, P: FnOnce(T) -> U>(self, f: P) -> MaybeUnset<U> {
MaybeUnset {
value: f(self.value),
set: self.set,
}
}
}
impl<T> AsRef<T> for MaybeUnset<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T: Clone> MaybeUnset<T> {
#[must_use]
pub fn get_cloned(&self) -> T {
self.value.clone()
}
#[must_use]
pub fn cloned(&self) -> Self {
self.clone()
}
}
impl<T: Copy> MaybeUnset<T> {
#[must_use]
pub fn get_copied(&self) -> T {
self.value
}
#[must_use]
pub fn copied(&self) -> Self {
*self
}
}
impl<T> Deref for MaybeUnset<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: Default> Default for MaybeUnset<T> {
fn default() -> Self {
Self {
value: T::default(),
set: false,
}
}
}
#[derive(Debug)]
pub struct CollectionNode {
pub display: MaybeUnset<bool>,
pub children: Vec<Box<dyn Node>>,
}
impl Default for CollectionNode {
fn default() -> Self {
Self::new()
}
}
impl CollectionNode {
#[must_use]
pub fn new() -> Self {
Self {
display: MaybeUnset::new(true),
children: Vec::new(),
}
}
#[must_use]
pub fn from_display(mut display: Properties, context: &CompileContext) -> Self {
let node = Self {
display: display.get("display").maybe_unset(true),
children: Vec::new(),
};
display.finish(context);
node
}
pub fn push<T: Node + 'static>(&mut self, node: T) {
self.children.push(Box::new(node));
}
pub fn push_boxed(&mut self, node: Box<dyn Node>) {
self.children.push(node);
}
pub fn extend<T: Node + 'static, U: IntoIterator<Item = T>>(&mut self, nodes: U) {
self.children
.extend(nodes.into_iter().map(|x| Box::new(x) as Box<dyn Node>));
}
}
impl Node for CollectionNode {
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.unwrap() {
for child in self.children {
child.build(compiler, figure);
}
}
}
}
pub trait BuildAssociated<T: Node>: Debug {
fn build_associated(
self: Box<Self>,
compiler: &mut Compiler,
figure: &mut Figure,
associated: &mut HierarchyNode<T>,
);
}
#[derive(Debug)]
pub enum AssociatedData {
Bool(MaybeUnset<bool>),
Style(MaybeUnset<Style>),
LineType(MaybeUnset<LineType>),
}
impl AssociatedData {
#[must_use]
pub fn as_bool(&self) -> Option<MaybeUnset<bool>> {
match self {
Self::Bool(v) => Some(v.copied()),
_ => None,
}
}
#[must_use]
pub fn as_style(&self) -> Option<MaybeUnset<Style>> {
match self {
Self::Style(v) => Some(v.copied()),
_ => None,
}
}
#[must_use]
pub fn as_line_type(&self) -> Option<MaybeUnset<LineType>> {
match self {
Self::LineType(v) => Some(v.copied()),
_ => None,
}
}
}
impl From<MaybeUnset<bool>> for AssociatedData {
fn from(value: MaybeUnset<bool>) -> Self {
Self::Bool(value)
}
}
impl From<MaybeUnset<Style>> for AssociatedData {
fn from(value: MaybeUnset<Style>) -> Self {
Self::Style(value)
}
}
impl From<MaybeUnset<LineType>> for AssociatedData {
fn from(value: MaybeUnset<LineType>) -> Self {
Self::LineType(value)
}
}
#[derive(Debug)]
pub struct HierarchyNode<T: Node> {
pub root: Box<T>,
pub children: Vec<Box<dyn Node>>,
pub associated: Option<Box<dyn BuildAssociated<T>>>,
pub associated_data: HashMap<&'static str, AssociatedData>,
}
impl<T: Node> Node for HierarchyNode<T> {
fn get_display(&self) -> bool {
self.root.get_display()
}
fn set_display(&mut self, display: bool) {
self.root.set_display(display);
}
fn build(mut self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.root.get_display() {
if let Some(associated) = self.associated.take() {
associated.build_associated(compiler, figure, &mut self);
}
self.root.build(compiler, figure);
for child in self.children {
child.build(compiler, figure);
}
}
}
}
impl<U: Displayed, T: FromExpr<U>> FromExpr<U> for HierarchyNode<T> {
fn from_expr(expr: &Expr<U>, props: Properties, context: &CompileContext) -> Self {
Self {
root: Box::new(T::from_expr(expr, props, context)),
children: Vec::new(),
associated: None,
associated_data: HashMap::new(),
}
}
}
impl<T: Node> HierarchyNode<T> {
pub fn new(root: T) -> Self {
Self {
root: Box::new(root),
children: Vec::new(),
associated: None,
associated_data: HashMap::new(),
}
}
pub fn push_child<U: Node + 'static>(&mut self, node: U) {
self.children.push(Box::new(node));
}
pub fn extend_boxed<Iter: IntoIterator<Item = Box<dyn Node>>>(&mut self, nodes: Iter) {
self.children.extend(nodes);
}
pub fn extend_children<U: Node + 'static, Iter: IntoIterator<Item = U>>(
&mut self,
nodes: Iter,
) {
self.children
.extend(nodes.into_iter().map(|x| Box::new(x) as Box<dyn Node>));
}
pub fn set_associated<U: BuildAssociated<T> + 'static>(&mut self, associated: U) {
self.associated = Some(Box::new(associated));
}
pub fn insert_data<U: Into<AssociatedData>>(&mut self, key: &'static str, data: U) {
self.associated_data.insert(key, data.into());
}
#[must_use]
pub fn get_data(&self, key: &'static str) -> Option<&AssociatedData> {
self.associated_data.get(key)
}
}
#[derive(Debug)]
pub struct PCNode {
pub display: MaybeUnset<bool>,
pub children: Vec<Option<HierarchyNode<<Point as Displayed>::Node>>>,
pub props: Option<Properties>,
}
impl Default for PCNode {
fn default() -> Self {
Self::new()
}
}
impl PCNode {
#[must_use]
pub fn new() -> Self {
Self {
display: MaybeUnset::new(true),
children: Vec::new(),
props: None,
}
}
pub fn push(&mut self, node: Option<HierarchyNode<<Point as Displayed>::Node>>) {
self.children.push(node);
}
pub fn extend<U: IntoIterator<Item = Option<HierarchyNode<<Point as Displayed>::Node>>>>(
&mut self,
nodes: U,
) {
self.children.extend(nodes);
}
}
impl Dummy for PCNode {
fn dummy() -> Self {
Self::new()
}
fn is_dummy(&self) -> bool {
self.children.is_empty()
}
}
impl Node for PCNode {
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.unwrap() {
for child in self.children.into_iter().flatten() {
child.build_unboxed(compiler, figure);
}
}
}
}
macro_rules! impl_from_for_any {
($from:ident) => {
impl From<HierarchyNode<<$from as Displayed>::Node>> for AnyExprNode {
fn from(value: HierarchyNode<<$from as Displayed>::Node>) -> Self {
Self::$from(value)
}
}
};
}
#[derive(Debug)]
pub enum AnyExprNode {
Point(HierarchyNode<<Point as Displayed>::Node>),
Line(HierarchyNode<<Line as Displayed>::Node>),
Circle(HierarchyNode<<Circle as Displayed>::Node>),
Scalar(HierarchyNode<<Scalar as Displayed>::Node>),
PointCollection(HierarchyNode<<PointCollection as Displayed>::Node>),
Bundle(HierarchyNode<<Bundle as Displayed>::Node>),
Unknown(HierarchyNode<<Unknown as Displayed>::Node>),
}
impl_from_for_any! {Point}
impl_from_for_any! {Line}
impl_from_for_any! {Circle}
impl_from_for_any! {Scalar}
impl_from_for_any! {PointCollection}
impl_from_for_any! {Bundle}
impl_from_for_any! {Unknown}
impl AnyExprNode {
#[must_use]
pub fn as_dyn(self) -> Box<dyn Node> {
match self {
Self::Point(v) => Box::new(v),
Self::Line(v) => Box::new(v),
Self::Circle(v) => Box::new(v),
Self::Scalar(v) => Box::new(v),
Self::PointCollection(v) => Box::new(v),
Self::Bundle(v) => Box::new(v),
Self::Unknown(v) => Box::new(v),
}
}
#[must_use]
pub fn as_point(self) -> HierarchyNode<<Point as Displayed>::Node> {
if let Self::Point(v) = self {
v
} else {
panic!("not a point")
}
}
#[must_use]
pub fn as_line(self) -> HierarchyNode<<Line as Displayed>::Node> {
if let Self::Line(v) = self {
v
} else {
panic!("not a line")
}
}
#[must_use]
pub fn as_circle(self) -> HierarchyNode<<Circle as Displayed>::Node> {
if let Self::Circle(v) = self {
v
} else {
panic!("not a circle")
}
}
#[must_use]
pub fn as_scalar(self) -> HierarchyNode<<Scalar as Displayed>::Node> {
if let Self::Scalar(v) = self {
v
} else {
panic!("not a scalar")
}
}
#[must_use]
pub fn as_point_collection(self) -> HierarchyNode<<PointCollection as Displayed>::Node> {
if let Self::PointCollection(v) = self {
v
} else {
panic!("not a point collection")
}
}
#[must_use]
pub fn as_bundle(self) -> HierarchyNode<<Bundle as Displayed>::Node> {
if let Self::Bundle(v) = self {
v
} else {
panic!("not a bundle")
}
}
#[must_use]
pub fn as_unknown(self) -> HierarchyNode<<Unknown as Displayed>::Node> {
if let Self::Unknown(v) = self {
v
} else {
panic!("not a unknown")
}
}
}
impl Node for AnyExprNode {
fn set_display(&mut self, display: bool) {
match self {
Self::Point(v) => v.set_display(display),
Self::Line(v) => v.set_display(display),
Self::Circle(v) => v.set_display(display),
Self::Scalar(v) => v.set_display(display),
Self::PointCollection(v) => v.set_display(display),
Self::Bundle(v) => v.set_display(display),
Self::Unknown(v) => v.set_display(display),
}
}
fn get_display(&self) -> bool {
match self {
Self::Point(v) => v.get_display(),
Self::Line(v) => v.get_display(),
Self::Circle(v) => v.get_display(),
Self::Scalar(v) => v.get_display(),
Self::PointCollection(v) => v.get_display(),
Self::Bundle(v) => v.get_display(),
Self::Unknown(v) => v.get_display(),
}
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
match *self {
Self::Point(v) => v.build_unboxed(compiler, figure),
Self::Line(v) => v.build_unboxed(compiler, figure),
Self::Circle(v) => v.build_unboxed(compiler, figure),
Self::Scalar(v) => v.build_unboxed(compiler, figure),
Self::PointCollection(v) => v.build_unboxed(compiler, figure),
Self::Bundle(v) => v.build_unboxed(compiler, figure),
Self::Unknown(v) => v.build_unboxed(compiler, figure),
}
}
}
#[derive(Debug)]
pub struct BundleNode {
pub display: MaybeUnset<bool>,
pub children: HashMap<String, AnyExpr>,
}
impl Default for BundleNode {
fn default() -> Self {
Self::new()
}
}
impl Dummy for BundleNode {
fn dummy() -> Self {
Self::new()
}
fn is_dummy(&self) -> bool {
self.children.is_empty()
}
}
impl BundleNode {
#[must_use]
pub fn new() -> Self {
Self {
display: MaybeUnset::new(true),
children: HashMap::new(),
}
}
pub fn insert<T: Displayed>(&mut self, key: String, expr: Expr<T>)
where
AnyExpr: From<Expr<T>>,
{
self.children.insert(key, AnyExpr::from(expr));
}
pub fn extend<U: IntoIterator<Item = (String, AnyExpr)>>(&mut self, nodes: U) {
self.children.extend(nodes);
}
}
impl Node for BundleNode {
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.get_copied() {
for mut child in self.children.into_values() {
if let Some(node) = child.replace_node(None) {
node.build_unboxed(compiler, figure);
}
}
}
}
}
#[derive(Debug)]
pub struct EmptyNode;
impl Dummy for EmptyNode {
fn dummy() -> Self {
Self
}
fn is_dummy(&self) -> bool {
true
}
}
impl Node for EmptyNode {
fn get_display(&self) -> bool {
false
}
fn set_display(&mut self, _display: bool) {}
fn build(self: Box<Self>, _compiler: &mut Compiler, _figure: &mut Figure) {}
}
#[derive(Debug)]
pub struct PointNode {
pub display: MaybeUnset<bool>,
pub label: MaybeUnset<MathString>,
pub display_label: MaybeUnset<bool>,
pub display_dot: MaybeUnset<bool>,
pub default_label: MathString,
pub expr: Expr<Point>,
}
impl Dummy for PointNode {
fn dummy() -> Self {
Self {
display: MaybeUnset::new(true),
label: MaybeUnset::new(MathString::new(span!(0, 0, 0, 0))),
display_label: MaybeUnset::new(true),
display_dot: MaybeUnset::new(true),
default_label: MathString::new(span!(0, 0, 0, 0)),
expr: Expr::dummy(),
}
}
fn is_dummy(&self) -> bool {
self.expr.is_dummy()
}
}
impl Node for PointNode {
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.unwrap() && !self.is_dummy() {
if !self.display_label.unwrap()
|| (self.label.as_ref().is_empty() && self.default_label.is_empty())
{
return;
}
figure.points.push((
compiler.compile(&self.expr),
if self.display_label.unwrap() {
if self.label.as_ref().is_empty() {
self.default_label
} else {
self.label.unwrap()
}
} else {
MathString::new(span!(0, 0, 0, 0))
},
));
}
}
}
impl FromExpr<Point> for PointNode {
fn from_expr(expr: &Expr<Point>, mut props: Properties, context: &CompileContext) -> Self {
let node = Self {
display: props.get("display").maybe_unset(true),
label: props
.get("label")
.maybe_unset(MathString::new(span!(0, 0, 0, 0))),
display_label: props.get("display_label").maybe_unset(true),
display_dot: props.get("display_dot").maybe_unset(true),
default_label: props
.get("default-label")
.ok_or(MathString::new(span!(0, 0, 0, 0))),
expr: expr.clone_without_node(),
};
props.finish(context);
node
}
}
#[derive(Debug)]
pub struct CircleNode {
pub display: MaybeUnset<bool>,
pub label: MaybeUnset<MathString>,
pub display_label: MaybeUnset<bool>,
pub default_label: MathString,
pub style: MaybeUnset<Style>,
pub expr: Expr<Circle>,
}
impl Dummy for CircleNode {
fn dummy() -> Self {
Self {
display: MaybeUnset::new(true),
label: MaybeUnset::new(MathString::new(span!(0, 0, 0, 0))),
display_label: MaybeUnset::new(true),
default_label: MathString::new(span!(0, 0, 0, 0)),
style: MaybeUnset::new(Style::default()),
expr: Expr::dummy(),
}
}
fn is_dummy(&self) -> bool {
self.expr.is_dummy()
}
}
impl Node for CircleNode {
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.unwrap() && !self.is_dummy() {
figure.circles.push((
compiler.compile(&self.expr),
Style::Solid, ));
}
}
}
impl FromExpr<Circle> for CircleNode {
fn from_expr(expr: &Expr<Circle>, mut props: Properties, context: &CompileContext) -> Self {
let _ = props.get::<MathString>("default-label");
let node = Self {
display: props.get("display").maybe_unset(true),
label: props
.get("label")
.maybe_unset(MathString::new(span!(0, 0, 0, 0))),
display_label: props.get("display_label").maybe_unset(false),
default_label: props
.get("default-label")
.ok_or(MathString::new(span!(0, 0, 0, 0))),
style: props.get("style").maybe_unset(Style::default()),
expr: expr.clone_without_node(),
};
props.finish(context);
node
}
}
macro_rules! property_enum_impl {
($name:ident { $($variant:ident: $key:literal),* $(,)? }) => {
impl FromProperty for $name {
fn from_property(property: PropertyValue) -> Result<Self, Error> {
match property {
PropertyValue::Number(n) => Err(Error::StringOrIdentExpected {
error_span: n.get_span(),
}),
PropertyValue::Ident(i) => match i.to_string().to_lowercase().as_str() {
$($key => Ok(Self::$variant),)*
&_ => Err(Error::EnumInvalidValue {
error_span: i.get_span(),
available_values: &[$($key),*],
received_value: i.to_string(),
}),
},
PropertyValue::String(s) => match s.content.to_lowercase().as_str() {
$($key => Ok(Self::$variant),)*
&_ => Err(Error::EnumInvalidValue {
error_span: s.get_span(),
available_values: &[$($key),*],
received_value: s.content,
}),
},
PropertyValue::RawString(s) => Err(Error::NonRawStringOrIdentExpected {
error_span: s.get_span(),
}),
}
}
}
};
}
macro_rules! property_enum {
($name:ident { $($variant:ident: $key:literal),* $(,)? }) => {
#[derive(Debug, Clone, Copy)]
pub enum $name {
$($variant),*
}
property_enum_impl! {$name { $($variant: $key),* }}
};
}
property_enum! {
LineType {
Line: "line",
Ray: "ray",
Segment: "segment"
}
}
property_enum_impl! {
Style {
Solid: "solid",
Dashed: "dashed",
Dotted: "dotted",
Bold: "bold"
}
}
#[derive(Debug)]
pub struct LineNode {
pub display: MaybeUnset<bool>,
pub label: MaybeUnset<MathString>,
pub display_label: MaybeUnset<bool>,
pub default_label: MathString,
pub line_type: MaybeUnset<LineType>,
pub style: MaybeUnset<Style>,
pub expr: Expr<Line>,
}
impl Dummy for LineNode {
fn dummy() -> Self {
Self {
display: MaybeUnset::new(true),
label: MaybeUnset::new(MathString::new(span!(0, 0, 0, 0))),
display_label: MaybeUnset::new(true),
default_label: MathString::new(span!(0, 0, 0, 0)),
line_type: MaybeUnset::new(LineType::Line),
style: MaybeUnset::new(Style::default()),
expr: Expr::dummy(),
}
}
fn is_dummy(&self) -> bool {
self.expr.is_dummy()
}
}
impl Node for LineNode {
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn build(self: Box<Self>, compiler: &mut Compiler, figure: &mut Figure) {
if self.display.unwrap() && !self.is_dummy() {
match self.line_type.unwrap() {
LineType::Line => figure
.lines
.push((compiler.compile(&self.expr), Style::Solid)),
LineType::Ray => match &self.expr.data.as_ref() {
Line::LineFromPoints(a, b) => {
figure
.rays
.push((compiler.compile(a), compiler.compile(b), Style::Solid));
}
Line::AngleBisector(a, b, c) => {
let x = Expr::new_spanless(Point::LineLineIntersection(
Expr::new_spanless(Line::LineFromPoints(
a.clone_without_node(),
c.clone_without_node(),
)),
self.expr.clone_without_node(),
));
figure
.rays
.push((compiler.compile(b), compiler.compile(&x), Style::Solid));
}
_ => unreachable!(),
},
LineType::Segment => match &self.expr.data.as_ref() {
Line::LineFromPoints(a, b) => figure.segments.push((
compiler.compile(a),
compiler.compile(b),
Style::Solid,
)),
_ => unreachable!(),
},
}
}
}
}
impl FromExpr<Line> for LineNode {
fn from_expr(expr: &Expr<Line>, mut props: Properties, context: &CompileContext) -> Self {
let _ = props.get::<MathString>("default-label");
let node = Self {
display: props.get("display").maybe_unset(true),
label: props
.get("label")
.maybe_unset(MathString::new(span!(0, 0, 0, 0))),
display_label: props.get("display_label").maybe_unset(false),
default_label: props
.get("default-label")
.ok_or(MathString::new(span!(0, 0, 0, 0))),
line_type: MaybeUnset::new(LineType::Line),
style: props.get("style").maybe_unset(Style::default()),
expr: expr.clone_without_node(),
};
props.finish(context);
node
}
}
#[derive(Debug)]
pub struct ScalarNode {
pub display: MaybeUnset<bool>,
pub expr: Expr<Scalar>,
}
impl Dummy for ScalarNode {
fn dummy() -> Self {
Self {
display: MaybeUnset::new(true),
expr: Expr::dummy(),
}
}
fn is_dummy(&self) -> bool {
self.expr.is_dummy()
}
}
impl Node for ScalarNode {
fn set_display(&mut self, display: bool) {
self.display.set(display);
}
fn get_display(&self) -> bool {
self.display.get_copied()
}
fn build(self: Box<Self>, _compiler: &mut Compiler, _figure: &mut Figure) {}
}
impl FromExpr<Scalar> for ScalarNode {
fn from_expr(expr: &Expr<Scalar>, mut props: Properties, context: &CompileContext) -> Self {
props.ignore("label");
props.ignore("display_label");
props.ignore("default-label");
let node = Self {
display: props.get("display").maybe_unset(true),
expr: expr.clone_without_node(),
};
props.finish(context);
node
}
}