Add import statement.
This commit is contained in:
parent
b94fa31e21
commit
2bdd174f16
@ -28,7 +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
|
no_import = [] # no 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
|
||||||
|
19
README.md
19
README.md
@ -23,6 +23,7 @@ Rhai's current features set:
|
|||||||
* [`no-std`](#optional-features) support
|
* [`no-std`](#optional-features) support
|
||||||
* Support for [function overloading](#function-overloading)
|
* Support for [function overloading](#function-overloading)
|
||||||
* Support for [operator overloading](#operator-overloading)
|
* Support for [operator overloading](#operator-overloading)
|
||||||
|
* Support for loading external [modules]
|
||||||
* Compiled script is [optimized](#script-optimization) for repeat evaluations
|
* Compiled script is [optimized](#script-optimization) for repeat evaluations
|
||||||
* Support for [minimal builds](#minimal-builds) by excluding unneeded language [features](#optional-features)
|
* Support for [minimal builds](#minimal-builds) by excluding unneeded language [features](#optional-features)
|
||||||
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/)
|
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/)
|
||||||
@ -69,6 +70,7 @@ Optional features
|
|||||||
| `no_object` | Disable support for custom types and objects. |
|
| `no_object` | Disable support for custom types and objects. |
|
||||||
| `no_float` | Disable floating-point numbers and math if not needed. |
|
| `no_float` | Disable floating-point numbers and math if not needed. |
|
||||||
| `no_optimize` | Disable the script optimizer. |
|
| `no_optimize` | Disable the script optimizer. |
|
||||||
|
| `no_import` | Disable modules. |
|
||||||
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||||
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||||
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||||
@ -84,6 +86,7 @@ Excluding unneeded functionalities can result in smaller, faster builds as well
|
|||||||
[`no_function`]: #optional-features
|
[`no_function`]: #optional-features
|
||||||
[`no_object`]: #optional-features
|
[`no_object`]: #optional-features
|
||||||
[`no_optimize`]: #optional-features
|
[`no_optimize`]: #optional-features
|
||||||
|
[`no_import`]: #optional-features
|
||||||
[`only_i32`]: #optional-features
|
[`only_i32`]: #optional-features
|
||||||
[`only_i64`]: #optional-features
|
[`only_i64`]: #optional-features
|
||||||
[`no_std`]: #optional-features
|
[`no_std`]: #optional-features
|
||||||
@ -1998,6 +2001,22 @@ for entry in logbook.read().unwrap().iter() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Using external modules
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
[module]: #using-external-modules
|
||||||
|
[modules]: #using-external-modules
|
||||||
|
|
||||||
|
```rust
|
||||||
|
import "crypto" as crypto; // Import an external script file as a module
|
||||||
|
|
||||||
|
crypto::encrypt(secret); // Use functions defined under the module via '::'
|
||||||
|
|
||||||
|
print(crypto::status); // Module variables are constants
|
||||||
|
|
||||||
|
crypto::hash::sha256(key); // Sub-modules are also supported
|
||||||
|
```
|
||||||
|
|
||||||
Script optimization
|
Script optimization
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
@ -476,25 +476,23 @@ 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,
|
||||||
namespaces: Option<&Box<StaticVec<(String, Position)>>>,
|
modules: Option<&Box<StaticVec<(String, Position)>>>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
if let Some(namespaces) = namespaces {
|
if let Some(modules) = modules {
|
||||||
let (id, root_pos) = namespaces.get(0); // First namespace
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
let mut sub_scope = scope
|
let mut sub_scope = scope
|
||||||
.find_sub_scope(id)
|
.find_sub_scope(id)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorNamespaceNotFound(id.into(), *root_pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?;
|
||||||
|
|
||||||
for x in 1..namespaces.len() {
|
for x in 1..modules.len() {
|
||||||
let (id, id_pos) = namespaces.get(x);
|
let (id, id_pos) = modules.get(x);
|
||||||
|
|
||||||
sub_scope = sub_scope
|
sub_scope = sub_scope
|
||||||
.get_mut(id)
|
.get_mut(id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.downcast_mut::<Map>()
|
.downcast_mut::<Map>()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
||||||
Box::new(EvalAltResult::ErrorNamespaceNotFound(id.into(), *id_pos))
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub_scope
|
sub_scope
|
||||||
@ -503,7 +501,7 @@ fn search_scope<'a>(
|
|||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))
|
||||||
} else {
|
} else {
|
||||||
let (index, _) = scope
|
let (index, _) = scope
|
||||||
.get(name)
|
.get_index(name)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
||||||
|
|
||||||
Ok(scope.get_mut(index))
|
Ok(scope.get_mut(index))
|
||||||
@ -863,7 +861,7 @@ 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
|
// xxx.module::fn_name(...) - syntax error
|
||||||
Expr::FnCall(_,_,_,_,_) => unreachable!(),
|
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() => {
|
||||||
@ -934,7 +932,7 @@ impl Engine {
|
|||||||
if may_be_changed {
|
if may_be_changed {
|
||||||
if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
// Reuse args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
args[1] = indexed_val;
|
args[1] = indexed_val;
|
||||||
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err {
|
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
@ -974,10 +972,10 @@ impl Engine {
|
|||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(id, namespaces, index, pos) => {
|
Expr::Variable(id, modules, 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, namespaces.as_ref(), *pos)?,
|
_ => search_scope(scope, id, modules.as_ref(), *pos)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
@ -1211,8 +1209,8 @@ impl Engine {
|
|||||||
Expr::Variable(_, None, 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, namespaces, _, pos) => {
|
Expr::Variable(id, modules, _, pos) => {
|
||||||
search_scope(scope, id, namespaces.as_ref(), *pos).map(|(v, _)| v.clone())
|
search_scope(scope, id, modules.as_ref(), *pos).map(|(v, _)| v.clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_, _) => unreachable!(),
|
Expr::Property(_, _) => unreachable!(),
|
||||||
|
|
||||||
@ -1225,8 +1223,8 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(name, namespaces, _, pos) => {
|
Expr::Variable(name, modules, _, pos) => {
|
||||||
match search_scope(scope, name, namespaces.as_ref(), *pos)? {
|
match search_scope(scope, name, modules.as_ref(), *pos)? {
|
||||||
(_, ScopeEntryType::Constant) => Err(Box::new(
|
(_, ScopeEntryType::Constant) => Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
||||||
)),
|
)),
|
||||||
@ -1299,8 +1297,8 @@ impl Engine {
|
|||||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||||
)))),
|
)))),
|
||||||
|
|
||||||
// TODO - handle namespaced function call
|
// TODO - handle moduled function call
|
||||||
Expr::FnCall(fn_name, namespaces, arg_exprs, def_val, pos) => {
|
Expr::FnCall(fn_name, modules, 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))
|
||||||
@ -1546,9 +1544,6 @@ impl Engine {
|
|||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import statement
|
|
||||||
Stmt::Import(_name_expr, _alias) => unimplemented!(),
|
|
||||||
|
|
||||||
// Const statement
|
// Const statement
|
||||||
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
||||||
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
@ -1560,6 +1555,25 @@ impl Engine {
|
|||||||
|
|
||||||
// Const expression not constant
|
// Const expression not constant
|
||||||
Stmt::Const(_, _, _) => unreachable!(),
|
Stmt::Const(_, _, _) => unreachable!(),
|
||||||
|
|
||||||
|
// Import statement
|
||||||
|
Stmt::Import(expr, name, _) => {
|
||||||
|
if let Some(path) = self
|
||||||
|
.eval_expr(scope, state, fn_lib, expr, level)?
|
||||||
|
.try_cast::<String>()
|
||||||
|
{
|
||||||
|
let mut module = Map::new();
|
||||||
|
module.insert("kitty".to_string(), "foo".to_string().into());
|
||||||
|
module.insert("path".to_string(), path.into());
|
||||||
|
|
||||||
|
// TODO - avoid copying module name in inner block?
|
||||||
|
let mod_name = name.as_ref().clone();
|
||||||
|
scope.push_sub_scope(mod_name, module);
|
||||||
|
Ok(Default::default())
|
||||||
|
} else {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position())))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,8 +596,8 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(id, namespaces, args, def_value, pos) =>
|
Expr::FnCall(id, modules, args, def_value, pos) =>
|
||||||
Expr::FnCall(id, namespaces, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
Expr::FnCall(id, modules, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(name, None, _, pos) if state.contains_constant(&name) => {
|
Expr::Variable(name, None, _, pos) if state.contains_constant(&name) => {
|
||||||
|
206
src/parser.rs
206
src/parser.rs
@ -186,7 +186,7 @@ pub enum ReturnType {
|
|||||||
|
|
||||||
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Stack(Vec<String>);
|
struct Stack(Vec<(String, ScopeEntryType)>);
|
||||||
|
|
||||||
impl Stack {
|
impl Stack {
|
||||||
/// Create a new `Stack`.
|
/// Create a new `Stack`.
|
||||||
@ -202,13 +202,31 @@ impl Stack {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, n)| *n == name)
|
.find(|(_, (n, typ))| match typ {
|
||||||
|
ScopeEntryType::Normal | ScopeEntryType::Constant => *n == name,
|
||||||
|
ScopeEntryType::SubScope => false,
|
||||||
|
})
|
||||||
|
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
||||||
|
}
|
||||||
|
/// Find a sub-scope by name in the `Stack`, searching in reverse.
|
||||||
|
/// The return value is the offset to be deducted from `Stack::len`,
|
||||||
|
/// i.e. the top element of the `Stack` is offset 1.
|
||||||
|
/// Return zero when the variable name is not found in the `Stack`.
|
||||||
|
pub fn find_sub_scope(&self, name: &str) -> Option<NonZeroUsize> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, (n, typ))| match typ {
|
||||||
|
ScopeEntryType::SubScope => *n == name,
|
||||||
|
ScopeEntryType::Normal | ScopeEntryType::Constant => false,
|
||||||
|
})
|
||||||
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Stack {
|
impl Deref for Stack {
|
||||||
type Target = Vec<String>;
|
type Target = Vec<(String, ScopeEntryType)>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
@ -248,8 +266,8 @@ pub enum Stmt {
|
|||||||
Break(Position),
|
Break(Position),
|
||||||
/// return/throw
|
/// return/throw
|
||||||
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
||||||
/// import expr
|
/// import expr as module
|
||||||
Import(Box<Expr>, Option<String>),
|
Import(Box<Expr>, Box<String>, Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
@ -259,14 +277,13 @@ impl Stmt {
|
|||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
| Stmt::Let(_, _, pos)
|
| Stmt::Let(_, _, pos)
|
||||||
| Stmt::Const(_, _, pos)
|
| Stmt::Const(_, _, pos)
|
||||||
|
| Stmt::Import(_, _, pos)
|
||||||
| Stmt::Block(_, pos)
|
| Stmt::Block(_, pos)
|
||||||
| Stmt::Continue(pos)
|
| Stmt::Continue(pos)
|
||||||
| Stmt::Break(pos)
|
| Stmt::Break(pos)
|
||||||
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||||
|
|
||||||
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) | Stmt::Import(expr, _) => {
|
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
||||||
expr.position()
|
|
||||||
}
|
|
||||||
|
|
||||||
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
||||||
}
|
}
|
||||||
@ -279,14 +296,14 @@ impl Stmt {
|
|||||||
| Stmt::While(_, _)
|
| Stmt::While(_, _)
|
||||||
| Stmt::Loop(_)
|
| Stmt::Loop(_)
|
||||||
| Stmt::For(_, _, _)
|
| Stmt::For(_, _, _)
|
||||||
| Stmt::Block(_, _)
|
| Stmt::Block(_, _) => true,
|
||||||
| Stmt::Import(_, _) => true,
|
|
||||||
|
|
||||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||||
Stmt::Noop(_) => false,
|
Stmt::Noop(_) => false,
|
||||||
|
|
||||||
Stmt::Let(_, _, _)
|
Stmt::Let(_, _, _)
|
||||||
| Stmt::Const(_, _, _)
|
| Stmt::Const(_, _, _)
|
||||||
|
| Stmt::Import(_, _, _)
|
||||||
| Stmt::Expr(_)
|
| Stmt::Expr(_)
|
||||||
| Stmt::Continue(_)
|
| Stmt::Continue(_)
|
||||||
| Stmt::Break(_)
|
| Stmt::Break(_)
|
||||||
@ -310,7 +327,7 @@ impl Stmt {
|
|||||||
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
||||||
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
||||||
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
||||||
Stmt::Import(_, _) => false,
|
Stmt::Import(_, _, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,7 +343,7 @@ pub enum Expr {
|
|||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(String, Position),
|
StringConstant(String, Position),
|
||||||
/// Variable access - (variable name, optional namespaces, optional index, position)
|
/// Variable access - (variable name, optional modules, optional index, position)
|
||||||
Variable(
|
Variable(
|
||||||
Box<String>,
|
Box<String>,
|
||||||
Option<Box<StaticVec<(String, Position)>>>,
|
Option<Box<StaticVec<(String, Position)>>>,
|
||||||
@ -337,7 +354,7 @@ pub enum Expr {
|
|||||||
Property(String, Position),
|
Property(String, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<Stmt>, Position),
|
Stmt(Box<Stmt>, Position),
|
||||||
/// func(expr, ... ) - (function name, optional namespaces, arguments, optional default value, position)
|
/// func(expr, ... ) - (function name, optional modules, 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(
|
||||||
@ -614,11 +631,11 @@ fn match_token(input: &mut Peekable<TokenIterator>, token: Token) -> Result<bool
|
|||||||
fn parse_paren_expr<'a>(
|
fn parse_paren_expr<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
begin: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
if match_token(input, Token::RightParen)? {
|
if match_token(input, Token::RightParen)? {
|
||||||
return Ok(Expr::Unit(begin));
|
return Ok(Expr::Unit(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
@ -630,7 +647,7 @@ fn parse_paren_expr<'a>(
|
|||||||
(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)),
|
||||||
// ( xxx ???
|
// ( xxx ???
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
")".into(),
|
Token::RightParen.into(),
|
||||||
"for a matching ( in this expression".into(),
|
"for a matching ( in this expression".into(),
|
||||||
)
|
)
|
||||||
.into_err(pos)),
|
.into_err(pos)),
|
||||||
@ -642,7 +659,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)>>>,
|
modules: 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>> {
|
||||||
@ -652,7 +669,7 @@ fn parse_call_expr<'a>(
|
|||||||
// id <EOF>
|
// id <EOF>
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
")".into(),
|
Token::RightParen.into(),
|
||||||
format!("to close the arguments list of this function call '{}'", id),
|
format!("to close the arguments list of this function call '{}'", id),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
@ -664,7 +681,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,
|
modules,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -683,7 +700,7 @@ fn parse_call_expr<'a>(
|
|||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(id.into()),
|
Box::new(id.into()),
|
||||||
namespaces,
|
modules,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -694,7 +711,7 @@ fn parse_call_expr<'a>(
|
|||||||
}
|
}
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
")".into(),
|
Token::RightParen.into(),
|
||||||
format!("to close the arguments list of this function call '{}'", id),
|
format!("to close the arguments list of this function call '{}'", id),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
@ -704,7 +721,7 @@ fn parse_call_expr<'a>(
|
|||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
Token::Comma.into(),
|
||||||
format!("to separate the arguments to function call '{}'", id),
|
format!("to separate the arguments to function call '{}'", id),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
@ -848,7 +865,7 @@ fn parse_index_chain<'a>(
|
|||||||
}
|
}
|
||||||
(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(
|
||||||
"]".into(),
|
Token::RightBracket.into(),
|
||||||
"for a matching [ in this index expression".into(),
|
"for a matching [ in this index expression".into(),
|
||||||
)
|
)
|
||||||
.into_err(*pos)),
|
.into_err(*pos)),
|
||||||
@ -859,7 +876,7 @@ fn parse_index_chain<'a>(
|
|||||||
fn parse_array_literal<'a>(
|
fn parse_array_literal<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
begin: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut arr = Vec::new();
|
let mut arr = Vec::new();
|
||||||
@ -875,17 +892,18 @@ fn parse_array_literal<'a>(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(
|
return Err(PERR::MissingToken(
|
||||||
PERR::MissingToken("]".into(), "to end this array literal".into())
|
Token::RightBracket.into(),
|
||||||
.into_err(*pos),
|
"to end this array literal".into(),
|
||||||
)
|
)
|
||||||
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => {
|
(Token::LexError(err), pos) => {
|
||||||
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
Token::Comma.into(),
|
||||||
"to separate the items of this array literal".into(),
|
"to separate the items of this array literal".into(),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
@ -894,14 +912,14 @@ fn parse_array_literal<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Array(arr, begin))
|
Ok(Expr::Array(arr, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a map literal.
|
/// Parse a map literal.
|
||||||
fn parse_map_literal<'a>(
|
fn parse_map_literal<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
begin: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut map = Vec::new();
|
let mut map = Vec::new();
|
||||||
@ -917,10 +935,16 @@ fn parse_map_literal<'a>(
|
|||||||
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
}
|
}
|
||||||
(_, pos) if map.is_empty() => {
|
(_, pos) if map.is_empty() => {
|
||||||
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -932,7 +956,7 @@ fn parse_map_literal<'a>(
|
|||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
":".into(),
|
Token::Colon.into(),
|
||||||
format!(
|
format!(
|
||||||
"to follow the property '{}' in this object map literal",
|
"to follow the property '{}' in this object map literal",
|
||||||
name
|
name
|
||||||
@ -956,7 +980,7 @@ fn parse_map_literal<'a>(
|
|||||||
}
|
}
|
||||||
(Token::Identifier(_), pos) => {
|
(Token::Identifier(_), pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
Token::Comma.into(),
|
||||||
"to separate the items of this object map literal".into(),
|
"to separate the items of this object map literal".into(),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
@ -965,7 +989,10 @@ fn parse_map_literal<'a>(
|
|||||||
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(*pos))
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(*pos),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -982,7 +1009,7 @@ fn parse_map_literal<'a>(
|
|||||||
})
|
})
|
||||||
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
Ok(Expr::Map(map, begin))
|
Ok(Expr::Map(map, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a primary expression.
|
/// Parse a primary expression.
|
||||||
@ -1037,25 +1064,28 @@ fn parse_primary<'a>(
|
|||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(id, namespaces, _, pos), Token::LeftParen) => {
|
(Expr::Variable(id, modules, _, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, *id, namespaces, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, *id, modules, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
(Expr::Property(id, pos), Token::LeftParen) => {
|
(Expr::Property(id, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
// Namespaced
|
// moduled
|
||||||
#[cfg(not(feature = "no_import"))]
|
#[cfg(not(feature = "no_import"))]
|
||||||
(Expr::Variable(id, mut namespaces, _, pos), Token::DoubleColon) => {
|
(Expr::Variable(id, mut modules, _, pos), Token::DoubleColon) => {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
if let Some(ref mut namespaces) = namespaces {
|
if let Some(ref mut modules) = modules {
|
||||||
namespaces.push((*id, pos));
|
modules.push((*id, pos));
|
||||||
} else {
|
} else {
|
||||||
let mut vec = StaticVec::new();
|
let mut vec = StaticVec::new();
|
||||||
vec.push((*id, pos));
|
vec.push((*id, pos));
|
||||||
namespaces = Some(Box::new(vec));
|
modules = Some(Box::new(vec));
|
||||||
}
|
}
|
||||||
Expr::Variable(Box::new(id2), namespaces, None, pos2)
|
|
||||||
|
let root = modules.as_ref().unwrap().get(0);
|
||||||
|
let index = stack.find_sub_scope(&root.0);
|
||||||
|
Expr::Variable(Box::new(id2), modules, index, pos2)
|
||||||
}
|
}
|
||||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
}
|
}
|
||||||
@ -1213,9 +1243,9 @@ fn make_dot_expr(
|
|||||||
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
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(_, Some(namespaces), _, _)) => {
|
(_, Expr::Variable(_, Some(modules), _, _)) => {
|
||||||
return Err(PERR::PropertyExpected.into_err(namespaces.get(0).1))
|
return Err(PERR::PropertyExpected.into_err(modules.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(
|
||||||
@ -1546,7 +1576,7 @@ fn parse_binary_op<'a>(
|
|||||||
#[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.into()).into_err(pos)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1701,7 +1731,7 @@ fn parse_for<'a>(
|
|||||||
(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) => {
|
(_, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
PERR::MissingToken(Token::In.into(), "after the iteration variable".into())
|
||||||
.into_err(pos),
|
.into_err(pos),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1712,7 +1742,7 @@ fn parse_for<'a>(
|
|||||||
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
let prev_len = stack.len();
|
let prev_len = stack.len();
|
||||||
stack.push(name.clone());
|
stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
|
|
||||||
let body = parse_block(input, stack, true, allow_stmt_expr)?;
|
let body = parse_block(input, stack, true, allow_stmt_expr)?;
|
||||||
|
|
||||||
@ -1746,12 +1776,12 @@ fn parse_let<'a>(
|
|||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
stack.push(name.clone());
|
stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
Ok(Stmt::Let(Box::new(name), Some(Box::new(init_value)), pos))
|
Ok(Stmt::Let(Box::new(name), Some(Box::new(init_value)), pos))
|
||||||
}
|
}
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
ScopeEntryType::Constant if init_value.is_constant() => {
|
ScopeEntryType::Constant if init_value.is_constant() => {
|
||||||
stack.push(name.clone());
|
stack.push((name.clone(), ScopeEntryType::Constant));
|
||||||
Ok(Stmt::Const(Box::new(name), Box::new(init_value), pos))
|
Ok(Stmt::Const(Box::new(name), Box::new(init_value), pos))
|
||||||
}
|
}
|
||||||
// const name = expr - error
|
// const name = expr - error
|
||||||
@ -1767,6 +1797,40 @@ fn parse_let<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an import statement.
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
fn parse_import<'a>(
|
||||||
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
|
allow_stmt_expr: bool,
|
||||||
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
|
// import ...
|
||||||
|
let pos = eat_token(input, Token::Import);
|
||||||
|
|
||||||
|
// import expr ...
|
||||||
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
|
// import expr as ...
|
||||||
|
match input.next().unwrap() {
|
||||||
|
(Token::As, _) => (),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::As.into(), "in this import statement".into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// import expr as name ...
|
||||||
|
let (name, _) = match input.next().unwrap() {
|
||||||
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Stmt::Import(Box::new(expr), Box::new(name), pos))
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a statement block.
|
/// Parse a statement block.
|
||||||
fn parse_block<'a>(
|
fn parse_block<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
@ -1779,9 +1843,11 @@ fn parse_block<'a>(
|
|||||||
(Token::LeftBrace, pos) => pos,
|
(Token::LeftBrace, pos) => 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) => {
|
(_, pos) => {
|
||||||
return Err(
|
return Err(PERR::MissingToken(
|
||||||
PERR::MissingToken("{".into(), "to start a statement block".into()).into_err(pos),
|
Token::LeftBrace.into(),
|
||||||
|
"to start a statement block".into(),
|
||||||
)
|
)
|
||||||
|
.into_err(pos))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1818,10 +1884,11 @@ fn parse_block<'a>(
|
|||||||
// { ... stmt ???
|
// { ... stmt ???
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
// Semicolons are not optional between statements
|
// Semicolons are not optional between statements
|
||||||
return Err(
|
return Err(PERR::MissingToken(
|
||||||
PERR::MissingToken(";".into(), "to terminate this statement".into())
|
Token::SemiColon.into(),
|
||||||
.into_err(*pos),
|
"to terminate this statement".into(),
|
||||||
);
|
)
|
||||||
|
.into_err(*pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1905,6 +1972,8 @@ fn parse_stmt<'a>(
|
|||||||
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
|
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
|
||||||
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
|
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
|
||||||
|
|
||||||
|
Token::Import => parse_import(input, stack, allow_stmt_expr),
|
||||||
|
|
||||||
_ => parse_expr_stmt(input, stack, allow_stmt_expr),
|
_ => parse_expr_stmt(input, stack, allow_stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1936,25 +2005,29 @@ fn parse_fn<'a>(
|
|||||||
loop {
|
loop {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => {
|
(Token::Identifier(s), pos) => {
|
||||||
stack.push(s.clone());
|
stack.push((s.clone(), ScopeEntryType::Normal));
|
||||||
params.push((s, pos))
|
params.push((s, pos))
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => {
|
(Token::LexError(err), pos) => {
|
||||||
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
}
|
}
|
||||||
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::RightParen, _) => break,
|
(Token::RightParen, _) => break,
|
||||||
(Token::Comma, _) => (),
|
(Token::Comma, _) => (),
|
||||||
(Token::Identifier(_), pos) => {
|
(Token::Identifier(_), pos) => {
|
||||||
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => {
|
(Token::LexError(err), pos) => {
|
||||||
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
}
|
}
|
||||||
(_, pos) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2064,10 +2137,11 @@ fn parse_global_level<'a>(
|
|||||||
// stmt ???
|
// stmt ???
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
// Semicolons are not optional between statements
|
// Semicolons are not optional between statements
|
||||||
return Err(
|
return Err(PERR::MissingToken(
|
||||||
PERR::MissingToken(";".into(), "to terminate this statement".into())
|
Token::SemiColon.into(),
|
||||||
.into_err(*pos),
|
"to terminate this statement".into(),
|
||||||
);
|
)
|
||||||
|
.into_err(*pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ pub enum EvalAltResult {
|
|||||||
ErrorNumericIndexExpr(Position),
|
ErrorNumericIndexExpr(Position),
|
||||||
/// Trying to index into a map with an index that is not `String`.
|
/// Trying to index into a map with an index that is not `String`.
|
||||||
ErrorStringIndexExpr(Position),
|
ErrorStringIndexExpr(Position),
|
||||||
|
/// Trying to import with an expression that is not `String`.
|
||||||
|
ErrorImportExpr(Position),
|
||||||
/// Invalid arguments for `in` operator.
|
/// Invalid arguments for `in` operator.
|
||||||
ErrorInExpr(Position),
|
ErrorInExpr(Position),
|
||||||
/// The guard expression in an `if` or `while` statement does not return a boolean value.
|
/// The guard expression in an `if` or `while` statement does not return a boolean value.
|
||||||
@ -61,8 +63,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.
|
/// Usage of an unknown module. Wrapped value is the name of the module.
|
||||||
ErrorNamespaceNotFound(String, Position),
|
ErrorModuleNotFound(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.
|
||||||
@ -108,6 +110,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorIndexingType(_, _) => {
|
Self::ErrorIndexingType(_, _) => {
|
||||||
"Indexing can only be performed on an array, an object map, or a string"
|
"Indexing can only be performed on an array, an object map, or a string"
|
||||||
}
|
}
|
||||||
|
Self::ErrorImportExpr(_) => "Importing a module expects a string path",
|
||||||
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
||||||
"Array access expects non-negative index"
|
"Array access expects non-negative index"
|
||||||
}
|
}
|
||||||
@ -121,7 +124,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::ErrorModuleNotFound(_, _) => "module not found",
|
||||||
Self::ErrorAssignmentToUnknownLHS(_) => {
|
Self::ErrorAssignmentToUnknownLHS(_) => {
|
||||||
"Assignment to an unsupported left-hand side expression"
|
"Assignment to an unsupported left-hand side expression"
|
||||||
}
|
}
|
||||||
@ -155,13 +158,14 @@ impl fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
Self::ErrorFunctionNotFound(s, pos)
|
Self::ErrorFunctionNotFound(s, pos)
|
||||||
| Self::ErrorVariableNotFound(s, pos)
|
| Self::ErrorVariableNotFound(s, pos)
|
||||||
| Self::ErrorNamespaceNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
| Self::ErrorModuleNotFound(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)
|
||||||
| Self::ErrorNumericIndexExpr(pos)
|
| Self::ErrorNumericIndexExpr(pos)
|
||||||
| Self::ErrorStringIndexExpr(pos)
|
| Self::ErrorStringIndexExpr(pos)
|
||||||
|
| Self::ErrorImportExpr(pos)
|
||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(pos)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
@ -270,10 +274,11 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorIndexingType(_, pos)
|
| Self::ErrorIndexingType(_, pos)
|
||||||
| Self::ErrorNumericIndexExpr(pos)
|
| Self::ErrorNumericIndexExpr(pos)
|
||||||
| Self::ErrorStringIndexExpr(pos)
|
| Self::ErrorStringIndexExpr(pos)
|
||||||
|
| Self::ErrorImportExpr(pos)
|
||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(pos)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableNotFound(_, pos)
|
| Self::ErrorVariableNotFound(_, pos)
|
||||||
| Self::ErrorNamespaceNotFound(_, pos)
|
| Self::ErrorModuleNotFound(_, pos)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
| Self::ErrorAssignmentToConstant(_, pos)
|
| Self::ErrorAssignmentToConstant(_, pos)
|
||||||
| Self::ErrorMismatchOutputType(_, pos)
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
@ -305,10 +310,11 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorIndexingType(_, pos)
|
| Self::ErrorIndexingType(_, pos)
|
||||||
| Self::ErrorNumericIndexExpr(pos)
|
| Self::ErrorNumericIndexExpr(pos)
|
||||||
| Self::ErrorStringIndexExpr(pos)
|
| Self::ErrorStringIndexExpr(pos)
|
||||||
|
| Self::ErrorImportExpr(pos)
|
||||||
| Self::ErrorLogicGuard(pos)
|
| Self::ErrorLogicGuard(pos)
|
||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableNotFound(_, pos)
|
| Self::ErrorVariableNotFound(_, pos)
|
||||||
| Self::ErrorNamespaceNotFound(_, pos)
|
| Self::ErrorModuleNotFound(_, pos)
|
||||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||||
| Self::ErrorAssignmentToConstant(_, pos)
|
| Self::ErrorAssignmentToConstant(_, pos)
|
||||||
| Self::ErrorMismatchOutputType(_, pos)
|
| Self::ErrorMismatchOutputType(_, pos)
|
||||||
|
29
src/scope.rs
29
src/scope.rs
@ -316,7 +316,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the Scope, starting from the last.
|
||||||
///
|
///
|
||||||
/// Sub-scopes are ignored.
|
/// Sub-scopes are ignored.
|
||||||
pub(crate) fn get(&self, name: &str) -> Option<(usize, EntryType)> {
|
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -333,13 +333,28 @@ impl<'a> Scope<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find a sub-scope in the Scope, starting from the last.
|
||||||
|
pub(crate) fn get_sub_scope_index(&self, name: &str) -> Option<usize> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev() // Always search a Scope in reverse order
|
||||||
|
.find_map(|(index, Entry { name: key, typ, .. })| match typ {
|
||||||
|
EntryType::SubScope => {
|
||||||
|
if name == key {
|
||||||
|
Some(index)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EntryType::Normal | EntryType::Constant => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Find a sub-scope in the Scope, starting from the last entry.
|
/// 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 find_sub_scope(&mut self, name: &str) -> Option<&mut Map> {
|
||||||
self.0
|
let index = self.get_sub_scope_index(name)?;
|
||||||
.iter_mut()
|
self.get_mut(index).0.downcast_mut()
|
||||||
.rev()
|
|
||||||
.find(|Entry { name: key, typ, .. }| name == key && *typ == EntryType::SubScope)
|
|
||||||
.and_then(|Entry { value, .. }| value.downcast_mut::<Map>())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -389,7 +404,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
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_index(name) {
|
||||||
None => self.push(name, value),
|
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)) => {
|
||||||
|
@ -422,6 +422,12 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Token> for String {
|
||||||
|
fn from(token: Token) -> Self {
|
||||||
|
token.syntax().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator on a `Token` stream.
|
/// An iterator on a `Token` stream.
|
||||||
pub struct TokenIterator<'a> {
|
pub struct TokenIterator<'a> {
|
||||||
/// Can the next token be a unary operator?
|
/// Can the next token be a unary operator?
|
||||||
|
Loading…
Reference in New Issue
Block a user