use antex::{leaf, node, Color, ColorMode, StyledText, TreeNode};
use dsntk_feel::{FeelType, Name};
use std::fmt;
use std::fmt::{Display, Write};
const DEFAULT_COLOR: Color = Color::White;
#[derive(Debug, Clone, PartialEq)]
pub enum AstNode {
Add(Box<AstNode>, Box<AstNode>),
And(Box<AstNode>, Box<AstNode>),
At(String),
Between(Box<AstNode>, Box<AstNode>, Box<AstNode>),
Boolean(bool),
CommaList(Vec<AstNode>),
Context(Vec<AstNode>),
ContextEntry(Box<AstNode>, Box<AstNode>),
ContextEntryKey(Name),
ContextType(Vec<AstNode>),
ContextTypeEntry(
Box<AstNode>,
Box<AstNode>,
),
ContextTypeEntryKey(Name),
Div(Box<AstNode>, Box<AstNode>),
Eq(Box<AstNode>, Box<AstNode>),
EvaluatedExpression(Box<AstNode>),
Every(
Box<AstNode>,
Box<AstNode>,
),
Exp(Box<AstNode>, Box<AstNode>),
ExpressionList(Vec<AstNode>),
FeelType(FeelType),
Filter(Box<AstNode>, Box<AstNode>),
For(
Box<AstNode>,
Box<AstNode>,
),
FormalParameter(
Box<AstNode>,
Box<AstNode>,
),
FormalParameters(Vec<AstNode>),
FunctionBody(Box<AstNode>, bool),
FunctionDefinition(Box<AstNode>, Box<AstNode>),
FunctionInvocation(Box<AstNode>, Box<AstNode>),
FunctionType(
Box<AstNode>,
Box<AstNode>,
),
Ge(Box<AstNode>, Box<AstNode>),
Gt(Box<AstNode>, Box<AstNode>),
If(
Box<AstNode>,
Box<AstNode>,
Box<AstNode>,
),
In(Box<AstNode>, Box<AstNode>),
InstanceOf(
Box<AstNode>,
Box<AstNode>,
),
IntervalEnd(Box<AstNode>, bool),
IntervalStart(Box<AstNode>, bool),
Irrelevant,
IterationContexts(Vec<AstNode>),
IterationContextSingle(
Box<AstNode>,
Box<AstNode>,
),
IterationContextInterval(
Box<AstNode>,
Box<AstNode>,
Box<AstNode>,
),
Le(Box<AstNode>, Box<AstNode>),
Lt(Box<AstNode>, Box<AstNode>),
List(Vec<AstNode>),
ListType(Box<AstNode>),
Mul(Box<AstNode>, Box<AstNode>),
Name(Name),
NamedParameter(
Box<AstNode>,
Box<AstNode>,
),
NamedParameters(Vec<AstNode>),
NegatedList(Vec<AstNode>),
Neg(Box<AstNode>),
Nq(Box<AstNode>, Box<AstNode>),
Null,
Numeric(
String,
String,
char,
String,
),
Or(Box<AstNode>, Box<AstNode>),
Out(Box<AstNode>, Box<AstNode>),
ParameterName(Name),
ParameterTypes(Vec<AstNode>),
Path(Box<AstNode>, Box<AstNode>),
PositionalParameters(Vec<AstNode>),
QualifiedName(Vec<AstNode>),
QualifiedNameSegment(Name),
QuantifiedContexts(Vec<AstNode>),
QuantifiedContext(
Box<AstNode>,
Box<AstNode>,
),
Range(Box<AstNode>, Box<AstNode>),
RangeType(Box<AstNode>),
Satisfies(Box<AstNode>),
Some(
Box<AstNode>,
Box<AstNode>,
),
String(String),
Sub(Box<AstNode>, Box<AstNode>),
UnaryGe(Box<AstNode>),
UnaryGt(Box<AstNode>),
UnaryLe(Box<AstNode>),
UnaryLt(Box<AstNode>),
UnaryEq(Box<AstNode>),
UnaryNe(Box<AstNode>),
}
impl Display for AstNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.tree(0, ColorMode::Off))
}
}
impl AstNode {
pub fn tree(&self, indent: usize, cm: ColorMode) -> String {
let root = ast_node_to_tree(self, cm);
let mut output = String::new();
let _ = root.write_indent(&mut output, indent);
output
}
pub fn trace(&self) -> String {
let root = ast_node_to_tree(self, ColorMode::Off);
let mut output = "\n".to_string();
let _ = root.write_indent(&mut output, 6);
let _ = write!(output, " ");
output
}
}
fn ast_node_to_tree(node: &AstNode, cm: ColorMode) -> TreeNode {
match node {
AstNode::Add(lhs, rhs) => ast_node_2("Add", lhs, rhs, cm),
AstNode::And(lhs, rhs) => ast_node_2("And", lhs, rhs, cm),
AstNode::At(mid) => ast_node_and_leaf("At", &format!("`{mid}`"), cm),
AstNode::Between(lhs, mid, rhs) => ast_node_3("Between", lhs, mid, rhs, cm),
AstNode::Boolean(mid) => ast_node_and_leaf("Boolean", &format!("`{mid}`"), cm),
AstNode::CommaList(mid) => ast_node_n("CommaList", mid, cm),
AstNode::Context(items) => ast_node_n("Context", items, cm),
AstNode::ContextEntry(lhs, rhs) => ast_node_2("ContextEntry", lhs, rhs, cm),
AstNode::ContextEntryKey(mid) => ast_node_and_leaf("ContextEntryKey", &format!("`{mid}`"), cm),
AstNode::ContextType(items) => ast_node_n("ContextType", items, cm),
AstNode::ContextTypeEntry(lhs, rhs) => ast_node_2("ContextTypeEntry", lhs, rhs, cm),
AstNode::ContextTypeEntryKey(mid) => ast_node_and_leaf("Name", &format!("`{mid}`"), cm),
AstNode::Div(lhs, rhs) => ast_node_2("Div", lhs, rhs, cm),
AstNode::Eq(lhs, rhs) => ast_node_2("Eq", lhs, rhs, cm),
AstNode::EvaluatedExpression(mid) => ast_node_1("EvaluatedExpression", mid, cm),
AstNode::Every(lhs, rhs) => ast_node_2("Every", lhs, rhs, cm),
AstNode::Exp(lhs, rhs) => ast_node_2("Exp", lhs, rhs, cm),
AstNode::ExpressionList(items) => ast_node_n("ExpressionList", items, cm),
AstNode::FeelType(lhs) => ast_node_and_leaf("FeelType", &lhs.to_string(), cm),
AstNode::Filter(lhs, rhs) => ast_node_2("Filter", lhs, rhs, cm),
AstNode::For(lhs, rhs) => ast_node_2("For", lhs, rhs, cm),
AstNode::FormalParameter(lhs, rhs) => ast_node_2("FormalParameter", lhs, rhs, cm),
AstNode::FormalParameters(items) => ast_node_n("FormalParameters", items, cm),
AstNode::FunctionBody(lhs, external) => ast_node_and_label("FunctionBody", lhs, " (external)", "", *external, cm),
AstNode::FunctionDefinition(lhs, rhs) => ast_node_2("FunctionDefinition", lhs, rhs, cm),
AstNode::FunctionInvocation(lhs, rhs) => ast_node_2("FunctionInvocation", lhs, rhs, cm),
AstNode::FunctionType(lhs, rhs) => ast_node_2("FunctionType", lhs, rhs, cm),
AstNode::Ge(lhs, rhs) => ast_node_2("Ge", lhs, rhs, cm),
AstNode::Gt(lhs, rhs) => ast_node_2("Gt", lhs, rhs, cm),
AstNode::If(lhs, mid, rhs) => ast_node_3("If", lhs, mid, rhs, cm),
AstNode::In(lhs, rhs) => ast_node_2("In", lhs, rhs, cm),
AstNode::InstanceOf(lhs, rhs) => ast_node_2("InstanceOf", lhs, rhs, cm),
AstNode::IntervalEnd(lhs, closed) => ast_node_and_label("IntervalEnd", lhs, " (closed)", " (opened)", *closed, cm),
AstNode::IntervalStart(lhs, closed) => ast_node_and_label("IntervalStart", lhs, " (closed)", " (opened)", *closed, cm),
AstNode::Irrelevant => ast_leaf("Irrelevant", cm),
AstNode::IterationContexts(items) => ast_node_n("IterationContexts", items, cm),
AstNode::IterationContextSingle(lhs, rhs) => ast_node_2("IterationContextSingle", lhs, rhs, cm),
AstNode::IterationContextInterval(lhs, mid, rhs) => ast_node_3("IterationContextInterval", lhs, mid, rhs, cm),
AstNode::Le(lhs, rhs) => ast_node_2("Le", lhs, rhs, cm),
AstNode::List(mid) => ast_node_n("List", mid, cm),
AstNode::ListType(lhs) => ast_node_1("ListType", lhs, cm),
AstNode::Lt(lhs, rhs) => ast_node_2("Lt", lhs, rhs, cm),
AstNode::Mul(lhs, rhs) => ast_node_2("Mul", lhs, rhs, cm),
AstNode::Name(mid) => ast_node_and_leaf("Name", &format!("`{mid}`"), cm),
AstNode::NamedParameter(lhs, rhs) => ast_node_2("NamedParameter", lhs, rhs, cm),
AstNode::NamedParameters(items) => ast_node_n("NamedParameters", items, cm),
AstNode::Neg(mid) => ast_node_1("Neg", mid, cm),
AstNode::NegatedList(mid) => ast_node_n("NegatedList", mid, cm),
AstNode::Nq(lhs, rhs) => ast_node_2("Nq", lhs, rhs, cm),
AstNode::Null => ast_leaf("Null", cm),
AstNode::Numeric(before, after, sign, exponent) => ast_node_and_leaf("Numeric", &numeric_to_string(before, after, sign, exponent), cm),
AstNode::Or(lhs, rhs) => ast_node_2("Or", lhs, rhs, cm),
AstNode::Out(lhs, rhs) => ast_node_2("Out", lhs, rhs, cm),
AstNode::ParameterName(lhs) => ast_node_and_leaf("ParameterName", &format!("`{lhs}`"), cm),
AstNode::ParameterTypes(items) => ast_node_n("ParameterTypes", items, cm),
AstNode::Path(lhs, rhs) => ast_node_2("Path", lhs, rhs, cm),
AstNode::PositionalParameters(items) => ast_node_n("PositionalParameters", items, cm),
AstNode::QualifiedName(items) => ast_node_n("QualifiedName", items, cm),
AstNode::QualifiedNameSegment(lhs) => ast_node_and_leaf("Name", &format!("`{lhs}`"), cm),
AstNode::QuantifiedContext(lhs, rhs) => ast_node_2("QuantifiedContext", lhs, rhs, cm),
AstNode::QuantifiedContexts(items) => ast_node_n("QuantifiedContexts", items, cm),
AstNode::Range(lhs, rhs) => ast_node_2("Range", lhs, rhs, cm),
AstNode::RangeType(lhs) => ast_node_1("RangeType", lhs, cm),
AstNode::Satisfies(mid) => ast_node_1("Satisfies", mid, cm),
AstNode::Some(lhs, rhs) => ast_node_2("Some", lhs, rhs, cm),
AstNode::String(mid) => ast_node_and_leaf("String", &format!("`{mid}`"), cm),
AstNode::Sub(lhs, rhs) => ast_node_2("Sub", lhs, rhs, cm),
AstNode::UnaryGe(mid) => ast_node_1("UnaryGe", mid, cm),
AstNode::UnaryGt(mid) => ast_node_1("UnaryGt", mid, cm),
AstNode::UnaryLe(mid) => ast_node_1("UnaryLe", mid, cm),
AstNode::UnaryLt(mid) => ast_node_1("UnaryLt", mid, cm),
AstNode::UnaryEq(mid) => ast_node_1("UnaryEq", mid, cm),
AstNode::UnaryNe(mid) => ast_node_1("UnaryNe", mid, cm),
}
}
fn ast_node_1(name: &str, mid: &AstNode, cm: ColorMode) -> TreeNode {
node(DEFAULT_COLOR, cm).line().s(name).end().child(ast_node_to_tree(mid, cm)).end()
}
fn ast_node_2(name: &str, lhs: &AstNode, rhs: &AstNode, cm: ColorMode) -> TreeNode {
node(DEFAULT_COLOR, cm)
.line()
.s(name)
.end()
.child(ast_node_to_tree(lhs, cm))
.child(ast_node_to_tree(rhs, cm))
.end()
}
fn ast_node_3(name: &str, lhs: &AstNode, mid: &AstNode, rhs: &AstNode, cm: ColorMode) -> TreeNode {
node(DEFAULT_COLOR, cm)
.line()
.s(name)
.end()
.child(ast_node_to_tree(lhs, cm))
.child(ast_node_to_tree(mid, cm))
.child(ast_node_to_tree(rhs, cm))
.end()
}
fn ast_node_n(name: &str, items: &[AstNode], cm: ColorMode) -> TreeNode {
let mut node_builder = node(DEFAULT_COLOR, cm).line().s(name).end();
if items.is_empty() {
node_builder.add_child(leaf(cm).line().s("(empty)").end().end());
} else {
for item in items {
node_builder.add_child(ast_node_to_tree(item, cm));
}
}
node_builder.end()
}
fn ast_node_and_leaf(name: &str, leaf_caption: &str, cm: ColorMode) -> TreeNode {
node(DEFAULT_COLOR, cm).line().s(name).end().child(leaf(cm).line().s(leaf_caption).end().end()).end()
}
fn ast_node_and_label(name: &str, lhs: &AstNode, label_true: &str, label_false: &str, label_flag: bool, cm: ColorMode) -> TreeNode {
let name_label = if label_flag { label_true } else { label_false };
node(DEFAULT_COLOR, cm).line().s(name).s(name_label).end().child(ast_node_to_tree(lhs, cm)).end()
}
fn ast_leaf(leaf_caption: &str, cm: ColorMode) -> TreeNode {
leaf(cm).line().s(leaf_caption).end().end()
}
fn numeric_to_string(before: &str, after: &str, sign: &char, exponent: &str) -> String {
let mut output = String::new();
let _ = write!(&mut output, "`{before}");
if !after.is_empty() {
let _ = write!(&mut output, ".{after}");
}
if !exponent.is_empty() {
let _ = write!(&mut output, "e{sign}{exponent}");
}
let _ = write!(&mut output, "`");
output
}