extern crate gluon_base as base;
extern crate gluon_parser as parser;
#[macro_use]
extern crate pretty_assertions;

#[macro_use]
mod support;

use crate::support::*;

use crate::base::{
    ast::*,
    metadata::*,
    mk_ast_arena,
    pos::{self, BytePos, Span, Spanned},
    types::{Alias, Field, Type, TypeContext},
};

use crate::parser::ReplLine;

test_parse! {
    dangling_in,
r#"
let x = 1
in

let y = 2
y
"#,
    |arena| let_(arena, "x", int(1), let_(arena, "y", int(2), id("y")))
}

test_parse! {
    expression1,
    "2 * 3 + 4",
    |arena| binop(arena, binop(arena, int(2), "*", int(3)), "+", int(4))
}

test_parse! {
    expression2,
    r#"\x y -> x + y"#,
    |arena| lambda(arena,
        "",
        vec![intern("x"), intern("y")],
        binop(arena, id("x"), "+", id("y")),
    )
}

test_parse! {
    expression3,
    r#"type Test = Int in 0"#,
    |arena| type_decl(arena, intern("Test"), vec![], typ(arena, "Int"), int(0))
}

test_parse! {
    application,
    "let f = \\x y -> x + y in f 1 2",
    |arena| let_(arena,
        "f",
        lambda(arena,
            "",
            vec![intern("x"), intern("y")],
            binop(arena, id("x"), "+", id("y")),
        ),
        app(arena, id("f"), vec![int(1), int(2)]),
    )
}

test_parse! {
    nested_application,
    "f (g 1 2)",
    |arena| app(arena,
        id("f"),
        vec![no_loc(Expr::Tuple {
            typ: Type::hole(),
            elems: arena.alloc_extend(vec![app(arena, id("g"), vec![int(1), int(2)])]),
        })]
    ),
}

test_parse! {
if_else_test,
    "if True then 1 else 0",
    |arena| if_else(arena, id("True"), int(1), int(0))
}

#[test]
fn let_type_decl() {
    let _ = ::env_logger::try_init();
    let e = parse_clear_span!("let f: Int = \\x y -> x + y in f 1 2");
    mk_ast_arena!(arena);
    let arena = (*arena).borrow();
    match &e.expr().value {
        Expr::LetBindings(bind, _) => assert_eq!(bind[0].typ, Some(typ(arena, "Int"))),
        _ => assert!(false),
    }
}

test_parse! {
    let_args,
    "let f x y = y in f 2",
    |arena| let_a(arena, "f", &["x", "y"], id("y"), app(arena, id("f"), vec![int(2)]))
}

test_parse! {
type_decl_record,
    "type Test = { x: Int, y: {} } in 1",
    |mut arena| {
        let record = arena.record(
            Default::default(),
            arena.alloc_extend(
                vec![
                    Field::new(intern("x").into(), typ(arena, "Int")),
                    Field::new(intern("y").into(), arena.clone().unit()),
                ]
            ),
        );
        type_decl(arena, intern("Test"), vec![], record, int(1))
    }
}

test_parse! {
    type_mutually_recursive,
    r#"
    rec
    /// Test
    type Test = | Test Int
    #[a]
    type Test2 = { x: Int, y: {} }
    in 1"#,
    |mut arena| {
        let test = arena.clone().variant(arena.alloc_extend(vec![Field::ctor_with(&mut arena.clone(), intern("Test").into(), vec![typ(arena, "Int")])]));
        let test2 = arena.record(
            Default::default(),
            arena.alloc_extend(vec![
                Field::new(intern("x").into(), typ(arena, "Int")),
                Field::new(intern("y").into(), arena.clone().unit()),
            ]),
        );
        let binds = vec![
            TypeBinding {
                metadata: BaseMetadata { metadata: Some(arena.alloc(line_comment("Test"))) },
                name: no_loc(intern("Test")),
                alias: alias(arena, intern("Test"), Vec::new(), test),
                finalized_alias: None,
            },
            TypeBinding {
                metadata: BaseMetadata {
                        metadata: Some(arena.alloc(Metadata {
                    attributes: vec![Attribute {
                        name: "a".into(),
                        arguments: None,
                    }],
                    ..Metadata::default()
                }) ) },
                name: no_loc(intern("Test2")),
                alias: alias(arena, intern("Test2"), Vec::new(), test2),
                finalized_alias: None,
            },
        ];
        type_decls(arena, binds, int(1))
    }
}

