kamo-macros 0.1.2

A macro for parsing s-expressions into kamo Values.
Documentation
use pest::iterators::Pair;
use proc_macro2::TokenStream;
use quote::quote;

use crate::sexpr::{error::Error, parser::Rule};

use super::Number;

pub fn emit_decimal<'a>(pair: Pair<'a, Rule>, out: &mut TokenStream) -> Result<Number, Error<'a>> {
    if pair.as_rule() == Rule::decimal {
        if let Ok(value) = pair.as_str().parse::<i64>() {
            out.extend(quote! { Value::new_int(#value) });
            Ok(Number::Integer(value))
        } else if let Ok(value) = pair.as_str().parse::<f64>() {
            out.extend(quote! { Value::new_float(#value) });
            if value.is_infinite() {
                return Ok(Number::Infinty);
            } else if value.is_nan() {
                return Ok(Number::NaN);
            }
            Ok(Number::Float(value))
        } else {
            Err(Error::InvalidDecimal(
                pair.as_span(),
                pair.as_str().to_owned(),
            ))
        }
    } else {
        Err(Error::ExpectedDecimal(pair.as_span()))
    }
}

#[cfg(test)]
mod tests {
    use pest::Parser;
    use quote::quote;

    use crate::sexpr::parser::SExpr;

    use super::*;

    #[test]
    fn emit_decimal_success() {
        let exprs = [
            ("+10", quote! { Value::new_int(10i64) }),
            ("-10", quote! { Value::new_int(-10i64) }),
            ("+10.0", quote! { Value::new_float(10f64) }),
            ("-10.0", quote! { Value::new_float(-10f64) }),
            ("10.42e+10", quote! { Value::new_float(104200000000f64) }),
            ("10.0", quote! { Value::new_float(10f64) }),
            ("10", quote! { Value::new_int(10i64) }),
            ("10e4", quote! { Value::new_float(100000f64) }),
            (".5", quote! { Value::new_float(0.5f64) }),
            (".5E-3", quote! { Value::new_float(0.0005f64) }),
        ];

        for (i, (input, expected)) in exprs.into_iter().enumerate() {
            let i = i + 1;
            let pairs = SExpr::parse(Rule::decimal, input);
            let pairs = match pairs {
                Ok(pairs) => pairs,
                Err(e) => panic!("unsuccessful parse {}: {}", i, e),
            };

            for pair in pairs {
                let mut out = TokenStream::new();

                emit_decimal(pair, &mut out).unwrap();
                assert_eq!(out.to_string(), expected.to_string(), "expr value {}", i);
            }
        }
    }

    #[test]
    fn emit_decimal_failure() {
        let exprs: [&str; 2] = ["#t", "#f"];

        for (i, input) in exprs.into_iter().enumerate() {
            let i = i + 1;
            let pairs = SExpr::parse(Rule::datum, input);
            let pairs = match pairs {
                Ok(pairs) => pairs,
                Err(e) => panic!("unsuccessful parse {}: {}", i, e),
            };

            for pair in pairs {
                let mut out = TokenStream::new();

                assert!(emit_decimal(pair, &mut out).is_err(), "expr value {}", i);
            }
        }
    }
}