arithmetic-parser 0.3.0

Parser for arithmetic expressions with flexible literals and type annotations.
Documentation
//! Tests for functions / methods.

use assert_matches::assert_matches;
use nom::Err as NomErr;

use super::{args, lsp, lvalue_tuple, sp, span, FieldGrammar, Literal, LiteralType, ValueType};
use crate::{
    parser::{expr, fn_def, simple_expr, Complete},
    BinaryOp, Block, ErrorKind, Expr, FnDefinition, InputSpan, Lvalue, Spanned,
};

#[test]
fn fun_works() {
    let input = InputSpan::new("ge(0x123456) + 1");
    assert_eq!(
        simple_expr::<FieldGrammar, Complete>(input).unwrap().1,
        sp(
            0,
            "ge(0x123456)",
            Expr::Function {
                name: Box::new(sp(0, "ge", Expr::Variable)),
                args: vec![sp(
                    3,
                    "0x123456",
                    Expr::Literal(Literal::Bytes {
                        value: vec![0x12, 0x34, 0x56],
                        ty: LiteralType::Bytes,
                    })
                ),]
            }
        )
    );

    let input = InputSpan::new("ge (  0x123456\t) + A");
    assert_eq!(
        simple_expr::<FieldGrammar, Complete>(input).unwrap().1,
        sp(
            0,
            "ge (  0x123456\t)",
            Expr::Function {
                name: Box::new(sp(0, "ge", Expr::Variable)),
                args: vec![sp(
                    6,
                    "0x123456",
                    Expr::Literal(Literal::Bytes {
                        value: vec![0x12, 0x34, 0x56],
                        ty: LiteralType::Bytes,
                    })
                )]
            }
        )
    );
}

#[test]
fn fun_call_with_terminating_comma() {
    let input = InputSpan::new("ge(1, 2 ,\n)");
    assert_eq!(
        simple_expr::<FieldGrammar, Complete>(input).unwrap().1,
        sp(
            0,
            "ge(1, 2 ,\n)",
            Expr::Function {
                name: Box::new(sp(0, "ge", Expr::Variable)),
                args: vec![
                    sp(3, "1", Expr::Literal(Literal::Number)),
                    sp(6, "2", Expr::Literal(Literal::Number)),
                ],
            }
        )
    );
}

#[test]
fn fun_works_with_complex_called_values() {
    let input = InputSpan::new("ge(x, 3)(0x123456) + 5");
    let inner_fn = sp(
        0,
        "ge(x, 3)",
        Expr::Function {
            name: Box::new(sp(0, "ge", Expr::Variable)),
            args: vec![
                sp(3, "x", Expr::Variable),
                sp(6, "3", Expr::Literal(Literal::Number)),
            ],
        },
    );
    assert_eq!(
        simple_expr::<FieldGrammar, Complete>(input).unwrap().1,
        sp(
            0,
            "ge(x, 3)(0x123456)",
            Expr::Function {
                name: Box::new(inner_fn),
                args: vec![sp(
                    9,
                    "0x123456",
                    Expr::Literal(Literal::Bytes {
                        value: vec![0x12, 0x34, 0x56],
                        ty: LiteralType::Bytes,
                    })
                )]
            }
        )
    );

    let input = InputSpan::new("(|x| x + 1)(0xs_123456) + 5");
    let (_, function) = simple_expr::<FieldGrammar, Complete>(input).unwrap();
    let function_value = match function.extra {
        Expr::Function { name, .. } => name.extra,
        other => panic!("unexpected expr: {:?}", other),
    };
    assert_matches!(function_value, Expr::FnDefinition(_));

    let input = InputSpan::new("|x| { x + 1 }(0xs_123456) + 5");
    let (_, function) = simple_expr::<FieldGrammar, Complete>(input).unwrap();
    let function_value = match function.extra {
        Expr::Function { name, .. } => name.extra,
        other => panic!("unexpected expr: {:?}", other),
    };
    assert_matches!(function_value, Expr::FnDefinition(_));
}