test_parse! {
type_decl_projection,
    "type Test = x.y.Z in 1",
    |arena| {
        let record = arena.clone().projection(["x", "y", "Z"].iter().map(|s| intern(s)).collect());
        type_decl(arena, intern("Test"), vec![], record, int(1))
    }
}

#[test]
fn tuple_type() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let _: (Int, String, Option Int) = (1, "", None)
        1"#;
    parse_new!(expr);
}

test_parse! {
    field_access_test,
    "{ x = 1 }.x",
    |arena| field_access(arena, record(arena, vec![(intern("x"), Some(int(1)))]), "x")
}

test_parse! {
    builtin_op,
    "x #Int+ 1",
    |arena| binop(arena, id("x"), "#Int+", int(1))
}

test_parse! {
    op_identifier,
    "let (==) = \\x y -> x #Int== y in (==) 1 2",
    |arena| {
        let_(arena,
            "==",
            lambda(arena,
                "",
                vec![intern("x"), intern("y")],
                binop(arena, id("x"), "#Int==", id("y")),
            ),
            app(arena, id("=="), vec![int(1), int(2)]),
        )
    }
}

test_parse! {
    variant_type,
    "type Option a = | None | Some a in Some 1",
    |arena| type_decl(arena,
            intern("Option"),
            vec![generic("a")],
            arena.clone().variant(arena.alloc_extend(vec![
                Field::ctor_with(&mut arena.clone(), intern("None").into(), vec![]),
                Field::ctor_with(&mut arena.clone(), intern("Some").into(), vec![typ(arena, "a")]),
            ])),
            app(arena, id("Some"), vec![int(1)]),
        )
}

test_parse! {
    case_expr,
    r#"
    match None with
    | Some x -> x
    | None -> 0"#,
    |arena| case(arena,
            id("None"),
            vec![
                (
                    Pattern::Constructor(
                        TypedIdent::new(intern("Some")),
                        arena.alloc_extend(vec![no_loc(Pattern::Ident(TypedIdent::new(intern("x"))))]),
                    ),
                    id("x"),
                ),
                (
                    Pattern::Constructor(TypedIdent::new(intern("None")), &mut []),
                    int(0),
                ),
            ],
        )
}

test_parse! {
    array_expr,
    "[1, a]",
    |arena| array(arena, vec![int(1), id("a")])
}

test_parse! {
    operator_expr,
    "test + 1 * 23 #Int- test",
    |arena| binop(arena,
            binop(arena, id("test"), "+", binop(arena, int(1), "*", int(23))),
            "#Int-",
            id("test"),
        )
}

test_parse! {
    record_trailing_comma,
    "{ y, x = z,}",
    |arena| record(arena, vec![("y".into(), None), ("x".into(), Some(id("z")))])
}

test_parse! {
    array_trailing_comma,
    "[y, 1, 2,]",
    |arena| array(arena, vec![id("y"), int(1), int(2)])
}

test_parse! {
    record_pattern,
    "match x with | { y, x = z } -> z",
    |arena| {
        let pattern = Pattern::Record {
            typ: Type::hole(),
            fields: arena.alloc_extend(vec![
                PatternField::Value {
                    name: no_loc(intern("y")),
                    value: None,
                },
                PatternField::Value {
                    name: no_loc(intern("x")),
                    value: Some(no_loc(Pattern::Ident(TypedIdent::new(intern("z"))))),
                },
            ]),
            implicit_import: None,
        };
        case(arena, id("x"), vec![(pattern, id("z"))])
    }
}

