roc/tests/test_parse.rs
Richard Feldman 8985b4bec7 Add lists
2019-08-28 00:04:08 -04:00

1449 lines
39 KiB
Rust

#[macro_use] extern crate pretty_assertions;
#[macro_use] extern crate indoc;
extern crate combine;
extern crate roc;
mod helpers;
#[cfg(test)]
mod test_parse {
use roc::expr::Expr::*;
use roc::expr::Pattern::*;
use roc::expr::{Expr, Ident, VariantName};
use roc::operator::Operator::*;
use roc::region::Located;
use roc::parse_state::IndentablePosition;
use combine::Parser;
use combine::easy;
use combine::stream::state::State;
use helpers::{loc, loc_box, zero_loc_expr, parse_without_loc, standalone_expr};
// PARSE TEST HELPERS
fn easy_parse_standalone(actual_str: &str) -> Result<(Expr, &str), easy::Errors<char, &str, IndentablePosition>> {
let parse_state = State::with_positioner(actual_str, IndentablePosition::default());
standalone_expr().easy_parse(parse_state).map(|(expr, state)| (expr, state.input))
}
fn assert_fully_parses(actual_str: &str, expected_expr: Expr) {
assert_eq!(
Ok((expected_expr, "".to_string())),
parse_without_loc(actual_str)
);
}
fn var(name: &str) -> Expr {
Var(Ident::Unqualified(name.to_string()))
}
fn call_by_name(name: &str, args: Vec<Located<Expr>>) -> Expr {
Apply(
loc_box(Var(raw(name))),
args.into_iter().map(|loc_expr| loc(zero_loc_expr(loc_expr.value))).collect()
)
}
// LIST LITERALS
#[test]
fn empty_list() {
assert_fully_parses(
indoc!(r#"
[]
"#),
EmptyList
);
}
#[test]
fn single_list() {
assert_fully_parses(
indoc!(r#"
[ 1 ]
"#),
List(vec![loc(Int(1))])
);
}
#[test]
fn multi_list() {
assert_fully_parses(
indoc!(r#"
[1 , 2,3]
"#),
List(vec![loc(Int(1)), loc(Int(2)), loc(Int(3))])
);
}
// STRING LITERALS
fn expect_parsed_str<'a>(expected_str: &'a str, actual_str: &'a str) {
assert_fully_parses(actual_str, Expr::Str(expected_str.to_string()))
}
fn expect_parsed_str_error<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
#[test]
fn empty_string() {
assert_fully_parses(
indoc!(r#"
""
"#),
EmptyStr);
}
#[test]
fn string_without_escape() {
expect_parsed_str("a", "\"a\"");
expect_parsed_str("ab", "\"ab\"");
expect_parsed_str("abc", "\"abc\"");
expect_parsed_str("123", "\"123\"");
expect_parsed_str("abc123", "\"abc123\"");
expect_parsed_str("123abc", "\"123abc\"");
expect_parsed_str("123 abc 456 def", "\"123 abc 456 def\"");
}
#[test]
fn string_with_special_escapes() {
expect_parsed_str("x\\x", "\"x\\\\x\"");
expect_parsed_str("x\"x", "\"x\\\"x\"");
expect_parsed_str("x\tx", "\"x\\tx\"");
expect_parsed_str("x\rx", "\"x\\rx\"");
expect_parsed_str("x\nx", "\"x\\nx\"");
}
#[test]
fn string_with_escaped_interpolation() {
assert_fully_parses(
// This should NOT be string interpolation, because of the \\
indoc!(r#"
"abcd\\(efg)hij"
"#),
Expr::Str(r#"abcd\(efg)hij"#.to_string()),
);
}
fn raw(string: &str) -> Ident {
Ident::Unqualified(string.to_string())
}
#[test]
fn string_with_interpolation_at_end() {
assert_fully_parses(
indoc!(r#"
"abcd\(efg)"
"#),
InterpolatedStr(
vec![("abcd".to_string(), loc(raw("efg")))],
"".to_string()
),
);
}
#[test]
fn string_with_interpolation() {
assert_fully_parses(
indoc!(r#"
"abcd\(efg)hij"
"#),
InterpolatedStr(
vec![("abcd".to_string(), loc(raw("efg")))],
"hij".to_string()
),
);
}
#[test]
fn string_with_single_qoute() {
// This shoud NOT be escaped in a string.
expect_parsed_str("x'x", r#""x'x""#);
}
#[test]
fn string_with_valid_unicode_escapes() {
expect_parsed_str("x\u{00A0}x", r#""x\u{00A0}x""#);
expect_parsed_str("x\u{101010}x", r#""x\u{101010}x""#);
}
#[test]
fn string_with_invalid_unicode_escapes() {
// Should be too big - max size should be 10FFFF.
// (Rust has this restriction. I assume it's a good idea.)
expect_parsed_str_error(r#""x\u{110000}x""#);
// No digits specified
expect_parsed_str_error(r#""x\u{}x""#);
// No opening curly brace
expect_parsed_str_error(r#""x\u}x""#);
// No closing curly brace
expect_parsed_str_error(r#""x\u{x""#);
// No curly braces
expect_parsed_str_error(r#""x\ux""#);
}
// // CHAR LITERALS
fn expect_parsed_char<'a>(expected: char, actual_str: &'a str) {
assert_fully_parses(actual_str, Char(expected))
}
fn expect_parsed_char_error<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
#[test]
fn empty_char() {
if easy_parse_standalone("''").is_ok() {
panic!("Expected parse error");
}
}
#[test]
fn char_without_escape() {
expect_parsed_char('a', "'a'");
expect_parsed_char('1', "'1'");
expect_parsed_char(' ', "' '");
}
#[test]
fn char_with_special_escapes() {
expect_parsed_char('\\', r#"'\\'"#);
expect_parsed_char('\'', r#"'\''"#);
expect_parsed_char('\t', r#"'\t'"#);
expect_parsed_char('\r', r#"'\r'"#);
expect_parsed_char('\n', r#"'\n'"#);
}
#[test]
fn char_with_double_qoute() {
// This shoud NOT be escaped in a char.
expect_parsed_char('"', r#"'"'"#);
}
#[test]
fn char_with_unicode_escapes() {
expect_parsed_char('\u{00A0}', r#"'\u{00A0}'"#);
expect_parsed_char('\u{101010}', r#"'\u{101010}'"#);
}
#[test]
fn char_with_invalid_unicode_escapes() {
// Should be too big - max size should be 10FFFF.
// (Rust has this rechariction. I assume it's a good idea.)
expect_parsed_char_error(r#""x\u{110000}x""#);
// No digits specified
expect_parsed_char_error(r#""x\u{}x""#);
// No opening curly brace
expect_parsed_char_error(r#""x\u}x""#);
// No closing curly brace
expect_parsed_char_error(r#""x\u{x""#);
// No curly braces
expect_parsed_char_error(r#""x\ux""#);
}
// // NUMBER LITERALS
fn expect_parsed_approx<'a>(expected: f64, actual: &str) {
assert_eq!(Ok((Approx(expected), "".to_string())), parse_without_loc(actual));
}
fn expect_parsed_int<'a>(expected: i64, actual: &str) {
assert_eq!(Ok((Int(expected), "".to_string())), parse_without_loc(actual));
}
fn expect_parsed_ratio<'a>(expected_numerator: i64, expected_denominator: i64, actual: &str) {
assert_eq!(Ok((Frac(expected_numerator, expected_denominator), "".to_string())), parse_without_loc(actual));
}
#[test]
fn positive_int() {
expect_parsed_int(1234, "1234");
}
#[test]
fn negative_int() {
expect_parsed_int(-1234, "-1234");
}
#[test]
fn positive_approx() {
expect_parsed_approx(123.4, "~123.4");
}
#[test]
fn negative_approx() {
expect_parsed_approx(-123.4, "~-123.4");
}
#[test]
fn positive_frac() {
expect_parsed_ratio(12345, 100, "123.45");
expect_parsed_ratio(4200, 100, "42.00");
}
#[test]
fn negative_ratio() {
expect_parsed_ratio(-1234567, 1000, "-1234.567");
expect_parsed_ratio(-1920, 10, "-192.0");
}
#[test]
fn ints_with_underscores() {
expect_parsed_int(987654321, "987_6_5_432_1");
expect_parsed_int(-1234567890, "-1_234_567_890");
}
#[test]
fn fracs_with_spaces() {
expect_parsed_ratio(-1234567, 1000, "-1_23_4.567");
expect_parsed_ratio(-1920, 10, "-19_2.0");
expect_parsed_ratio(12345, 100, "1_2_3.45");
expect_parsed_ratio(4200, 100, "4_2.00");
}
#[test]
fn single_operator_with_var() {
assert_eq!(
// It's important that this isn't mistaken for
// a declaration like (x = 1)
parse_without_loc("x == 1"),
Ok((Operator(
loc_box(var("x")),
loc(Equals),
loc_box(Int(1))
), "".to_string()))
);
}
#[test]
fn comparison_operators() {
assert_eq!(
parse_without_loc("x >= 0"),
Ok((Operator(
loc_box(var("x")),
loc(GreaterThanOrEq),
loc_box(Int(0))
), "".to_string()))
);
assert_eq!(
parse_without_loc("x > 0"),
Ok((Operator(
loc_box(var("x")),
loc(GreaterThan),
loc_box(Int(0))
), "".to_string()))
);
assert_eq!(
parse_without_loc("x <= 0"),
Ok((Operator(
loc_box(var("x")),
loc(LessThanOrEq),
loc_box(Int(0))
), "".to_string()))
);
assert_eq!(
parse_without_loc("x < 0"),
Ok((Operator(
loc_box(var("x")),
loc(LessThan),
loc_box(Int(0))
), "".to_string()))
);
}
#[test]
fn single_operator() {
match parse_without_loc("1234 + 567") {
Ok((Operator(v1, op, v2), string)) => {
assert_eq!(string, "".to_string());
assert_eq!((*v1).value, Int(1234));
assert_eq!(op.value, Plus);
assert_eq!((*v2).value, Int(567));
},
_ => panic!("Expression didn't parse"),
}
}
#[test]
fn multiple_operators() {
assert_eq!(parse_without_loc("1 + 2 ~/ 3"),
Ok((Operator(
loc_box(Int(1)),
loc(Plus),
loc_box(Operator(loc_box(Int(2)), loc(TildeSlash), loc_box(Int(3))))
), "".to_string()))
);
}
#[test]
fn operators_with_parens() {
assert_eq!(parse_without_loc("(1 + 2)"),
Ok((Operator(
loc_box(Int(1)),
loc(Plus),
loc_box(Int(2))
), "".to_string()))
);
assert_eq!(parse_without_loc("(1 - 2)"),
Ok((Operator(
loc_box(Int(1)),
loc(Minus),
loc_box(Int(2))
), "".to_string()))
);
assert_eq!(parse_without_loc("(1 + 2 * 3)"),
Ok((Operator(
loc_box(Int(1)),
loc(Plus),
loc_box(Operator(loc_box(Int(2)), loc(Star), loc_box(Int(3))))
), "".to_string()))
);
assert_eq!(parse_without_loc("1 + (2 * 3)"),
Ok((Operator(
loc_box(Int(1)),
loc(Plus),
loc_box(Operator(loc_box(Int(2)), loc(Star), loc_box(Int(3))))
), "".to_string()))
);
assert_eq!(parse_without_loc("(1 + 2) * 3"),
Ok((Operator(
loc_box(Operator(loc_box(Int(1)), loc(Plus), loc_box(Int(2)))),
loc(Star),
loc_box(Int(3)),
), "".to_string()))
);
}
// VAR
fn expect_parsed_var<'a>(expected_str: &'a str) {
assert_eq!(Ok((var(expected_str), "".to_string())), parse_without_loc(expected_str));
}
fn expect_parsed_var_error<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
#[test]
fn basic_var() {
expect_parsed_var("x");
expect_parsed_var("x2");
expect_parsed_var("foo");
expect_parsed_var("foo2furious");
}
#[test]
fn invalid_var() {
expect_parsed_var_error("5x");
expect_parsed_var_error("2foo2furious");
expect_parsed_var_error("2Foo2Furious");
}
#[test]
fn var_with_parens() {
assert_eq!(parse_without_loc("( x)"), Ok(( var("x"), "".to_string() )));
assert_eq!(parse_without_loc("(x )"), Ok(( var("x"), "".to_string() )));
assert_eq!(parse_without_loc("( x )"), Ok(( var("x"), "".to_string() )));
}
fn vname(name: &str) -> VariantName {
VariantName::Unqualified(name.to_string())
}
// APPLY
fn expect_parsed_apply<'a>(parse_str: &'a str, expr1: Expr, expr2: Expr) {
assert_eq!(
Ok((Apply(loc_box(expr1), vec![loc(expr2)]), "".to_string())),
parse_without_loc(parse_str)
);
}
fn expect_parsed_apply_error<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
#[test]
fn apply() {
expect_parsed_apply(
"(x) y",
var("x"),
var("y")
);
expect_parsed_apply(
"(x 5) y",
call_by_name("x", vec![loc(Int(5))]),
var("y")
);
expect_parsed_apply(
"(x 5) (y 6)",
call_by_name("x", vec![loc(Int(5))]),
call_by_name("y", vec![loc(Int(6))]),
);
expect_parsed_apply(
"(5) (6)",
Int(5),
Int(6)
);
}
#[test]
fn invalid_apply() {
expect_parsed_apply_error("(x 5)y");
}
// TODO write a bunch of parenthetical expression tests - try to repeat
// all of the above tests except with parens too!
// Also, verify them all with variable paren counts; ((foo)) should work.
// CLOSURE
#[test]
fn single_arg_closure() {
assert_fully_parses(
indoc!(r#"
\a -> b
"#),
Closure(
vec![loc(Identifier("a".to_string()))],
loc_box(var("b"))
)
);
}
#[test]
fn multi_arg_closure() {
assert_fully_parses(
indoc!(r#"
\a b -> c
"#),
Closure(
vec![loc(Identifier("a".to_string())), loc(Identifier("b".to_string()))],
loc_box(var("c"))
)
);
}
#[test]
fn assign_closure() {
assert_fully_parses(
indoc!(r#"
foo = \a b -> c
foo
"#),
Assign(
vec![(
loc(Identifier("foo".to_string())),
loc(Closure(
vec![loc(Identifier("a".to_string())), loc(Identifier("b".to_string()))],
loc_box(var("c"))
))
)],
loc_box(var("foo"))
)
);
}
#[test]
fn call_named_closure() {
assert_fully_parses(
indoc!(r#"
x = \a b -> 5
foo 1
"#),
Assign(
vec![(
loc(Identifier("x".to_string())),
loc(Closure(
vec![loc(Identifier("a".to_string())), loc(Identifier("b".to_string()))],
loc_box(Int(5))
))
)],
loc_box(call_by_name("foo", vec![loc(Int(1))]))
)
);
}
#[test]
fn multiple_assign_call_closure() {
assert_fully_parses(
indoc!(r#"
foo = \a b -> 7
bar = 5
baz 1
"#),
Assign(
vec![
(
loc(Identifier("foo".to_string())),
loc(Closure(
vec![loc(Identifier("a".to_string())), loc(Identifier("b".to_string()))],
loc_box(Int(7))
))
),
(
loc(Identifier("bar".to_string())),
loc(Int(5))
)
],
loc_box(call_by_name("baz", vec![loc(Int(1))]))
)
);
}
// FUNC
fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, args: Vec<Located<Expr>>) {
assert_eq!(
Ok((call_by_name(func_str, args), "".to_string())),
parse_without_loc(parse_str)
);
}
fn expect_parsed_func_syntax_problem<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
fn expect_parsed_func_error<'a>(actual_str: &'a str) {
assert!(
parse_without_loc(actual_str).is_err(),
"Expected parsing error"
);
}
#[test]
fn single_arg_func() {
expect_parsed_func("f 1", "f", vec![loc(Int(1))]);
expect_parsed_func("foo bar", "foo", vec![loc(var("bar"))]);
expect_parsed_func("foo \"hi\"", "foo", vec![loc(Str("hi".to_string()))]);
}
#[test]
fn multi_arg_func() {
expect_parsed_func("f 1 23 456", "f", vec![loc(Int(1)), loc(Int(23)), loc(Int(456))]);
expect_parsed_func("foo bar 'z'", "foo", vec![loc(var("bar")), loc(Char('z'))]);
expect_parsed_func("foo \"hi\" 1 blah", "foo", vec![loc(Str("hi".to_string())), loc(Int(1)), loc(var("blah"))]);
}
#[test]
fn multi_arg_func_with_parens() {
expect_parsed_func("f (1) 23 456", "f", vec![loc(Int(1)), loc(Int(23)), loc(Int(456))]);
expect_parsed_func("foo bar ('z')", "foo", vec![loc(var("bar")), loc(Char('z'))]);
expect_parsed_func("foo 1 (bar \"hi\") 2 (blah)", "foo", vec![
loc(Int(1)),
loc(call_by_name("bar", vec![loc(Str("hi".to_string()))])),
loc(Int(2)),
loc(var("blah"))
]);
}
#[test]
fn multiline_func() {
expect_parsed_func("f\n 1", "f", vec![loc(Int(1))]);
expect_parsed_func("foo bar\n 'z'", "foo", vec![loc(var("bar")), loc(Char('z'))]);
expect_parsed_func("foo \"hi\"\n 1\n blah", "foo", vec![loc(Str("hi".to_string())), loc(Int(1)), loc(var("blah"))]);
}
#[test]
fn func_with_operator() {
assert_eq!(
parse_without_loc("f 5 + 6"),
Ok(
(
Operator(
loc_box(
call_by_name("f",
vec![loc(Int(5))],
)
),
loc(Plus),
loc_box(Int(6))
),
"".to_string()
)
)
);
}
#[test]
fn func_with_operator_and_multiple_args() {
assert_eq!(
parse_without_loc("f 1 2 3 + 6"),
Ok(
(
Operator(
loc_box(
call_by_name("f",
vec![loc(Int(1)), loc(Int(2)), loc(Int(3))],
)
),
loc(Plus),
loc_box(Int(6))
),
"".to_string())
)
);
}
#[test]
fn invalid_func() {
expect_parsed_func_syntax_problem("1 f");
expect_parsed_func_syntax_problem("(1 f)");
}
// PARENS
#[test]
fn basic_parens() {
expect_parsed_int(1, "(1)");
expect_parsed_int(-2, "((-2))");
expect_parsed_str("a", "(\"a\")");
expect_parsed_str("abc", "((\"abc\"))");
expect_parsed_func("(f 1)", "f", vec![loc(Int(1))]);
expect_parsed_func("(foo bar)", "foo", vec![loc(var("bar"))]);
}
#[test]
fn parens_with_spaces() {
expect_parsed_func("(a 1 )", "a", vec![loc(Int(1))]);
expect_parsed_func("( b \"y\")", "b", vec![loc(Str("y".to_string()))]);
expect_parsed_func("( c \"z\" )", "c", vec![loc(Str("z".to_string()))]);
}
#[test]
fn invalid_parens_func() {
expect_parsed_func_error("(1 f");
expect_parsed_func_error("(f 1");
}
// CASE
#[test]
fn one_branch_case() {
assert_eq!(
parse_without_loc("case 1 when x then 2"),
Ok((
Case(
loc_box(Int(1)),
vec![( loc(Identifier("x".to_string())), loc(Int(2)) )]
),
"".to_string()
))
);
}
#[test]
fn case_matching_multi_arg_variant() {
assert_eq!(
parse_without_loc("case 1 when Foo bar baz then 2"),
Ok((
Case(
loc_box(Int(1)),
vec![(
loc(Variant(loc(vname("Foo")),
Some(vec![
loc(Identifier("bar".to_string())),
loc(Identifier("baz".to_string()))
])
)),
loc(Int(2)) )
]
),
"".to_string()
))
);
}
#[test]
fn two_branch_case() {
assert_eq!(
parse_without_loc("case 1 when x then 2 when y then 3"),
Ok((
Case(
loc_box(Int(1)),
vec![
( loc(Identifier("x".to_string())), loc(Int(2)) ),
( loc(Identifier("y".to_string())), loc(Int(3)) )
]
),
"".to_string()
))
);
}
#[test]
fn two_branch_case_with_two_newlines() {
assert_eq!(
parse_without_loc("case a\n\n when b then 1\n\n when\n c then 2"),
Ok((
Case(
loc_box(var("a")),
vec![
( loc(Identifier("b".to_string())), loc(Int(1)) ),
( loc(Identifier("c".to_string())), loc(Int(2)) ),
]
),
"".to_string()
))
);
}
#[test]
fn multi_newline_case_regression() {
assert_eq!(
parse_without_loc("a =\n case x\n when b then 1\n\n when c then 2\na"),
Ok((
Assign(
vec![(
loc(Identifier("a".to_string())),
loc(Case(
loc_box(var("x")),
vec![
( loc(Identifier("b".to_string())), loc(Int(1)) ),
( loc(Identifier("c".to_string())), loc(Int(2)) ),
]
))
)],
loc_box(var("a"))
),
"".to_string()
))
);
}
#[test]
fn case_with_two_newlines() {
assert_eq!(
parse_without_loc("case a\n\n when b then 1"),
Ok((
Case(
loc_box(var("a")),
vec![
( loc(Identifier("b".to_string())), loc(Int(1)) ),
]
),
"".to_string()
))
);
}
#[test]
fn case_with_number_pattern() {
assert_eq!(
parse_without_loc("case 1 when 2 then 3"),
Ok((
Case(
loc_box(Int(1)),
vec![
( loc(Integer(2)), loc(Int(3)) ),
]
),
"".to_string()
))
);
}
#[test]
fn case_with_empty_variant() {
assert_eq!(
parse_without_loc("case 1 when Foo then 3"),
Ok((
Case(
loc_box(Int(1)),
vec![
( loc(Variant(loc(vname("Foo")), None)), loc(Int(3)) ),
]
),
"".to_string()
))
);
}
#[test]
fn case_with_nonempty_variant() {
assert_eq!(
parse_without_loc("case 1 when Foo x then 3"),
Ok((
Case(
loc_box(Int(1)),
vec![
( loc(Variant(loc(vname("Foo")), Some(vec![loc(Identifier("x".to_string()))]))), loc(Int(3)) ),
]
),
"".to_string()
))
);
}
#[test]
fn case_with_two_branches_and_function_call() {
assert_eq!(
parse_without_loc("case 0 when 2 then foo 9 when 1 then bar 8"),
Ok((
Case(
loc_box(Int(0)),
vec![
( loc(Integer(2)), loc(call_by_name("foo", vec![loc(Int(9))])) ),
( loc(Integer(1)), loc(call_by_name("bar", vec![loc(Int(8))])) ),
]
),
"".to_string()
))
);
}
// IF
#[test]
fn indented_if() {
assert_eq!(
parse_without_loc("if 12345 then\n 54321\n else 1337"),
Ok(
(
If(
loc_box(Int(12345)),
loc_box(Int(54321)),
loc_box(Int(1337))
),
"".to_string())
)
);
}
#[test]
fn if_underscore_separated_number() {
assert_eq!(
parse_without_loc("if 12_34_5 then 5_4_32_1 else 1_3_37"),
Ok(
(
If(
loc_box(Int(12345)),
loc_box(Int(54321)),
loc_box(Int(1337))
),
"".to_string())
)
);
}
#[test]
fn single_line_if() {
assert_eq!(
parse_without_loc("if foo then 1 else 2"),
Ok(
(
If(
loc_box(var("foo")),
loc_box(Int(1)),
loc_box(Int(2))
),
"".to_string())
)
);
}
// INLINE COMMENT
#[test]
fn inline_comment() {
assert_eq!(
parse_without_loc("if 12345 then # blah blah\n 54321 #whee!\n else 1337"),
Ok(
(
If(
loc_box(Int(12345)),
loc_box(Int(54321)),
loc_box(Int(1337))
),
"".to_string())
)
);
}
#[test]
fn inline_comment_in_assignment() {
assert_eq!(
parse_without_loc("foo = 1\n# comment\nbar"),
Ok(
(
Assign(
vec![(
loc(Identifier("foo".to_string())),
loc(Int(1))
)],
loc_box(var("bar")),
),
"".to_string())
)
);
}
#[test]
fn horizontal_line_comment() {
assert_eq!(
parse_without_loc("if 12345 then ##### Heading #####\n 54321 #whee!\n else 1337"),
Ok(
(
If(
loc_box(Int(12345)),
loc_box(Int(54321)),
loc_box(Int(1337))
),
"".to_string())
)
);
}
// BLOCK COMMENT
#[test]
fn block_comment() {
assert_eq!(
parse_without_loc("if 12345### blah\n\nblah etc\nwhee #comment ###then\n 54321\n else 1337"),
Ok(
(
If(
loc_box(Int(12345)),
loc_box(Int(54321)),
loc_box(Int(1337))
),
"".to_string())
)
);
}
// VARIANT
#[test]
fn basic_variant() {
assert_eq!(
parse_without_loc("Abc"),
Ok((
ApplyVariant(vname("Abc"), None),
"".to_string()
))
);
}
#[test]
fn variant_with_one_arg() {
assert_eq!(
parse_without_loc("Bbc 1"),
Ok((
ApplyVariant(vname("Bbc"), Some(vec![loc(Int(1))])),
"".to_string()
))
);
}
#[test]
fn variant_with_two_args() {
assert_eq!(
parse_without_loc("Bbc 1 2"),
Ok((
ApplyVariant(vname("Bbc"), Some(vec![loc(Int(1)), loc(Int(2))])),
"".to_string()
))
);
}
#[test]
fn variant_regression() {
// Somehow parsing the variant "Abc" worked but "Foo" failed (?!)
assert_eq!(
parse_without_loc("F"),
Ok((
ApplyVariant(vname("F"), None),
"".to_string()
))
);
}
// COMPLEX EXPRESSIONS
#[test]
fn nested_let_variant() {
assert_eq!(
parse_without_loc("one = Abc\n\ntwo = Bar\n\none"),
Ok((
Assign(vec![
(
loc(Identifier(
"one".to_string()
)),
loc(ApplyVariant(
vname("Abc"),
None
)),
),
(
loc(Identifier(
"two".to_string()
)),
loc(ApplyVariant(
vname("Bar"),
None
)),
)],
loc_box(var("one"))
),
"".to_string()
))
);
}
#[test]
fn complex_expressions() {
expect_parsed_apply(
"(x 5) (y + (f 6))",
call_by_name("x", vec![loc(Int(5))]),
Operator(
loc_box(var("y")),
loc(Plus),
loc_box(call_by_name("f", vec![loc(Int(6))])),
)
);
assert_eq!(
parse_without_loc("(x 5)"),
Ok((
call_by_name("x", vec![loc(Int(5))]),
"".to_string())
)
);
assert_eq!(
parse_without_loc("(5)"),
Ok((
Int(5),
"".to_string())
)
);
assert_eq!(
parse_without_loc("((1905))"),
Ok((
Int(1905),
"".to_string())
)
);
assert_eq!(
parse_without_loc("6 + (685)"),
Ok((
Operator(
loc_box(Int(6)),
loc(Plus),
loc_box(Int(685))
),
"".to_string())
)
);
assert_eq!(
parse_without_loc("12 + 34"),
Ok((
Operator(
loc_box(Int(12)),
loc(Plus),
loc_box(Int(34))
),
"".to_string())
)
);
assert_eq!(
parse_without_loc("(51) + 19"),
Ok((
Operator(
loc_box(Int(51)),
loc(Plus),
loc_box(Int(19))
),
"".to_string())
)
);
assert_eq!(
parse_without_loc("(x 5) + 123"),
Ok((
Operator(
loc_box(call_by_name("x", vec![loc(Int(5))])),
loc(Plus),
loc_box(Int(123))
),
"".to_string())
)
);
assert_eq!(
parse_without_loc("(x 5) + (2 * y)"),
Ok((
Operator(
loc_box(call_by_name("x", vec![loc(Int(5))])),
loc(Plus),
loc_box(
Operator(
loc_box(Int(2)),
loc(Star),
loc_box(var("y"))
)
)
),
"".to_string())
)
);
}
// ASSIGN
#[test]
fn assign_with_function_application() {
assert_eq!(
parse_without_loc("abc =\n y 1\n\nabc"),
Ok((
Assign(vec![(
loc(Identifier(
"abc".to_string()
)),
loc(call_by_name(
"y",
vec![
loc(Int(
1
))
]
))
)],
loc_box(var("abc"))
),
"".to_string()
))
)
}
#[test]
fn assign_returning_number() {
assert_eq!(
// let x = 5 in -10
parse_without_loc("x = 5\n-10"),
Ok((
Assign(vec![(loc(Identifier("x".to_string())), loc(Int(5)))], loc_box(Int(-10))),
"".to_string())
)
);
assert_eq!(
// let x = 5 in 10
parse_without_loc("x=5\n-10"),
Ok((
Assign(vec![(loc(Identifier("x".to_string())), loc(Int(5)))], loc_box(Int(-10))),
"".to_string())
)
);
}
#[test]
fn assign_with_operator() {
assert_eq!(
// let x = 5 + 10 in -20
parse_without_loc("x =(5 + 10)\n-20"),
Ok((
Assign(
vec![(
loc(Identifier("x".to_string())),
loc(Operator(loc_box(Int(5)), loc(Plus), loc_box(Int(10)))),
)],
loc_box(Int(-20))),
"".to_string())
)
);
assert_eq!(
// let x = 5 + 10 in -20
parse_without_loc("x= 5 + 10\n-20"),
Ok((
Assign(
vec![(
loc(Identifier("x".to_string())),
loc(Operator(loc_box(Int(5)), loc(Plus), loc_box(Int(10)))),
)],
loc_box(Int(-20))),
"".to_string())
)
);
assert_eq!(
// let x = 5 + 10 in -20
parse_without_loc("x=5\n + 10\n-20"),
Ok((
Assign(
vec![(
loc(Identifier("x".to_string())),
loc(Operator(loc_box(Int(5)), loc(Plus), loc_box(Int(10)))),
)],
loc_box(Int(-20))),
"".to_string())
)
);
}
#[test]
fn invalid_assign_returning_number() {
assert!(
parse_without_loc("x=5\n -10").is_err(),
"Expected parsing error"
);
}
#[test]
fn assign_multiple() {
assert_fully_parses(
indoc!(r#"
x = 5
y = 12
z = 7
3
"#),
Assign(
vec![
(
loc(Identifier("x".to_string())),
loc(Int(5))
),
(
loc(Identifier("y".to_string())),
loc(Int(12))
),
(
loc(Identifier("z".to_string())),
loc(Int(7))
)
],
loc_box(Int(3))
)
);
assert_eq!(
// let x = 5 in let y = 12 in 3
parse_without_loc("x = 5 - -3\ny = 12 + 7\n3 * -5"),
Ok((
Assign(
vec![
(
loc(Identifier("x".to_string())),
loc(
Operator(
loc_box(Int(5)), loc(Minus), loc_box(Int(-3))
)
)
),
(
loc(Identifier("y".to_string())),
loc(Operator(
loc_box(Int(12)), loc(Plus), loc_box(Int(7))
))
)
],
loc_box(Operator(
loc_box(Int(3)), loc(Star), loc_box(Int(-5))
)),
),
"".to_string())
)
);
}
#[test]
fn assign_returning_var() {
assert_eq!(
parse_without_loc("x=5\nx"),
Ok((
Assign(vec![(loc(Identifier("x".to_string())), loc(Int(5)))], loc_box(var("x"))),
"".to_string()
))
);
}
#[test]
fn bad_equals_indent_let() {
assert!(
parse_without_loc(" x=\n5\n\n5").is_err(),
"Expected parsing error"
);
}
#[test]
fn regression_on_calling_function_named_c() {
// This was broken because case-expressions were greedily consuming 'c' characters for "case"
assert_eq!(
parse_without_loc("f = \\x -> c 1\n\nf"),
Ok((
Assign(
vec![(
loc(Identifier("f".to_string())),
loc(Closure(
vec![loc(Identifier("x".to_string()))],
loc_box(call_by_name("c", vec![loc(Int(1))]))
)),
)],
loc_box(var("f"))
),
"".to_string()
))
);
}
#[test]
fn regression_on_passing_arguments_named_i() {
// This was broken because if-expressions were greedily consuming 'i' characters for "if"
assert_eq!(
parse_without_loc("x i"),
Ok((
call_by_name("x", vec![loc(var("i"))]),
"".to_string()
))
);
}
}