orql 0.1.0

A toy SQL parser for a subset of the Oracle dialect.
Documentation
use super::Literal;
use crate::{
    ast::{BinaryExpr, BinaryExprOp, Expr, Node, Value},
    parser::{
        Comment, CommentStyle, DefaultTracker, MetaTracker, Metadata, ParserInner, Result, Scanner,
    },
};

fn parse_expr<'s, M: MetaTracker<'s> + Default>(
    s: &'s str,
) -> Result<(Expr<'s, M::NodeId>, M::Metadata)> {
    let mut p = ParserInner::new(Scanner::new(s), M::default());
    p.parse_expr()
        .and_then(|expr| p.finish().map(|meta| (expr, meta)))
}

#[test]
fn test_comments() {
    // ~ note: we do _not_ test trailing comments for `Expr::Nested` in this test
    let (expr, meta) = parse_expr::<DefaultTracker>(
        r#"-- before everything else
( /* asdfqwer */ (1 -- blah blah
+ /* ej */ 2 /* hip */ /* hop */
* 3 / 4 /* almost */))"#,
    )
    .expect("invalid source");
    assert_eq!(expr.literal().to_string(), "((1 + 2 * 3 / 4))");

    // ~ 1st level of nesting
    let_expect!(Expr::Nested(Node(nested, id)) = expr);
    assert_eq!(meta.location(id), (2, 1).into());
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: " before everything else",
                style: CommentStyle::Line,
                loc: (1, 1).into()
            }][..],
            &[][..]
        )
    );

    // ~ 2nd level of nesting
    let_expect!(Expr::Nested(Node(nested, id)) = *nested);
    assert_eq!(meta.location(id), (2, 18).into());
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: " asdfqwer ",
                style: CommentStyle::Block,
                loc: (2, 3).into()
            }][..],
            &[][..]
        )
    );
    // the "{+ 1 ...}"
    let_expect!(Expr::Binary(binary_add) = *nested);
    let BinaryExpr { left, op, right } = *binary_add;
    // ~ operator
    assert_eq!(op.0, BinaryExprOp::Add);
    assert_eq!(meta.location(op.1), (3, 1).into());
    assert_eq!(
        meta.comments(op.1),
        (
            &[Comment {
                text: " blah blah",
                style: CommentStyle::Line,
                loc: (2, 21).into()
            },][..],
            &[Comment {
                text: " ej ",
                style: CommentStyle::Block,
                loc: (3, 3).into()
            }][..]
        )
    );
    // ~ left hand side of "+"; the "1"
    let_expect!(Expr::Value(Node(Value::Integer("1"), id)) = left);
    assert_eq!(meta.location(id), (2, 19).into());
    assert_eq!(
        meta.comments(id),
        (
            &[][..],
            &[Comment {
                text: " blah blah",
                style: CommentStyle::Line,
                loc: (2, 21).into()
            }][..]
        )
    );
    // ~ right hand side of "+"; the "{/ {* 2 3} 4}"
    let_expect!(Expr::Binary(binary_div) = right);
    let BinaryExpr {
        left: left_div,
        op,
        right: right_div,
    } = *binary_div;
    assert_eq!(op.0, BinaryExprOp::Div);
    assert_eq!(meta.location(op.1), (4, 5).into());
    assert_eq!(meta.comments(op.1), (&[][..], &[][..]));
    // ~ left hand again of "/"; the "{* 2 3}"
    let_expect!(Expr::Binary(binary_mul) = left_div);
    let BinaryExpr {
        left: left_mul,
        op,
        right: right_mul,
    } = *binary_mul;
    assert_eq!(op.0, BinaryExprOp::Mul);
    assert_eq!(meta.location(op.1), (4, 1).into());
    assert_eq!(
        meta.comments(op.1),
        (
            &[
                Comment {
                    text: " hip ",
                    style: CommentStyle::Block,
                    loc: (3, 14).into()
                },
                Comment {
                    text: " hop ",
                    style: CommentStyle::Block,
                    loc: (3, 24).into()
                },
            ][..],
            &[][..]
        )
    );
    let_expect!(Expr::Value(Node(Value::Integer("2"), id)) = left_mul);
    assert_eq!(meta.location(id), (3, 12).into());
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: " ej ",
                style: CommentStyle::Block,
                loc: (3, 3).into()
            }][..],
            &[
                Comment {
                    text: " hip ",
                    style: CommentStyle::Block,
                    loc: (3, 14).into()
                },
                Comment {
                    text: " hop ",
                    style: CommentStyle::Block,
                    loc: (3, 24).into()
                },
            ][..]
        )
    );
    // ~ right hand again of "{* 2 3}"; the "3"
    let_expect!(Expr::Value(Node(Value::Integer("3"), id)) = right_mul);
    assert_eq!(meta.location(id), (4, 3).into());
    assert_eq!(meta.comments(id), (&[][..], &[][..]));

    // ~ finally the right hand side of "/"; the "4"
    let_expect!(Expr::Value(Node(Value::Integer("4"), id)) = right_div);
    assert_eq!(meta.location(id), (4, 7).into());
    assert_eq!(
        meta.comments(id),
        (
            &[][..],
            &[Comment {
                text: " almost ",
                style: CommentStyle::Block,
                loc: (4, 9).into()
            }][..]
        )
    );
}

