Add namespacing syntax.
This commit is contained in:
parent
4bf07182dc
commit
ead9716f6d
@ -28,6 +28,7 @@ no_float = [] # no floating-point
|
|||||||
no_function = [] # no script-defined functions
|
no_function = [] # no script-defined functions
|
||||||
no_object = [] # no custom objects
|
no_object = [] # no custom objects
|
||||||
no_optimize = [] # no script optimizer
|
no_optimize = [] # no script optimizer
|
||||||
|
no_import = [] # no namespaces/modules
|
||||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST};
|
use rhai::{Dynamic, Engine, EvalAltResult, Map, Scope, AST, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use rhai::OptimizationLevel;
|
use rhai::OptimizationLevel;
|
||||||
|
107
src/engine.rs
107
src/engine.rs
@ -120,7 +120,7 @@ impl Target<'_> {
|
|||||||
chars.iter().for_each(|&ch| s.push(ch));
|
chars.iter().for_each(|&ch| s.push(ch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("should be String"),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +139,10 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type to hold a number of `Dynamic` values in static storage for speed,
|
/// A type to hold a number of values in static storage for speed,
|
||||||
/// and any spill-overs in a `Vec`.
|
/// and any spill-overs in a `Vec`.
|
||||||
struct StaticVec<T: Default> {
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StaticVec<T: Default + Clone> {
|
||||||
/// Total number of values held.
|
/// Total number of values held.
|
||||||
len: usize,
|
len: usize,
|
||||||
/// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection.
|
/// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection.
|
||||||
@ -150,7 +151,7 @@ struct StaticVec<T: Default> {
|
|||||||
more: Vec<T>,
|
more: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default> StaticVec<T> {
|
impl<T: Default + Clone> StaticVec<T> {
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -191,6 +192,21 @@ impl<T: Default> StaticVec<T> {
|
|||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
/// Get the number of items in this `StaticVec`.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
pub fn get(&self, index: usize) -> &T {
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("index OOB in StaticVec");
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < self.list.len() {
|
||||||
|
self.list.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
self.more.get(index - self.list.len()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that holds all the current states of the Engine.
|
/// A type that holds all the current states of the Engine.
|
||||||
@ -460,14 +476,39 @@ fn default_print(s: &str) {
|
|||||||
fn search_scope<'a>(
|
fn search_scope<'a>(
|
||||||
scope: &'a mut Scope,
|
scope: &'a mut Scope,
|
||||||
name: &str,
|
name: &str,
|
||||||
begin: Position,
|
namespaces: Option<&Box<StaticVec<(String, Position)>>>,
|
||||||
|
pos: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
|
if let Some(namespaces) = namespaces {
|
||||||
|
let (id, root_pos) = namespaces.get(0); // First namespace
|
||||||
|
let mut sub_scope = scope
|
||||||
|
.find_sub_scope(id)
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorNamespaceNotFound(id.into(), *root_pos)))?;
|
||||||
|
|
||||||
|
for x in 1..namespaces.len() {
|
||||||
|
let (id, id_pos) = namespaces.get(x);
|
||||||
|
|
||||||
|
sub_scope = sub_scope
|
||||||
|
.get_mut(id)
|
||||||
|
.unwrap()
|
||||||
|
.downcast_mut::<Map>()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Box::new(EvalAltResult::ErrorNamespaceNotFound(id.into(), *id_pos))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub_scope
|
||||||
|
.get_mut(name)
|
||||||
|
.map(|v| (v, ScopeEntryType::Constant))
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))
|
||||||
|
} else {
|
||||||
let (index, _) = scope
|
let (index, _) = scope
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), begin)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
||||||
|
|
||||||
Ok(scope.get_mut(index))
|
Ok(scope.get_mut(index))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Create a new `Engine`
|
/// Create a new `Engine`
|
||||||
@ -813,7 +854,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(fn_name, _, def_val, pos) => {
|
Expr::FnCall(fn_name, None,_, def_val, pos) => {
|
||||||
let mut args: Vec<_> = once(obj)
|
let mut args: Vec<_> = once(obj)
|
||||||
.chain(idx_val.downcast_mut::<Array>().unwrap().iter_mut())
|
.chain(idx_val.downcast_mut::<Array>().unwrap().iter_mut())
|
||||||
.collect();
|
.collect();
|
||||||
@ -822,6 +863,8 @@ impl Engine {
|
|||||||
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
||||||
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
||||||
}
|
}
|
||||||
|
// xxx.namespace::fn_name(...) - syntax error
|
||||||
|
Expr::FnCall(_,_,_,_,_) => unreachable!(),
|
||||||
// {xxx:map}.id = ???
|
// {xxx:map}.id = ???
|
||||||
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
|
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
|
||||||
let mut indexed_val =
|
let mut indexed_val =
|
||||||
@ -927,10 +970,10 @@ impl Engine {
|
|||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(id, index, pos) => {
|
Expr::Variable(id, namespaces, index, pos) => {
|
||||||
let (target, typ) = match index {
|
let (target, typ) = match index {
|
||||||
Some(i) if !state.always_search => scope.get_mut(scope.len() - i.get()),
|
Some(i) if !state.always_search => scope.get_mut(scope.len() - i.get()),
|
||||||
_ => search_scope(scope, id, *pos)?,
|
_ => search_scope(scope, id, namespaces.as_ref(), *pos)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
@ -984,7 +1027,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::FnCall(_, arg_exprs, _, _) => {
|
Expr::FnCall(_, None, arg_exprs, _, _) => {
|
||||||
let arg_values = arg_exprs
|
let arg_values = arg_exprs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level))
|
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level))
|
||||||
@ -992,6 +1035,7 @@ impl Engine {
|
|||||||
|
|
||||||
idx_values.push(arg_values)
|
idx_values.push(arg_values)
|
||||||
}
|
}
|
||||||
|
Expr::FnCall(_, _, _, _, _) => unreachable!(),
|
||||||
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
||||||
Expr::Index(lhs, rhs, _) | Expr::Dot(lhs, rhs, _) => {
|
Expr::Index(lhs, rhs, _) | Expr::Dot(lhs, rhs, _) => {
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
@ -1160,11 +1204,13 @@ impl Engine {
|
|||||||
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
||||||
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
||||||
Expr::CharConstant(c, _) => Ok((*c).into()),
|
Expr::CharConstant(c, _) => Ok((*c).into()),
|
||||||
Expr::Variable(_, Some(index), _) if !state.always_search => {
|
Expr::Variable(_, None, Some(index), _) if !state.always_search => {
|
||||||
Ok(scope.get_mut(scope.len() - index.get()).0.clone())
|
Ok(scope.get_mut(scope.len() - index.get()).0.clone())
|
||||||
}
|
}
|
||||||
Expr::Variable(id, _, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()),
|
Expr::Variable(id, namespaces, _, pos) => {
|
||||||
Expr::Property(_, _) => panic!("unexpected property."),
|
search_scope(scope, id, namespaces.as_ref(), *pos).map(|(v, _)| v.clone())
|
||||||
|
}
|
||||||
|
Expr::Property(_, _) => unreachable!(),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, fn_lib, stmt, level),
|
Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, fn_lib, stmt, level),
|
||||||
@ -1175,22 +1221,19 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(name, _, pos) => match scope.get(name) {
|
Expr::Variable(name, namespaces, _, pos) => {
|
||||||
None => {
|
match search_scope(scope, name, namespaces.as_ref(), *pos)? {
|
||||||
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
(_, ScopeEntryType::Constant) => Err(Box::new(
|
||||||
name.to_string(),
|
|
||||||
*pos,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
Some((_, ScopeEntryType::Constant)) => Err(Box::new(
|
|
||||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
||||||
)),
|
)),
|
||||||
Some((index, ScopeEntryType::Normal)) => {
|
(value_ptr, ScopeEntryType::Normal) => {
|
||||||
*scope.get_mut(index).0 = rhs_val;
|
*value_ptr = rhs_val;
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
},
|
}
|
||||||
Some((_, ScopeEntryType::Subscope)) => unreachable!(),
|
// End variable cannot be a sub-scope
|
||||||
},
|
(_, ScopeEntryType::SubScope) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
// idx_lhs[idx_expr] = rhs
|
// idx_lhs[idx_expr] = rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
@ -1252,7 +1295,8 @@ impl Engine {
|
|||||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||||
)))),
|
)))),
|
||||||
|
|
||||||
Expr::FnCall(fn_name, arg_exprs, def_val, pos) => {
|
// TODO - handle namespaced function call
|
||||||
|
Expr::FnCall(fn_name, namespaces, arg_exprs, def_val, pos) => {
|
||||||
let mut arg_values = arg_exprs
|
let mut arg_values = arg_exprs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
|
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
|
||||||
@ -1322,7 +1366,7 @@ impl Engine {
|
|||||||
Expr::False(_) => Ok(false.into()),
|
Expr::False(_) => Ok(false.into()),
|
||||||
Expr::Unit(_) => Ok(().into()),
|
Expr::Unit(_) => Ok(().into()),
|
||||||
|
|
||||||
_ => panic!("should not appear: {:?}", expr),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1499,9 +1543,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
Stmt::Import(_name_expr, _alias) => {
|
Stmt::Import(_name_expr, _alias) => unimplemented!(),
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Const statement
|
// Const statement
|
||||||
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
||||||
@ -1512,7 +1554,8 @@ impl Engine {
|
|||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Const(_, _, _) => panic!("constant expression not constant!"),
|
// Const expression not constant
|
||||||
|
Stmt::Const(_, _, _) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,10 +368,12 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
//id = id2 = expr2
|
//id = id2 = expr2
|
||||||
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
||||||
// var = var = expr2 -> var = expr2
|
// var = var = expr2 -> var = expr2
|
||||||
(Expr::Variable(var, sp, _), Expr::Variable(var2, sp2, _)) if var == var2 && sp == sp2 => {
|
(Expr::Variable(var, None, sp, _), Expr::Variable(var2, None, sp2, _))
|
||||||
|
if var == var2 && sp == sp2 =>
|
||||||
|
{
|
||||||
// Assignment to the same variable - fold
|
// Assignment to the same variable - fold
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::Assignment(Box::new(Expr::Variable(var, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos)
|
Expr::Assignment(Box::new(Expr::Variable(var, None, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos)
|
||||||
}
|
}
|
||||||
// id1 = id2 = expr2
|
// id1 = id2 = expr2
|
||||||
(id1, id2) => Expr::Assignment(
|
(id1, id2) => Expr::Assignment(
|
||||||
@ -547,18 +549,18 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=>
|
Expr::FnCall(id, None, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=>
|
||||||
Expr::FnCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FnCall(id, args, def_value, pos)
|
Expr::FnCall(id, None, args, def_value, pos)
|
||||||
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
return Expr::FnCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
return Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||||
@ -589,16 +591,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
})
|
})
|
||||||
).unwrap_or_else(||
|
).unwrap_or_else(||
|
||||||
// Optimize function call arguments
|
// Optimize function call arguments
|
||||||
Expr::FnCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(id, args, def_value, pos) =>
|
Expr::FnCall(id, namespaces, args, def_value, pos) =>
|
||||||
Expr::FnCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
Expr::FnCall(id, namespaces, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(name, _, pos) if state.contains_constant(&name) => {
|
Expr::Variable(name, None, _, pos) if state.contains_constant(&name) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
|
144
src/parser.rs
144
src/parser.rs
@ -1,7 +1,7 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Union};
|
use crate::any::{Dynamic, Union};
|
||||||
use crate::engine::{calc_fn_def, Engine, FunctionsLib};
|
use crate::engine::{calc_fn_def, Engine, FunctionsLib, StaticVec};
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
@ -246,10 +246,10 @@ pub enum Stmt {
|
|||||||
Continue(Position),
|
Continue(Position),
|
||||||
/// break
|
/// break
|
||||||
Break(Position),
|
Break(Position),
|
||||||
/// `return`/`throw`
|
/// return/throw
|
||||||
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
||||||
/// import expr
|
/// import expr
|
||||||
Import(Box<Expr>, Option<String>)
|
Import(Box<Expr>, Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
@ -264,9 +264,9 @@ impl Stmt {
|
|||||||
| Stmt::Break(pos)
|
| Stmt::Break(pos)
|
||||||
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||||
|
|
||||||
Stmt::IfThenElse(expr, _, _)
|
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) | Stmt::Import(expr, _) => {
|
||||||
| Stmt::Expr(expr)
|
expr.position()
|
||||||
| Stmt::Import(expr, _) => expr.position(),
|
}
|
||||||
|
|
||||||
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
||||||
}
|
}
|
||||||
@ -326,27 +326,23 @@ pub enum Expr {
|
|||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(String, Position),
|
StringConstant(String, Position),
|
||||||
/// Variable access.
|
/// Variable access - (variable name, optional namespaces, optional index, position)
|
||||||
Variable(Box<String>, Option<NonZeroUsize>, Position),
|
Variable(
|
||||||
|
Box<String>,
|
||||||
|
Option<Box<StaticVec<(String, Position)>>>,
|
||||||
|
Option<NonZeroUsize>,
|
||||||
|
Position,
|
||||||
|
),
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(String, Position),
|
Property(String, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<Stmt>, Position),
|
Stmt(Box<Stmt>, Position),
|
||||||
/// func(expr, ... )
|
/// func(expr, ... ) - (function name, optional namespaces, arguments, optional default value, position)
|
||||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
/// 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`.
|
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||||
FnCall(
|
FnCall(
|
||||||
Box<Cow<'static, str>>,
|
Box<Cow<'static, str>>,
|
||||||
Box<Vec<Expr>>,
|
Option<Box<StaticVec<(String, Position)>>>,
|
||||||
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>>,
|
Box<Vec<Expr>>,
|
||||||
Option<Box<Dynamic>>,
|
Option<Box<Dynamic>>,
|
||||||
Position,
|
Position,
|
||||||
@ -444,11 +440,10 @@ impl Expr {
|
|||||||
| Self::StringConstant(_, pos)
|
| Self::StringConstant(_, pos)
|
||||||
| Self::Array(_, pos)
|
| Self::Array(_, pos)
|
||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, _, pos)
|
| Self::Variable(_, _, _, pos)
|
||||||
| Self::Property(_, pos)
|
| Self::Property(_, pos)
|
||||||
| Self::Stmt(_, pos)
|
| Self::Stmt(_, pos)
|
||||||
| Self::FnCall(_, _, _, pos)
|
| Self::FnCall(_, _, _, _, pos)
|
||||||
| Self::SubscopeFnCall(_, _, _, _, pos)
|
|
||||||
| Self::And(_, _, pos)
|
| Self::And(_, _, pos)
|
||||||
| Self::Or(_, _, pos)
|
| Self::Or(_, _, pos)
|
||||||
| Self::In(_, _, pos)
|
| Self::In(_, _, pos)
|
||||||
@ -471,11 +466,10 @@ impl Expr {
|
|||||||
| Self::StringConstant(_, pos)
|
| Self::StringConstant(_, pos)
|
||||||
| Self::Array(_, pos)
|
| Self::Array(_, pos)
|
||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, _, pos)
|
| Self::Variable(_, _, _, pos)
|
||||||
| Self::Property(_, pos)
|
| Self::Property(_, pos)
|
||||||
| Self::Stmt(_, pos)
|
| Self::Stmt(_, pos)
|
||||||
| Self::FnCall(_, _, _, pos)
|
| Self::FnCall(_, _, _, _, pos)
|
||||||
| Self::SubscopeFnCall(_, _, _, _, pos)
|
|
||||||
| Self::And(_, _, pos)
|
| Self::And(_, _, pos)
|
||||||
| Self::Or(_, _, pos)
|
| Self::Or(_, _, pos)
|
||||||
| Self::In(_, _, pos)
|
| Self::In(_, _, pos)
|
||||||
@ -503,7 +497,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Stmt(stmt, _) => stmt.is_pure(),
|
Self::Stmt(stmt, _) => stmt.is_pure(),
|
||||||
|
|
||||||
Self::Variable(_, _, _) => true,
|
Self::Variable(_, _, _, _) => true,
|
||||||
|
|
||||||
expr => expr.is_constant(),
|
expr => expr.is_constant(),
|
||||||
}
|
}
|
||||||
@ -552,8 +546,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::StringConstant(_, _)
|
Self::StringConstant(_, _)
|
||||||
| Self::Stmt(_, _)
|
| Self::Stmt(_, _)
|
||||||
| Self::FnCall(_, _, _, _)
|
| Self::FnCall(_, _, _, _, _)
|
||||||
| Self::SubscopeFnCall(_, _, _, _, _)
|
|
||||||
| Self::Assignment(_, _, _)
|
| Self::Assignment(_, _, _)
|
||||||
| Self::Dot(_, _, _)
|
| Self::Dot(_, _, _)
|
||||||
| Self::Index(_, _, _)
|
| Self::Index(_, _, _)
|
||||||
@ -563,7 +556,19 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Variable(_, _, _) | Self::Property(_, _) => match token {
|
Self::Variable(_, None, _, _) => match token {
|
||||||
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
Token::DoubleColon => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Self::Variable(_, _, _, _) => match token {
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
Token::DoubleColon => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
|
||||||
|
Self::Property(_, _) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
@ -573,7 +578,7 @@ impl Expr {
|
|||||||
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
||||||
pub(crate) fn into_property(self) -> Self {
|
pub(crate) fn into_property(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(id, _, pos) => Self::Property(*id, pos),
|
Self::Variable(id, None, _, pos) => Self::Property(*id, pos),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -637,6 +642,7 @@ fn parse_call_expr<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
id: String,
|
id: String,
|
||||||
|
namespaces: Option<Box<StaticVec<(String, Position)>>>,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
@ -658,6 +664,7 @@ fn parse_call_expr<'a>(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(id.into()),
|
Box::new(id.into()),
|
||||||
|
namespaces,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -673,8 +680,10 @@ fn parse_call_expr<'a>(
|
|||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::RightParen, _) => {
|
(Token::RightParen, _) => {
|
||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(id.into()),
|
Box::new(id.into()),
|
||||||
|
namespaces,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -1001,7 +1010,7 @@ fn parse_primary<'a>(
|
|||||||
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
let index = stack.find(&s);
|
let index = stack.find(&s);
|
||||||
Expr::Variable(Box::new(s), index, pos)
|
Expr::Variable(Box::new(s), None, index, pos)
|
||||||
}
|
}
|
||||||
Token::LeftParen => parse_paren_expr(input, stack, pos, allow_stmt_expr)?,
|
Token::LeftParen => parse_paren_expr(input, stack, pos, allow_stmt_expr)?,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1024,19 +1033,36 @@ fn parse_primary<'a>(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (token, pos) = input.next().unwrap();
|
let (token, token_pos) = input.next().unwrap();
|
||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(id, _, pos), Token::LeftParen) => {
|
(Expr::Variable(id, namespaces, _, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, *id, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, *id, namespaces, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
(Expr::Property(id, pos), Token::LeftParen) => {
|
(Expr::Property(id, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, id, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
||||||
|
}
|
||||||
|
// Namespaced
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
(Expr::Variable(id, mut namespaces, _, pos), Token::DoubleColon) => {
|
||||||
|
match input.next().unwrap() {
|
||||||
|
(Token::Identifier(id2), pos2) => {
|
||||||
|
if let Some(ref mut namespaces) = namespaces {
|
||||||
|
namespaces.push((*id, pos));
|
||||||
|
} else {
|
||||||
|
let mut vec = StaticVec::new();
|
||||||
|
vec.push((*id, pos));
|
||||||
|
namespaces = Some(Box::new(vec));
|
||||||
|
}
|
||||||
|
Expr::Variable(Box::new(id2), namespaces, None, pos2)
|
||||||
|
}
|
||||||
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Indexing
|
// Indexing
|
||||||
(expr, Token::LeftBracket) => {
|
(expr, Token::LeftBracket) => {
|
||||||
parse_index_chain(input, stack, expr, pos, allow_stmt_expr)?
|
parse_index_chain(input, stack, expr, token_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),
|
||||||
@ -1092,6 +1118,7 @@ fn parse_unary<'a>(
|
|||||||
// Call negative function
|
// Call negative function
|
||||||
e => Ok(Expr::FnCall(
|
e => Ok(Expr::FnCall(
|
||||||
Box::new("-".into()),
|
Box::new("-".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![e]),
|
Box::new(vec![e]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1108,6 +1135,7 @@ fn parse_unary<'a>(
|
|||||||
let pos = eat_token(input, Token::Bang);
|
let pos = eat_token(input, Token::Bang);
|
||||||
Ok(Expr::FnCall(
|
Ok(Expr::FnCall(
|
||||||
Box::new("!".into()),
|
Box::new("!".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
||||||
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
pos,
|
pos,
|
||||||
@ -1161,25 +1189,34 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
let args = vec![lhs_copy, rhs];
|
let args = vec![lhs_copy, rhs];
|
||||||
let rhs_expr = Expr::FnCall(Box::new(op.into()), Box::new(args), None, pos);
|
let rhs_expr = Expr::FnCall(Box::new(op.into()), None, Box::new(args), None, pos);
|
||||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs_expr), pos))
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs_expr), pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a dot expression.
|
/// Make a dot expression.
|
||||||
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position, is_index: bool) -> Expr {
|
fn make_dot_expr(
|
||||||
match (lhs, rhs) {
|
lhs: Expr,
|
||||||
|
rhs: Expr,
|
||||||
|
op_pos: Position,
|
||||||
|
is_index: bool,
|
||||||
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
|
Ok(match (lhs, rhs) {
|
||||||
// idx_lhs[idx_rhs].rhs
|
// idx_lhs[idx_rhs].rhs
|
||||||
// Attach dot chain to the bottom level of indexing chain
|
// Attach dot chain to the bottom level of indexing chain
|
||||||
(Expr::Index(idx_lhs, idx_rhs, idx_pos), rhs) => Expr::Index(
|
(Expr::Index(idx_lhs, idx_rhs, idx_pos), rhs) => Expr::Index(
|
||||||
idx_lhs,
|
idx_lhs,
|
||||||
Box::new(make_dot_expr(*idx_rhs, rhs, op_pos, true)),
|
Box::new(make_dot_expr(*idx_rhs, rhs, op_pos, true)?),
|
||||||
idx_pos,
|
idx_pos,
|
||||||
),
|
),
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, rhs @ Expr::Variable(_, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
(lhs, rhs @ Expr::Variable(_, None, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
||||||
let lhs = if is_index { lhs.into_property() } else { lhs };
|
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||||
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
||||||
}
|
}
|
||||||
|
// lhs.namespace::id - syntax error
|
||||||
|
(_, Expr::Variable(_, Some(namespaces), _, _)) => {
|
||||||
|
return Err(PERR::PropertyExpected.into_err(namespaces.get(0).1))
|
||||||
|
}
|
||||||
// lhs.dot_lhs.dot_rhs
|
// lhs.dot_lhs.dot_rhs
|
||||||
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(
|
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(
|
||||||
Box::new(lhs),
|
Box::new(lhs),
|
||||||
@ -1202,7 +1239,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position, is_index: bool) -> Expr
|
|||||||
),
|
),
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos),
|
(lhs, rhs) => Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make an 'in' expression.
|
/// Make an 'in' expression.
|
||||||
@ -1380,24 +1417,28 @@ fn parse_binary_op<'a>(
|
|||||||
current_lhs = match op_token {
|
current_lhs = match op_token {
|
||||||
Token::Plus => Expr::FnCall(
|
Token::Plus => Expr::FnCall(
|
||||||
Box::new("+".into()),
|
Box::new("+".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::Minus => Expr::FnCall(
|
Token::Minus => Expr::FnCall(
|
||||||
Box::new("-".into()),
|
Box::new("-".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::Multiply => Expr::FnCall(
|
Token::Multiply => Expr::FnCall(
|
||||||
Box::new("*".into()),
|
Box::new("*".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::Divide => Expr::FnCall(
|
Token::Divide => Expr::FnCall(
|
||||||
Box::new("/".into()),
|
Box::new("/".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1405,24 +1446,28 @@ fn parse_binary_op<'a>(
|
|||||||
|
|
||||||
Token::LeftShift => Expr::FnCall(
|
Token::LeftShift => Expr::FnCall(
|
||||||
Box::new("<<".into()),
|
Box::new("<<".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::RightShift => Expr::FnCall(
|
Token::RightShift => Expr::FnCall(
|
||||||
Box::new(">>".into()),
|
Box::new(">>".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::Modulo => Expr::FnCall(
|
Token::Modulo => Expr::FnCall(
|
||||||
Box::new("%".into()),
|
Box::new("%".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::PowerOf => Expr::FnCall(
|
Token::PowerOf => Expr::FnCall(
|
||||||
Box::new("~".into()),
|
Box::new("~".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1431,36 +1476,42 @@ fn parse_binary_op<'a>(
|
|||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo => Expr::FnCall(
|
Token::EqualsTo => Expr::FnCall(
|
||||||
Box::new("==".into()),
|
Box::new("==".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::NotEqualsTo => Expr::FnCall(
|
Token::NotEqualsTo => Expr::FnCall(
|
||||||
Box::new("!=".into()),
|
Box::new("!=".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::LessThan => Expr::FnCall(
|
Token::LessThan => Expr::FnCall(
|
||||||
Box::new("<".into()),
|
Box::new("<".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::LessThanEqualsTo => Expr::FnCall(
|
Token::LessThanEqualsTo => Expr::FnCall(
|
||||||
Box::new("<=".into()),
|
Box::new("<=".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::GreaterThan => Expr::FnCall(
|
Token::GreaterThan => Expr::FnCall(
|
||||||
Box::new(">".into()),
|
Box::new(">".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::GreaterThanEqualsTo => Expr::FnCall(
|
Token::GreaterThanEqualsTo => Expr::FnCall(
|
||||||
Box::new(">=".into()),
|
Box::new(">=".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1470,18 +1521,21 @@ fn parse_binary_op<'a>(
|
|||||||
Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs), pos),
|
Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs), pos),
|
||||||
Token::Ampersand => Expr::FnCall(
|
Token::Ampersand => Expr::FnCall(
|
||||||
Box::new("&".into()),
|
Box::new("&".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::Pipe => Expr::FnCall(
|
Token::Pipe => Expr::FnCall(
|
||||||
Box::new("|".into()),
|
Box::new("|".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::XOr => Expr::FnCall(
|
Token::XOr => Expr::FnCall(
|
||||||
Box::new("^".into()),
|
Box::new("^".into()),
|
||||||
|
None,
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1490,7 +1544,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::In => make_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 => make_dot_expr(current_lhs, rhs, pos, false),
|
Token::Period => make_dot_expr(current_lhs, rhs, pos, false)?,
|
||||||
|
|
||||||
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
|
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -1704,8 +1758,8 @@ fn parse_let<'a>(
|
|||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
||||||
}
|
}
|
||||||
|
// Variable cannot be a sub-scope
|
||||||
ScopeEntryType::Subscope => unreachable!(),
|
ScopeEntryType::SubScope => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// let name
|
// let name
|
||||||
|
@ -61,6 +61,8 @@ pub enum EvalAltResult {
|
|||||||
ErrorFor(Position),
|
ErrorFor(Position),
|
||||||
/// Usage of an unknown variable. Wrapped value is the name of the variable.
|
/// Usage of an unknown variable. Wrapped value is the name of the variable.
|
||||||
ErrorVariableNotFound(String, Position),
|
ErrorVariableNotFound(String, Position),
|
||||||
|
/// Usage of an unknown namespace. Wrapped value is the name of the namespace.
|
||||||
|
ErrorNamespaceNotFound(String, Position),
|
||||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||||
ErrorAssignmentToUnknownLHS(Position),
|
ErrorAssignmentToUnknownLHS(Position),
|
||||||
/// Assignment to a constant variable.
|
/// Assignment to a constant variable.
|
||||||
@ -119,6 +121,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorLogicGuard(_) => "Boolean value expected",
|
Self::ErrorLogicGuard(_) => "Boolean value expected",
|
||||||
Self::ErrorFor(_) => "For loop expects an array, object map, or range",
|
Self::ErrorFor(_) => "For loop expects an array, object map, or range",
|
||||||
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
||||||
|
Self::ErrorNamespaceNotFound(_, _) => "Namespace not found",
|
||||||
Self::ErrorAssignmentToUnknownLHS(_) => {
|
Self::ErrorAssignmentToUnknownLHS(_) => {
|
||||||
"Assignment to an unsupported left-hand side expression"
|
"Assignment to an unsupported left-hand side expression"
|
||||||
}
|
}
|
||||||
@ -150,9 +153,10 @@ impl fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
||||||
|
|
||||||
Self::ErrorFunctionNotFound(s, pos) | Self::ErrorVariableNotFound(s, pos) => {
|
Self::ErrorFunctionNotFound(s, pos)
|
||||||
write!(f, "{}: '{}' ({})", desc, s, pos)
|
| Self::ErrorVariableNotFound(s, pos)
|
||||||
}
|
| Self::ErrorNamespaceNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
||||||
|
|
||||||
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
||||||
|
|
||||||
Self::ErrorIndexingType(_, pos)
|
Self::ErrorIndexingType(_, pos)
|
||||||
@ -269,6 +273,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(pos)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableNotFound(_, pos)
|
| Self::ErrorVariableNotFound(_, pos)
|
||||||
|
| Self::ErrorNamespaceNotFound(_, pos)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
| Self::ErrorAssignmentToConstant(_, pos)
|
| Self::ErrorAssignmentToConstant(_, pos)
|
||||||
| Self::ErrorMismatchOutputType(_, pos)
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
@ -303,6 +308,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(pos)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableNotFound(_, pos)
|
| Self::ErrorVariableNotFound(_, pos)
|
||||||
|
| Self::ErrorNamespaceNotFound(_, pos)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
| Self::ErrorAssignmentToConstant(_, pos)
|
| Self::ErrorAssignmentToConstant(_, pos)
|
||||||
| Self::ErrorMismatchOutputType(_, pos)
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
|
64
src/scope.rs
64
src/scope.rs
@ -1,6 +1,7 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
|
use crate::engine::Map;
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr};
|
use crate::parser::{map_dynamic_to_expr, Expr};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
@ -13,8 +14,8 @@ pub enum EntryType {
|
|||||||
Normal,
|
Normal,
|
||||||
/// Immutable constant value.
|
/// Immutable constant value.
|
||||||
Constant,
|
Constant,
|
||||||
/// Name of a subscope, allowing member access with the :: operator.
|
/// Name of a sub-scope, allowing member access with the :: operator.
|
||||||
Subscope,
|
SubScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An entry in the Scope.
|
/// An entry in the Scope.
|
||||||
@ -167,22 +168,27 @@ impl<'a> Scope<'a> {
|
|||||||
self.push_dynamic_value(name, EntryType::Normal, value, false);
|
self.push_dynamic_value(name, EntryType::Normal, value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new subscope to the Scope.
|
/// Add (push) a new sub-scope to the Scope.
|
||||||
///
|
///
|
||||||
/// Subscopes are used for access to members in modules and plugins.
|
/// Sub-scopes are used for accessing members in modules and plugins under a namespace.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::Scope;
|
/// use rhai::{Scope, Map};
|
||||||
///
|
///
|
||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push_subscope("x".to_string(), "My Plugin".to_string());
|
/// let mut sub_scope = Map::new();
|
||||||
/// assert_eq!(my_scope.get_subscope("x").unwrap(), "My Plugin");
|
/// sub_scope.insert("x".to_string(), 42_i64.into());
|
||||||
|
///
|
||||||
|
/// my_scope.push_sub_scope("my_plugin", sub_scope);
|
||||||
|
///
|
||||||
|
/// let s = my_scope.find_sub_scope("my_plugin").unwrap();
|
||||||
|
/// assert_eq!(*s.get("x").unwrap().downcast_ref::<i64>().unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_subscope(&mut self, name: String, value: String) {
|
pub fn push_sub_scope<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Map) {
|
||||||
self.push_dynamic_value(name, EntryType::Subscope, Dynamic::from(value), true);
|
self.push_dynamic_value(name, EntryType::SubScope, value.into(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new constant to the Scope.
|
/// Add (push) a new constant to the Scope.
|
||||||
@ -284,6 +290,8 @@ impl<'a> Scope<'a> {
|
|||||||
|
|
||||||
/// Does the scope contain the entry?
|
/// Does the scope contain the entry?
|
||||||
///
|
///
|
||||||
|
/// Sub-scopes are ignored.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -299,37 +307,45 @@ impl<'a> Scope<'a> {
|
|||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.any(|Entry { name: key, .. }| name == key)
|
.any(|Entry { name: key, typ, .. }| match typ {
|
||||||
|
EntryType::Normal | EntryType::Constant => name == key,
|
||||||
|
EntryType::SubScope => false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the Scope, starting from the last.
|
||||||
|
///
|
||||||
|
/// Sub-scopes are ignored.
|
||||||
pub(crate) fn get(&self, name: &str) -> Option<(usize, EntryType)> {
|
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(|(index, Entry { name: key, typ, .. })| {
|
.find_map(|(index, Entry { name: key, typ, .. })| match typ {
|
||||||
|
EntryType::Normal | EntryType::Constant => {
|
||||||
if name == key {
|
if name == key {
|
||||||
Some((index, *typ))
|
Some((index, *typ))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
EntryType::SubScope => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the subscope of an entry in the Scope, starting from the last.
|
/// Find a sub-scope in the Scope, starting from the last entry.
|
||||||
///
|
pub fn find_sub_scope(&mut self, name: &str) -> Option<&mut Map> {
|
||||||
pub fn get_subscope(&self, name: &str) -> Option<String> {
|
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|Entry { name: key, typ, .. }| name == key &&
|
.find(|Entry { name: key, typ, .. }| name == key && *typ == EntryType::SubScope)
|
||||||
std::mem::discriminant(typ) == std::mem::discriminant(&EntryType::Subscope))
|
.and_then(|Entry { value, .. }| value.downcast_mut::<Map>())
|
||||||
.and_then(|Entry { value, .. }| value.downcast_ref::<String>().cloned())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
|
/// Sub-scopes are ignored.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -344,7 +360,10 @@ impl<'a> Scope<'a> {
|
|||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|Entry { name: key, .. }| name == key)
|
.find(|Entry { name: key, typ, .. }| match typ {
|
||||||
|
EntryType::Normal | EntryType::Constant => name == key,
|
||||||
|
EntryType::SubScope => false,
|
||||||
|
})
|
||||||
.and_then(|Entry { value, .. }| value.downcast_ref::<T>().cloned())
|
.and_then(|Entry { value, .. }| value.downcast_ref::<T>().cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,14 +390,13 @@ 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) {
|
||||||
|
None => self.push(name, value),
|
||||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||||
Some((index, EntryType::Normal)) => {
|
Some((index, EntryType::Normal)) => {
|
||||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||||
}
|
}
|
||||||
Some((index, EntryType::Subscope)) => {
|
// Sub-scopes cannot be modified
|
||||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
Some((_, EntryType::SubScope)) => unreachable!(),
|
||||||
}
|
|
||||||
None => self.push(name, value),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/token.rs
26
src/token.rs
@ -153,6 +153,7 @@ pub enum Token {
|
|||||||
RightShift,
|
RightShift,
|
||||||
SemiColon,
|
SemiColon,
|
||||||
Colon,
|
Colon,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
DoubleColon,
|
DoubleColon,
|
||||||
Comma,
|
Comma,
|
||||||
Period,
|
Period,
|
||||||
@ -198,6 +199,12 @@ pub enum Token {
|
|||||||
XOrAssign,
|
XOrAssign,
|
||||||
ModuloAssign,
|
ModuloAssign,
|
||||||
PowerOfAssign,
|
PowerOfAssign,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
Import,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
Export,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
As,
|
||||||
LexError(Box<LexError>),
|
LexError(Box<LexError>),
|
||||||
EOF,
|
EOF,
|
||||||
}
|
}
|
||||||
@ -231,6 +238,7 @@ impl Token {
|
|||||||
Divide => "/",
|
Divide => "/",
|
||||||
SemiColon => ";",
|
SemiColon => ";",
|
||||||
Colon => ":",
|
Colon => ":",
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
DoubleColon => "::",
|
DoubleColon => "::",
|
||||||
Comma => ",",
|
Comma => ",",
|
||||||
Period => ".",
|
Period => ".",
|
||||||
@ -245,6 +253,8 @@ impl Token {
|
|||||||
Else => "else",
|
Else => "else",
|
||||||
While => "while",
|
While => "while",
|
||||||
Loop => "loop",
|
Loop => "loop",
|
||||||
|
For => "for",
|
||||||
|
In => "in",
|
||||||
LessThan => "<",
|
LessThan => "<",
|
||||||
GreaterThan => ">",
|
GreaterThan => ">",
|
||||||
Bang => "!",
|
Bang => "!",
|
||||||
@ -278,8 +288,12 @@ impl Token {
|
|||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "~",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "~=",
|
||||||
For => "for",
|
#[cfg(not(feature = "no_import"))]
|
||||||
In => "in",
|
Import => "import",
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
Export => "export",
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
As => "as",
|
||||||
EOF => "{EOF}",
|
EOF => "{EOF}",
|
||||||
_ => panic!("operator should be match in outer scope"),
|
_ => panic!("operator should be match in outer scope"),
|
||||||
})
|
})
|
||||||
@ -743,6 +757,13 @@ impl<'a> TokenIterator<'a> {
|
|||||||
"for" => Token::For,
|
"for" => Token::For,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
"import" => Token::Import,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
"export" => Token::Export,
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
"as" => Token::As,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
"fn" => Token::Fn,
|
"fn" => Token::Fn,
|
||||||
|
|
||||||
@ -897,6 +918,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
('=', _) => return Some((Token::Equals, pos)),
|
('=', _) => return Some((Token::Equals, pos)),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
(':', ':') => {
|
(':', ':') => {
|
||||||
self.eat_next();
|
self.eat_next();
|
||||||
return Some((Token::DoubleColon, pos));
|
return Some((Token::DoubleColon, pos));
|
||||||
|
Loading…
Reference in New Issue
Block a user