Merge branch 'namespace' of https://github.com/schungx/rhai into namespace

This commit is contained in:
Stephen Chung 2020-05-04 10:42:57 +08:00
commit 4bf07182dc
4 changed files with 74 additions and 4 deletions

View File

@ -1188,7 +1188,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"))]
@ -1497,6 +1498,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)?;

View File

@ -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

View File

@ -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),
}
}

View File

@ -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));