From 04595a9e71f7eb0813096e68f61b48a6ed084a48 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 1 Sep 2019 00:50:11 -0400 Subject: [PATCH] Frac+Approx --> Float --- src/canonicalize.rs | 23 ++++----- src/constrain.rs | 59 ++++++++++++---------- src/expr.rs | 9 ++-- src/parse.rs | 113 +++++-------------------------------------- src/solve.rs | 2 +- src/subs.rs | 2 +- src/types.rs | 3 +- src/unify.rs | 2 +- tests/helpers/mod.rs | 4 +- tests/test_infer.rs | 58 +++++++++++----------- tests/test_parse.rs | 44 ++++++----------- 11 files changed, 112 insertions(+), 207 deletions(-) diff --git a/src/canonicalize.rs b/src/canonicalize.rs index bec3e10c36..608e9ef151 100644 --- a/src/canonicalize.rs +++ b/src/canonicalize.rs @@ -13,8 +13,7 @@ use self::PatternType::*; pub enum Expr { // Literals Int(i64), - Frac(i64, i64), - Approx(f64), + Float(f64), EmptyStr, Str(String), Char(char), @@ -72,8 +71,8 @@ pub enum Problem { pub enum Pattern { Identifier(Symbol), Variant(Symbol, Option>>), - Integer(i64), - Fraction(i64, i64), + IntLiteral(i64), + FloatLiteral(f64), ExactString(String), EmptyRecordLiteral, Underscore, @@ -303,8 +302,7 @@ fn canonicalize( let (expr, output) = match loc_expr.value { expr::Expr::Int(num) => ( Int(num), Output::new() ), - expr::Expr::Frac(numerator, denominator) => ( Frac(numerator, denominator), Output::new()), - expr::Expr::Approx(num) => ( Approx(num), Output::new()), + expr::Expr::Float(num) => ( Float(num), Output::new() ), expr::Expr::EmptyRecord => ( EmptyRecord, Output::new()), expr::Expr::Str(string) => ( Str(string), Output::new()), expr::Expr::Char(ch) => ( Char(ch), Output::new()), @@ -577,7 +575,6 @@ fn canonicalize( // Store the referenced locals in the refs_by_assignment map, so we can later figure out // which assigned names reference each other. for (ident, (symbol, region)) in idents_from_patterns(std::iter::once(&loc_pattern), &scope) { - println!("* * * symbol: {:?}, is_closure_defn: {:?}", symbol, renamed_closure_assignment); let refs = // Functions' references don't count in assignments. // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its @@ -1024,7 +1021,7 @@ fn add_idents_from_pattern( } } }, - &Integer(_) | &Fraction(_, _) | &ExactString(_) + &IntLiteral(_) | &FloatLiteral(_) | &ExactString(_) | &EmptyRecordLiteral | &Underscore => () } } @@ -1042,7 +1039,7 @@ fn remove_idents( remove_idents(loc_arg.value, idents); } }, - Variant(_, None) | Integer(_) | Fraction(_, _) | ExactString(_) + Variant(_, None) | IntLiteral(_) | FloatLiteral(_) | ExactString(_) | EmptyRecordLiteral | Underscore => {} } } @@ -1234,16 +1231,16 @@ fn canonicalize_pattern( } }, - &Integer(ref num) => { + &IntLiteral(ref num) => { match pattern_type { - CaseBranch => Pattern::Integer(*num), + CaseBranch => Pattern::IntLiteral(*num), ptype @ Assignment | ptype @ FunctionArg => unsupported_pattern(env, *ptype, ®ion, &loc_pattern.value) } }, - &Fraction(ref numerator, ref denominator) => { + &FloatLiteral(ref num) => { match pattern_type { - CaseBranch => Pattern::Fraction(*numerator, *denominator), + CaseBranch => Pattern::FloatLiteral(*num), ptype @ Assignment | ptype @ FunctionArg => unsupported_pattern(env, *ptype, ®ion, &loc_pattern.value) } }, diff --git a/src/constrain.rs b/src/constrain.rs index fa38f78bae..8a1a8126a0 100644 --- a/src/constrain.rs +++ b/src/constrain.rs @@ -28,9 +28,8 @@ pub fn constrain( let region = loc_expr.region; match loc_expr.value { - Int(_) => { Eq(num(subs.mk_flex_var()), expected, region) }, - Frac(_, _) => { fractional(subs, expected, region) }, - Approx(_) => { fractional(subs, expected, region) }, + Int(_) => { int_literal(subs, expected, region) }, + Float(_) => { float_literal(subs, expected, region) }, Str(_) => { Eq(string(), expected, region) }, EmptyStr => { Eq(string(), expected, region) }, InterpolatedStr(pairs, _) => { @@ -337,33 +336,43 @@ fn list(loc_elems: Vec>, bound_vars: &BoundTypeVars, subs: &mut Su And(constraints) } -fn fractional(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { - // We'll make a Num var1 and a Fractional var2, - // and then add a constraint that var1 needs to equal Fractional var2 - let num_var = subs.mk_flex_var(); // Num var1 - let fractional_var = subs.mk_flex_var(); // Fractional var2 - let fractional_type = - Type::Apply( - "Num".to_string(), - "Fractional".to_string(), - vec![Type::Variable(fractional_var)] - ); - let num_var_type = Type::Variable(num_var); - let num_type = - Type::Apply( - "Num".to_string(), - "Num".to_string(), - vec![num_var_type.clone()] - ); - let expected_fractional = - ForReason(Reason::FractionalLiteral, fractional_type, region.clone()); +#[inline(always)] +fn int_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { + let typ = number_literal_type("Int", "Integer"); + let reason = Reason::IntLiteral; + + num_literal(typ, reason, subs, expected, region) +} + +#[inline(always)] +fn float_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { + let typ = number_literal_type("Float", "FloatingPoint"); + let reason = Reason::FloatLiteral; + + num_literal(typ, reason, subs, expected, region) +} + + +#[inline(always)] +fn num_literal(literal_type: Type, reason: Reason, subs: &mut Subs, expected: Expected, region: Region) -> Constraint { + let num_var = subs.mk_flex_var(); + let num_type = Variable(num_var); + let expected_literal = ForReason(reason, literal_type, region.clone()); And(vec![ - Eq(num_type, expected, region.clone()), - Eq(num_var_type, expected_fractional, region), + Eq(num_type.clone(), expected_literal, region.clone()), + Eq(num_type, expected, region.clone()) ]) } +#[inline(always)] +fn number_literal_type(module_name: &str, type_name: &str) -> Type { + builtin_type("Num", "Num", + vec![builtin_type(module_name, type_name, Vec::new())] + ) +} + +#[inline(always)] fn builtin_type(module_name: &str, type_name: &str, args: Vec) -> Type { Type::Apply(module_name.to_string(), type_name.to_string(), args) } diff --git a/src/expr.rs b/src/expr.rs index bdae2f77a5..b6cdf9ec9b 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -6,8 +6,7 @@ use std::fmt; pub enum Expr { // Literals Int(i64), - Frac(i64, i64), - Approx(f64), + Float(f64), EmptyStr, Str(String), Char(char), @@ -99,8 +98,8 @@ impl fmt::Display for VariantName { pub enum Pattern { Identifier(String), Variant(Located, Option>>), - Integer(i64), - Fraction(i64, i64), + IntLiteral(i64), + FloatLiteral(f64), ExactString(String), EmptyRecordLiteral, Underscore, @@ -115,7 +114,7 @@ impl Expr { let transformed = transform(self); match transformed { - Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | InterpolatedStr(_, _) | EmptyList => transformed, + Int(_) | Float(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | InterpolatedStr(_, _) | EmptyList => transformed, List(elems) => { let new_elems = elems.into_iter() diff --git a/src/parse.rs b/src/parse.rs index 4e6b559135..76f6d3bd75 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -199,9 +199,6 @@ parser! { string_literal(), int_or_frac_literal(), negative_int_or_frac_literal(), - char('~').with( - negative_approx_literal().or(approx_literal()) - ), char_literal(), if_expr(min_indent), case_expr(min_indent), @@ -895,68 +892,6 @@ where I: Stream, ) } -pub fn negative_approx_literal() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - char('-') - .with(digits_before_decimal()) - .and(optional(char('.').with(digits_after_decimal()))) - .then(|(int_digits, decimals): (Vec, Option>)| { - // TODO check length of digits and make sure not to overflow - let int_str: String = int_digits.into_iter().collect(); - - match decimals { - Some(nums) => { - let decimal_str: String = nums.into_iter().collect(); - - match format!("-{}.{}", int_str, decimal_str).parse::() { - Ok(float) => { - value(Expr::Approx(float)).right() - }, - Err(_) => { - unexpected_any("looked like a negative number literal but was actually malformed identifier").left() - } - } - }, - None => { - unexpected_any("negative number literal with ~ but without the decimal point that ~ number literals require").left() - } - } - }) -} - -pub fn approx_literal() -> impl Parser -where I: Stream, - I::Error: ParseError -{ - digits_before_decimal() - .and(optional(char('.').with(digits_after_decimal()))) - .then(|(int_digits, decimals): (Vec, Option>)| { - // TODO check length of digits and make sure not to overflow - let int_str: String = int_digits.into_iter().collect(); - - match decimals { - Some(nums) => { - let decimal_str: String = nums.into_iter().collect(); - - match format!("{}.{}", int_str, decimal_str).parse::() { - Ok(float) => { - value(Expr::Approx(float)).right() - }, - Err(_) => { - unexpected_any("looked like a negative number literal but was actually malformed identifier").left() - } - } - }, - None => { - unexpected_any("negative number literal with ~ but without the decimal point that ~ number literals require").left() - } - } - }) -} - - pub fn negative_int_or_frac_literal() -> impl Parser where I: Stream, I::Error: ParseError @@ -978,21 +913,15 @@ where I: Stream, (Ok(int_val), None) => { value(Expr::Int(-int_val as i64)).right() }, - (Ok(int_val), Some(nums)) => { + (Ok(_), Some(nums)) => { let decimal_str: String = nums.into_iter().collect(); - // calculate numerator and denominator - // e.g. 123.45 == 12345 / 100 - let denom = (10 as i64).pow(decimal_str.len() as u32); - match decimal_str.parse::() { - Ok(decimal) => { - // Only the numerator may ever be signed! - let numerator = (int_val * denom) + (decimal as i64); - - value(Expr::Frac(-numerator, denom)).right() + match format!("{}.{}", int_str, decimal_str).parse::() { + Ok(float) => { + value(Expr::Float(-float)).right() }, Err(_) => { - unexpected_any("non-digit characters after decimal point in a negative number literal").left() + unexpected_any("looked like a negative Float literal but was actually malformed identifier").left() } } }, @@ -1020,16 +949,10 @@ where I: Stream, }, (Ok(int_val), Some(nums)) => { let decimal_str: String = nums.into_iter().collect(); - // calculate numerator and denominator - // e.g. 123.45 == 12345 / 100 - let denom = (10 as i64).pow(decimal_str.len() as u32); - match decimal_str.parse::() { - Ok(decimal) => { - // Only the numerator may ever be signed! - let numerator = (int_val * denom) + (decimal as i64); - - value(Expr::Frac(numerator, denom)).right() + match format!("{}.{}", int_str, decimal_str).parse::() { + Ok(float) => { + value(Expr::Float(float)).right() }, Err(_) => { unexpected_any("non-digit characters after decimal point in a number literal").left() @@ -1087,27 +1010,17 @@ where I: Stream, match ( int_str.parse::(), decimals ) { (Ok(int_val), None) => { if is_positive { - value(Pattern::Integer(int_val as i64)).right() + value(Pattern::IntLiteral(int_val as i64)).right() } else { - value(Pattern::Integer(-int_val as i64)).right() + value(Pattern::IntLiteral(-int_val as i64)).right() } }, (Ok(int_val), Some(nums)) => { let decimal_str: String = nums.into_iter().collect(); - // calculate numerator and denominator - // e.g. 123.45 == 12345 / 100 - let denom = (10 as i64).pow(decimal_str.len() as u32); - match decimal_str.parse::() { - Ok(decimal) => { - // Only the numerator may ever be signed. - let numerator = (int_val * denom) + (decimal as i64); - - if is_positive { - value(Pattern::Fraction(numerator, denom)).right() - } else { - value(Pattern::Fraction(-numerator, denom)).right() - } + match format!("{}.{}", int_str, decimal_str).parse::() { + Ok(float) => { + value(Pattern::FloatLiteral(float)).right() }, Err(_) => { unexpected_any("non-digit characters after decimal point in a number literal").left() diff --git a/src/solve.rs b/src/solve.rs index c74b6d4939..e916fcf7c4 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -22,7 +22,7 @@ use types::Type::{self, *}; type Env = ImMap; pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { - println!("\nSolving:\n\n\t{:?}\n\n", constraint); + // println!("\nSolving:\n\n\t{:?}\n\n", constraint); match constraint { True => (), Eq(typ, expected_type, region) => { diff --git a/src/subs.rs b/src/subs.rs index 8fc4c70c24..34455faeae 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -151,6 +151,6 @@ pub enum FlatType { #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum Builtin { - Str, Int, Frac, Approx, + Str, Int, Float, EmptyRecord, } diff --git a/src/types.rs b/src/types.rs index 5b2c12797f..4eb444713a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -75,7 +75,8 @@ pub enum Reason { NamedFnCall(String /* function name */, u8 /* arity */), OperatorArg(Operator, ArgSide), OperatorRet(Operator), - FractionalLiteral, + FloatLiteral, + IntLiteral, InterpolatedStringVar, ElemInList, } diff --git a/src/unify.rs b/src/unify.rs index 516bef76f0..89ea99f69e 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -33,7 +33,7 @@ pub fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descript } }; - println!("\nUnifying:\n\n\t{:?}\n\n\t{:?}\n\n\t-----\n\n\t{:?}\n\n", left.content, right.content, answer.content); + // println!("\nUnifying:\n\n\t{:?}\n\n\t{:?}\n\n\t-----\n\n\t{:?}\n\n", left.content, right.content, answer.content); answer } diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 15b2d6a049..a19ca9f328 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -38,7 +38,7 @@ pub fn zero_loc_expr(expr: Expr) -> Expr { use roc::expr::Expr::*; match expr { - Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | EmptyList => expr, + Int(_) | Float(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | EmptyList => expr, InterpolatedStr(pairs, string) => InterpolatedStr(pairs.into_iter().map(|( prefix, ident )| ( prefix, zero_loc(ident))).collect(), string), List(elems) => { let zeroed_elems = @@ -78,7 +78,7 @@ pub fn zero_loc_pattern(loc_pattern: Located) -> Located { let pattern = loc_pattern.value; match pattern { - Identifier(_) | Integer(_) | Fraction(_, _) | ExactString(_) | EmptyRecordLiteral | Underscore => loc(pattern), + Identifier(_) | IntLiteral(_) | FloatLiteral(_) | ExactString(_) | EmptyRecordLiteral | Underscore => loc(pattern), Variant(loc_name, None) => loc(Variant(loc(loc_name.value), None)), Variant(loc_name, Some(opt_located_patterns)) => diff --git a/tests/test_infer.rs b/tests/test_infer.rs index 9913a39893..600a6d4242 100644 --- a/tests/test_infer.rs +++ b/tests/test_infer.rs @@ -74,12 +74,12 @@ mod test_infer { #[test] fn int_literal() { - infer_eq("5", "Num.Num *"); + infer_eq("5", "Int"); } #[test] fn fractional_literal() { - infer_eq("0.5", "Num.Num (Num.Fractional *)"); + infer_eq("0.5", "Float"); } #[test] @@ -112,7 +112,7 @@ mod test_infer { indoc!(r#" [] "#), - "List.List *" + "List *" ); } @@ -122,7 +122,7 @@ mod test_infer { indoc!(r#" [[]] "#), - "List.List (List.List *)" + "List (List *)" ); } @@ -132,7 +132,7 @@ mod test_infer { indoc!(r#" [[[]]] "#), - "List.List (List.List (List.List *))" + "List (List (List *))" ); } @@ -142,47 +142,47 @@ mod test_infer { indoc!(r#" [ [], [ [] ] ] "#), - "List.List (List.List (List.List *))" + "List (List (List *))" ); } #[test] - fn list_of_one_num() { + fn list_of_one_int() { infer_eq( indoc!(r#" [42] "#), - "List.List (Num.Num *)" + "List Int" ); } #[test] - fn triple_nested_num_list() { + fn triple_nested_int_list() { infer_eq( indoc!(r#" [[[ 5 ]]] "#), - "List.List (List.List (List.List (Num.Num *)))" + "List (List (List Int))" ); } #[test] - fn list_of_nums() { + fn list_of_ints() { infer_eq( indoc!(r#" [ 1, 2, 3 ] "#), - "List.List (Num.Num *)" + "List Int" ); } #[test] - fn nested_list_of_nums() { + fn nested_list_of_ints() { infer_eq( indoc!(r#" [ [ 1 ], [ 2, 3 ] ] "#), - "List.List (List.List (Num.Num *))" + "List (List Int)" ); } @@ -192,7 +192,7 @@ mod test_infer { indoc!(r#" [ "cowabunga" ] "#), - "List.List String.String" + "List String.String" ); } @@ -202,7 +202,7 @@ mod test_infer { indoc!(r#" [[[ "foo" ]]] "#), - "List.List (List.List (List.List String.String))" + "List (List (List String.String))" ); } @@ -212,7 +212,7 @@ mod test_infer { indoc!(r#" [ "foo", "bar" ] "#), - "List.List String.String" + "List String.String" ); } @@ -239,7 +239,7 @@ mod test_infer { indoc!(r#" [ "foo", 5 ] "#), - "List.List " + "List " ); } @@ -249,7 +249,7 @@ mod test_infer { indoc!(r#" [ [ "foo", 5 ] ] "#), - "List.List (List.List )" + "List (List )" ); } @@ -259,7 +259,7 @@ mod test_infer { indoc!(r#" [ [ 1 ], [ [] ] ] "#), - "List.List (List.List )" + "List (List )" ); } @@ -276,12 +276,12 @@ mod test_infer { } #[test] - fn two_arg_return_num() { + fn two_arg_return_int() { infer_eq( indoc!(r#" \_ _ -> 42 "#), - "*, * -> Num.Num *" + "*, * -> Int" ); } @@ -341,7 +341,7 @@ mod test_infer { func "#), - "*, * -> Num.Num *" + "*, * -> Int" ); } @@ -397,7 +397,7 @@ mod test_infer { c "#), - "Num.Num *" + "Int" ); } @@ -411,7 +411,7 @@ mod test_infer { alwaysFive "stuff" "#), - "Num.Num *" + "Int" ); } @@ -423,7 +423,7 @@ mod test_infer { enlist 5 "#), - "List.List (Num.Num *)" + "List Int" ); } @@ -474,7 +474,7 @@ mod test_infer { indoc!(r#" \l r -> l / r "#), - "Num.Num Float.FloatingPoint, Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint" + "Float, Float -> Float" ); } @@ -484,7 +484,7 @@ mod test_infer { indoc!(r#" 1 / 2 "#), - "Num.Num Float.FloatingPoint" + "Float" ); } @@ -494,7 +494,7 @@ mod test_infer { // indoc!(r#" // 1 + 2 // "#), - // "Num.Num *" + // "Num *" // ); // } diff --git a/tests/test_parse.rs b/tests/test_parse.rs index f7a61f7472..83a2e28517 100644 --- a/tests/test_parse.rs +++ b/tests/test_parse.rs @@ -295,18 +295,14 @@ mod test_parse { // // 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_float<'a>(expected: f64, actual: &str) { + assert_eq!(Ok((Float(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"); @@ -318,25 +314,15 @@ mod test_parse { } #[test] - fn positive_approx() { - expect_parsed_approx(123.4, "~123.4"); + fn positive_float() { + expect_parsed_float(123.45, "123.45"); + expect_parsed_float(42.00, "42.00"); } #[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"); + fn negative_float() { + expect_parsed_float(-1234.567, "-1234.567"); + expect_parsed_float(-192.0, "-192.0"); } #[test] @@ -347,10 +333,10 @@ mod test_parse { #[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"); + expect_parsed_float(-1234.567, "-1_23_4.567"); + expect_parsed_float(-192.0, "-19_2.0"); + expect_parsed_float(123.45, "1_2_3.45"); + expect_parsed_float(42.00, "4_2.00"); } #[test] @@ -910,7 +896,7 @@ mod test_parse { Case( loc_box(Int(1)), vec![ - ( loc(Integer(2)), loc(Int(3)) ), + ( loc(IntLiteral(2)), loc(Int(3)) ), ] ), "".to_string() @@ -958,8 +944,8 @@ mod test_parse { 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))])) ), + ( loc(IntLiteral(2)), loc(call_by_name("foo", vec![loc(Int(9))])) ), + ( loc(IntLiteral(1)), loc(call_by_name("bar", vec![loc(Int(8))])) ), ] ), "".to_string()