value-expr 0.1.3

easy expression
Documentation
use super::data::*;
use proc_macro2::TokenStream;
use std::str::FromStr;
use syn::ext::IdentExt;
use syn::parse::discouraged::Speculative;
use syn::parse::{Parse, ParseStream};
use syn::{parenthesized, token, Ident, LitInt, Token};

macro_rules! op {
    ($name:ident => $msg:literal; $([$($tt:tt)+] => $ident:ident);+$(;)?) => {
        impl Parse for $name {
            fn parse(input: ParseStream) -> syn::Result<Self> {
                let lookahead = input.lookahead1();
                $(if lookahead.peek(Token![$($tt)+]) {
                    input.parse::<Token![$($tt)+]>().map(|_|Self::$ident)
                } else)+ {
                    Err(input.error($msg))
                }
            }
        }
        impl $name {
            #[allow(unused)]
            pub fn desc(self) -> &'static str {
                match self {
                    $(Self::$ident => stringify!($($tt)+)),+
                }
            }
        }
    };
}

op! {
    UnOp => "expected unary operator";
    [!] => Not;
    [-] => Neg;
}

op! {
    BinOp => "expected binary operator";
    [&&] => And;
    [||] => Or;
    [<<] => Shl;
    [>>] => Shr;
    [==] => Eq;
    [<=] => Le;
    [!=] => Ne;
    [>=] => Ge;
    [+] => Add;
    [-] => Sub;
    [*] => Mul;
    [/] => Div;
    [%] => Rem;
    [^] => BitXor;
    [&] => BitAnd;
    [|] => BitOr;
    [<] => Lt;
    [>] => Gt;
}

op! {
    AssignOp => "expected assignment operator";
    [=] => Assign;
    [+=] => AddAssign;
    [-=] => SubAssign;
    [*=] => MulAssign;
    [/=] => DivAssign;
    [%=] => RemAssign;
    [^=] => BitAndAssign;
    [&=] => BitOrAssign;
    [|=] => BitXorAssign;
    [<<=] => ShlAssign;
    [>>=] => ShrAssign;
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
enum Precedence {
    Any,
    Or,
    And,
    Compare,
    BitOr,
    BitXor,
    BitAnd,
    Shift,
    Arithmetic,
    Term,
}

impl Precedence {
    fn of(op: &BinOp) -> Self {
        match op {
            BinOp::Add | BinOp::Sub => Precedence::Arithmetic,
            BinOp::Mul | BinOp::Div | BinOp::Rem => Precedence::Term,
            BinOp::And => Precedence::And,
            BinOp::Or => Precedence::Or,
            BinOp::BitXor => Precedence::BitXor,
            BinOp::BitAnd => Precedence::BitAnd,
            BinOp::BitOr => Precedence::BitOr,
            BinOp::Shl | BinOp::Shr => Precedence::Shift,
            BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
                Precedence::Compare
            }
        }
    }
}

fn unary_value(input: ParseStream) -> syn::Result<Value> {
    if input.peek(Token![!]) || input.peek(Token![-]) {
        Ok(Value::Unary(input.parse()?, Box::new(unary_value(input)?)))
    } else {
        atom_value(input)
    }
}

fn atom_value(input: ParseStream) -> syn::Result<Value> {
    if input.peek(token::Paren) {
        let content;
        parenthesized!(content in input);
        let data = content
            .parse_terminated(Value::parse, Token![,])?
            .into_iter()
            .collect();
        return Ok(Value::Paren(data));
    }
    if input.peek(LitInt) {
        let integer = input.parse::<LitInt>()?.base10_parse::<i32>()?;
        return Ok(Value::Integer(integer));
    }
    if input.peek(Ident::peek_any) {
        let ident = input.call(Ident::parse_any)?.to_string();
        if input.peek(token::Paren) {
            let content;
            parenthesized!(content in input);
            let data = content
                .parse_terminated(Value::parse, Token![,])?
                .into_iter()
                .collect();
            return Ok(Value::FuncLike(ident, data));
        }
        if !input.peek(Token![==]) {
            let ahead = input.fork();
            if let Ok(op) = ahead.parse::<AssignOp>() {
                input.advance_to(&ahead);
                return Ok(Value::Assign(op, ident, input.parse()?));
            }
        }
        return Ok(Value::Ident(ident));
    }
    Err(input.lookahead1().error())
}

fn peek_precedence(input: ParseStream) -> Precedence {
    if let Ok(op) = input.fork().parse() {
        Precedence::of(&op)
    } else {
        Precedence::Any
    }
}

fn parse_value(input: ParseStream, mut lhs: Value, base: Precedence) -> syn::Result<Value> {
    loop {
        let ahead = input.fork();
        if let Some(op) = match ahead.parse::<BinOp>() {
            Ok(op) if Precedence::of(&op) >= base => Some(op),
            _ => None,
        } {
            input.advance_to(&ahead);
            let precedence = Precedence::of(&op);
            let mut rhs = unary_value(input)?;
            loop {
                let next = peek_precedence(input);
                if next > precedence {
                    rhs = parse_value(input, rhs, next)?;
                } else {
                    break;
                }
            }
            lhs = Value::Binary(op, Box::new(lhs), Box::new(rhs));
        } else {
            break;
        }
    }
    Ok(lhs)
}

impl Parse for Value {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let lhs = unary_value(input)?;
        parse_value(input, lhs, Precedence::Any)
    }
}

impl Value {
    pub fn parse_str(input: &str) -> syn::Result<Self> {
        syn::parse2(TokenStream::from_str(input)?)
    }
}