use cldr_pluralrules_parser::ast::*;
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::quote;
fn convert_literal(num: usize) -> Literal {
Literal::u64_unsuffixed(num as u64)
}
fn convert_range(low: usize, up: usize) -> (Literal, Literal) {
let u = convert_literal(up);
let d = convert_literal(low);
(d, u)
}
fn convert_rangl(rangl: RangeList) -> (Vec<Literal>, Vec<(Literal, Literal)>) {
let mut litints = Vec::new();
let mut litrange = Vec::new();
for x in rangl.0 {
match &x {
RangeListItem::Value(x) => litints.push(convert_literal(x.0)),
RangeListItem::Range(x) => litrange.push(convert_range(x.lower_val.0, x.upper_val.0)),
}
}
(litints, litrange)
}
fn get_operator_symbol(op: &Operator) -> TokenStream {
match op {
Operator::In | Operator::Is | Operator::EQ => quote!(==),
Operator::NotIn | Operator::IsNot | Operator::NotEQ => quote!(!=),
Operator::Within => quote!(<=),
Operator::NotWithin => quote!(>),
}
}
fn create_relation(rel: Relation) -> TokenStream {
let left = rel.expression;
let operator = rel.operator;
let right = rel.range_list;
let mut relations = Vec::<TokenStream>::new();
let l = match left.operand {
Operand::N => "n",
Operand::I => "i",
Operand::V => "v",
Operand::T => "t",
Operand::W => "w",
Operand::F => "f",
};
let l = Ident::new(l, Span::call_site());
let o = get_operator_symbol(&operator);
let r1 = convert_rangl(right);
let (mod_check, m) = if left.modulus != None {
(true, convert_literal((left.modulus.unwrap().0).0))
} else {
(false, convert_literal(0))
};
if operator == Operator::Within || operator == Operator::NotWithin {
let rfront = &(r1.1)[0].0;
let rback = &(r1.1)[0].1;
let (rfront, rback, whole_symbol) = if left.operand == Operand::N {
if !mod_check {
(quote!(#rfront.0), quote!(#rback.0), quote!(po.#l))
} else {
(quote!(#rfront), quote!(#rback), quote!(po.i % #m))
}
} else {
(
quote!(#rfront),
quote!(#rback),
if !mod_check {
quote!(po.#l)
} else {
quote!(po.i % #m)
},
)
};
let rel_tokens = quote! { #rfront #o #whole_symbol && #whole_symbol #o #rback};
relations.push(rel_tokens);
} else {
for r in r1.0 {
let (symbol, rval) = if left.operand == Operand::N {
if !mod_check {
(quote!(po.#l), quote!(#r.0))
} else {
(quote!(po.i % #m), quote!(#r))
}
} else {
(
if !mod_check {
quote!(po.#l)
} else {
quote!(po.#l % #m)
},
quote!(#r),
)
};
let rel_tokens = quote! { #symbol #o #rval };
relations.push(rel_tokens);
}
for r in r1.1 {
let rfront = r.0;
let rback = r.1;
let (symbol, perim) = if left.operand == Operand::N {
if !mod_check {
(quote!(po.i), quote! { && po.f == 0})
} else {
(quote!(po.i), quote! {})
}
} else {
(
if !mod_check {
quote!(po.#l)
} else {
quote!(po.#l % #m)
},
quote! {},
)
};
let rel_tokens = match operator {
Operator::In | Operator::Is | Operator::EQ => {
quote! { (#rfront ..= #rback).contains(&(#symbol)) #perim }
}
Operator::NotIn | Operator::NotEQ | Operator::IsNot => {
quote! { !(#rfront ..= #rback).contains(&(#symbol)) #perim }
}
Operator::Within | Operator::NotWithin => {
panic!("There was a problem with the source file.")
}
};
relations.push(rel_tokens);
}
}
match operator {
Operator::In | Operator::Is | Operator::EQ => {
if relations.len() > 1 {
quote! { ( #(#relations)||* ) }
} else {
quote! { #(#relations)||* }
}
}
Operator::NotIn | Operator::NotEQ | Operator::IsNot => quote! { #(#relations)&&* },
Operator::Within | Operator::NotWithin => quote! { #(#relations)||* },
}
}
fn create_and_condition(acond: AndCondition) -> TokenStream {
let mut andcondvec = Vec::<TokenStream>::new();
for a in acond.0 {
andcondvec.push(create_relation(a));
}
quote! { ( #(#andcondvec)&&* ) }
}
fn create_condition(cond: Condition) -> TokenStream {
let mut condvec = Vec::<TokenStream>::new();
for c in cond.0 {
condvec.push(create_and_condition(c));
}
quote! { #(#condvec)||* }
}
pub fn gen_pr(cond: Condition) -> TokenStream {
create_condition(cond)
}