Add timestamp support.
This commit is contained in:
@@ -27,6 +27,7 @@ use crate::stdlib::{
|
||||
format,
|
||||
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Shl, Shr, Sub},
|
||||
string::{String, ToString},
|
||||
time::Instant,
|
||||
vec::Vec,
|
||||
{i32, i64, u32},
|
||||
};
|
||||
@@ -57,6 +58,34 @@ macro_rules! reg_op_result1 {
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! reg_cmp {
|
||||
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
$self.register_fn($x, $op as fn(x: $y, y: $y)->bool);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
// Comparison operators
|
||||
fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x < y
|
||||
}
|
||||
fn lte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x <= y
|
||||
}
|
||||
fn gt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x > y
|
||||
}
|
||||
fn gte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x >= y
|
||||
}
|
||||
fn eq<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x == y
|
||||
}
|
||||
fn ne<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x != y
|
||||
}
|
||||
|
||||
impl Engine<'_> {
|
||||
/// Register the core built-in library.
|
||||
pub(crate) fn register_core_lib(&mut self) {
|
||||
@@ -176,26 +205,6 @@ impl Engine<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Comparison operators
|
||||
fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x < y
|
||||
}
|
||||
fn lte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x <= y
|
||||
}
|
||||
fn gt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x > y
|
||||
}
|
||||
fn gte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||
x >= y
|
||||
}
|
||||
fn eq<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x == y
|
||||
}
|
||||
fn ne<T: PartialEq>(x: T, y: T) -> bool {
|
||||
x != y
|
||||
}
|
||||
|
||||
// Logic operators
|
||||
fn and(x: bool, y: bool) -> bool {
|
||||
x && y
|
||||
@@ -395,14 +404,6 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
{
|
||||
macro_rules! reg_cmp {
|
||||
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
$self.register_fn($x, $op as fn(x: $y, y: $y)->bool);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
reg_cmp!(self, "<", lt, INT, String, char);
|
||||
reg_cmp!(self, "<=", lte, INT, String, char);
|
||||
reg_cmp!(self, ">", gt, INT, String, char);
|
||||
@@ -433,7 +434,7 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
// `&&` and `||` are treated specially as they short-circuit.
|
||||
// They are implemented as special `Expr` instances, not function calls.
|
||||
// They are implemented as special `Expr` Instants, not function calls.
|
||||
//reg_op!(self, "||", or, bool);
|
||||
//reg_op!(self, "&&", and, bool);
|
||||
|
||||
@@ -1031,5 +1032,39 @@ impl Engine<'_> {
|
||||
*s = trimmed.to_string();
|
||||
}
|
||||
});
|
||||
|
||||
// Register date/time functions
|
||||
self.register_fn("timestamp", || Instant::now());
|
||||
|
||||
self.register_fn("-", |ts1: Instant, ts2: Instant| {
|
||||
if ts2 > ts1 {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return -(ts2 - ts1).as_secs_f64();
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
return -((ts2 - ts1).as_secs() as INT);
|
||||
} else {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return (ts1 - ts2).as_secs_f64();
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
return (ts1 - ts2).as_secs() as INT;
|
||||
}
|
||||
});
|
||||
|
||||
reg_cmp!(self, "<", lt, Instant);
|
||||
reg_cmp!(self, "<=", lte, Instant);
|
||||
reg_cmp!(self, ">", gt, Instant);
|
||||
reg_cmp!(self, ">=", gte, Instant);
|
||||
reg_cmp!(self, "==", eq, Instant);
|
||||
reg_cmp!(self, "!=", ne, Instant);
|
||||
|
||||
self.register_fn("elapsed", |timestamp: Instant| {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return timestamp.elapsed().as_secs_f64();
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
return timestamp.elapsed().as_secs() as INT;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
156
src/engine.rs
156
src/engine.rs
@@ -19,6 +19,7 @@ use crate::stdlib::{
|
||||
rc::Rc,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
@@ -362,6 +363,7 @@ impl Engine<'_> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
(type_name::<Map>(), "map"),
|
||||
(type_name::<String>(), "string"),
|
||||
(type_name::<Instant>(), "timestamp"),
|
||||
(type_name::<Dynamic>(), "dynamic"),
|
||||
(type_name::<Variant>(), "variant"),
|
||||
]
|
||||
@@ -719,23 +721,24 @@ impl Engine<'_> {
|
||||
let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level);
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
if let Some(src) = src {
|
||||
match src.typ {
|
||||
ScopeEntryType::Constant => {
|
||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.name.to_string(),
|
||||
idx_lhs.position(),
|
||||
));
|
||||
}
|
||||
ScopeEntryType::Normal => {
|
||||
Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src,
|
||||
index,
|
||||
(val, dot_rhs.position()),
|
||||
)?;
|
||||
}
|
||||
match src.map(|s| s.typ) {
|
||||
None => (),
|
||||
|
||||
Some(ScopeEntryType::Constant) => {
|
||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.unwrap().name.to_string(),
|
||||
idx_lhs.position(),
|
||||
));
|
||||
}
|
||||
|
||||
Some(ScopeEntryType::Normal) => {
|
||||
Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src.unwrap(),
|
||||
index,
|
||||
(val, dot_rhs.position()),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,16 +1109,16 @@ impl Engine<'_> {
|
||||
match dot_lhs {
|
||||
// id.???
|
||||
Expr::Variable(id, pos) => {
|
||||
let (entry, mut target) = Self::search_scope(scope, id, *pos)?;
|
||||
let (src, mut target) = Self::search_scope(scope, id, *pos)?;
|
||||
|
||||
match entry.typ {
|
||||
match src.typ {
|
||||
ScopeEntryType::Constant => Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
id.to_string(),
|
||||
op_pos,
|
||||
)),
|
||||
_ => {
|
||||
// Avoid referencing scope which is used below as mut
|
||||
let entry = ScopeSource { name: id, ..entry };
|
||||
let entry = ScopeSource { name: id, ..src };
|
||||
let this_ptr = target.as_mut();
|
||||
let value = self
|
||||
.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level);
|
||||
@@ -1139,23 +1142,24 @@ impl Engine<'_> {
|
||||
self.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level);
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
if let Some(src) = src {
|
||||
match src.typ {
|
||||
ScopeEntryType::Constant => {
|
||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.name.to_string(),
|
||||
lhs.position(),
|
||||
));
|
||||
}
|
||||
ScopeEntryType::Normal => {
|
||||
Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src,
|
||||
index,
|
||||
(target, val_pos),
|
||||
)?;
|
||||
}
|
||||
match src.map(|x| x.typ) {
|
||||
None => (),
|
||||
|
||||
Some(ScopeEntryType::Constant) => {
|
||||
return Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.unwrap().name.to_string(),
|
||||
lhs.position(),
|
||||
));
|
||||
}
|
||||
|
||||
Some(ScopeEntryType::Normal) => {
|
||||
Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src.unwrap(),
|
||||
index,
|
||||
(target, val_pos),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,29 +1264,36 @@ impl Engine<'_> {
|
||||
|
||||
match lhs.as_ref() {
|
||||
// name = rhs
|
||||
Expr::Variable(name, pos) => match scope
|
||||
.get(name)
|
||||
.ok_or_else(|| {
|
||||
EvalAltResult::ErrorVariableNotFound(name.clone().into_owned(), *pos)
|
||||
})?
|
||||
.0
|
||||
{
|
||||
entry
|
||||
@
|
||||
ScopeSource {
|
||||
typ: ScopeEntryType::Normal,
|
||||
..
|
||||
} => {
|
||||
Expr::Variable(name, pos) => match scope.get(name) {
|
||||
None => {
|
||||
return Err(EvalAltResult::ErrorVariableNotFound(
|
||||
name.clone().into_owned(),
|
||||
*pos,
|
||||
))
|
||||
}
|
||||
|
||||
Some((
|
||||
entry
|
||||
@
|
||||
ScopeSource {
|
||||
typ: ScopeEntryType::Normal,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => {
|
||||
// Avoid referencing scope which is used below as mut
|
||||
let entry = ScopeSource { name, ..entry };
|
||||
*scope.get_mut(entry) = rhs_val.clone();
|
||||
Ok(rhs_val)
|
||||
}
|
||||
|
||||
ScopeSource {
|
||||
typ: ScopeEntryType::Constant,
|
||||
..
|
||||
} => Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
Some((
|
||||
ScopeSource {
|
||||
typ: ScopeEntryType::Constant,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
name.to_string(),
|
||||
*op_pos,
|
||||
)),
|
||||
@@ -1294,26 +1305,25 @@ impl Engine<'_> {
|
||||
let (idx_src_type, src, index, _) =
|
||||
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
||||
|
||||
if let Some(src) = src {
|
||||
match src.typ {
|
||||
ScopeEntryType::Constant => {
|
||||
Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.name.to_string(),
|
||||
idx_lhs.position(),
|
||||
))
|
||||
}
|
||||
ScopeEntryType::Normal => Ok(Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src,
|
||||
index,
|
||||
(rhs_val, rhs.position()),
|
||||
)?),
|
||||
}
|
||||
} else {
|
||||
Err(EvalAltResult::ErrorAssignmentToUnknownLHS(
|
||||
match src.map(|x| x.typ) {
|
||||
None => Err(EvalAltResult::ErrorAssignmentToUnknownLHS(
|
||||
idx_lhs.position(),
|
||||
))
|
||||
)),
|
||||
|
||||
Some(ScopeEntryType::Constant) => {
|
||||
Err(EvalAltResult::ErrorAssignmentToConstant(
|
||||
src.unwrap().name.to_string(),
|
||||
idx_lhs.position(),
|
||||
))
|
||||
}
|
||||
|
||||
Some(ScopeEntryType::Normal) => Ok(Self::update_indexed_var_in_scope(
|
||||
idx_src_type,
|
||||
scope,
|
||||
src.unwrap(),
|
||||
index,
|
||||
(rhs_val, rhs.position()),
|
||||
)?),
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user