#[test]
fn test_comments_trailing_on_nested_0() {
    let (expr, meta) = parse_expr::<DefaultTracker>("/*a*/ (1) /*e*/").expect("invalid source");
    assert_eq!(expr.literal().to_string(), "(1)");

    let_expect!(Expr::Nested(Node(nested, id)) = &expr);
    assert_eq!(
        meta.comments(*id),
        (
            &[Comment {
                text: "a",
                style: CommentStyle::Block,
                loc: (1, 1).into()
            }][..],
            &[Comment {
                text: "e",
                style: CommentStyle::Block,
                loc: (1, 11).into()
            }][..]
        )
    );
    let_expect!(Expr::Value(Node(Value::Integer("1"), id)) = **nested);
    assert_eq!(meta.comments(id), (&[][..], &[][..]));
}

#[test]
fn test_comments_trailing_on_nested_1() {
    #[rustfmt::skip]
    // .................................................123456789.123456789.123456789.123456789.123456789.1
    let (expr, meta) = parse_expr::<DefaultTracker>("/*a*/ (/*w*/ (/*x*/ 1 /*y*/) /*z*/) /*e*/ + 2 /*f*/")
        .expect("invalid source");
    assert_eq!(expr.literal().to_string(), "((1)) + 2");
    assert_eq!(
        expr.literal().with(&meta).to_string(),
        "/*a*/ ( /*w*/ ( /*x*/ 1 /*y*/ ) /*z*/ ) /*e*/ + 2 /*f*/"
    );

    let_expect!(Expr::Binary(binary) = expr);
    let_expect!(
        BinaryExpr {
            left,
            op: Node(BinaryExprOp::Add, id),
            right
        } = *binary
    );
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: "e",
                style: CommentStyle::Block,
                loc: (1, 37).into()
            }][..],
            &[][..]
        )
    );
    // ~ the right hand side, ie. the "2"
    let_expect!(Expr::Value(Node(Value::Integer("2"), id)) = right);
    assert_eq!(
        meta.comments(id),
        (
            &[][..],
            &[Comment {
                text: "f",
                style: CommentStyle::Block,
                loc: (1, 47).into()
            }][..]
        )
    );

    // ~ the left hand side; ie. the "{{1}}"
    let_expect!(Expr::Nested(Node(nested, id)) = left);
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: "a",
                style: CommentStyle::Block,
                loc: (1, 1).into()
            }][..],
            &[Comment {
                text: "e",
                style: CommentStyle::Block,
                loc: (1, 37).into()
            }][..]
        )
    );
    let_expect!(Expr::Nested(Node(nested, id)) = *nested);
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: "w",
                style: CommentStyle::Block,
                loc: (1, 8).into()
            }][..],
            &[Comment {
                text: "z",
                style: CommentStyle::Block,
                loc: (1, 30).into()
            }][..]
        )
    );

    let_expect!(Expr::Value(Node(Value::Integer("1"), id)) = *nested);
    assert_eq!(
        meta.comments(id),
        (
            &[Comment {
                text: "x",
                style: CommentStyle::Block,
                loc: (1, 15).into()
            }][..],
            &[Comment {
                text: "y",
                style: CommentStyle::Block,
                loc: (1, 23).into()
            }][..]
        )
    );
}

#[test]
#[rustfmt::skip]
fn test_comments_on_function_calls_0() {
    let (expr, meta) = parse_expr::<DefaultTracker>("/*a*/ foo /*b*/ (/*c*/)/*e*/")
        .expect("invalid source");
    assert_eq!(expr.literal().to_string(), "foo()");
    assert_eq!(expr.literal().with(&meta).to_string(), "/*a*/ foo /*b*/ ( /*c*/ ) /*e*/");
}

#[test]
#[rustfmt::skip]
fn test_comments_on_function_calls_1() {
    let (expr, meta) = parse_expr::<DefaultTracker>("foo /*b*/ (1 /*c*/)/*e*/")
        .expect("invalid source");
    assert_eq!(expr.literal().to_string(), "foo(1)");
    assert_eq!(expr.literal().with(&meta).to_string(), "foo /*b*/ (1 /*c*/ ) /*e*/");
}

#[test]
#[rustfmt::skip]
fn test_comments_on_function_calls_2() {
    let (expr, meta) = parse_expr::<DefaultTracker>("foo /*b*/ (1 /*c*/, /*e*/ 2)")
        .expect("invalid source");
    assert_eq!(expr.literal().to_string(), "foo(1, 2)");
    assert_eq!(expr.literal().with(&meta).to_string(), "foo /*b*/ (1 /*c*/ , /*e*/ 2)");
}

#[rustfmt::skip]
#[test]
fn test_comments_on_function_calls_3() {
    assert_sql!(
        expr("foo( /*b*/ bar /*x*/ (1 /*c*/, /*e*/ 2) /*f*/, /*g*/ /*h*/ 3 /*i*/, quux(/*j*/ /*k*/) /*l*/) /*m*/"),
        "foo(bar(1, 2), 3, quux())"
    );
    assert_sql!(
        expr("foo( /*b*/ bar /*x*/ (1 /*c*/, /*e*/ 2) /*f*/, /*g*/ /*h*/ 3 /*i*/, quux(/*j*/ /*k*/) /*l*/) /*m*/"),
        with_meta("foo( /*b*/ bar /*x*/ (1 /*c*/ , /*e*/ 2) /*f*/ , /*g*/ /*h*/ 3 /*i*/ , quux( /*j*/ /*k*/ ) /*l*/ ) /*m*/")
    );
}