use std::{iter::Peekable, rc::Rc};
use kconfig_represent::ConfigRegistry;
use proc_macro::{token_stream::IntoIter, Delimiter, Group, Spacing, TokenTree};
use super::{comp::Comparison, tag_registry::TagRegistry, util::parse_string};
#[derive(Clone)]
pub(crate) struct Eval {
config_registry: Rc<ConfigRegistry>,
group: Group,
}
impl Eval {
pub(crate) fn new_tagged(tag_name: &str, group: &Group) -> Self {
let config_registry = TagRegistry::get_config_registry(tag_name);
Self::new(&config_registry, group)
}
pub(crate) fn new(config_registry: &Rc<ConfigRegistry>, group: &Group) -> Self {
Self {
config_registry: config_registry.clone(),
group: group.clone(),
}
}
pub(crate) fn eval(&self) -> ValueRef {
match self.group.delimiter() {
Delimiter::Parenthesis => {
let tokens = self.group.stream();
self.evaluate_tokens(&mut tokens.into_iter().peekable())
}
_ => panic!("Expected (, found {}", self.group.to_string()),
}
}
fn evaluate_tokens(&self, iter: &mut Peekable<IntoIter>) -> ValueRef {
let lhs = match iter.next() {
Some(tt) => match tt {
TokenTree::Group(_) => self.eval(),
TokenTree::Ident(i) => {
let s = i.to_string();
if s == "true" {
ValueRef::Boolean(true)
} else if s == "false" {
ValueRef::Boolean(false)
} else {
ValueRef::Config((s, self.config_registry.clone()))
}
}
TokenTree::Punct(p) => {
panic!(
"Expected group, identifier or literal, not: {}",
p.to_string()
)
}
TokenTree::Literal(l) => {
let content = l.to_string();
if content.starts_with('"') {
ValueRef::String(parse_string(&content))
} else {
match content.parse::<i64>() {
Ok(value) => ValueRef::Int(value),
_ => panic!("Literal value {} could not be parsed", content),
}
}
}
},
None => panic!("End of input not expected"),
};
let comp = match iter.next() {
Some(tt) => match tt {
TokenTree::Punct(p) => match p.spacing() {
Spacing::Alone => Comparison::single(&p),
Spacing::Joint => match iter.peek() {
Some(peeked) => match peeked {
TokenTree::Punct(p2) => Comparison::dual(&p, p2),
_ => Comparison::single(&p),
},
None => Comparison::single(&p),
},
},
_ => panic!("Expected ==, !=, <, <=, >=, >, &&, ||"),
},
None => Comparison::None,
};
match comp {
Comparison::Lt | Comparison::Gt | Comparison::None => (),
_ => {
iter.next();
()
}
}
match comp {
Comparison::None => lhs,
_ => {
let rhs = self.evaluate_tokens(iter);
comp.compare(lhs, rhs)
}
}
}
}
#[derive(Clone)]
pub(crate) enum ValueRef {
Config((String, Rc<ConfigRegistry>)),
Boolean(bool),
String(String),
Int(i64),
}
impl ValueRef {
pub(crate) fn as_value(&self) -> Value {
match self {
ValueRef::Config((symbol, config_registry)) => {
let value = config_registry.config_value(symbol);
Value::String(value)
}
ValueRef::Boolean(b) => Value::Boolean(b.clone()),
ValueRef::String(s) => Value::String(s.clone()),
ValueRef::Int(i) => Value::Int(i.clone()),
}
}
pub(crate) fn as_bool(&self) -> bool {
self.as_value().as_bool()
}
pub(crate) fn as_string(&self) -> String {
self.as_value().as_string()
}
pub(crate) fn as_int(&self) -> Option<i64> {
self.as_value().as_int()
}
}
#[derive(Clone)]
pub(crate) enum Value {
String(String),
Boolean(bool),
Int(i64),
}
impl Value {
pub(crate) fn as_bool(&self) -> bool {
match self {
Value::Boolean(b) => b.clone(),
Value::String(s) => s != "" && s != "n",
Value::Int(i) => i.clone() > 0,
}
}
pub(crate) fn as_string(&self) -> String {
match self {
Value::Boolean(b) => match b {
true => "y".to_owned(),
false => "n".to_owned(),
},
Value::String(s) => s.clone(),
Value::Int(i) => i.to_string(),
}
}
pub(crate) fn as_int(&self) -> Option<i64> {
match self {
Value::String(s) => match s.parse::<i64>() {
Ok(v) => Some(v),
_ => None,
},
Value::Boolean(b) => match b {
false => Some(0),
true => Some(1),
},
Value::Int(u) => Some(u.clone()),
}
}
}