test_parse! {
    let_pattern,
    "let {x, y} = test in x",
    |arena| no_loc(Expr::let_binding(
            arena,
            ValueBinding {
                metadata: BaseMetadata::default(),
                name: no_loc(Pattern::Record {
                    typ: Type::hole(),
                    fields: arena.alloc_extend(vec![
                        PatternField::Value {
                            name: no_loc(intern("x")),
                            value: None,
                        },
                        PatternField::Value {
                            name: no_loc(intern("y")),
                            value: None,
                        },
                    ]),
                    implicit_import: None,
                }),
                typ: None,
                resolved_type: Type::hole(),
                args: &mut [],
                expr: id("test"),
            },
            id("x"),
        ))
}

test_parse! {
    nested_pattern,
    "match x with | { y = Some x } -> z",
    |arena| {
        let nested = no_loc(Pattern::Constructor(
            TypedIdent::new(intern("Some")),
            arena.alloc_extend(vec![no_loc(Pattern::Ident(TypedIdent::new(intern("x"))))]),
        ));
        let pattern = Pattern::Record {
            typ: Type::hole(),
            fields: arena.alloc_extend(vec![PatternField::Value {
                name: no_loc(intern("y")),
                value: Some(nested),
            }]),
            implicit_import: None,
        };
        case(arena, id("x"), vec![(pattern, id("z"))])
    }
}

test_parse! {
    nested_pattern_parens,
    "match x with | (Some (Some z)) -> z",
    |arena| {
        let inner_pattern = no_loc(Pattern::Constructor(
            TypedIdent::new(intern("Some")),
            arena.alloc_extend(vec![no_loc(Pattern::Ident(TypedIdent::new(intern("z"))))]),
        ));
        let pattern = Pattern::Constructor(TypedIdent::new(intern("Some")), arena.alloc_extend(vec![inner_pattern]));
        case(arena, id("x"), vec![(pattern, id("z"))])
    }
}

test_parse! {
    raw_string_literal,
    r##"  r"asdf" "##,
    |_arena| {
        string("asdf")
    }
}

#[test]
fn span_identifier() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!("test");
    assert_eq!(e.expr().span, Span::new(BytePos::from(0), BytePos::from(4)));
}

#[test]
fn span_integer() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!("1234");
    assert_eq!(e.expr().span, Span::new(BytePos::from(0), BytePos::from(4)));
}

#[test]
fn span_string_literal() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!(r#" "test" "#);
    assert_eq!(e.expr().span, Span::new(BytePos::from(1), BytePos::from(7)));
}

#[test]
fn span_app() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!(r#" f 123 "asd""#);
    assert_eq!(
        e.expr().span,
        Span::new(BytePos::from(1), BytePos::from(12))
    );
}

#[test]
fn span_match() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!(
        r#"
match False with
    | True -> "asd"
    | False -> ""
"#
    );
    assert_eq!(
        e.expr().span,
        Span::new(BytePos::from(1), BytePos::from(55))
    );
}

#[test]
fn span_if_else() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!(
        r#"
if True then
    1
else
    123.45
"#
    );
    assert_eq!(
        e.expr().span,
        Span::new(BytePos::from(1), BytePos::from(35))
    );
}

#[test]
fn span_byte() {
    let _ = ::env_logger::try_init();

    let e = parse_zero_index!(r#"124b"#);
    assert_eq!(e.expr().span, Span::new(BytePos::from(0), BytePos::from(4)));
}

#[test]
fn span_field_access() {
    let _ = ::env_logger::try_init();
    let expr = parse_zero_index!("record.x");
    assert_eq!(
        expr.expr().span,
        Span::new(BytePos::from(0), BytePos::from(8))
    );
    match expr.expr().value {
        Expr::Projection(ref e, _, _) => {
            assert_eq!(e.span, Span::new(BytePos::from(0), BytePos::from(6)));
        }
        _ => panic!(),
    }
}

#[test]
fn comment_on_let() {
    let _ = ::env_logger::try_init();
    let text = r#"
/// The identity function
let id x = x
id
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        no_loc(Expr::LetBindings(
            ValueBindings::Plain(arena.alloc(ValueBinding {
                metadata: BaseMetadata {
                    metadata: Some(arena.alloc(Metadata {
                        comment: Some(Comment {
                            typ: CommentType::Line,
                            content: "The identity function".into(),
                        }),
                        ..Metadata::default()
                    }))
                },
                name: no_loc(Pattern::Ident(TypedIdent::new(intern("id")))),
                typ: None,
                resolved_type: Type::hole(),
                args: arena.alloc_extend(vec![Argument::explicit(no_loc(TypedIdent::new(
                    intern("x")
                )))]),
                expr: id("x"),
            })),
            arena.alloc(id("id")),
        ),)
    );
}

