Merge branch 'master' into plugins
This commit is contained in:
commit
ec036e4039
@ -1947,7 +1947,7 @@ Properties and methods in a Rust custom type registered with the [`Engine`] can
|
|||||||
```rust
|
```rust
|
||||||
let a = new_ts(); // constructor function
|
let a = new_ts(); // constructor function
|
||||||
a.field = 500; // property access
|
a.field = 500; // property access
|
||||||
a.update(); // method call
|
a.update(); // method call, 'a' can be changed
|
||||||
|
|
||||||
update(a); // this works, but 'a' is unchanged because only
|
update(a); // this works, but 'a' is unchanged because only
|
||||||
// a COPY of 'a' is passed to 'update' by VALUE
|
// a COPY of 'a' is passed to 'update' by VALUE
|
||||||
|
@ -51,7 +51,7 @@ fn bench_eval_array_large_set(bench: &mut Bencher) {
|
|||||||
let script = r#"let x = [ 1, 2.345, "hello", true,
|
let script = r#"let x = [ 1, 2.345, "hello", true,
|
||||||
[ 1, 2, 3, [ "hey", [ "deeply", "nested" ], "jude" ] ]
|
[ 1, 2, 3, [ "hey", [ "deeply", "nested" ], "jude" ] ]
|
||||||
];
|
];
|
||||||
x[4] = 42
|
x[4][3][1][1] = 42
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -16,9 +16,7 @@ fn mat_gen(n) {
|
|||||||
|
|
||||||
for i in range(0, n) {
|
for i in range(0, n) {
|
||||||
for j in range(0, n) {
|
for j in range(0, n) {
|
||||||
let foo = m[i];
|
m[i][j] = tmp * (i.to_float() - j.to_float()) * (i.to_float() + j.to_float());
|
||||||
foo[j] = tmp * (i.to_float() - j.to_float()) * (i.to_float() + j.to_float());
|
|
||||||
m[i] = foo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,9 +32,7 @@ fn mat_mul(a, b) {
|
|||||||
|
|
||||||
for i in range(0, n) {
|
for i in range(0, n) {
|
||||||
for j in range(0, p) {
|
for j in range(0, p) {
|
||||||
let foo = b2[j];
|
b2[j][i] = b[i][j];
|
||||||
foo[i] = b[i][j];
|
|
||||||
b2[j] = foo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
const MAX_NUMBER_TO_CHECK = 10_000; // 1229 primes <= 10000
|
const MAX_NUMBER_TO_CHECK = 100_000; // 9592 primes <= 100000
|
||||||
|
|
||||||
let prime_mask = [];
|
let prime_mask = [];
|
||||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||||
|
14
src/any.rs
14
src/any.rs
@ -212,7 +212,7 @@ impl fmt::Display for Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => write!(f, "{}", value),
|
Union::Float(value) => write!(f, "{}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "?"),
|
Union::Variant(_) => write!(f, "?"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => write!(f, "{:?}", value),
|
Union::Float(value) => write!(f, "{:?}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
Union::Variant(_) => write!(f, "<dynamic>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,16 +268,6 @@ fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// Get a reference to the inner `Union`.
|
|
||||||
pub(crate) fn get_ref(&self) -> &Union {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a mutable reference to the inner `Union`.
|
|
||||||
pub(crate) fn get_mut(&mut self) -> &mut Union {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
||||||
///
|
///
|
||||||
/// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`.
|
/// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`.
|
||||||
|
21
src/api.rs
21
src/api.rs
@ -146,14 +146,8 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) {
|
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) {
|
||||||
if self.type_names.is_none() {
|
|
||||||
self.type_names = Some(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the pretty-print type name into the map
|
// Add the pretty-print type name into the map
|
||||||
self.type_names
|
self.type_names
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.insert(type_name::<T>().to_string(), name.to_string());
|
.insert(type_name::<T>().to_string(), name.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,7 +798,7 @@ impl Engine {
|
|||||||
ast.0
|
ast.0
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
|
self.eval_stmt(scope, ast.1.as_ref(), stmt, 0)
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::Return(out, _) => Ok(out),
|
EvalAltResult::Return(out, _) => Ok(out),
|
||||||
@ -867,7 +861,7 @@ impl Engine {
|
|||||||
ast.0
|
ast.0
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0)
|
self.eval_stmt(scope, ast.1.as_ref(), stmt, 0)
|
||||||
})
|
})
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -930,8 +924,7 @@ impl Engine {
|
|||||||
.get_function(name, args.len())
|
.get_function(name, args.len())
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
||||||
|
|
||||||
let result =
|
let result = self.call_fn_from_lib(Some(scope), fn_lib, fn_def, &mut args, pos, 0)?;
|
||||||
self.call_fn_from_lib(Some(scope), Some(&fn_lib), fn_def, &mut args, pos, 0)?;
|
|
||||||
|
|
||||||
let return_type = self.map_type_name(result.type_name());
|
let return_type = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -995,7 +988,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
||||||
self.on_print = Some(Box::new(callback));
|
self.print = Box::new(callback);
|
||||||
}
|
}
|
||||||
/// Override default action of `print` (print to stdout using `println!`)
|
/// Override default action of `print` (print to stdout using `println!`)
|
||||||
///
|
///
|
||||||
@ -1023,7 +1016,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
self.on_print = Some(Box::new(callback));
|
self.print = Box::new(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override default action of `debug` (print to stdout using `println!`)
|
/// Override default action of `debug` (print to stdout using `println!`)
|
||||||
@ -1052,7 +1045,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
||||||
self.on_debug = Some(Box::new(callback));
|
self.debug = Box::new(callback);
|
||||||
}
|
}
|
||||||
/// Override default action of `debug` (print to stdout using `println!`)
|
/// Override default action of `debug` (print to stdout using `println!`)
|
||||||
///
|
///
|
||||||
@ -1080,6 +1073,6 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
self.on_debug = Some(Box::new(callback));
|
self.debug = Box::new(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1106
src/engine.rs
1106
src/engine.rs
File diff suppressed because it is too large
Load Diff
@ -100,8 +100,6 @@ pub enum ParseErrorType {
|
|||||||
FnMissingBody(String),
|
FnMissingBody(String),
|
||||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||||
AssignmentToInvalidLHS,
|
AssignmentToInvalidLHS,
|
||||||
/// Assignment to a copy of a value.
|
|
||||||
AssignmentToCopy,
|
|
||||||
/// Assignment to an a constant variable.
|
/// Assignment to an a constant variable.
|
||||||
AssignmentToConstant(String),
|
AssignmentToConstant(String),
|
||||||
/// Break statement not inside a loop.
|
/// Break statement not inside a loop.
|
||||||
@ -150,7 +148,6 @@ impl ParseError {
|
|||||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
||||||
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value",
|
|
||||||
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant variable.",
|
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant variable.",
|
||||||
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
||||||
}
|
}
|
||||||
|
@ -646,12 +646,12 @@ fn optimize<'a>(
|
|||||||
.filter(|ScopeEntry { typ, expr, .. }| {
|
.filter(|ScopeEntry { typ, expr, .. }| {
|
||||||
// Get all the constants with definite constant expressions
|
// Get all the constants with definite constant expressions
|
||||||
*typ == ScopeEntryType::Constant
|
*typ == ScopeEntryType::Constant
|
||||||
&& expr.as_ref().map(Expr::is_constant).unwrap_or(false)
|
&& expr.as_ref().map(|v| v.is_constant()).unwrap_or(false)
|
||||||
})
|
})
|
||||||
.for_each(|ScopeEntry { name, expr, .. }| {
|
.for_each(|ScopeEntry { name, expr, .. }| {
|
||||||
state.push_constant(
|
state.push_constant(
|
||||||
name.as_ref(),
|
name.as_ref(),
|
||||||
expr.as_ref().expect("should be Some(expr)").clone(),
|
(**expr.as_ref().expect("should be Some(expr)")).clone(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,12 +12,10 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
|
fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
|
||||||
map.iter()
|
map.iter().map(|(k, _)| k.to_string().into()).collect()
|
||||||
.map(|(k, _)| k.to_string().into())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
}
|
||||||
fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
|
fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
|
||||||
map.iter().map(|(_, v)| v.clone()).collect::<Vec<_>>()
|
map.iter().map(|(_, v)| v.clone()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
@ -37,7 +37,7 @@ fn sub_string(s: &mut String, start: INT, len: INT) -> String {
|
|||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
chars[offset..][..len].into_iter().collect::<String>()
|
chars[offset..][..len].into_iter().collect()
|
||||||
}
|
}
|
||||||
fn crop_string(s: &mut String, start: INT, len: INT) {
|
fn crop_string(s: &mut String, start: INT, len: INT) {
|
||||||
let offset = if s.is_empty() || len <= 0 {
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
|
@ -87,10 +87,10 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
{
|
{
|
||||||
if seconds > (MAX_INT as u64) {
|
if seconds > (MAX_INT as u64) {
|
||||||
return Err(EvalAltResult::ErrorArithmetic(
|
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Integer overflow for timestamp.elapsed(): {}", seconds),
|
format!("Integer overflow for timestamp.elapsed(): {}", seconds),
|
||||||
Position::none(),
|
Position::none(),
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(seconds as INT);
|
return Ok(seconds as INT);
|
||||||
|
220
src/parser.rs
220
src/parser.rs
@ -476,34 +476,42 @@ impl Expr {
|
|||||||
/// Is a particular token allowed as a postfix operator to this expression?
|
/// Is a particular token allowed as a postfix operator to this expression?
|
||||||
pub fn is_valid_postfix(&self, token: &Token) -> bool {
|
pub fn is_valid_postfix(&self, token: &Token) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Expr::IntegerConstant(_, _)
|
Self::IntegerConstant(_, _)
|
||||||
| Expr::FloatConstant(_, _)
|
| Self::FloatConstant(_, _)
|
||||||
| Expr::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Expr::In(_, _, _)
|
| Self::In(_, _, _)
|
||||||
| Expr::And(_, _, _)
|
| Self::And(_, _, _)
|
||||||
| Expr::Or(_, _, _)
|
| Self::Or(_, _, _)
|
||||||
| Expr::True(_)
|
| Self::True(_)
|
||||||
| Expr::False(_)
|
| Self::False(_)
|
||||||
| Expr::Unit(_) => false,
|
| Self::Unit(_) => false,
|
||||||
|
|
||||||
Expr::StringConstant(_, _)
|
Self::StringConstant(_, _)
|
||||||
| Expr::Stmt(_, _)
|
| Self::Stmt(_, _)
|
||||||
| Expr::FunctionCall(_, _, _, _)
|
| Self::FunctionCall(_, _, _, _)
|
||||||
| Expr::Assignment(_, _, _)
|
| Self::Assignment(_, _, _)
|
||||||
| Expr::Dot(_, _, _)
|
| Self::Dot(_, _, _)
|
||||||
| Expr::Index(_, _, _)
|
| Self::Index(_, _, _)
|
||||||
| Expr::Array(_, _)
|
| Self::Array(_, _)
|
||||||
| Expr::Map(_, _) => match token {
|
| Self::Map(_, _) => match token {
|
||||||
Token::LeftBracket => true,
|
Token::LeftBracket => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Expr::Variable(_, _) | Expr::Property(_, _) => match token {
|
Self::Variable(_, _) | Self::Property(_, _) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
||||||
|
pub(crate) fn into_property(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Variable(id, pos) => Self::Property(id, pos),
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume a particular token, checking that it is the expected one.
|
/// Consume a particular token, checking that it is the expected one.
|
||||||
@ -619,9 +627,10 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an indexing expression.
|
/// Parse an indexing chain.
|
||||||
fn parse_index_expr<'a>(
|
/// Indexing binds to the right, so this call parses all possible levels of indexing following in the input.
|
||||||
lhs: Box<Expr>,
|
fn parse_index_chain<'a>(
|
||||||
|
lhs: Expr,
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
@ -638,7 +647,7 @@ fn parse_index_expr<'a>(
|
|||||||
))
|
))
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
Expr::IntegerConstant(_, pos) => match *lhs {
|
Expr::IntegerConstant(_, pos) => match lhs {
|
||||||
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
|
||||||
|
|
||||||
Expr::Map(_, _) => {
|
Expr::Map(_, _) => {
|
||||||
@ -667,7 +676,7 @@ fn parse_index_expr<'a>(
|
|||||||
},
|
},
|
||||||
|
|
||||||
// lhs[string]
|
// lhs[string]
|
||||||
Expr::StringConstant(_, pos) => match *lhs {
|
Expr::StringConstant(_, pos) => match lhs {
|
||||||
Expr::Map(_, _) => (),
|
Expr::Map(_, _) => (),
|
||||||
|
|
||||||
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
|
||||||
@ -734,7 +743,20 @@ fn parse_index_expr<'a>(
|
|||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::RightBracket, _) => {
|
(Token::RightBracket, _) => {
|
||||||
eat_token(input, Token::RightBracket);
|
eat_token(input, Token::RightBracket);
|
||||||
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
|
||||||
|
// Any more indexing following?
|
||||||
|
match input.peek().unwrap() {
|
||||||
|
// If another indexing level, right-bind it
|
||||||
|
(Token::LeftBracket, _) => {
|
||||||
|
let follow_pos = eat_token(input, Token::LeftBracket);
|
||||||
|
// Recursively parse the indexing chain, right-binding each
|
||||||
|
let follow = parse_index_chain(idx_expr, input, follow_pos, allow_stmt_expr)?;
|
||||||
|
// Indexing binds to right
|
||||||
|
Ok(Expr::Index(Box::new(lhs), Box::new(follow), pos))
|
||||||
|
}
|
||||||
|
// Otherwise terminate the indexing chain
|
||||||
|
_ => Ok(Expr::Index(Box::new(lhs), Box::new(idx_expr), pos)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
@ -926,9 +948,7 @@ fn parse_primary<'a>(
|
|||||||
parse_call_expr(id, input, pos, allow_stmt_expr)?
|
parse_call_expr(id, input, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
// Indexing
|
// Indexing
|
||||||
(expr, Token::LeftBracket) => {
|
(expr, Token::LeftBracket) => parse_index_chain(expr, input, pos, allow_stmt_expr)?,
|
||||||
parse_index_expr(Box::new(expr), input, pos, allow_stmt_expr)?
|
|
||||||
}
|
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr),
|
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr),
|
||||||
}
|
}
|
||||||
@ -1005,71 +1025,6 @@ fn parse_unary<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an assignment.
|
|
||||||
fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, Box<ParseError>> {
|
|
||||||
// Is the LHS in a valid format for an assignment target?
|
|
||||||
fn valid_assignment_chain(expr: &Expr, is_top: bool) -> Option<Box<ParseError>> {
|
|
||||||
match expr {
|
|
||||||
// var
|
|
||||||
Expr::Variable(_, _) => {
|
|
||||||
assert!(is_top, "property expected but gets variable");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
// property
|
|
||||||
Expr::Property(_, _) => {
|
|
||||||
assert!(!is_top, "variable expected but gets property");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// idx_lhs[...]
|
|
||||||
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
|
|
||||||
// var[...]
|
|
||||||
Expr::Variable(_, _) => {
|
|
||||||
assert!(is_top, "property expected but gets variable");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
// property[...]
|
|
||||||
Expr::Property(_, _) => {
|
|
||||||
assert!(!is_top, "variable expected but gets property");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
// ???[...][...]
|
|
||||||
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
|
||||||
// idx_lhs[...]
|
|
||||||
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
|
||||||
},
|
|
||||||
|
|
||||||
// dot_lhs.dot_rhs
|
|
||||||
Expr::Dot(dot_lhs, dot_rhs, pos) => match dot_lhs.as_ref() {
|
|
||||||
// var.dot_rhs
|
|
||||||
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
|
||||||
// property.dot_rhs
|
|
||||||
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
|
||||||
// idx_lhs[...].dot_rhs
|
|
||||||
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
|
||||||
// var[...].dot_rhs
|
|
||||||
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
|
||||||
// property[...].dot_rhs
|
|
||||||
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
|
||||||
// ???[...][...].dot_rhs
|
|
||||||
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
|
||||||
// idx_lhs[...].dot_rhs
|
|
||||||
_ => Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position())),
|
|
||||||
},
|
|
||||||
|
|
||||||
expr => panic!("unexpected dot expression {:#?}", expr),
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(expr.position())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match valid_assignment_chain(&lhs, true) {
|
|
||||||
None => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
|
||||||
Some(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_assignment_stmt<'a>(
|
fn parse_assignment_stmt<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
@ -1077,7 +1032,7 @@ fn parse_assignment_stmt<'a>(
|
|||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let pos = eat_token(input, Token::Equals);
|
let pos = eat_token(input, Token::Equals);
|
||||||
let rhs = parse_expr(input, allow_stmt_expr)?;
|
let rhs = parse_expr(input, allow_stmt_expr)?;
|
||||||
parse_assignment(lhs, rhs, pos)
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator-assignment expression.
|
/// Parse an operator-assignment expression.
|
||||||
@ -1108,15 +1063,52 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
let rhs = parse_expr(input, allow_stmt_expr)?;
|
let rhs = parse_expr(input, allow_stmt_expr)?;
|
||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
parse_assignment(
|
let rhs_expr = Expr::FunctionCall(op.into(), vec![lhs_copy, rhs], None, pos);
|
||||||
lhs,
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs_expr), pos))
|
||||||
Expr::FunctionCall(op.into(), vec![lhs_copy, rhs], None, pos),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an 'in' expression.
|
/// Make a dot expression.
|
||||||
fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<ParseError>> {
|
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position, is_index: bool) -> Expr {
|
||||||
|
match (lhs, rhs) {
|
||||||
|
// idx_lhs[idx_rhs].rhs
|
||||||
|
// Attach dot chain to the bottom level of indexing chain
|
||||||
|
(Expr::Index(idx_lhs, idx_rhs, idx_pos), rhs) => Expr::Index(
|
||||||
|
idx_lhs,
|
||||||
|
Box::new(make_dot_expr(*idx_rhs, rhs, op_pos, true)),
|
||||||
|
idx_pos,
|
||||||
|
),
|
||||||
|
// lhs.id
|
||||||
|
(lhs, rhs @ Expr::Variable(_, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
||||||
|
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||||
|
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
||||||
|
}
|
||||||
|
// lhs.dot_lhs.dot_rhs
|
||||||
|
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(
|
||||||
|
Box::new(lhs),
|
||||||
|
Box::new(Expr::Dot(
|
||||||
|
Box::new(dot_lhs.into_property()),
|
||||||
|
Box::new(dot_rhs.into_property()),
|
||||||
|
dot_pos,
|
||||||
|
)),
|
||||||
|
op_pos,
|
||||||
|
),
|
||||||
|
// lhs.idx_lhs[idx_rhs]
|
||||||
|
(lhs, Expr::Index(idx_lhs, idx_rhs, idx_pos)) => Expr::Dot(
|
||||||
|
Box::new(lhs),
|
||||||
|
Box::new(Expr::Index(
|
||||||
|
Box::new(idx_lhs.into_property()),
|
||||||
|
Box::new(idx_rhs.into_property()),
|
||||||
|
idx_pos,
|
||||||
|
)),
|
||||||
|
op_pos,
|
||||||
|
),
|
||||||
|
// lhs.rhs
|
||||||
|
(lhs, rhs) => Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make an 'in' expression.
|
||||||
|
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<ParseError>> {
|
||||||
match (&lhs, &rhs) {
|
match (&lhs, &rhs) {
|
||||||
(_, Expr::IntegerConstant(_, pos))
|
(_, Expr::IntegerConstant(_, pos))
|
||||||
| (_, Expr::FloatConstant(_, pos))
|
| (_, Expr::FloatConstant(_, pos))
|
||||||
@ -1321,34 +1313,10 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos),
|
Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos),
|
||||||
Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos),
|
Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos),
|
||||||
|
|
||||||
Token::In => parse_in_expr(current_lhs, rhs, pos)?,
|
Token::In => make_in_expr(current_lhs, rhs, pos)?,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => make_dot_expr(current_lhs, rhs, pos, false),
|
||||||
fn check_property(expr: Expr) -> Result<Expr, Box<ParseError>> {
|
|
||||||
match expr {
|
|
||||||
// xxx.lhs.rhs
|
|
||||||
Expr::Dot(lhs, rhs, pos) => Ok(Expr::Dot(
|
|
||||||
Box::new(check_property(*lhs)?),
|
|
||||||
Box::new(check_property(*rhs)?),
|
|
||||||
pos,
|
|
||||||
)),
|
|
||||||
// xxx.lhs[idx]
|
|
||||||
Expr::Index(lhs, idx, pos) => {
|
|
||||||
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
|
|
||||||
}
|
|
||||||
// xxx.id
|
|
||||||
Expr::Variable(id, pos) => Ok(Expr::Property(id, pos)),
|
|
||||||
// xxx.prop
|
|
||||||
expr @ Expr::Property(_, _) => Ok(expr),
|
|
||||||
// xxx.fn()
|
|
||||||
expr @ Expr::FunctionCall(_, _, _, _) => Ok(expr),
|
|
||||||
expr => Err(PERR::PropertyExpected.into_err(expr.position())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
|
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
|
||||||
};
|
};
|
||||||
|
89
src/scope.rs
89
src/scope.rs
@ -25,15 +25,7 @@ pub struct Entry<'a> {
|
|||||||
/// Current value of the entry.
|
/// Current value of the entry.
|
||||||
pub value: Dynamic,
|
pub value: Dynamic,
|
||||||
/// A constant expression if the initial value matches one of the recognized types.
|
/// A constant expression if the initial value matches one of the recognized types.
|
||||||
pub expr: Option<Expr>,
|
pub expr: Option<Box<Expr>>,
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a particular entry in the Scope.
|
|
||||||
#[derive(Debug, Hash, Copy, Clone)]
|
|
||||||
pub(crate) struct EntryRef<'a> {
|
|
||||||
pub name: &'a str,
|
|
||||||
pub index: usize,
|
|
||||||
pub typ: EntryType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type containing information about the current scope.
|
/// A type containing information about the current scope.
|
||||||
@ -226,15 +218,17 @@ impl<'a> Scope<'a> {
|
|||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
map_expr: bool,
|
map_expr: bool,
|
||||||
) {
|
) {
|
||||||
|
let expr = if map_expr {
|
||||||
|
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
self.0.push(Entry {
|
self.0.push(Entry {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
typ: entry_type,
|
typ: entry_type,
|
||||||
value: value.clone(),
|
value: value.into(),
|
||||||
expr: if map_expr {
|
expr,
|
||||||
map_dynamic_to_expr(value, Position::none())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,35 +283,18 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the Scope, starting from the last.
|
||||||
pub(crate) fn get(&self, name: &str) -> Option<(EntryRef, Dynamic)> {
|
pub(crate) fn get(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.find_map(
|
.find_map(|(index, Entry { name: key, typ, .. })| {
|
||||||
|(
|
if name == key {
|
||||||
index,
|
Some((index, *typ))
|
||||||
Entry {
|
} else {
|
||||||
name: key,
|
None
|
||||||
typ,
|
}
|
||||||
value,
|
})
|
||||||
..
|
|
||||||
},
|
|
||||||
)| {
|
|
||||||
if name == key {
|
|
||||||
Some((
|
|
||||||
EntryRef {
|
|
||||||
name: key,
|
|
||||||
index,
|
|
||||||
typ: *typ,
|
|
||||||
},
|
|
||||||
value.clone(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of an entry in the Scope, starting from the last.
|
/// Get the value of an entry in the Scope, starting from the last.
|
||||||
@ -363,36 +340,23 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
||||||
match self.get(name) {
|
match self.get(name) {
|
||||||
Some((
|
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||||
EntryRef {
|
Some((index, EntryType::Normal)) => {
|
||||||
typ: EntryType::Constant,
|
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||||
..
|
}
|
||||||
},
|
|
||||||
_,
|
|
||||||
)) => panic!("variable {} is constant", name),
|
|
||||||
Some((
|
|
||||||
EntryRef {
|
|
||||||
index,
|
|
||||||
typ: EntryType::Normal,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
_,
|
|
||||||
)) => self.0.get_mut(index).unwrap().value = Dynamic::from(value),
|
|
||||||
None => self.push(name, value),
|
None => self.push(name, value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the Scope.
|
||||||
pub(crate) fn get_mut(&mut self, key: EntryRef) -> &mut Dynamic {
|
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
|
||||||
let entry = self.0.get_mut(key.index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
assert_eq!(entry.typ, key.typ, "entry type not matched");
|
|
||||||
|
|
||||||
// assert_ne!(
|
// assert_ne!(
|
||||||
// entry.typ,
|
// entry.typ,
|
||||||
// EntryType::Constant,
|
// EntryType::Constant,
|
||||||
// "get mut of constant entry"
|
// "get mut of constant entry"
|
||||||
// );
|
// );
|
||||||
assert_eq!(entry.name, key.name, "incorrect key at Scope entry");
|
|
||||||
|
|
||||||
&mut entry.value
|
&mut entry.value
|
||||||
}
|
}
|
||||||
@ -409,16 +373,13 @@ impl Default for Scope<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a>
|
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||||
where
|
|
||||||
K: Into<Cow<'a, str>>,
|
|
||||||
{
|
|
||||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||||
self.0
|
self.0
|
||||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
typ,
|
typ,
|
||||||
value,
|
value: value.into(),
|
||||||
expr: None,
|
expr: None,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ fn test_map_return() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
fn test_map_for() -> Result<(), Box<EvalAltResult>> {
|
fn test_map_for() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user