[WIP] Update can & pattern to store numbers as validated strs

This commit is contained in:
Jared Ramirez 2021-08-16 16:16:10 -07:00
parent 6741d93517
commit adabf70132
4 changed files with 164 additions and 59 deletions

View file

@ -285,7 +285,11 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i64::MAX.into());
let body = Int(
int_var,
int_precision_var,
i64::MAX.to_string().into_boxed_str(),
);
Def {
annotation: None,
@ -300,7 +304,11 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i64::MIN.into());
let body = Int(
int_var,
int_precision_var,
i64::MIN.to_string().into_boxed_str(),
);
Def {
annotation: None,
@ -685,7 +693,10 @@ fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::Eq,
args: vec![
(arg_var, Var(Symbol::ARG_1)),
(arg_var, Num(unbound_zero_var, 0)),
(
arg_var,
Num(unbound_zero_var, "0".to_string().into_boxed_str()),
),
],
ret_var: bool_var,
};
@ -708,7 +719,10 @@ fn num_is_negative(symbol: Symbol, var_store: &mut VarStore) -> Def {
let body = RunLowLevel {
op: LowLevel::NumGt,
args: vec![
(arg_var, Num(unbound_zero_var, 0)),
(
arg_var,
Num(unbound_zero_var, "0".to_string().into_boxed_str()),
),
(arg_var, Var(Symbol::ARG_1)),
],
ret_var: bool_var,
@ -733,7 +747,10 @@ fn num_is_positive(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NumGt,
args: vec![
(arg_var, Var(Symbol::ARG_1)),
(arg_var, Num(unbound_zero_var, 0)),
(
arg_var,
Num(unbound_zero_var, "0".to_string().into_boxed_str()),
),
],
ret_var: bool_var,
};
@ -756,14 +773,24 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
let body = RunLowLevel {
op: LowLevel::Eq,
args: vec![
(arg_var, Int(var_store.fresh(), var_store.fresh(), 1)),
(
arg_var,
Int(
var_store.fresh(),
var_store.fresh(),
1.to_string().into_boxed_str(),
),
),
(
arg_var,
RunLowLevel {
op: LowLevel::NumRemUnchecked,
args: vec![
(arg_var, Var(Symbol::ARG_1)),
(arg_var, Num(unbound_two_var, 2)),
(
arg_var,
Num(unbound_two_var, "2".to_string().into_boxed_str()),
),
],
ret_var: arg_var,
},
@ -790,14 +817,14 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def {
let body = RunLowLevel {
op: LowLevel::Eq,
args: vec![
(arg_var, Num(arg_num_var, 0)),
(arg_var, Num(arg_num_var, "0".to_string().into_boxed_str())),
(
arg_var,
RunLowLevel {
op: LowLevel::NumRemUnchecked,
args: vec![
(arg_var, Var(Symbol::ARG_1)),
(arg_var, Num(arg_num_var, 2)),
(arg_var, Num(arg_num_var, "2".to_string().into_boxed_str())),
],
ret_var: arg_var,
},
@ -851,7 +878,14 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NumGte,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
(
float_var,
Float(
unbound_zero_var,
precision_var,
"0.0".to_string().into_boxed_str(),
),
),
],
ret_var: bool_var,
}),
@ -897,7 +931,14 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NumGt,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
(
float_var,
Float(
unbound_zero_var,
precision_var,
"0.0".to_string().into_boxed_str(),
),
),
],
ret_var: bool_var,
}),
@ -1127,7 +1168,11 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
let int_var = var_store.fresh();
let int_precision_var = var_store.fresh();
let body = Int(int_var, int_precision_var, i128::MAX);
let body = Int(
int_var,
int_precision_var,
i128::MAX.to_string().into_boxed_str(),
);
let std = roc_builtins::std::types();
let solved = std.get(&symbol).unwrap();
@ -1160,7 +1205,10 @@ fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
let body = RunLowLevel {
op: LowLevel::Eq,
args: vec![
(len_var, Num(unbound_zero_var, 0)),
(
len_var,
Num(unbound_zero_var, "0".to_string().into_boxed_str()),
),
(
len_var,
RunLowLevel {
@ -2039,7 +2087,10 @@ fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(list_var, Var(Symbol::ARG_1)),
(closure_var, list_sum_add(num_var, var_store)),
(num_var, Num(var_store.fresh(), 0)),
(
num_var,
Num(var_store.fresh(), "0".to_string().into_boxed_str()),
),
],
ret_var,
};
@ -2081,7 +2132,10 @@ fn list_product(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(list_var, Var(Symbol::ARG_1)),
(closure_var, list_product_mul(num_var, var_store)),
(num_var, Num(var_store.fresh(), 1)),
(
num_var,
Num(var_store.fresh(), "1".to_string().into_boxed_str()),
),
],
ret_var,
};
@ -2559,7 +2613,10 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NotEq,
args: vec![
(num_var, Var(Symbol::ARG_2)),
(num_var, Num(unbound_zero_var, 0)),
(
num_var,
Num(unbound_zero_var, "0".to_string().into_boxed_str()),
),
],
ret_var: bool_var,
},
@ -2662,7 +2719,14 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::NotEq,
args: vec![
(num_var, Var(Symbol::ARG_2)),
(num_var, Float(unbound_zero_var, precision_var, 0.0)),
(
num_var,
Float(
unbound_zero_var,
precision_var,
"0.0".to_string().into_boxed_str(),
),
),
],
ret_var: bool_var,
},
@ -2727,7 +2791,11 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
(num_var, Var(Symbol::ARG_2)),
(
num_var,
Int(unbound_zero_var, unbound_zero_precision_var, 0),
Int(
unbound_zero_var,
unbound_zero_precision_var,
0.to_string().into_boxed_str(),
),
),
],
ret_var: bool_var,
@ -2797,7 +2865,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(len_var, Int(zero_var, zero_precision_var, 0)),
(
len_var,
Int(zero_var, zero_precision_var, 0.to_string().into_boxed_str()),
),
(
len_var,
RunLowLevel {
@ -2821,7 +2892,14 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
op: LowLevel::ListGetUnsafe,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(len_var, Int(zero_var, zero_precision_var, 0)),
(
len_var,
Int(
zero_var,
zero_precision_var,
0.to_string().into_boxed_str(),
),
),
],
ret_var: list_elem_var,
},
@ -2878,7 +2956,10 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(len_var, Int(num_var, num_precision_var, 0)),
(
len_var,
Int(num_var, num_precision_var, 0.to_string().into_boxed_str()),
),
(
len_var,
RunLowLevel {
@ -2917,7 +2998,14 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
ret_var: len_var,
},
),
(arg_var, Int(num_var, num_precision_var, 1)),
(
arg_var,
Int(
num_var,
num_precision_var,
1.to_string().into_boxed_str(),
),
),
],
ret_var: len_var,
},