#[test]
fn comment_on_rec_let() {
    let _ = ::env_logger::try_init();
    let text = r#"
rec
let id x = x
/// The identity function
let id2 y = y
id
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        no_loc(Expr::LetBindings(
            ValueBindings::Recursive(arena.alloc_extend(vec![
                ValueBinding {
                    metadata: BaseMetadata::default(),
                    name: no_loc(Pattern::Ident(TypedIdent::new(intern("id")))),
                    typ: None,
                    resolved_type: Type::hole(),
                    args: arena.alloc_extend(vec![Argument::explicit(no_loc(TypedIdent::new(
                        intern("x")
                    )))]),
                    expr: id("x"),
                },
                ValueBinding {
                    metadata: BaseMetadata {
                        metadata: Some(arena.alloc(Metadata {
                            comment: Some(Comment {
                                typ: CommentType::Line,
                                content: "The identity function".into(),
                            }),
                            ..Metadata::default()
                        }))
                    },
                    name: no_loc(Pattern::Ident(TypedIdent::new(intern("id2")))),
                    typ: None,
                    resolved_type: Type::hole(),
                    args: arena.alloc_extend(vec![Argument::explicit(no_loc(TypedIdent::new(
                        intern("y")
                    )))]),
                    expr: id("y"),
                },
            ])),
            arena.alloc(id("id")),
        ))
    );
}

#[test]
fn comment_on_type() {
    let _ = ::env_logger::try_init();
    let text = r#"
/** Test type */
type Test = Int
id
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    let arena = (*arena).borrow();
    assert_eq!(
        *e.expr(),
        type_decls(
            arena,
            vec![TypeBinding {
                metadata: BaseMetadata {
                    metadata: Some(arena.alloc(Metadata {
                        comment: Some(Comment {
                            typ: CommentType::Block,
                            content: "Test type".into(),
                        }),
                        ..Metadata::default()
                    }))
                },
                name: no_loc(intern("Test")),
                alias: alias(arena, intern("Test"), Vec::new(), typ(arena, "Int")),
                finalized_alias: None,
            }],
            id("id"),
        )
    );
}

#[test]
fn comment_after_integer() {
    let _ = ::env_logger::try_init();
    let text = r#"
let x = 1

/** Test type */
type Test = Int
id
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    let arena = arena.borrow();
    assert_eq!(
        *e.expr(),
        let_a(
            arena,
            "x",
            &[],
            int(1),
            type_decls(
                arena,
                vec![TypeBinding {
                    metadata: BaseMetadata {
                        metadata: Some(arena.alloc(Metadata {
                            comment: Some(Comment {
                                typ: CommentType::Block,
                                content: "Test type".into(),
                            }),
                            ..Metadata::default()
                        }))
                    },
                    name: no_loc(intern("Test")),
                    alias: alias(arena, intern("Test"), Vec::new(), typ(arena, "Int")),
                    finalized_alias: None,
                }],
                id("id"),
            ),
        )
    );
}

#[test]
fn merge_line_comments() {
    let _ = ::env_logger::try_init();
    let text = r#"
/// Merge
/// consecutive
/// line comments.
type Test = Int
id
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    let arena = (*arena).borrow();
    assert_eq!(
        *e.expr(),
        type_decls(
            arena,
            vec![TypeBinding {
                metadata: BaseMetadata {
                    metadata: Some(arena.alloc(Metadata {
                        comment: Some(Comment {
                            typ: CommentType::Line,
                            content: "Merge\nconsecutive\nline comments.".into(),
                        }),
                        ..Metadata::default()
                    }))
                },
                name: no_loc(intern("Test")),
                alias: alias(arena, intern("Test"), Vec::new(), typ(arena, "Int")),
                finalized_alias: None,
            }],
            id("id"),
        )
    );
}

