From c8edddfd486a9555d4b805fad54943c8baf2f5a0 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 25 Jul 2019 08:27:50 -0400 Subject: [PATCH] Fix parsing edge case re: function calls at eof --- src/expr.rs | 6 +- src/lib.rs | 2 +- src/parse.rs | 9 +- src/region.rs | 26 +- tests/helpers/mod.rs | 67 ++ tests/test_canonicalize.rs | 212 ++++ tests/test_parse.rs | 1924 ++++++++++++++++++------------------ 7 files changed, 1283 insertions(+), 963 deletions(-) create mode 100644 tests/helpers/mod.rs diff --git a/src/expr.rs b/src/expr.rs index 99d3086c75..9dee7299d2 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -57,6 +57,10 @@ pub enum Ident { pub struct Path(String); impl Path { + pub fn new(string: String) -> Path { + Path(string) + } + pub fn into_string(self) -> String { let Path(str) = self; @@ -176,4 +180,4 @@ impl Expr { } } } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 8162ffb367..ac444f721c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub mod parse_state; pub mod operator; pub mod region; pub mod canonicalize; -mod collections; +pub mod collections; // mod ena; // #[macro_use] diff --git a/src/parse.rs b/src/parse.rs index fd16ca3245..849a73654d 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -394,10 +394,11 @@ where I: Stream, string("then"), string("else"), string("when"), + eof().with(value("")) )) ) ) - ), + ) ) ) ) @@ -538,7 +539,7 @@ pub fn func_or_var(min_indent: u32) -> impl Parser where I: Stream, I::Error: ParseError { - ident().and(optional(attempt(apply_args(min_indent)))) + ident().and(optional(apply_args(min_indent))) .map(|(name, opt_args): (String, Option>>)| { // Use optional(sep_by1()) over sep_by() to avoid // allocating a Vec in the common case where this is a var @@ -559,10 +560,10 @@ where I: Stream, .with( sep_by1( located(pattern(min_indent)), - attempt(many1::, _>(skipped_indented_whitespace_char(min_indent).skip(not_followed_by(string("=>"))))) + attempt(many1::, _>(skipped_indented_whitespace_char(min_indent).skip(not_followed_by(string("->"))))) )) .skip(indented_whitespaces(min_indent)) - .skip(string("=>")) + .skip(string("->")) .skip(indented_whitespaces1(min_indent)) .and(located(expr_body(min_indent))) .map(|(patterns, closure_body)| { diff --git a/src/region.rs b/src/region.rs index 928623fa17..4aff25fba5 100644 --- a/src/region.rs +++ b/src/region.rs @@ -1,3 +1,5 @@ +use std::fmt; + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Region { pub start_line: u32, @@ -7,7 +9,7 @@ pub struct Region { pub end_col: u32, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct Located { pub region: Region, pub value: T, @@ -29,4 +31,26 @@ impl Located { { Located { region: self.region, value: transform(&self.value) } } +} + + +impl fmt::Debug for Located +where T: fmt::Debug +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let region = self.region; + + if region.start_line == 0 && region.start_col == 0 && region.end_line == 0 && region.end_col == 0 { + // In tests, it's super common to set all Located values to 0. + // Also in tests, we don't want to bother printing the locations + // because it makes failed assertions much harder to read. + self.value.fmt(f) + } else { + write!(f, "|L {}, C {} - L {}, C {}| {:?}", + region.start_line, region.start_col, + region.end_line, region.end_col, + self.value + ) + } + } } \ No newline at end of file diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs new file mode 100644 index 0000000000..992f65acf3 --- /dev/null +++ b/tests/helpers/mod.rs @@ -0,0 +1,67 @@ +use roc::expr::{Expr, Pattern}; +use roc::region::{Located, Region}; + +pub fn loc_box(val: T) -> Box> { + Box::new(loc(val)) +} + +pub fn loc(val: T) -> Located { + Located::new(val, Region { + start_line: 0, + start_col: 0, + + end_line: 0, + end_col: 0, + }) +} + +pub fn zero_loc(located_val: Located) -> Located { + loc(located_val.value) +} + +/// Zero out the parse locations on everything in this Expr, so we can compare expected/actual without +/// having to account for that. +pub fn zero_loc_expr(expr: Expr) -> Expr { + use roc::expr::Expr::*; + + match expr { + Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord => expr, + InterpolatedStr(pairs, string) => InterpolatedStr(pairs.into_iter().map(|( prefix, ident )| ( prefix, zero_loc(ident))).collect(), string), + Assign(assignments, loc_ret) => { + let zeroed_assignments = + assignments.into_iter().map(|( pattern, loc_expr )| + ( zero_loc_pattern(pattern), loc(zero_loc_expr(loc_expr.value)) ) + ).collect(); + + Assign(zeroed_assignments, loc_box(zero_loc_expr((*loc_ret).value))) + }, + CallByName(ident, args) => CallByName(ident, args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect()), + Apply(fn_expr, args) => Apply(loc_box(zero_loc_expr((*fn_expr).value)), args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect()), + Operator(left, op, right) => Operator(loc_box(zero_loc_expr((*left).value)), zero_loc(op), loc_box(zero_loc_expr((*right).value))), + Closure(patterns, body) => Closure(patterns.into_iter().map(zero_loc).collect(), loc_box(zero_loc_expr((*body).value))), + ApplyVariant(_, None) => expr, + ApplyVariant(name, Some(args)) => ApplyVariant(name, Some(args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect())), + If(condition, if_true, if_false) => If(loc_box(zero_loc_expr((*condition).value)), loc_box(zero_loc_expr((*if_true).value)), loc_box(zero_loc_expr((*if_false).value))), + Case(condition, branches) => + Case( + loc_box(zero_loc_expr((*condition).value)), + branches.into_iter().map(|( pattern, loc_expr )| ( zero_loc_pattern(pattern), loc(zero_loc_expr(loc_expr.value)) )).collect() + ), + } +} + +/// Zero out the parse locations on everything in this Pattern, so we can compare expected/actual without +/// having to account for that. +pub fn zero_loc_pattern(loc_pattern: Located) -> Located { + use roc::expr::Pattern::*; + + let pattern = loc_pattern.value; + + match pattern { + Identifier(_) | Integer(_) | Fraction(_, _) | ExactString(_) | EmptyRecordLiteral | Underscore => loc(pattern), + Variant(loc_name, None) => + loc(Variant(loc(loc_name.value), None)), + Variant(loc_name, Some(opt_located_patterns)) => + loc(Variant(loc(loc_name.value), Some(opt_located_patterns.into_iter().map(|loc_pat| zero_loc_pattern(loc_pat)).collect()))), + } +} \ No newline at end of file diff --git a/tests/test_canonicalize.rs b/tests/test_canonicalize.rs index fb58b60e6f..96d9dfc2ad 100644 --- a/tests/test_canonicalize.rs +++ b/tests/test_canonicalize.rs @@ -1,10 +1,222 @@ #[macro_use] extern crate pretty_assertions; +#[macro_use] extern crate indoc; extern crate combine; extern crate roc; +mod helpers; + #[cfg(test)] mod test_canonicalize { + use roc::canonicalize; + use roc::canonicalize::{Expr, Output, Problem, Resolved, LocalSymbol, Symbol}; + use roc::canonicalize::Expr::*; + use roc::canonicalize::Pattern::*; + use roc::expr::{Path, Ident}; + use roc::operator::Operator::*; + use roc::expr; + use roc::region::Located; + use roc::parse; + use roc::collections::{ImMap, ImSet}; + use roc::parse_state::{IndentablePosition}; + use combine::{Parser, eof}; + use combine::stream::state::{State}; + use helpers::{loc, loc_box, zero_loc_expr}; + + fn can_expr(expr_str: &str) -> (Expr, Output, Vec) { + can_expr_with(expr_str, &ImMap::default(), &ImMap::default()) + } + + fn can_expr_with( + expr_str: &str, + declared_idents: &ImMap<(Option, String), Located>, + declared_variants: &ImMap<(Path, String), Located>, + ) -> (Expr, Output, Vec) { + let parse_state: State<&str, IndentablePosition> = State::with_positioner(expr_str, IndentablePosition::default()); + let expr = match parse::expr().skip(eof()).easy_parse(parse_state) { + Ok((expr, state)) => { + if !state.input.is_empty() { + panic!("There were unconsumed chars left over after parsing \"{}\" - the leftover string was: \"{}\"", + expr_str.to_string(), state.input.to_string()) + } + + expr + }, + Err(errors) => { + panic!("Parse error trying to parse \"{}\" - {}", expr_str.to_string(), errors.to_string()) + } + }; + + let home = Path::new("TestModule".to_string()); + let (loc_expr, output, problems) = + canonicalize::canonicalize_declaration(home, loc(zero_loc_expr(expr)), declared_idents, declared_variants); + + (loc_expr.value, output, problems) + } + + fn recognized_local_sym(string: &str) -> Resolved { + Resolved::Recognized(local_sym(string)) + } + + fn local_sym(string: &str) -> Symbol { + Symbol::Local(local(string)) + } + + fn local(string: &str) -> LocalSymbol { + LocalSymbol::new(string.to_string()) + } + + fn check_output( + output: Output, + applied_variants: Vec<(Path, &str)>, + referenced_idents: Vec<(Option, &str)>, + tail_call: Option + ) { + assert_eq!( + output.applied_variants, + ImSet::from( + applied_variants.into_iter().map(|(path, str_ref)| + (path, str_ref.to_string()) + ).collect::>() + ) + ); + assert_eq!( + output.referenced_idents, + ImSet::from( + referenced_idents.into_iter().map(|(opt_path, str_ref)| + (opt_path, str_ref.to_string()) + ).collect::>() + ) + ); + assert_eq!(output.tail_call, tail_call); + } + + #[test] + fn basic_unrecognized_constant() { + let (expr, output, problems) = can_expr(indoc!(r#" + x + "#)); + + assert_eq!(problems, vec![ + Problem::UnrecognizedConstant(loc(Ident::Unqualified("x".to_string()))) + ]); + + assert_eq!(expr, + Var(Resolved::UnrecognizedConstant(loc(Ident::Unqualified("x".to_string())))) + ); + + check_output(output, vec![], vec![], None); + } + + #[test] + fn complex_unrecognized_constant() { + let (expr, output, problems) = can_expr(indoc!(r#" + a = 5 + b = 6 + + a + b * z + "#)); + + assert_eq!(problems, vec![ + Problem::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))) + ]); + + assert_eq!(expr, + Assign( + vec![ + (loc(Identifier(local_sym("a"))), loc(Int(5))), + (loc(Identifier(local_sym("b"))), loc(Int(6))), + ], + loc_box(Operator( + loc_box(Var(recognized_local_sym("a"))), + loc(Plus), + loc_box(Operator( + loc_box(Var(recognized_local_sym("b"))), + loc(Star), + loc_box(Var(Resolved::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))))) + )), + )) + ) + ); + + check_output(output, vec![], vec![(None, "a"), (None, "b")], None); + } + + #[test] + fn can_fibonacci() { + return (); + let (expr, output, problems) = can_expr(indoc!(r#" + fibonacci = \num => + if num < 2 then + num + else + fibonacci (num - 1) + fibonacci (num - 2) + + fibonacci 9 + "#)); + + // assert_eq!(problems, vec![ + // Problem::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))) + // ]); + + // assert_eq!(expr, + // Assign( + // vec![ + // (loc(Identifier(local_sym("a"))), loc(Int(5))), + // (loc(Identifier(local_sym("b"))), loc(Int(6))), + // ], + // loc_box(Operator( + // loc_box(Var(recognized_local_sym("a"))), + // loc(Plus), + // loc_box(Operator( + // loc_box(Var(recognized_local_sym("b"))), + // loc(Star), + // loc_box(Var(Resolved::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))))) + // )), + // )) + // ) + // ); + + check_output(output, vec![], vec![(None, "num"), (None, "fibonacci")], None); + } + + + // #[test] + // fn can_fibonacci() { + // let (expr, output, problems) = can_expr(indoc!(r#" + // fibonacci = \num => + // if num < 2 then + // num + // else + // fibonacci (num - 1) + fibonacci (num - 2) + // "#)); + + // assert_eq!(problems, vec![ + // Problem::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))) + // ]); + + // assert_eq!(expr, + // Assign( + // vec![ + // (loc(Identifier(local_sym("a"))), loc(Int(5))), + // (loc(Identifier(local_sym("b"))), loc(Int(6))), + // ], + // loc_box(Operator( + // loc_box(Var(recognized_local_sym("a"))), + // loc(Plus), + // loc_box(Operator( + // loc_box(Var(recognized_local_sym("b"))), + // loc(Star), + // loc_box(Var(Resolved::UnrecognizedConstant(loc(Ident::Unqualified("z".to_string()))))) + // )), + // )) + // ) + // ); + + // check_output(output, vec![], vec![(None, "a".to_string()), (None, "b".to_string())], None); + // } + + // OPERATOR PRECEDENCE diff --git a/tests/test_parse.rs b/tests/test_parse.rs index 05bc46a4e0..3b9ee4ed45 100644 --- a/tests/test_parse.rs +++ b/tests/test_parse.rs @@ -3,6 +3,8 @@ extern crate combine; extern crate roc; +mod helpers; + #[cfg(test)] mod test_parse { use roc::expr::Expr::*; @@ -18,6 +20,9 @@ mod test_parse { use combine::stream::{Stream}; use combine::easy; use combine::stream::state::{State}; + use helpers::{loc, loc_box, zero_loc_expr, zero_loc_pattern}; + + // PARSE TEST HELPERS fn standalone_expr() -> impl Parser where I: Stream, @@ -26,53 +31,6 @@ mod test_parse { parse::expr().skip(eof()) } - /// Zero out the parse locations on everything in this Expr, so we can compare expected/actual without - /// having to account for that. - fn zero_loc_expr(expr: Expr) -> Expr { - match expr { - Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord => expr, - InterpolatedStr(pairs, string) => InterpolatedStr(pairs.into_iter().map(|( prefix, ident )| ( prefix, zero_loc(ident))).collect(), string), - Assign(assignments, loc_ret) => { - let zeroed_assignments = - assignments.into_iter().map(|( pattern, loc_expr )| - ( zero_loc_pattern(pattern), loc(zero_loc_expr(loc_expr.value)) ) - ).collect(); - - Assign(zeroed_assignments, loc_box(zero_loc_expr((*loc_ret).value))) - }, - CallByName(ident, args) => CallByName(ident, args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect()), - Apply(fn_expr, args) => Apply(loc_box(zero_loc_expr((*fn_expr).value)), args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect()), - Operator(left, op, right) => Operator(loc_box(zero_loc_expr((*left).value)), zero_loc(op), loc_box(zero_loc_expr((*right).value))), - Closure(patterns, body) => Closure(patterns.into_iter().map(zero_loc).collect(), loc_box(zero_loc_expr((*body).value))), - ApplyVariant(_, None) => expr, - ApplyVariant(name, Some(args)) => ApplyVariant(name, Some(args.into_iter().map(|arg| loc(zero_loc_expr(arg.value))).collect())), - If(condition, if_true, if_false) => If(loc_box(zero_loc_expr((*condition).value)), loc_box(zero_loc_expr((*if_true).value)), loc_box(zero_loc_expr((*if_false).value))), - Case(condition, branches) => - Case( - loc_box(zero_loc_expr((*condition).value)), - branches.into_iter().map(|( pattern, loc_expr )| ( zero_loc_pattern(pattern), loc(zero_loc_expr(loc_expr.value)) )).collect() - ), - } - } - - /// Zero out the parse locations on everything in this Pattern, so we can compare expected/actual without - /// having to account for that. - fn zero_loc_pattern(loc_pattern: Located) -> Located { - let pattern = loc_pattern.value; - - match pattern { - Identifier(_) | Integer(_) | Fraction(_, _) | ExactString(_) | EmptyRecordLiteral | Underscore => loc(pattern), - Variant(loc_name, None) => - loc(Variant(loc(loc_name.value), None)), - Variant(loc_name, Some(opt_located_patterns)) => - loc(Variant(loc(loc_name.value), Some(opt_located_patterns.into_iter().map(|loc_pat| zero_loc_pattern(loc_pat)).collect()))), - } - } - - fn zero_loc(located_val: Located) -> Located { - loc(located_val.value) - } - fn parse_without_loc(actual_str: &str) -> Result<(Expr, String), String>{ parse_standalone(actual_str) .map(|(expr, leftover)| (zero_loc_expr(expr), leftover)) @@ -93,20 +51,6 @@ mod test_parse { standalone_expr().easy_parse(parse_state).map(|(expr, state)| (expr, state.input)) } - fn loc_box(val: T) -> Box> { - Box::new(loc(val)) - } - - fn loc(val: T) -> Located { - Located::new(val, Region { - start_line: 0, - start_col: 0, - - end_line: 0, - end_col: 0, - }) - } - fn assert_fully_parses(actual_str: &str, expected_expr: Expr) { assert_eq!( Ok((expected_expr, "".to_string())), @@ -114,6 +58,17 @@ mod test_parse { ); } + fn var(name: &str) -> Expr { + Var(Ident::Unqualified(name.to_string())) + } + + fn call_by_name(name: &str, args: Vec>) -> Expr { + CallByName( + raw(name), + args.into_iter().map(|loc_expr| loc(zero_loc_expr(loc_expr.value))).collect() + ) + } + // STRING LITERALS fn expect_parsed_str<'a>(expected_str: &'a str, actual_str: &'a str) { @@ -297,182 +252,182 @@ mod test_parse { // // NUMBER LITERALS - // fn expect_parsed_approx<'a>(expected: f64, actual: &str) { - // assert_eq!(Ok((Approx(expected), "")), parse_without_loc(actual)); - // } + 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), "")), 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), "")), 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 positive_int() { + expect_parsed_int(1234, "1234"); + } - // #[test] - // fn negative_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 positive_approx() { + expect_parsed_approx(123.4, "~123.4"); + } - // #[test] - // fn negative_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 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 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 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 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)) - // ), "")) - // ); - // } + #[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)) - // ), "")) - // ); - // assert_eq!( - // parse_without_loc("x > 0"), - // Ok((Operator( - // loc_box(var("x")), - // loc(GreaterThan), - // loc_box(Int(0)) - // ), "")) - // ); - // assert_eq!( - // parse_without_loc("x <= 0"), - // Ok((Operator( - // loc_box(var("x")), - // loc(LessThanOrEq), - // loc_box(Int(0)) - // ), "")) - // ); - // assert_eq!( - // parse_without_loc("x < 0"), - // Ok((Operator( - // loc_box(var("x")), - // loc(LessThan), - // loc_box(Int(0)) - // ), "")) - // ); - // } + #[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), "")) => { - // assert_eq!((*v1).value, Int(1234)); - // assert_eq!(op.value, Plus); - // assert_eq!((*v2).value, Int(567)); - // }, + #[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"), - // } - // } + _ => 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)))) - // ), "")) - // ); - // } + #[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)) - // ), "")) - // ); + #[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)) - // ), "")) - // ); + 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)))) - // ), "")) - // ); + 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)))) - // ), "")) - // ); - - // 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)), - // ), "")) - // ); - // } + 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 @@ -566,41 +521,110 @@ mod test_parse { // // all of the above tests except with parens too! // // Also, verify them all with variable paren counts; ((foo)) should work. - // // CLOSURE + // CLOSURE - // #[test] - // fn single_arg_closure() { - // assert_eq!( - // parse_without_loc("\\a => b"), - // Ok(( - // Closure( - // vec![loc(Identifier("a".to_string()))], - // loc_box(var("b")) - // ), - // "" - // )) - // ); - // } + #[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_eq!( - // parse_without_loc("\\a b => c"), - // Ok(( - // Closure( - // vec![loc(Identifier("a".to_string())), loc(Identifier("b".to_string()))], - // loc_box(var("c")) - // ), - // "" - // )) - // ); - // } + #[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")) + ) + ); + } - // // FUNC + #[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>) { // assert_eq!( - // Ok((call_by_name(func_str, args), "")), + // Ok((call_by_name(func_str, args), "".to_string())), // parse_without_loc(parse_str) // ); // } @@ -619,7 +643,6 @@ mod test_parse { // ); // } - // #[test] // fn single_arg_func() { // expect_parsed_func("f 1", "f", vec![loc(Int(1))]); @@ -669,7 +692,8 @@ mod test_parse { // loc(Plus), // loc_box(Int(6)) // ), - // "") + // "".to_string() + // ) // ) // ); // } @@ -689,744 +713,732 @@ mod test_parse { // 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 + // // // 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_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"))]); + // 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 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)) )] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ) + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ) + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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")) + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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)) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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))])) ), + // // ] + // // ), + // // "" + // // )) + // // ); + // // } + + // // // 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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // #[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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // #[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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // // 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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // #[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")), + // // ), + // // "") + // // ) + // // ); + // // } + + // // #[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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // // 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)) + // // ), + // // "") + // // ) + // // ); + // // } + + // // // VARIANT + + // // #[test] + // // fn basic_variant() { + // // assert_eq!( + // // parse_without_loc("Abc"), + // // Ok(( + // // ApplyVariant(vname("Abc"), None), + // // "" + // // )) + // // ); + // // } + + // // #[test] + // // fn variant_with_one_arg() { + // // assert_eq!( + // // parse_without_loc("Bbc 1"), + // // Ok(( + // // ApplyVariant(vname("Bbc"), Some(vec![loc(Int(1))])), + // // "" + // // )) + // // ); + // // } + + // // #[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))])), + // // "" + // // )) + // // ); + // // } + + // // #[test] + // // fn variant_regression() { + // // // Somehow parsing the variant "Abc" worked but "Foo" failed (?!) + // // assert_eq!( + // // parse_without_loc("F"), + // // Ok(( + // // ApplyVariant(vname("F"), None), + // // "" + // // )) + // // ); + // // } + + + // // // 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")) + // // ), + // // "" + // // )) + // // ); + // // } + + // // #[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))]), + // // "") + // // ) + // // ); + + // // assert_eq!( + // // parse_without_loc("(5)"), + // // Ok(( + // // Int(5), + // // "") + // // ) + // // ); + + // // assert_eq!( + // // parse_without_loc("((1905))"), + // // Ok(( + // // Int(1905), + // // "") + // // ) + // // ); + + // // assert_eq!( + // // parse_without_loc("6 + (685)"), + // // Ok(( + // // Operator( + // // loc_box(Int(6)), + // // loc(Plus), + // // loc_box(Int(685)) + // // ), + // // "") + // // ) + // // ); + + // // assert_eq!( + // // parse_without_loc("12 + 34"), + // // Ok(( + // // Operator( + // // loc_box(Int(12)), + // // loc(Plus), + // // loc_box(Int(34)) + // // ), + // // "") + // // ) + // // ); + + // // assert_eq!( + // // parse_without_loc("(51) + 19"), + // // Ok(( + // // Operator( + // // loc_box(Int(51)), + // // loc(Plus), + // // loc_box(Int(19)) + // // ), + // // "") + // // ) + // // ); + + // // 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)) + // // ), + // // "") + // // ) + // // ); + + // // 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")) + // // ) + // // ) + // // ), + // // "") + // // ) + // // ); + // // } + + // // // 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")) + // // ), + // // "" + // // )) + // // ) + // // } + + // // #[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))), + // // "") + // // ) + // // ); + + // // 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))), + // // "") + // // ) + // // ); + // // } + + // // #[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))), + // // "") + // // ) + // // ); + + // // 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))), + // // "") + // // ) + // // ); + + // // 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))), + // // "") + // // ) + // // ); + // // } + + // // #[test] + // // fn invalid_assign_returning_number() { + // // assert!( + // // parse_without_loc("x=5\n -10").is_err(), + // // "Expected parsing error" + // // ); + // // } // #[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)) )] - // ), - // "" - // )) - // ); - // } - - // #[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)) ) - // ] - // ), - // "" - // )) - // ); - // } - - // #[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)) ) - // ] - // ), - // "" - // )) - // ); - // } - - // #[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)) ), - // ] - // ), - // "" - // )) - // ); - // } - - // #[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")) - // ), - // "" - // )) - // ); - // } - - // #[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)) ), - // ] - // ), - // "" - // )) - // ); - // } - - // #[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)) ), - // ] - // ), - // "" - // )) - // ); - // } - - // #[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)) ), - // ] - // ), - // "" - // )) - // ); - // } - - // #[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)) ), - // ] - // ), - // "" - // )) - // ); - // } - - // #[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))])) ), - // ] - // ), - // "" - // )) - // ); - // } - - // // 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)) - // ), - // "") - // ) - // ); - // } - - // #[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)) - // ), - // "") - // ) - // ); - // } - - // #[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)) - // ), - // "") - // ) - // ); - // } - - // // 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)) - // ), - // "") - // ) - // ); - // } - - // #[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")), - // ), - // "") - // ) - // ); - // } - - // #[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)) - // ), - // "") - // ) - // ); - // } - - // // 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)) - // ), - // "") - // ) - // ); - // } - - // // VARIANT - - // #[test] - // fn basic_variant() { - // assert_eq!( - // parse_without_loc("Abc"), - // Ok(( - // ApplyVariant(vname("Abc"), None), - // "" - // )) - // ); - // } - - // #[test] - // fn variant_with_one_arg() { - // assert_eq!( - // parse_without_loc("Bbc 1"), - // Ok(( - // ApplyVariant(vname("Bbc"), Some(vec![loc(Int(1))])), - // "" - // )) - // ); - // } - - // #[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))])), - // "" - // )) - // ); - // } - - // #[test] - // fn variant_regression() { - // // Somehow parsing the variant "Abc" worked but "Foo" failed (?!) - // assert_eq!( - // parse_without_loc("F"), - // Ok(( - // ApplyVariant(vname("F"), None), - // "" - // )) - // ); - // } - - - // // COMPLEX EXPRESSIONS - - // #[test] - // fn nested_let_variant() { - // assert_eq!( - // parse_without_loc("one = Abc\n\ntwo = Bar\n\none"), - // Ok(( - // Assign(vec![ + // fn assign_multiple() { + // assert_fully_parses( + // indoc!(r#" + // x = 5 + // y = 12 + // z = 7 + // 3 + // "#), + // Assign( + // vec![ // ( - // loc(Identifier( - // "one".to_string() - // )), - // loc(ApplyVariant( - // vname("Abc"), - // None - // )), + // loc(Identifier("x".to_string())), + // loc(Int(5)) // ), // ( - // loc(Identifier( - // "two".to_string() - // )), - // loc(ApplyVariant( - // vname("Bar"), - // None - // )), - // )], - // loc_box(var("one")) - // ), - // "" - // )) - // ); - // } - - // #[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))]), - // "") - // ) - // ); - - // assert_eq!( - // parse_without_loc("(5)"), - // Ok(( - // Int(5), - // "") - // ) - // ); - - // assert_eq!( - // parse_without_loc("((1905))"), - // Ok(( - // Int(1905), - // "") - // ) - // ); - - // assert_eq!( - // parse_without_loc("6 + (685)"), - // Ok(( - // Operator( - // loc_box(Int(6)), - // loc(Plus), - // loc_box(Int(685)) - // ), - // "") - // ) - // ); - - // assert_eq!( - // parse_without_loc("12 + 34"), - // Ok(( - // Operator( - // loc_box(Int(12)), - // loc(Plus), - // loc_box(Int(34)) - // ), - // "") - // ) - // ); - - // assert_eq!( - // parse_without_loc("(51) + 19"), - // Ok(( - // Operator( - // loc_box(Int(51)), - // loc(Plus), - // loc_box(Int(19)) - // ), - // "") - // ) - // ); - - // 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)) - // ), - // "") - // ) - // ); - - // 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")) - // ) + // loc(Identifier("y".to_string())), + // loc(Int(12)) + // ), + // ( + // loc(Identifier("z".to_string())), + // loc(Int(7)) // ) - // ), - // "") + // ], + // loc_box(Int(3)) // ) // ); // } - // // ASSIGN + // // 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)) + // // )), + // // ), + // // "") + // // ) + // // ); + // // } - // #[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")) - // ), - // "" - // )) - // ) - // } + // // #[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"))), + // // "") + // // ) + // // ); + // // } - // #[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))), - // "") - // ) - // ); + // // #[test] + // // fn bad_equals_indent_let() { + // // assert!( + // // parse_without_loc(" x=\n5\n\n5").is_err(), + // // "Expected parsing error" + // // ); + // // } - // 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))), - // "") - // ) - // ); - // } + // // #[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")) + // // ), + // // "" + // // )) + // // ); + // // } - // #[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))), - // "") - // ) - // ); - - // 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))), - // "") - // ) - // ); - - // 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))), - // "") - // ) - // ); - // } - - // #[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)) - // )), - // ), - // "") - // ) - // ); - // } - - // #[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"))), - // "") - // ) - // ); - // } - - // #[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")) - // ), - // "" - // )) - // ); - // } - - // #[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"))]), - // "" - // )) - // ); - // } - - // fn var(name: &str) -> Expr { - // Var(Ident::Unqualified(name.to_string())) - // } - - // fn call_by_name(name: &str, args: Vec>) -> Expr { - // CallByName( - // raw(name), - // args.into_iter().map(|loc_expr| loc(zero_loc_expr(loc_expr.value))).collect() - // ) - // } + // // #[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"))]), + // // "" + // // )) + // // ); + // // } }