View file

@ -3,8 +3,8 @@ use crate::builtins::builtin_defs_map;
use crate::def::{can_defs_with_return, Def};
use crate::env::Env;
use crate::num::{
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
int_expr_from_result, num_expr_from_result,
finish_parsing_base, finish_parsing_int, float_expr_from_result, int_expr_from_result,
num_expr_from_result, validate_float_str,
};
use crate::pattern::{canonicalize_pattern, Pattern};
use crate::procedure::References;
@ -21,7 +21,7 @@ use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::Alias;
use std::fmt::Debug;
use std::{char, i64, u32};
use std::{char, u32};
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Output {
@ -52,11 +52,11 @@ pub enum Expr {
// Num stores the `a` variable in `Num a`. Not the same as the variable
// stored in Int and Float below, which is strictly for better error messages
Num(Variable, i64),
Num(Variable, Box<str>),
// Int and Float store a variable to generate better error messages
Int(Variable, Variable, i128),
Float(Variable, Variable, f64),
Int(Variable, Variable, Box<str>),
Float(Variable, Variable, Box<str>),
Str(Box<str>),
List {
elem_var: Variable,
@ -207,13 +207,22 @@ pub fn canonicalize_expr<'a>(
let (expr, output) = match expr {
ast::Expr::Num(string) => {
let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env);
let answer = num_expr_from_result(
var_store,
finish_parsing_int(*string).map(|_| *string),
region,
env,
);
(answer, Output::default())
}
ast::Expr::Float(string) => {
let answer =
float_expr_from_result(var_store, finish_parsing_float(string), region, env);
ast::Expr::Float(str) => {
let answer = float_expr_from_result(
var_store,
validate_float_str(str).map(|()| *str),
region,
env,
);
(answer, Output::default())
}
@ -795,8 +804,16 @@ pub fn canonicalize_expr<'a>(
is_negative,
} => {
// the minus sign is added before parsing, to get correct overflow/underflow behavior
let result = finish_parsing_base(string, *base, *is_negative);
let answer = int_expr_from_result(var_store, result, region, *base, env);
let answer = match finish_parsing_base(string, *base, *is_negative) {
Ok(int) => {
// Done in this kinda round about way with intermediate variables
// to keep borrowed values around and make this compile
let int_string = int.to_string();
let int_str = int_string.as_str();
int_expr_from_result(var_store, Ok(int_str), region, *base, env)
}
Err(e) => int_expr_from_result(var_store, Err(e), region, *base, env),
};
(answer, Output::default())
}

View file

@ -16,12 +16,12 @@ use std::i64;
#[inline(always)]
pub fn num_expr_from_result(
var_store: &mut VarStore,
result: Result<i64, (&str, IntErrorKind)>,
result: Result<&str, (&str, IntErrorKind)>,
region: Region,
env: &mut Env,
) -> Expr {
match result {
Ok(int) => Expr::Num(var_store.fresh(), int),
Ok(str) => Expr::Num(var_store.fresh(), (*str).into()),
Err((raw, error)) => {
// (Num *) compiles to Int if it doesn't
// get specialized to something else first,
@ -38,14 +38,14 @@ pub fn num_expr_from_result(
#[inline(always)]
pub fn int_expr_from_result(
var_store: &mut VarStore,
result: Result<i64, (&str, IntErrorKind)>,
result: Result<&str, (&str, IntErrorKind)>,
region: Region,
base: Base,
env: &mut Env,
) -> Expr {
// Int stores a variable to generate better error messages
match result {
Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int.into()),
Ok(str) => Expr::Int(var_store.fresh(), var_store.fresh(), (*str).into()),
Err((raw, error)) => {
let runtime_error = InvalidInt(error, base, region, raw.into());
@ -59,13 +59,13 @@ pub fn int_expr_from_result(
#[inline(always)]
pub fn float_expr_from_result(
var_store: &mut VarStore,
result: Result<f64, (&str, FloatErrorKind)>,
result: Result<&str, (&str, FloatErrorKind)>,
region: Region,
env: &mut Env,
) -> Expr {
// Float stores a variable to generate better error messages
match result {
Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float),
Ok(str) => Expr::Float(var_store.fresh(), var_store.fresh(), (*str).into()),
Err((raw, error)) => {
let runtime_error = InvalidFloat(error, region, raw.into());
@ -77,10 +77,12 @@ pub fn float_expr_from_result(
}
#[inline(always)]
pub fn finish_parsing_int(raw: &str) -> Result<i64, (&str, IntErrorKind)> {
pub fn finish_parsing_int(raw: &str) -> Result<&str, (&str, IntErrorKind)> {
// Ignore underscores.
let radix = 10;
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e.kind))
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix)
.map(|_| raw)
.map_err(|e| (raw, e.kind))
}
#[inline(always)]
@ -106,10 +108,10 @@ pub fn finish_parsing_base(
}
#[inline(always)]
pub fn finish_parsing_float(raw: &str) -> Result<f64, (&str, FloatErrorKind)> {
pub fn validate_float_str(raw: &str) -> Result<(), (&str, FloatErrorKind)> {
// Ignore underscores.
match raw.replace("_", "").parse::<f64>() {
Ok(float) if float.is_finite() => Ok(float),
Ok(float) if float.is_finite() => Ok(()),
Ok(float) => {
if float.is_sign_positive() {
Err((raw, FloatErrorKind::PositiveInfinity))

View file

@ -1,6 +1,6 @@
use crate::env::Env;
use crate::expr::{canonicalize_expr, unescape_char, Expr, Output};
use crate::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
use crate::num::{finish_parsing_base, finish_parsing_int, validate_float_str};
use crate::scope::Scope;
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::symbol::Symbol;
@ -25,9 +25,9 @@ pub enum Pattern {
ext_var: Variable,
destructs: Vec<Located<RecordDestruct>>,
},
IntLiteral(Variable, i64),
NumLiteral(Variable, i64),
FloatLiteral(Variable, f64),
IntLiteral(Variable, Box<str>),
NumLiteral(Variable, Box<str>),
FloatLiteral(Variable, Box<str>),
StrLiteral(Box<str>),
Underscore,
@ -185,13 +185,13 @@ pub fn canonicalize_pattern<'a>(
}
}
FloatLiteral(string) => match pattern_type {
WhenBranch => match finish_parsing_float(string) {
FloatLiteral(str) => match pattern_type {
WhenBranch => match validate_float_str(str) {
Err(_error) => {
let problem = MalformedPatternProblem::MalformedFloat;
malformed_pattern(env, problem, region)
}
Ok(float) => Pattern::FloatLiteral(var_store.fresh(), float),
Ok(_float) => Pattern::FloatLiteral(var_store.fresh(), (*str).into()),
},
ptype => unsupported_pattern(env, ptype, region),
},
@ -201,13 +201,13 @@ pub fn canonicalize_pattern<'a>(
TopLevelDef | DefExpr => bad_underscore(env, region),
},
NumLiteral(string) => match pattern_type {
WhenBranch => match finish_parsing_int(string) {
NumLiteral(str) => match pattern_type {
WhenBranch => match finish_parsing_int(str) {
Err(_error) => {
let problem = MalformedPatternProblem::MalformedInt;
malformed_pattern(env, problem, region)
}
Ok(int) => Pattern::NumLiteral(var_store.fresh(), int),
Ok(_int) => Pattern::NumLiteral(var_store.fresh(), (*str).into()),
},
ptype => unsupported_pattern(env, ptype, region),
},
@ -223,11 +223,9 @@ pub fn canonicalize_pattern<'a>(
malformed_pattern(env, problem, region)
}
Ok(int) => {
if *is_negative {
Pattern::IntLiteral(var_store.fresh(), -int)
} else {
Pattern::IntLiteral(var_store.fresh(), int)
}
let sign_str = if *is_negative { "-" } else { "" };
let int_str = format!("{}{}", sign_str, int.to_string()).into_boxed_str();
Pattern::IntLiteral(var_store.fresh(), int_str)
}
},
ptype => unsupported_pattern(env, ptype, region),