#[test]
fn partial_field_access_simple() {
    let _ = ::env_logger::try_init();
    let text = r#"test."#;
    let e = parse(text);
    assert!(e.is_err());
    let e = clear_span(e.unwrap_err().0.unwrap());
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        no_loc(Expr::Projection(
            arena.alloc(id("test")),
            intern(""),
            Type::hole()
        ),)
    );
}

#[test]
fn partial_field_access_in_block() {
    let _ = ::env_logger::try_init();
    let text = r#"
test.
test
"#;
    let e = parse(text);
    assert!(e.is_err());
    let e = clear_span(e.unwrap_err().0.unwrap());
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        no_loc(Expr::Do(arena.alloc(Do {
            id: None,
            typ: None,
            bound: arena.alloc(Spanned {
                span: Span::new(BytePos::from(0), BytePos::from(0)),
                value: Expr::Projection(arena.alloc(id("test")), intern(""), Type::hole()),
            }),
            body: arena.alloc(id("test")),
            flat_map_id: None,
        })))
    );
}

#[test]
fn function_operator_application() {
    let _ = ::env_logger::try_init();
    let text = r#"
let x: ((->) Int Int) = x
x
"#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    let arena = (*arena).borrow();
    assert_eq!(
        *e.expr(),
        no_loc(Expr::LetBindings(
            ValueBindings::Plain(arena.alloc(ValueBinding {
                metadata: BaseMetadata::default(),
                name: no_loc(Pattern::Ident(TypedIdent::new(intern("x")))),
                typ: Some(arena.clone().app(
                    typ(arena, "->"),
                    arena.alloc_extend(vec![typ(arena, "Int"), typ(arena, "Int")])
                )),
                resolved_type: Type::hole(),
                args: &mut [],
                expr: id("x"),
            })),
            arena.alloc(id("x")),
        ),)
    );
}

#[test]
fn quote_in_identifier() {
    let _ = ::env_logger::try_init();
    let e = parse_clear_span!("let f' = \\x y -> x + y in f' 1 2");
    mk_ast_arena!(arena);
    let arena = arena.borrow();
    let a = let_(
        arena,
        "f'",
        lambda(
            arena,
            "",
            vec![intern("x"), intern("y")],
            binop(arena, id("x"), "+", id("y")),
        ),
        app(arena, id("f'"), vec![int(1), int(2)]),
    );
    assert_eq!(*e.expr(), a);
}

#[test]
fn record_type_field() {
    let _ = ::env_logger::try_init();
    let text = r"{ Test, x }";
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        record_a(
            arena.borrow(),
            vec![("Test".into(), None)],
            vec![("x".into(), None)]
        )
    )
}

#[test]
fn parse_macro() {
    let _ = ::env_logger::try_init();
    let text = r#" import! "#;
    let e = parse_clear_span!(text);
    assert_eq!(*e.expr(), id("import!"));
}

#[test]
fn doc_comment_on_record_field() {
    let _ = ::env_logger::try_init();
    let text = r"{ /** test*/ Test,
    /// x binding
    x = 1 }";
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        no_loc(Expr::Record {
            typ: Type::hole(),
            types: arena.alloc_extend(vec![ExprField {
                metadata: BaseMetadata {
                    metadata: Some(arena.alloc(Metadata {
                        comment: Some(Comment {
                            typ: CommentType::Block,
                            content: "test".into(),
                        }),
                        ..Metadata::default()
                    }))
                },
                name: no_loc("Test".into()),
                value: None,
            }]),
            exprs: arena.alloc_extend(vec![ExprField {
                metadata: BaseMetadata {
                    metadata: Some(arena.alloc(Metadata {
                        comment: Some(Comment {
                            typ: CommentType::Line,
                            content: "x binding".into(),
                        }),
                        ..Metadata::default()
                    }))
                },
                name: no_loc("x".into()),
                value: Some(int(1)),
            }]),
            base: None,
        })
    )
}

