This commit is contained in:
Folkert 2020-07-06 20:38:10 +02:00
parent a0d39ff10f
commit e595c14fae
6 changed files with 213 additions and 54 deletions

View File

@ -178,12 +178,13 @@ 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), env);
let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env);
(answer, Output::default())
}
ast::Expr::Float(string) => {
let answer = float_expr_from_result(var_store, finish_parsing_float(string), env);
let answer =
float_expr_from_result(var_store, finish_parsing_float(string), region, env);
(answer, Output::default())
}
@ -630,7 +631,7 @@ pub fn canonicalize_expr<'a>(
result = result.map(i64::neg);
}
let answer = int_expr_from_result(var_store, result, env);
let answer = int_expr_from_result(var_store, result, region, env);
(answer, Output::default())
}

View File

@ -3,22 +3,30 @@ use crate::expr::Expr;
use roc_parse::ast::Base;
use roc_problem::can::Problem;
use roc_problem::can::RuntimeError::*;
use roc_region::all::Region;
use roc_types::subs::VarStore;
use std::i64;
use std::num::{ParseFloatError, ParseIntError};
// TODO distinguish number parsing failures
//
// We're waiting for rust here, see https://github.com/rust-lang/rust/issues/22639
// There is a nightly API for exposing the parse error.
#[inline(always)]
pub fn num_expr_from_result(
var_store: &mut VarStore,
result: Result<i64, &str>,
result: Result<i64, (&str, ParseIntError)>,
region: Region,
env: &mut Env,
) -> Expr {
match result {
Ok(int) => Expr::Num(var_store.fresh(), int),
Err(raw) => {
Err((raw, _error)) => {
// (Num *) compiles to Int if it doesn't
// get specialized to something else first,
// so use int's overflow bounds here.
let runtime_error = IntOutsideRange(raw.into());
let runtime_error = IntOutsideRange(raw.into(), region);
env.problem(Problem::RuntimeError(runtime_error.clone()));
@ -30,14 +38,15 @@ pub fn num_expr_from_result(
#[inline(always)]
pub fn int_expr_from_result(
var_store: &mut VarStore,
result: Result<i64, &str>,
result: Result<i64, (&str, ParseIntError)>,
region: Region,
env: &mut Env,
) -> Expr {
// Int stores a variable to generate better error messages
match result {
Ok(int) => Expr::Int(var_store.fresh(), int),
Err(raw) => {
let runtime_error = IntOutsideRange(raw.into());
Err((raw, _error)) => {
let runtime_error = IntOutsideRange(raw.into(), region);
env.problem(Problem::RuntimeError(runtime_error.clone()));
@ -49,14 +58,15 @@ pub fn int_expr_from_result(
#[inline(always)]
pub fn float_expr_from_result(
var_store: &mut VarStore,
result: Result<f64, &str>,
result: Result<f64, (&str, ParseFloatError)>,
region: Region,
env: &mut Env,
) -> Expr {
// Float stores a variable to generate better error messages
match result {
Ok(float) => Expr::Float(var_store.fresh(), float),
Err(raw) => {
let runtime_error = FloatOutsideRange(raw.into());
Err((raw, _error)) => {
let runtime_error = FloatOutsideRange(raw.into(), region);
env.problem(Problem::RuntimeError(runtime_error.clone()));
@ -66,13 +76,13 @@ pub fn float_expr_from_result(
}
#[inline(always)]
pub fn finish_parsing_int(raw: &str) -> Result<i64, &str> {
pub fn finish_parsing_int(raw: &str) -> Result<i64, (&str, ParseIntError)> {
// Ignore underscores.
raw.replace("_", "").parse::<i64>().map_err(|_| raw)
raw.replace("_", "").parse::<i64>().map_err(|e| (raw, e))
}
#[inline(always)]
pub fn finish_parsing_base(raw: &str, base: Base) -> Result<i64, &str> {
pub fn finish_parsing_base(raw: &str, base: Base) -> Result<i64, (&str, ParseIntError)> {
let radix = match base {
Base::Hex => 16,
Base::Octal => 8,
@ -80,14 +90,15 @@ pub fn finish_parsing_base(raw: &str, base: Base) -> Result<i64, &str> {
};
// Ignore underscores.
i64::from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|_| raw)
i64::from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e))
}
#[inline(always)]
pub fn finish_parsing_float(raw: &str) -> Result<f64, &str> {
pub fn finish_parsing_float(raw: &str) -> Result<f64, (&str, ParseFloatError)> {
// Ignore underscores.
match raw.replace("_", "").parse::<f64>() {
Ok(float) if float.is_finite() => Ok(float),
_ => Err(raw),
Ok(_float) => panic!("TODO handle infinite float literal"),
Err(e) => Err((raw, e)),
}
}

View File

@ -96,7 +96,7 @@ pub fn canonicalize_pattern<'a>(
use roc_parse::ast::Pattern::*;
use PatternType::*;
let can_pattern = match pattern {
let can_pattern = match dbg!(pattern) {
Identifier(name) => match scope.introduce(
(*name).into(),
&env.exposed_ident_ids,

View File

@ -72,7 +72,6 @@ pub enum RuntimeError {
UnsupportedPattern(Region),
// Example: when 1 is 1.X -> 32
MalformedPattern(MalformedPatternProblem, Region),
UnrecognizedFunctionName(Located<InlinableString>),
LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>),
ValueNotExposed {
module_name: InlinableString,
@ -87,8 +86,8 @@ pub enum RuntimeError {
InvalidPrecedence(PrecedenceProblem, Region),
MalformedIdentifier(Box<str>, Region),
MalformedClosure(Region),
FloatOutsideRange(Box<str>),
IntOutsideRange(Box<str>),
FloatOutsideRange(Box<str>, Region),
IntOutsideRange(Box<str>, Region),
InvalidHex(std::num::ParseIntError, Box<str>),
InvalidOctal(std::num::ParseIntError, Box<str>),
InvalidBinary(std::num::ParseIntError, Box<str>),

View File

@ -372,39 +372,46 @@ fn pretty_runtime_error<'b>(
hint,
])
}
other => {
// // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
// UnsupportedPattern(Region),
// UnrecognizedFunctionName(Located<InlinableString>),
// SymbolNotExposed {
// module_name: InlinableString,
// ident: InlinableString,
// region: Region,
// },
// ModuleNotImported {
// module_name: InlinableString,
// ident: InlinableString,
// region: Region,
// },
// InvalidPrecedence(PrecedenceProblem, Region),
// MalformedIdentifier(Box<str>, Region),
// MalformedClosure(Region),
// FloatOutsideRange(Box<str>),
// IntOutsideRange(Box<str>),
// InvalidHex(std::num::ParseIntError, Box<str>),
// InvalidOctal(std::num::ParseIntError, Box<str>),
// InvalidBinary(std::num::ParseIntError, Box<str>),
// QualifiedPatternIdent(InlinableString),
// CircularDef(
// Vec<Located<Ident>>,
// Vec<(Region /* pattern */, Region /* expr */)>,
// ),
//
// /// When the author specifies a type annotation but no implementation
// NoImplementation,
todo!("TODO implement run time error reporting for {:?}", other)
RuntimeError::UnsupportedPattern(_) => {
todo!("unsupported patterns are currently not parsed!")
}
RuntimeError::ValueNotExposed { .. } => todo!("value not exposed"),
RuntimeError::ModuleNotImported { .. } => todo!("module not imported"),
RuntimeError::InvalidPrecedence(_, _) => {
// do nothing, reported with PrecedenceProblem
unreachable!()
}
RuntimeError::MalformedIdentifier(_, _) => {
todo!("malformed identifier, currently gives a parse error and thus is unreachable")
}
RuntimeError::MalformedClosure(_) => todo!(""),
RuntimeError::FloatOutsideRange(_raw_str, _region) => todo!(""),
RuntimeError::IntOutsideRange(raw_str, region) => {
let big_or_small = if raw_str.starts_with('-') {
"small"
} else {
"big"
};
let hint = alloc
.hint()
.append(alloc.reflow("Learn more about number literals at TODO"));
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This integer literal is too "),
alloc.text(big_or_small),
alloc.reflow(":"),
]),
alloc.region(region),
alloc.reflow("Roc uses signed 64-bit integers, allowing values between 9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."),
hint,
])
}
RuntimeError::InvalidHex(_, _) => todo!("invalid hex, unreachable"),
RuntimeError::InvalidOctal(_, _) => todo!("invalid octal, unreachable"),
RuntimeError::InvalidBinary(_, _) => todo!("invalid binary, unreachable"),
RuntimeError::NoImplementation => todo!("no implementation, unreachable"),
}
}

View File

@ -3108,4 +3108,145 @@ mod test_reporting {
),
)
}
#[test]
fn integer_out_of_range() {
report_problem_as(
indoc!(
r#"
x = 9_223_372_036_854_775_807_000
y = -9_223_372_036_854_775_807_000
x + y
"#
),
indoc!(
r#"
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too small:
3 y = -9_223_372_036_854_775_807_000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
1 x = 9_223_372_036_854_775_807_000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
"#
),
)
}
#[test]
fn integer_malformed() {
// the generated messages here are incorrect. Waiting for a rust nightly feature to land,
// see https://github.com/rust-lang/rust/issues/22639
// this test is here to spot regressions in error reporting
report_problem_as(
indoc!(
r#"
dec = 100A
hex = 0xZZZ
oct = 0o9
bin = 0b2
dec + hex + oct + bin
"#
),
indoc!(
r#"
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
3 hex = 0xZZZ
^^^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
5 oct = 0o9
^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
7 bin = 0b2
^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
1 dec = 100A
^^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
"#
),
)
}
#[test]
fn precedence_conflict() {
report_problem_as(
indoc!(
r#"
foo@bar
"#
),
indoc!(
r#"
-- SYNTAX PROBLEM --------------------------------------------------------------
This integer literal is too big:
1 dec = 100A
^^^^
Roc uses signed 64-bit integers, allowing values between
9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
Hint: Learn more about number literals at TODO
"#
),
)
}
}