commit
329942b67f
@ -1180,7 +1180,8 @@ impl Engine {
|
||||
Some((index, ScopeEntryType::Normal)) => {
|
||||
*scope.get_mut(index).0 = rhs_val;
|
||||
Ok(Default::default())
|
||||
}
|
||||
},
|
||||
Some((_, ScopeEntryType::Subscope)) => unreachable!(),
|
||||
},
|
||||
// idx_lhs[idx_expr] = rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1489,6 +1490,11 @@ impl Engine {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
// Import statement
|
||||
Stmt::Import(_name_expr, _alias) => {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Const statement
|
||||
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
||||
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||
|
@ -248,6 +248,8 @@ pub enum Stmt {
|
||||
Break(Position),
|
||||
/// `return`/`throw`
|
||||
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
||||
/// import expr
|
||||
Import(Box<Expr>, Option<String>)
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
@ -261,7 +263,11 @@ impl Stmt {
|
||||
| Stmt::Continue(pos)
|
||||
| Stmt::Break(pos)
|
||||
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
||||
|
||||
Stmt::IfThenElse(expr, _, _)
|
||||
| Stmt::Expr(expr)
|
||||
| Stmt::Import(expr, _) => expr.position(),
|
||||
|
||||
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
||||
}
|
||||
}
|
||||
@ -273,7 +279,8 @@ impl Stmt {
|
||||
| Stmt::While(_, _)
|
||||
| Stmt::Loop(_)
|
||||
| Stmt::For(_, _, _)
|
||||
| Stmt::Block(_, _) => true,
|
||||
| Stmt::Block(_, _)
|
||||
| Stmt::Import(_, _) => true,
|
||||
|
||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||
Stmt::Noop(_) => false,
|
||||
@ -303,6 +310,7 @@ impl Stmt {
|
||||
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
||||
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
||||
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
||||
Stmt::Import(_, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,6 +341,16 @@ pub enum Expr {
|
||||
Option<Box<Dynamic>>,
|
||||
Position,
|
||||
),
|
||||
/// subscope::func(expr, ... )
|
||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||
SubscopeFnCall(
|
||||
String,
|
||||
Box<Cow<'static, str>>,
|
||||
Box<Vec<Expr>>,
|
||||
Option<Box<Dynamic>>,
|
||||
Position,
|
||||
),
|
||||
/// expr = expr
|
||||
Assignment(Box<Expr>, Box<Expr>, Position),
|
||||
/// lhs.rhs
|
||||
@ -430,6 +448,7 @@ impl Expr {
|
||||
| Self::Property(_, pos)
|
||||
| Self::Stmt(_, pos)
|
||||
| Self::FnCall(_, _, _, pos)
|
||||
| Self::SubscopeFnCall(_, _, _, _, pos)
|
||||
| Self::And(_, _, pos)
|
||||
| Self::Or(_, _, pos)
|
||||
| Self::In(_, _, pos)
|
||||
@ -456,6 +475,7 @@ impl Expr {
|
||||
| Self::Property(_, pos)
|
||||
| Self::Stmt(_, pos)
|
||||
| Self::FnCall(_, _, _, pos)
|
||||
| Self::SubscopeFnCall(_, _, _, _, pos)
|
||||
| Self::And(_, _, pos)
|
||||
| Self::Or(_, _, pos)
|
||||
| Self::In(_, _, pos)
|
||||
@ -533,6 +553,7 @@ impl Expr {
|
||||
Self::StringConstant(_, _)
|
||||
| Self::Stmt(_, _)
|
||||
| Self::FnCall(_, _, _, _)
|
||||
| Self::SubscopeFnCall(_, _, _, _, _)
|
||||
| Self::Assignment(_, _, _)
|
||||
| Self::Dot(_, _, _)
|
||||
| Self::Index(_, _, _)
|
||||
@ -1683,6 +1704,8 @@ fn parse_let<'a>(
|
||||
ScopeEntryType::Constant => {
|
||||
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
||||
}
|
||||
|
||||
ScopeEntryType::Subscope => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
// let name
|
||||
|
34
src/scope.rs
34
src/scope.rs
@ -13,6 +13,8 @@ pub enum EntryType {
|
||||
Normal,
|
||||
/// Immutable constant value.
|
||||
Constant,
|
||||
/// Name of a subscope, allowing member access with the :: operator.
|
||||
Subscope,
|
||||
}
|
||||
|
||||
/// An entry in the Scope.
|
||||
@ -165,6 +167,24 @@ impl<'a> Scope<'a> {
|
||||
self.push_dynamic_value(name, EntryType::Normal, value, false);
|
||||
}
|
||||
|
||||
/// Add (push) a new subscope to the Scope.
|
||||
///
|
||||
/// Subscopes are used for access to members in modules and plugins.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_subscope("x".to_string(), "My Plugin".to_string());
|
||||
/// assert_eq!(my_scope.get_subscope("x").unwrap(), "My Plugin");
|
||||
/// ```
|
||||
pub fn push_subscope(&mut self, name: String, value: String) {
|
||||
self.push_dynamic_value(name, EntryType::Subscope, Dynamic::from(value), true);
|
||||
}
|
||||
|
||||
/// Add (push) a new constant to the Scope.
|
||||
///
|
||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||
@ -297,6 +317,17 @@ impl<'a> Scope<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the subscope of an entry in the Scope, starting from the last.
|
||||
///
|
||||
pub fn get_subscope(&self, name: &str) -> Option<String> {
|
||||
self.0
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|Entry { name: key, typ, .. }| name == key &&
|
||||
std::mem::discriminant(typ) == std::mem::discriminant(&EntryType::Subscope))
|
||||
.and_then(|Entry { value, .. }| value.downcast_ref::<String>().cloned())
|
||||
}
|
||||
|
||||
/// Get the value of an entry in the Scope, starting from the last.
|
||||
///
|
||||
/// # Examples
|
||||
@ -344,6 +375,9 @@ impl<'a> Scope<'a> {
|
||||
Some((index, EntryType::Normal)) => {
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||
}
|
||||
Some((index, EntryType::Subscope)) => {
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||
}
|
||||
None => self.push(name, value),
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ pub enum Token {
|
||||
RightShift,
|
||||
SemiColon,
|
||||
Colon,
|
||||
DoubleColon,
|
||||
Comma,
|
||||
Period,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -230,6 +231,7 @@ impl Token {
|
||||
Divide => "/",
|
||||
SemiColon => ";",
|
||||
Colon => ":",
|
||||
DoubleColon => "::",
|
||||
Comma => ",",
|
||||
Period => ".",
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -874,7 +876,6 @@ impl<'a> TokenIterator<'a> {
|
||||
('/', _) => return Some((Token::Divide, pos)),
|
||||
|
||||
(';', _) => return Some((Token::SemiColon, pos)),
|
||||
(':', _) => return Some((Token::Colon, pos)),
|
||||
(',', _) => return Some((Token::Comma, pos)),
|
||||
('.', _) => return Some((Token::Period, pos)),
|
||||
|
||||
@ -896,6 +897,12 @@ impl<'a> TokenIterator<'a> {
|
||||
}
|
||||
('=', _) => return Some((Token::Equals, pos)),
|
||||
|
||||
(':', ':') => {
|
||||
self.eat_next();
|
||||
return Some((Token::DoubleColon, pos));
|
||||
}
|
||||
(':', _) => return Some((Token::Colon, pos)),
|
||||
|
||||
('<', '=') => {
|
||||
self.eat_next();
|
||||
return Some((Token::LessThanEqualsTo, pos));
|
||||
|
Loading…
Reference in New Issue
Block a user