#[test]
fn shebang_at_top_is_ignored() {
    let _ = ::env_logger::try_init();
    let text = r"#!/bin/gluon
{ Test, x }";
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    assert_eq!(
        *e.expr(),
        record_a(
            arena.borrow(),
            vec![("Test".into(), None)],
            vec![("x".into(), None)]
        )
    )
}

#[test]
fn do_in_parens() {
    let _ = ::env_logger::try_init();
    let text = r"
        scope_state (
            seq add_args
            eval_exprs
        )
    ";
    parse_clear_span!(text);
}

#[test]
fn parse_repl_line() {
    let _ = ::env_logger::try_init();

    let mut module = MockEnv::new();

    let line = "let x = test";
    mk_ast_arena!(arena);
    match parser::parse_partial_repl_line(arena.borrow(), &mut module, line) {
        Ok(x) => assert_eq!(
            x,
            Some(ReplLine::Let(arena.alloc(ValueBinding {
                metadata: BaseMetadata::default(),
                name: pos::spanned2(
                    // Add one to each position since codespan return 1-indexed positions
                    5.into(),
                    6.into(),
                    Pattern::Ident(TypedIdent::new(intern("x")))
                ),
                typ: None,
                resolved_type: Type::hole(),
                args: &mut [],
                expr: pos::spanned2(
                    9.into(),
                    13.into(),
                    Expr::Ident(TypedIdent::new(intern("test")))
                ),
            })))
        ),
        Err((_, err)) => panic!("{}", err),
    }
}

#[test]
fn alias_in_record_type() {
    let _ = ::env_logger::try_init();

    let text = r#"
        type Test = { MyInt }
        1
        "#;
    let e = parse_clear_span!(text);
    mk_ast_arena!(arena);
    let arena = (*arena).borrow();
    assert_eq!(
        *e.expr(),
        no_loc(Expr::TypeBindings(
            arena.alloc_extend(vec![TypeBinding {
                metadata: BaseMetadata::default(),
                name: no_loc(intern("Test")),
                alias: alias(
                    arena,
                    intern("Test"),
                    Vec::new(),
                    arena.clone().record(
                        arena.alloc_extend(vec![Field {
                            name: intern("MyInt").into(),
                            typ: Alias::new_with(
                                &mut arena.clone(),
                                intern("MyInt"),
                                &mut [],
                                arena.clone().hole()
                            ),
                        }]),
                        Default::default(),
                    ),
                ),
                finalized_alias: None,
            }]),
            arena.alloc(int(1))
        ),)
    )
}

#[test]
fn let_then_doc_comment() {
    let _ = ::env_logger::try_init();
    let text = r"
let map2 = 1

/// Maps over three actions
let map3 = 2
()
    ";
    parse_clear_span!(text);
}

#[test]
fn rec_let_indentation() {
    let _ = ::env_logger::try_init();
    let text = r#"
rec let id x =
    let y = x
    y
id
"#;
    parse_clear_span!(text);
}

#[test]
fn rec_let_with_doc_comment() {
    let _ = ::env_logger::try_init();
    let text = r#"
let x = { }

/// a
rec let id x =
    let y = x
    y
id
"#;
    parse_clear_span!(text);
}

#[test]
fn rec_let_rec_let() {
    let _ = ::env_logger::try_init();
    let text = r#"
rec let x = 0

rec let y = 2

1
"#;
    parse_clear_span!(text);
}

#[test]
fn rec_let_doc_rec_let() {
    let _ = ::env_logger::try_init();
    let text = r#"
rec let x = 0

/// y
rec let y = 2

1
"#;
    parse_clear_span!(text);
}

#[test]
fn gadt() {
    let _ = ::env_logger::try_init();
    let text = r#"
type Expr a =
    | Int : Int -> Expr Int
    | Add : Expr Int -> Expr Int -> Expr Int
    | If : Expr Bool -> Expr a -> Expr a -> Expr a


1
"#;
    parse_clear_span!(text);
}

#[test]
fn application_does_not_get_eof_as_end() {
    let _ = ::env_logger::try_init();
    let text = r#"
f 1 // abc
"#;
    assert_eq!(
        parse_new!(text).expr().span,
        pos::Span::new(2.into(), 5.into())
    );
}
