General cleanup.

This commit is contained in:
Stephen Chung 2020-03-11 11:03:18 +08:00
parent 708c285a0a
commit 880bce1114
9 changed files with 124 additions and 116 deletions

View File

@ -18,6 +18,7 @@ include = [
num-traits = "*"
[features]
#default = ["no_index", "no_float", "only_i32", "no_stdlib", "unchecked"]
default = []
debug_msgs = []
unchecked = []

View File

@ -2,45 +2,23 @@
//! _standard library_ of utility functions.
use crate::any::Any;
use crate::engine::Engine;
use crate::fn_register::RegisterFn;
use crate::parser::INT;
#[cfg(not(feature = "unchecked"))]
use crate::{parser::Position, result::EvalAltResult, RegisterResultFn};
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
#[cfg(not(feature = "no_float"))]
use crate::engine::Engine;
use crate::fn_register::{RegisterFn, RegisterResultFn};
use crate::parser::{Position, INT};
use crate::result::EvalAltResult;
use crate::FLOAT;
use num_traits::{
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
CheckedShr, CheckedSub,
};
use std::{
fmt::{Debug, Display},
ops::{BitAnd, BitOr, BitXor, Range},
};
#[cfg(feature = "unchecked")]
use std::ops::{Shl, Shr};
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_float"))]
use std::{i32, i64};
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "only_i32"))]
use std::u32;
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
#[cfg(not(feature = "unchecked"))]
use {
num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr,
CheckedSub,
},
std::convert::TryFrom,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Shl, Shr, Sub},
{i32, i64, u32},
};
macro_rules! reg_op {
@ -162,12 +140,9 @@ impl Engine<'_> {
#[cfg(not(feature = "unchecked"))]
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
where
T: Display + CheckedDiv + PartialEq + TryFrom<i8>,
{
if y == <T as TryFrom<i8>>::try_from(0)
.map_err(|_| ())
.expect("zero should always succeed")
T: Display + CheckedDiv + PartialEq + Zero,
{
if y == T::zero() {
return Err(EvalAltResult::ErrorArithmetic(
format!("Division by zero: {} / {}", x, y),
Position::none(),
@ -191,8 +166,10 @@ impl Engine<'_> {
})
}
#[cfg(not(feature = "unchecked"))]
fn abs<T: Display + CheckedNeg + PartialOrd + From<i8>>(x: T) -> Result<T, EvalAltResult> {
if x >= 0.into() {
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, EvalAltResult> {
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
// when the number is ::MIN instead of returning ::MIN itself.
if x >= <T as Zero>::zero() {
Ok(x)
} else {
x.checked_neg().ok_or_else(|| {
@ -224,14 +201,15 @@ impl Engine<'_> {
-x
}
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
fn abs_u<T: Neg + PartialOrd + From<i8>>(x: T) -> T
fn abs_u<T>(x: T) -> <T as Neg>::Output
where
<T as Neg>::Output: Into<T>,
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
{
if x < 0.into() {
(-x).into()
// Numbers should default to zero
if x < Default::default() {
-x
} else {
x
x.into()
}
}
fn lt<T: PartialOrd>(x: T, y: T) -> bool {

View File

@ -4,8 +4,6 @@ use crate::any::{Any, AnyExt, Dynamic, Variant};
use crate::parser::{Expr, FnDef, Position, Stmt};
use crate::result::EvalAltResult;
use crate::scope::Scope;
#[cfg(not(feature = "no_index"))]
use crate::INT;
use std::{
@ -788,6 +786,9 @@ impl Engine<'_> {
.eval_index_expr(scope, lhs, idx_expr, *idx_pos)
.map(|(_, _, _, x)| x),
#[cfg(feature = "no_index")]
Expr::Index(_, _, _) => panic!("encountered an index expression during no_index!"),
// Statement block
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt),
@ -855,6 +856,8 @@ impl Engine<'_> {
Ok(Box::new(arr))
}
#[cfg(feature = "no_index")]
Expr::Array(_, _) => panic!("encountered an array during no_index!"),
Expr::FunctionCall(fn_name, args, def_val, pos) => {
let mut args = args

View File

@ -120,8 +120,9 @@ macro_rules! def_register {
const NUM_ARGS: usize = count_args!($($par)*);
if args.len() != NUM_ARGS {
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
} else {
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
}
#[allow(unused_variables, unused_mut)]
let mut drain = args.drain(..);
$(
@ -133,7 +134,6 @@ macro_rules! def_register {
// potentially clone the value, otherwise pass the reference.
let r = f($(($clone)($par)),*);
Ok(Box::new(r) as Dynamic)
}
};
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
}
@ -152,8 +152,9 @@ macro_rules! def_register {
const NUM_ARGS: usize = count_args!($($par)*);
if args.len() != NUM_ARGS {
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
} else {
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
}
#[allow(unused_variables, unused_mut)]
let mut drain = args.drain(..);
$(
@ -164,7 +165,6 @@ macro_rules! def_register {
// Call the user-supplied function using ($clone) to
// potentially clone the value, otherwise pass the reference.
Ok(f($(($clone)($par)),*))
}
};
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
}
@ -184,8 +184,9 @@ macro_rules! def_register {
const NUM_ARGS: usize = count_args!($($par)*);
if args.len() != NUM_ARGS {
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
} else {
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos));
}
#[allow(unused_variables, unused_mut)]
let mut drain = args.drain(..);
$(
@ -202,7 +203,6 @@ macro_rules! def_register {
Err(err)
}
}
}
};
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
}

View File

@ -141,6 +141,7 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Box::new(optimize_expr(*rhs, changed)),
pos,
),
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
(Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
@ -158,6 +159,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
pos,
),
},
#[cfg(feature = "no_index")]
Expr::Index(_, _, _) => panic!("encountered an index expression during no_index!"),
#[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => {
let original_len = items.len();
@ -172,6 +176,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Expr::Array(items, pos)
}
#[cfg(feature = "no_index")]
Expr::Array(_, _) => panic!("encountered an array during no_index!"),
Expr::And(lhs, rhs) => match (*lhs, *rhs) {
(Expr::True(_), rhs) => {
*changed = true;
@ -208,7 +215,6 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Box::new(optimize_expr(rhs, changed)),
),
},
Expr::FunctionCall(id, args, def_value, pos) => {
let original_len = args.len();

View File

@ -3,13 +3,18 @@
use crate::any::Dynamic;
use crate::error::{LexError, ParseError, ParseErrorType};
use crate::optimize::optimize;
use std::{borrow::Cow, char, fmt, iter::Peekable, str::Chars, str::FromStr, usize};
/// The system integer type
/// The system integer type.
///
/// If the `only_i32` feature is enabled, this will be `i32` instead.
#[cfg(not(feature = "only_i32"))]
pub type INT = i64;
/// The system integer type
///
/// If the `only_i32` feature is not enabled, this will be `i64` instead.
#[cfg(feature = "only_i32")]
pub type INT = i32;
@ -179,9 +184,7 @@ pub enum Expr {
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
Assignment(Box<Expr>, Box<Expr>, Position),
Dot(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Index(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Array(Vec<Expr>, Position),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
@ -197,24 +200,21 @@ impl Expr {
| Expr::Identifier(_, pos)
| Expr::CharConstant(_, pos)
| Expr::StringConstant(_, pos)
| Expr::FunctionCall(_, _, _, pos)
| Expr::Stmt(_, pos)
| Expr::FunctionCall(_, _, _, pos)
| Expr::Array(_, pos)
| Expr::True(pos)
| Expr::False(pos)
| Expr::Unit(pos) => *pos,
Expr::Assignment(e, _, _) | Expr::Dot(e, _, _) | Expr::And(e, _) | Expr::Or(e, _) => {
e.position()
}
Expr::Assignment(e, _, _)
| Expr::Dot(e, _, _)
| Expr::Index(e, _, _)
| Expr::And(e, _)
| Expr::Or(e, _) => e.position(),
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => *pos,
#[cfg(not(feature = "no_index"))]
Expr::Index(e, _, _) => e.position(),
#[cfg(not(feature = "no_index"))]
Expr::Array(_, pos) => *pos,
}
}

View File

@ -3,6 +3,7 @@
use crate::any::Dynamic;
use crate::error::ParseError;
use crate::parser::{Position, INT};
use std::{error::Error, fmt};
/// Evaluation result.
@ -75,12 +76,12 @@ impl Error for EvalAltResult {
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
"Array access expects non-negative index"
}
Self::ErrorArrayBounds(max, _, _) if *max == 0 => "Access of empty array",
Self::ErrorArrayBounds(0, _, _) => "Access of empty array",
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
Self::ErrorStringBounds(_, index, _) if *index < 0 => {
"Indexing a string expects a non-negative index"
}
Self::ErrorStringBounds(max, _, _) if *max == 0 => "Indexing of empty string",
Self::ErrorStringBounds(0, _, _) => "Indexing of empty string",
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
Self::ErrorIfGuard(_) => "If guard expects boolean expression",
Self::ErrorFor(_) => "For loop expects array or range",
@ -128,6 +129,16 @@ impl fmt::Display for EvalAltResult {
write!(f, "{} '{}': {}", desc, filename, err)
}
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
Self::ErrorFunctionArgsMismatch(fun, 0, n, pos) => write!(
f,
"Function '{}' expects no argument but {} found ({})",
fun, n, pos
),
Self::ErrorFunctionArgsMismatch(fun, 1, n, pos) => write!(
f,
"Function '{}' expects one argument but {} found ({})",
fun, n, pos
),
Self::ErrorFunctionArgsMismatch(fun, need, n, pos) => write!(
f,
"Function '{}' expects {} argument(s) but {} found ({})",
@ -142,26 +153,30 @@ impl fmt::Display for EvalAltResult {
Self::ErrorArrayBounds(_, index, pos) if *index < 0 => {
write!(f, "{}: {} < 0 ({})", desc, index, pos)
}
Self::ErrorArrayBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
Self::ErrorArrayBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
Self::ErrorArrayBounds(1, index, pos) => write!(
f,
"Array index {} is out of bounds: only one element in the array ({})",
index, pos
),
Self::ErrorArrayBounds(max, index, pos) => write!(
f,
"Array index {} is out of bounds: only {} element{} in the array ({})",
index,
max,
if *max > 1 { "s" } else { "" },
pos
"Array index {} is out of bounds: only {} elements in the array ({})",
index, max, pos
),
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
write!(f, "{}: {} < 0 ({})", desc, index, pos)
}
Self::ErrorStringBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
Self::ErrorStringBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
Self::ErrorStringBounds(1, index, pos) => write!(
f,
"String index {} is out of bounds: only one character in the string ({})",
index, pos
),
Self::ErrorStringBounds(max, index, pos) => write!(
f,
"String index {} is out of bounds: only {} character{} in the string ({})",
index,
max,
if *max > 1 { "s" } else { "" },
pos
"String index {} is out of bounds: only {} characters in the string ({})",
index, max, pos
),
}
}

View File

@ -1,6 +1,7 @@
//! Module that defines the `Scope` type representing a function call-stack scope.
use crate::any::{Any, Dynamic};
use std::borrow::Cow;
/// A type containing information about current scope.

View File

@ -24,6 +24,10 @@ fn test_math() -> Result<(), EvalAltResult> {
{
#[cfg(not(feature = "only_i32"))]
{
match engine.eval::<INT>("(-9223372036854775808).abs()") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return overflow error: {:?}", r),
}
match engine.eval::<INT>("9223372036854775807 + 1") {
Err(EvalAltResult::ErrorArithmetic(_, _)) => (),
r => panic!("should return overflow error: {:?}", r),