#[test]
fn method_expr_works() {
    let input = InputSpan::new("x.sin();");
    let (_, call) = simple_expr::<FieldGrammar, Complete>(input).unwrap();
    assert_eq!(
        call,
        sp(
            0,
            "x.sin()",
            Expr::Method {
                name: span(2, "sin").into(),
                receiver: Box::new(sp(0, "x", Expr::Variable)),
                args: vec![],
            }
        )
    );

    let input = InputSpan::new("(1, x, 2).foo(y) + 3;");
    let (_, call) = simple_expr::<FieldGrammar, Complete>(input).unwrap();
    let expected = sp(
        0,
        "(1, x, 2).foo(y)",
        Expr::Method {
            name: span(10, "foo").into(),
            receiver: Box::new(sp(
                0,
                "(1, x, 2)",
                Expr::Tuple(vec![
                    sp(1, "1", Expr::Literal(Literal::Number)),
                    sp(4, "x", Expr::Variable),
                    sp(7, "2", Expr::Literal(Literal::Number)),
                ]),
            )),
            args: vec![sp(14, "y", Expr::Variable)],
        },
    );
    assert_eq!(call, expected);

    let input = InputSpan::new("(1, x, 2).foo(y)(7.bar());");
    let (_, call) = simple_expr::<FieldGrammar, Complete>(input).unwrap();
    assert_eq!(
        call,
        sp(
            0,
            "(1, x, 2).foo(y)(7.bar())",
            Expr::Function {
                name: Box::new(expected),
                args: vec![sp(
                    17,
                    "7.bar()",
                    Expr::Method {
                        name: span(19, "bar").into(),
                        receiver: Box::new(sp(17, "7", Expr::Literal(Literal::Number))),
                        args: vec![],
                    }
                )],
            }
        )
    );
}

#[test]
fn fn_definition_parsing() {
    let input = InputSpan::new("|x| x + z;");
    assert_eq!(
        fn_def::<FieldGrammar, Complete>(input).unwrap().1,
        FnDefinition {
            args: args(
                span(0, "|x|"),
                vec![lsp(1, "x", Lvalue::Variable { ty: None })]
            ),
            body: Block {
                statements: vec![],
                return_value: Some(Box::new(sp(
                    4,
                    "x + z",
                    Expr::Binary {
                        lhs: Box::new(sp(4, "x", Expr::Variable)),
                        op: BinaryOp::from_span(span(6, "+")),
                        rhs: Box::new(sp(8, "z", Expr::Variable)),
                    }
                ))),
            }
        }
    );

    let input = InputSpan::new("|x| { x + 3 }");
    assert_eq!(
        fn_def::<FieldGrammar, Complete>(input).unwrap().1,
        FnDefinition {
            args: args(
                span(0, "|x|"),
                vec![lsp(1, "x", Lvalue::Variable { ty: None })]
            ),
            body: Block {
                statements: vec![],
                return_value: Some(Box::new(sp(
                    6,
                    "x + 3",
                    Expr::Binary {
                        lhs: Box::new(sp(6, "x", Expr::Variable)),
                        op: BinaryOp::from_span(span(8, "+")),
                        rhs: Box::new(sp(10, "3", Expr::Literal(Literal::Number))),
                    }
                )))
            }
        }
    );

    let input = InputSpan::new("|x: Sc, (y, _: Ge)| { x + y }");
    let mut def = fn_def::<FieldGrammar, Complete>(input).unwrap().1;
    assert!(def.body.statements.is_empty());
    assert!(def.body.return_value.is_some());

    def.body = Block {
        statements: vec![],
        return_value: None,
    };
    assert_eq!(
        def,
        FnDefinition {
            args: args(
                span(0, "|x: Sc, (y, _: Ge)|"),
                vec![
                    lsp(
                        1,
                        "x",
                        Lvalue::Variable {
                            ty: Some(Spanned::new(span(4, "Sc"), ValueType::Scalar)),
                        }
                    ),
                    lsp(
                        8,
                        "(y, _: Ge)",
                        lvalue_tuple(vec![
                            lsp(9, "y", Lvalue::Variable { ty: None }),
                            lsp(
                                12,
                                "_",
                                Lvalue::Variable {
                                    ty: Some(Spanned::new(span(15, "Ge"), ValueType::Element)),
                                }
                            )
                        ])
                    ),
                ]
            ),
            body: Block {
                statements: vec![],
                return_value: None
            },
        }
    );
}

#[test]
fn function_call_with_literal_as_fn_name() {
    let input = InputSpan::new("1(2, 3);");
    let err = expr::<FieldGrammar, Complete>(input).unwrap_err();
    let spanned_err = match err {
        NomErr::Failure(spanned) => spanned,
        _ => panic!("Unexpected error: {}", err),
    };
    assert_matches!(spanned_err.kind(), ErrorKind::LiteralName);
}