use std::{rc::Rc};
use proc_macro::{TokenStream, TokenTree};
use crate::{errors::CfgBoostError, config::{get_cfg_boost_alias, get_cfg_boost_predicate}};
pub(crate) type Node = Rc<SyntaxTreeNode>;
pub(crate) const NEGATIVE_SYMBOL : char = '!';
pub(crate) const AND_SYMBOL : char = '&';
pub(crate) const OR_SYMBOL : char = '|';
#[derive(Debug)]
pub(crate) enum SyntaxTreeNode {
NOT(Node),
ANY(Node, Node),
ALL(Node, Node),
LEAF(String)
}
impl ToString for SyntaxTreeNode {
fn to_string(&self) -> String {
match self {
SyntaxTreeNode::NOT(node) => format!("not({})", node.to_string()),
SyntaxTreeNode::ANY(left_node, right_node) => format!("any({},{})", left_node.to_string(), right_node.to_string()),
SyntaxTreeNode::ALL(left_node, right_node) => format!("all({},{})", left_node.to_string(), right_node.to_string()),
SyntaxTreeNode::LEAF(label) =>
match get_cfg_boost_predicate(&label.as_str()) {
Ok(predicate) => format!("{}", predicate),
Err(err) => panic!("{}", err.message(label)),
},
}
}
}
impl SyntaxTreeNode {
pub fn not_node(child : Node) -> Node {
Rc::new(SyntaxTreeNode::NOT(child.clone()))
}
pub fn all_node(left : Node, right : Node) -> Node {
Rc::new(SyntaxTreeNode::ALL(left.clone(), right.clone()))
}
pub fn any_node(left : Node, right : Node) -> Node {
Rc::new(SyntaxTreeNode::ANY(left.clone(), right.clone()))
}
pub(crate) fn generate(stream : TokenStream) -> Node {
match split_tokenstream_at_operator(stream.clone()){
Some((operator, left, right)) =>
match operator {
AND_SYMBOL => { return Self::all_node(Self::generate(left), Self::generate(right));
}
OR_SYMBOL => { return Self::any_node(Self::generate(left), Self::generate(right));
},
_ => panic!("{}", CfgBoostError::InvalidCharacter(String::from(operator)).message(&stream.to_string())),
},
None => {
let (symbol, content) = extract_negative_symbol(stream.clone());
if is_not_node(symbol) {
return Self::not_node(Self::generate(content));
} else {
match extract_group(content.clone()) {
Some(group) => return Self::generate(group),
None => {
if content.to_string().eq("") { panic!("{}", CfgBoostError::EmptyNode.message(&content.to_string()));
}
match content.to_string().find(":"){
Some(pos) => { match content.to_string()[..pos].trim().find(" "){ Some(_) => panic!("{}", CfgBoostError::MissingOperator.message(&content.to_string())),
None => {},
}
return Rc::new(SyntaxTreeNode::LEAF(content.to_string()));
},
None => { match content.to_string().find(" "){ Some(_) => panic!("{}", CfgBoostError::MissingOperator.message(&content.to_string())),
None => {},
}
match get_cfg_boost_alias(&content.to_string()) {
Ok(alias) => Self::generate(alias.parse().unwrap()),
Err(err) => panic!("{}", err.message(&stream.to_string())),
}
},
}
},
}
}
},
}
}
}
#[inline(always)]
fn extract_group(stream : TokenStream) -> Option<TokenStream> {
for t in stream {
match t {
TokenTree::Group(group) => return Some(group.stream()),
_ => {},
}
}
None
}
#[inline(always)]
pub(crate) fn extract_negative_symbol(stream: TokenStream) -> (TokenStream, TokenStream) {
let mut symbol = TokenStream::new();
let mut content = TokenStream::new();
let mut is_symbol = true;
for t in stream.clone() {
if is_symbol {
match t.clone() {
proc_macro::TokenTree::Punct(punc) => {
match punc.as_char() {
NEGATIVE_SYMBOL => symbol.extend(TokenStream::from(t)),
_ => panic!("{}", CfgBoostError::InvalidCharacter(String::from(punc.as_char())).message(&stream.to_string())),
}
},
_ => {
is_symbol = false;
content.extend(TokenStream::from(t));
},
}
} else {
content.extend(TokenStream::from(t));
}
}
(symbol, content)
}
#[inline(always)]
fn is_not_node(symbol : TokenStream) -> bool{
for t in symbol {
match t {
proc_macro::TokenTree::Punct(punc) => {
match punc.as_char() {
NEGATIVE_SYMBOL => return true,
_ => {},
}
},
_ => {},
}
}
false
}
#[inline(always)]
pub(crate) fn split_tokenstream_at_operator(stream : TokenStream) -> Option<(char, TokenStream, TokenStream)> {
let mut left = TokenStream::new();
let mut right = TokenStream::new();
let mut operator: char = '?';
for t in stream.clone() {
match operator {
'?' => {
match t.clone() {
proc_macro::TokenTree::Punct(symbol) => {
match symbol.as_char() {
AND_SYMBOL => { operator = AND_SYMBOL
}
OR_SYMBOL => { operator = OR_SYMBOL
}
NEGATIVE_SYMBOL | '_' | '-' | ' ' | ':' | '.' => left.extend(TokenStream::from(t)),
_ => {
panic!("{}", CfgBoostError::InvalidCharacter(String::from(symbol.as_char())).message(&stream.to_string()));
},
}
},
_ => left.extend(TokenStream::from(t)), }
},
_ => right.extend(TokenStream::from(t)), }
}
match operator {
'?' => None, _ => Some((operator, left, right)),
}
}
#[inline(always)]
pub(crate) fn split_items(stream : TokenStream) -> Vec<TokenStream> {
let mut item = TokenStream::new();
let mut items : Vec<TokenStream> = Vec::new();
for t in stream {
match &t {
proc_macro::TokenTree::Group(grp) => {
match grp.delimiter(){
proc_macro::Delimiter::Brace => { item.extend(TokenStream::from(t)); items.push(item); item = TokenStream::new(); },
_ => item.extend(TokenStream::from(t)), }
}
,
proc_macro::TokenTree::Punct(punc) => {
if punc.as_char().eq(&';') { item.extend(TokenStream::from(t)); items.push(item); item = TokenStream::new(); } else {
item.extend(TokenStream::from(t)); }
},
_ => item.extend(TokenStream::from(t)), }
}
items
}