Frac+Approx --> Float

This commit is contained in:
Richard Feldman 2019-09-01 00:50:11 -04:00
parent 695d1eb467
commit 04595a9e71
11 changed files with 112 additions and 207 deletions

View file

@ -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<Vec<Located<Pattern>>>),
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, &region, &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, &region, &loc_pattern.value)
}
},

View file

@ -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<Located<Expr>>, bound_vars: &BoundTypeVars, subs: &mut Su
And(constraints)
}
fn fractional(subs: &mut Subs, expected: Expected<Type>, 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<Type>, 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<Type>, 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<Type>, 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 {
Type::Apply(module_name.to_string(), type_name.to_string(), args)
}

View file

@ -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<VariantName>, Option<Vec<Located<Pattern>>>),
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()

View file

@ -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<Item = char, Position = IndentablePosition>,
)
}
pub fn negative_approx_literal<I>() -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
{
char('-')
.with(digits_before_decimal())
.and(optional(char('.').with(digits_after_decimal())))
.then(|(int_digits, decimals): (Vec<char>, Option<Vec<char>>)| {
// 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::<f64>() {
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<I>() -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
{
digits_before_decimal()
.and(optional(char('.').with(digits_after_decimal())))
.then(|(int_digits, decimals): (Vec<char>, Option<Vec<char>>)| {
// 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::<f64>() {
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<I>() -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
@ -978,21 +913,15 @@ where I: Stream<Item = char, Position = IndentablePosition>,
(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::<u32>() {
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::<f64>() {
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<Item = char, Position = IndentablePosition>,
},
(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::<u32>() {
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::<f64>() {
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<Item = char, Position = IndentablePosition>,
match ( int_str.parse::<i64>(), 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::<u32>() {
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::<f64>() {
Ok(float) => {
value(Pattern::FloatLiteral(float)).right()
},
Err(_) => {
unexpected_any("non-digit characters after decimal point in a number literal").left()

View file

@ -22,7 +22,7 @@ use types::Type::{self, *};
type Env = ImMap<Symbol, Variable>;
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) => {

View file

@ -151,6 +151,6 @@ pub enum FlatType {
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Builtin {
Str, Int, Frac, Approx,
Str, Int, Float,
EmptyRecord,
}

View file

@ -75,7 +75,8 @@ pub enum Reason {
NamedFnCall(String /* function name */, u8 /* arity */),
OperatorArg(Operator, ArgSide),
OperatorRet(Operator),
FractionalLiteral,
FloatLiteral,
IntLiteral,
InterpolatedStringVar,
ElemInList,
}

View file

@ -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
}

View file

@ -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<Pattern>) -> Located<Pattern> {
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)) =>

View file

@ -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 <type mismatch>"
"List <type mismatch>"
);
}
@ -249,7 +249,7 @@ mod test_infer {
indoc!(r#"
[ [ "foo", 5 ] ]
"#),
"List.List (List.List <type mismatch>)"
"List (List <type mismatch>)"
);
}
@ -259,7 +259,7 @@ mod test_infer {
indoc!(r#"
[ [ 1 ], [ [] ] ]
"#),
"List.List (List.List <type mismatch>)"
"List (List <type mismatch>)"
);
}
@ -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 *"
// );
// }

View file

@ -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()