Merge branch 'master' into plugins
This commit is contained in:
commit
10756cdaf2
10
src/api.rs
10
src/api.rs
@ -1,7 +1,7 @@
|
|||||||
//! Module that defines the extern API of `Engine`.
|
//! Module that defines the extern API of `Engine`.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::engine::{make_getter, make_setter, Engine, Map};
|
use crate::engine::{make_getter, make_setter, Engine, Map, State};
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::fn_call::FuncArgs;
|
use crate::fn_call::FuncArgs;
|
||||||
use crate::fn_register::RegisterFn;
|
use crate::fn_register::RegisterFn;
|
||||||
@ -795,10 +795,12 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
ast.0
|
ast.0
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, ast.1.as_ref(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::Return(out, _) => Ok(out),
|
EvalAltResult::Return(out, _) => Ok(out),
|
||||||
@ -858,10 +860,12 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
ast.0
|
ast.0
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, ast.1.as_ref(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
||||||
})
|
})
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
|
160
src/engine.rs
160
src/engine.rs
@ -188,6 +188,24 @@ impl StaticVec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that holds all the current states of the Engine.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct State {
|
||||||
|
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
|
||||||
|
/// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned.
|
||||||
|
/// When that happens, this flag is turned on to force a scope lookup by name.
|
||||||
|
pub always_search: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
/// Create a new `State`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
always_search: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A type that holds a library (`HashMap`) of script-defined functions.
|
/// A type that holds a library (`HashMap`) of script-defined functions.
|
||||||
///
|
///
|
||||||
/// Since script-defined functions have `Dynamic` parameters, functions with the same name
|
/// Since script-defined functions have `Dynamic` parameters, functions with the same name
|
||||||
@ -439,11 +457,11 @@ fn search_scope<'a>(
|
|||||||
name: &str,
|
name: &str,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
let (index, typ) = 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(), begin)))?;
|
||||||
|
|
||||||
Ok((scope.get_mut(index), typ))
|
Ok(scope.get_mut(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
@ -601,6 +619,7 @@ impl Engine {
|
|||||||
// Extern scope passed in which is not empty
|
// Extern scope passed in which is not empty
|
||||||
Some(scope) if scope.len() > 0 => {
|
Some(scope) if scope.len() > 0 => {
|
||||||
let scope_len = scope.len();
|
let scope_len = scope.len();
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
scope.extend(
|
scope.extend(
|
||||||
// Put arguments into scope as variables - variable name is copied
|
// Put arguments into scope as variables - variable name is copied
|
||||||
@ -614,7 +633,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt(scope, fn_lib, &fn_def.body, level + 1)
|
.eval_stmt(scope, &mut state, fn_lib, &fn_def.body, level + 1)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -628,6 +647,7 @@ impl Engine {
|
|||||||
// No new scope - create internal scope
|
// No new scope - create internal scope
|
||||||
_ => {
|
_ => {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
scope.extend(
|
scope.extend(
|
||||||
// Put arguments into scope as variables
|
// Put arguments into scope as variables
|
||||||
@ -640,7 +660,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
return self
|
return self
|
||||||
.eval_stmt(&mut scope, fn_lib, &fn_def.body, level + 1)
|
.eval_stmt(&mut scope, &mut state, fn_lib, &fn_def.body, level + 1)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -784,7 +804,7 @@ impl Engine {
|
|||||||
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();
|
||||||
let def_val = def_val.as_ref();
|
let def_val = def_val.as_deref();
|
||||||
// A function call is assumed to have side effects, so the value is changed
|
// A function call is assumed to have side effects, so the value is changed
|
||||||
// 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))
|
||||||
@ -879,6 +899,7 @@ impl Engine {
|
|||||||
fn eval_dot_index_chain(
|
fn eval_dot_index_chain(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
state: &mut State,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
dot_lhs: &Expr,
|
dot_lhs: &Expr,
|
||||||
dot_rhs: &Expr,
|
dot_rhs: &Expr,
|
||||||
@ -889,12 +910,15 @@ impl Engine {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let idx_values = &mut StaticVec::new();
|
let idx_values = &mut StaticVec::new();
|
||||||
|
|
||||||
self.eval_indexed_chain(scope, fn_lib, dot_rhs, idx_values, 0, level)?;
|
self.eval_indexed_chain(scope, state, fn_lib, dot_rhs, idx_values, 0, level)?;
|
||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(id, pos) => {
|
Expr::Variable(id, index, pos) => {
|
||||||
let (target, typ) = search_scope(scope, id, *pos)?;
|
let (target, typ) = match index {
|
||||||
|
Some(i) if !state.always_search => scope.get_mut(scope.len() - i.get()),
|
||||||
|
_ => search_scope(scope, id, *pos)?,
|
||||||
|
};
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
@ -927,7 +951,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {expr}.??? or {expr}[???]
|
// {expr}.??? or {expr}[???]
|
||||||
expr => {
|
expr => {
|
||||||
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
fn_lib,
|
fn_lib,
|
||||||
@ -952,6 +976,7 @@ impl Engine {
|
|||||||
fn eval_indexed_chain(
|
fn eval_indexed_chain(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
state: &mut State,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
idx_values: &mut StaticVec,
|
idx_values: &mut StaticVec,
|
||||||
@ -962,7 +987,7 @@ impl Engine {
|
|||||||
Expr::FunctionCall(_, arg_exprs, _, _) => {
|
Expr::FunctionCall(_, arg_exprs, _, _) => {
|
||||||
let arg_values = arg_exprs
|
let arg_values = arg_exprs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg_expr| self.eval_expr(scope, fn_lib, arg_expr, level))
|
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
idx_values.push(arg_values)
|
idx_values.push(arg_values)
|
||||||
@ -973,15 +998,15 @@ impl Engine {
|
|||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
let lhs_val = match lhs.as_ref() {
|
let lhs_val = match lhs.as_ref() {
|
||||||
Expr::Property(_, _) => ().into(), // Store a placeholder in case of a property
|
Expr::Property(_, _) => ().into(), // Store a placeholder in case of a property
|
||||||
_ => self.eval_expr(scope, fn_lib, lhs, level)?,
|
_ => self.eval_expr(scope, state, fn_lib, lhs, level)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
self.eval_indexed_chain(scope, fn_lib, rhs, idx_values, size, level)?;
|
self.eval_indexed_chain(scope, state, fn_lib, rhs, idx_values, size, level)?;
|
||||||
|
|
||||||
idx_values.push(lhs_val);
|
idx_values.push(lhs_val);
|
||||||
}
|
}
|
||||||
_ => idx_values.push(self.eval_expr(scope, fn_lib, expr, level)?),
|
_ => idx_values.push(self.eval_expr(scope, state, fn_lib, expr, level)?),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1066,13 +1091,14 @@ impl Engine {
|
|||||||
fn eval_in_expr(
|
fn eval_in_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
state: &mut State,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
lhs: &Expr,
|
lhs: &Expr,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?;
|
let mut lhs_value = self.eval_expr(scope, state, fn_lib, lhs, level)?;
|
||||||
let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?;
|
let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?;
|
||||||
|
|
||||||
match rhs_value {
|
match rhs_value {
|
||||||
Dynamic(Union::Array(mut rhs_value)) => {
|
Dynamic(Union::Array(mut rhs_value)) => {
|
||||||
@ -1119,6 +1145,7 @@ impl Engine {
|
|||||||
fn eval_expr(
|
fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
state: &mut State,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1129,19 +1156,22 @@ 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(id, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()),
|
Expr::Variable(_, Some(index), _) if !state.always_search => {
|
||||||
|
Ok(scope.get_mut(scope.len() - index.get()).0.clone())
|
||||||
|
}
|
||||||
|
Expr::Variable(id, _, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()),
|
||||||
Expr::Property(_, _) => panic!("unexpected property."),
|
Expr::Property(_, _) => panic!("unexpected property."),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level),
|
Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, fn_lib, stmt, level),
|
||||||
|
|
||||||
// lhs = rhs
|
// lhs = rhs
|
||||||
Expr::Assignment(lhs, rhs, op_pos) => {
|
Expr::Assignment(lhs, rhs, op_pos) => {
|
||||||
let rhs_val = self.eval_expr(scope, fn_lib, rhs, level)?;
|
let rhs_val = self.eval_expr(scope, state, fn_lib, rhs, level)?;
|
||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(name, pos) => match scope.get(name) {
|
Expr::Variable(name, _, pos) => match scope.get(name) {
|
||||||
None => {
|
None => {
|
||||||
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
@ -1151,7 +1181,7 @@ impl Engine {
|
|||||||
|
|
||||||
Some((index, ScopeEntryType::Normal)) => {
|
Some((index, ScopeEntryType::Normal)) => {
|
||||||
// Avoid referencing scope which is used below as mut
|
// Avoid referencing scope which is used below as mut
|
||||||
*scope.get_mut(index) = rhs_val.clone();
|
*scope.get_mut(index).0 = rhs_val.clone();
|
||||||
Ok(rhs_val)
|
Ok(rhs_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1165,7 +1195,7 @@ impl Engine {
|
|||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let new_val = Some(rhs_val);
|
let new_val = Some(rhs_val);
|
||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, fn_lib, idx_lhs, idx_expr, true, *op_pos, level, new_val,
|
scope, state, fn_lib, idx_lhs, idx_expr, true, *op_pos, level, new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// dot_lhs.dot_rhs = rhs
|
// dot_lhs.dot_rhs = rhs
|
||||||
@ -1173,7 +1203,7 @@ impl Engine {
|
|||||||
Expr::Dot(dot_lhs, dot_rhs, _) => {
|
Expr::Dot(dot_lhs, dot_rhs, _) => {
|
||||||
let new_val = Some(rhs_val);
|
let new_val = Some(rhs_val);
|
||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, fn_lib, dot_lhs, dot_rhs, false, *op_pos, level, new_val,
|
scope, state, fn_lib, dot_lhs, dot_rhs, false, *op_pos, level, new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Error assignment to constant
|
// Error assignment to constant
|
||||||
@ -1193,22 +1223,22 @@ impl Engine {
|
|||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => {
|
Expr::Index(lhs, idx_expr, op_pos) => self.eval_dot_index_chain(
|
||||||
self.eval_dot_index_chain(scope, fn_lib, lhs, idx_expr, true, *op_pos, level, None)
|
scope, state, fn_lib, lhs, idx_expr, true, *op_pos, level, None,
|
||||||
}
|
),
|
||||||
|
|
||||||
// lhs.dot_rhs
|
// lhs.dot_rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(lhs, dot_rhs, op_pos) => {
|
Expr::Dot(lhs, dot_rhs, op_pos) => self.eval_dot_index_chain(
|
||||||
self.eval_dot_index_chain(scope, fn_lib, lhs, dot_rhs, false, *op_pos, level, None)
|
scope, state, fn_lib, lhs, dot_rhs, false, *op_pos, level, None,
|
||||||
}
|
),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(contents, _) => {
|
Expr::Array(contents, _) => {
|
||||||
let mut arr = Array::new();
|
let mut arr = Array::new();
|
||||||
|
|
||||||
contents.into_iter().try_for_each(|item| {
|
contents.into_iter().try_for_each(|item| {
|
||||||
self.eval_expr(scope, fn_lib, item, level)
|
self.eval_expr(scope, state, fn_lib, item, level)
|
||||||
.map(|val| arr.push(val))
|
.map(|val| arr.push(val))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -1220,9 +1250,10 @@ impl Engine {
|
|||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
|
|
||||||
contents.into_iter().try_for_each(|(key, expr, _)| {
|
contents.into_iter().try_for_each(|(key, expr, _)| {
|
||||||
self.eval_expr(scope, fn_lib, &expr, level).map(|val| {
|
self.eval_expr(scope, state, fn_lib, &expr, level)
|
||||||
map.insert(key.clone(), val);
|
.map(|val| {
|
||||||
})
|
map.insert(key.clone(), val);
|
||||||
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Dynamic(Union::Map(Box::new(map))))
|
Ok(Dynamic(Union::Map(Box::new(map))))
|
||||||
@ -1231,7 +1262,7 @@ impl Engine {
|
|||||||
Expr::FunctionCall(fn_name, arg_exprs, def_val, pos) => {
|
Expr::FunctionCall(fn_name, 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, fn_lib, expr, level))
|
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
||||||
@ -1241,28 +1272,39 @@ impl Engine {
|
|||||||
&& args.len() == 1
|
&& args.len() == 1
|
||||||
&& !self.has_override(fn_lib, KEYWORD_EVAL)
|
&& !self.has_override(fn_lib, KEYWORD_EVAL)
|
||||||
{
|
{
|
||||||
|
let prev_len = scope.len();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
return self.eval_script_expr(scope, fn_lib, args[0], arg_exprs[0].position());
|
let result =
|
||||||
|
self.eval_script_expr(scope, fn_lib, args[0], arg_exprs[0].position());
|
||||||
|
|
||||||
|
if scope.len() != prev_len {
|
||||||
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
|
// all variable offsets from this point on will be mis-aligned.
|
||||||
|
state.always_search = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
let def_val = def_val.as_ref();
|
let def_val = def_val.as_deref();
|
||||||
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, level)
|
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::In(lhs, rhs, _) => {
|
Expr::In(lhs, rhs, _) => {
|
||||||
self.eval_in_expr(scope, fn_lib, lhs.as_ref(), rhs.as_ref(), level)
|
self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::And(lhs, rhs, _) => Ok((self
|
Expr::And(lhs, rhs, _) => Ok((self
|
||||||
.eval_expr(scope, fn_lib,lhs.as_ref(), level)?
|
.eval_expr(scope, state, fn_lib, lhs.as_ref(), level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
&& // Short-circuit using &&
|
&& // Short-circuit using &&
|
||||||
self
|
self
|
||||||
.eval_expr(scope, fn_lib,rhs.as_ref(), level)?
|
.eval_expr(scope, state, fn_lib, rhs.as_ref(), level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
||||||
@ -1270,14 +1312,14 @@ impl Engine {
|
|||||||
.into()),
|
.into()),
|
||||||
|
|
||||||
Expr::Or(lhs, rhs, _) => Ok((self
|
Expr::Or(lhs, rhs, _) => Ok((self
|
||||||
.eval_expr(scope,fn_lib, lhs.as_ref(), level)?
|
.eval_expr(scope, state, fn_lib, lhs.as_ref(), level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
|| // Short-circuit using ||
|
|| // Short-circuit using ||
|
||||||
self
|
self
|
||||||
.eval_expr(scope,fn_lib, rhs.as_ref(), level)?
|
.eval_expr(scope, state, fn_lib, rhs.as_ref(), level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
||||||
@ -1296,6 +1338,7 @@ impl Engine {
|
|||||||
pub(crate) fn eval_stmt(
|
pub(crate) fn eval_stmt(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
state: &mut State,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1306,7 +1349,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Expression as statement
|
// Expression as statement
|
||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
let result = self.eval_expr(scope, fn_lib, expr, level)?;
|
let result = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
|
|
||||||
Ok(if let Expr::Assignment(_, _, _) = *expr.as_ref() {
|
Ok(if let Expr::Assignment(_, _, _) = *expr.as_ref() {
|
||||||
// If it is an assignment, erase the result at the root
|
// If it is an assignment, erase the result at the root
|
||||||
@ -1321,24 +1364,28 @@ impl Engine {
|
|||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
|
|
||||||
let result = block.iter().try_fold(().into(), |_, stmt| {
|
let result = block.iter().try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, fn_lib, stmt, level)
|
self.eval_stmt(scope, state, fn_lib, stmt, level)
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.rewind(prev_len);
|
scope.rewind(prev_len);
|
||||||
|
|
||||||
|
// The impact of an eval statement goes away at the end of a block
|
||||||
|
// because any new variables introduced will go out of scope
|
||||||
|
state.always_search = false;
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// If-else statement
|
// If-else statement
|
||||||
Stmt::IfThenElse(guard, if_body, else_body) => self
|
Stmt::IfThenElse(guard, if_body, else_body) => self
|
||||||
.eval_expr(scope, fn_lib, guard, level)?
|
.eval_expr(scope, state, fn_lib, guard, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(guard.position())))
|
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(guard.position())))
|
||||||
.and_then(|guard_val| {
|
.and_then(|guard_val| {
|
||||||
if guard_val {
|
if guard_val {
|
||||||
self.eval_stmt(scope, fn_lib, if_body, level)
|
self.eval_stmt(scope, state, fn_lib, if_body, level)
|
||||||
} else if let Some(stmt) = else_body {
|
} else if let Some(stmt) = else_body {
|
||||||
self.eval_stmt(scope, fn_lib, stmt.as_ref(), level)
|
self.eval_stmt(scope, state, fn_lib, stmt.as_ref(), level)
|
||||||
} else {
|
} else {
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
@ -1346,8 +1393,11 @@ impl Engine {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(guard, body) => loop {
|
Stmt::While(guard, body) => loop {
|
||||||
match self.eval_expr(scope, fn_lib, guard, level)?.as_bool() {
|
match self
|
||||||
Ok(true) => match self.eval_stmt(scope, fn_lib, body, level) {
|
.eval_expr(scope, state, fn_lib, guard, level)?
|
||||||
|
.as_bool()
|
||||||
|
{
|
||||||
|
Ok(true) => match self.eval_stmt(scope, state, fn_lib, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -1364,7 +1414,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Loop statement
|
// Loop statement
|
||||||
Stmt::Loop(body) => loop {
|
Stmt::Loop(body) => loop {
|
||||||
match self.eval_stmt(scope, fn_lib, body, level) {
|
match self.eval_stmt(scope, state, fn_lib, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -1376,7 +1426,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(name, expr, body) => {
|
Stmt::For(name, expr, body) => {
|
||||||
let arr = self.eval_expr(scope, fn_lib, expr, level)?;
|
let arr = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
let tid = arr.type_id();
|
let tid = arr.type_id();
|
||||||
|
|
||||||
if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| {
|
if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| {
|
||||||
@ -1390,9 +1440,9 @@ impl Engine {
|
|||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
|
|
||||||
for a in iter_fn(arr) {
|
for a in iter_fn(arr) {
|
||||||
*scope.get_mut(index) = a;
|
*scope.get_mut(index).0 = a;
|
||||||
|
|
||||||
match self.eval_stmt(scope, fn_lib, body, level) {
|
match self.eval_stmt(scope, state, fn_lib, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -1422,7 +1472,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(Box::new(
|
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(Box::new(
|
||||||
EvalAltResult::Return(self.eval_expr(scope, fn_lib, a, level)?, *pos),
|
EvalAltResult::Return(self.eval_expr(scope, state, fn_lib, a, level)?, *pos),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// Empty throw
|
// Empty throw
|
||||||
@ -1432,7 +1482,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
||||||
let val = self.eval_expr(scope, fn_lib, a, level)?;
|
let val = self.eval_expr(scope, state, fn_lib, a, level)?;
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
val.take_string().unwrap_or_else(|_| "".to_string()),
|
val.take_string().unwrap_or_else(|_| "".to_string()),
|
||||||
*pos,
|
*pos,
|
||||||
@ -1441,7 +1491,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Let statement
|
// Let statement
|
||||||
Stmt::Let(name, Some(expr), _) => {
|
Stmt::Let(name, Some(expr), _) => {
|
||||||
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
// TODO - avoid copying variable name in inner block?
|
// TODO - avoid copying variable name in inner block?
|
||||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
@ -1455,7 +1505,7 @@ impl Engine {
|
|||||||
|
|
||||||
// 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, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, state, fn_lib, expr, level)?;
|
||||||
// TODO - avoid copying variable name in inner block?
|
// TODO - avoid copying variable name in inner block?
|
||||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
|
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
|
@ -368,12 +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, _), Expr::Variable(var2, _)) if var == var2 => {
|
(Expr::Variable(var, sp, _), Expr::Variable(var2, 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(
|
Expr::Assignment(
|
||||||
Box::new(Expr::Variable(var, pos)),
|
Box::new(Expr::Variable(var, sp, pos)),
|
||||||
Box::new(optimize_expr(*expr2, state)),
|
Box::new(optimize_expr(*expr2, state)),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
@ -397,13 +397,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(items, pos), Expr::Property(s, _))
|
(Expr::Map(items, pos), Expr::Property(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
||||||
if items.iter().all(|(_, x, _)| x.is_pure()) =>
|
|
||||||
{
|
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
items.into_iter().find(|(name, _, _)| name == s.as_ref())
|
items.into_iter().find(|(name, _, _)| name == &s)
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
.map(|(_, expr, _)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
@ -428,13 +426,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
items.remove(i as usize).set_position(pos)
|
items.remove(i as usize).set_position(pos)
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(items, pos), Expr::StringConstant(s, _))
|
(Expr::Map(items, pos), Expr::StringConstant(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
||||||
if items.iter().all(|(_, x, _)| x.is_pure()) =>
|
|
||||||
{
|
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
items.into_iter().find(|(name, _, _)| name == s.as_ref())
|
items.into_iter().find(|(name, _, _)| name == &s)
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
.map(|(_, expr, _)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
@ -470,7 +466,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// "xxx" in "xxxxx"
|
// "xxx" in "xxxxx"
|
||||||
(Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => {
|
(Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if rhs.contains(lhs.as_ref()) {
|
if rhs.contains(&lhs) {
|
||||||
Expr::True(pos)
|
Expr::True(pos)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(pos)
|
Expr::False(pos)
|
||||||
@ -563,7 +559,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=>
|
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=>
|
||||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
Expr::FunctionCall(id, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FunctionCall(id, args, def_value, pos)
|
Expr::FunctionCall(id, args, def_value, pos)
|
||||||
@ -573,7 +569,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// 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 && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(name, len)| name == &id && *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::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos);
|
return Expr::FunctionCall(id, 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();
|
||||||
@ -595,7 +591,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
// Otherwise use the default value, if any
|
||||||
def_value.clone()
|
def_value.clone().map(|v| *v)
|
||||||
}
|
}
|
||||||
}).and_then(|result| map_dynamic_to_expr(result, pos))
|
}).and_then(|result| map_dynamic_to_expr(result, pos))
|
||||||
.map(|expr| {
|
.map(|expr| {
|
||||||
@ -604,16 +600,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::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos)
|
Expr::FunctionCall(id, 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::FunctionCall(id, args, def_value, pos) =>
|
Expr::FunctionCall(id, args, def_value, pos) =>
|
||||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
Expr::FunctionCall(id, 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, _, pos) if state.contains_constant(&name) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
@ -733,10 +729,10 @@ pub fn optimize_into_ast(
|
|||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let mut body =
|
let mut body =
|
||||||
optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
optimize(vec![*fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
||||||
|
|
||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = Box::new(match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
// { return val; } -> val
|
// { return val; } -> val
|
||||||
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val),
|
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val),
|
||||||
// { return; } -> ()
|
// { return; } -> ()
|
||||||
@ -745,7 +741,7 @@ pub fn optimize_into_ast(
|
|||||||
}
|
}
|
||||||
// All others
|
// All others
|
||||||
stmt => stmt,
|
stmt => stmt,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
fn_def
|
fn_def
|
||||||
})
|
})
|
||||||
|
361
src/parser.rs
361
src/parser.rs
@ -12,9 +12,9 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
char,
|
char,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Display,
|
|
||||||
format,
|
format,
|
||||||
iter::Peekable,
|
iter::Peekable,
|
||||||
|
num::NonZeroUsize,
|
||||||
ops::Add,
|
ops::Add,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
@ -170,7 +170,7 @@ pub struct FnDef {
|
|||||||
/// Names of function parameters.
|
/// Names of function parameters.
|
||||||
pub params: Vec<String>,
|
pub params: Vec<String>,
|
||||||
/// Function body.
|
/// Function body.
|
||||||
pub body: Stmt,
|
pub body: Box<Stmt>,
|
||||||
/// Position of the function definition.
|
/// Position of the function definition.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
@ -184,6 +184,41 @@ pub enum ReturnType {
|
|||||||
Exception,
|
Exception,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Stack(Vec<String>);
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
/// Create a new `Stack`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(Vec::new())
|
||||||
|
}
|
||||||
|
/// Get the number of variables in the `Stack`.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
/// Push (add) a new variable onto the `Stack`.
|
||||||
|
pub fn push(&mut self, name: String) {
|
||||||
|
self.0.push(name);
|
||||||
|
}
|
||||||
|
/// Rewind the stack to a previous size.
|
||||||
|
pub fn rewind(&mut self, len: usize) {
|
||||||
|
self.0.truncate(len);
|
||||||
|
}
|
||||||
|
/// Find a variable 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(&self, name: &str) -> Option<NonZeroUsize> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, n)| *n == name)
|
||||||
|
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A statement.
|
/// A statement.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
@ -196,11 +231,11 @@ pub enum Stmt {
|
|||||||
/// loop { stmt }
|
/// loop { stmt }
|
||||||
Loop(Box<Stmt>),
|
Loop(Box<Stmt>),
|
||||||
/// for id in expr { stmt }
|
/// for id in expr { stmt }
|
||||||
For(Cow<'static, str>, Box<Expr>, Box<Stmt>),
|
For(String, Box<Expr>, Box<Stmt>),
|
||||||
/// let id = expr
|
/// let id = expr
|
||||||
Let(Cow<'static, str>, Option<Box<Expr>>, Position),
|
Let(String, Option<Box<Expr>>, Position),
|
||||||
/// const id = expr
|
/// const id = expr
|
||||||
Const(Cow<'static, str>, Box<Expr>, Position),
|
Const(String, Box<Expr>, Position),
|
||||||
/// { stmt; ... }
|
/// { stmt; ... }
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
@ -280,15 +315,22 @@ pub enum Expr {
|
|||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(Cow<'static, str>, Position),
|
StringConstant(String, Position),
|
||||||
/// Variable access.
|
/// Variable access.
|
||||||
Variable(Cow<'static, str>, Position),
|
Variable(String, Option<NonZeroUsize>, Position),
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(Cow<'static, str>, Position),
|
Property(String, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<Stmt>, Position),
|
Stmt(Box<Stmt>, Position),
|
||||||
/// func(expr, ... )
|
/// func(expr, ... )
|
||||||
FunctionCall(Cow<'static, str>, Vec<Expr>, Option<Dynamic>, Position),
|
/// 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`.
|
||||||
|
FunctionCall(
|
||||||
|
Cow<'static, str>,
|
||||||
|
Box<Vec<Expr>>,
|
||||||
|
Option<Box<Dynamic>>,
|
||||||
|
Position,
|
||||||
|
),
|
||||||
/// expr = expr
|
/// expr = expr
|
||||||
Assignment(Box<Expr>, Box<Expr>, Position),
|
Assignment(Box<Expr>, Box<Expr>, Position),
|
||||||
/// lhs.rhs
|
/// lhs.rhs
|
||||||
@ -325,7 +367,7 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Self::FloatConstant(f, _) => (*f).into(),
|
Self::FloatConstant(f, _) => (*f).into(),
|
||||||
Self::CharConstant(c, _) => (*c).into(),
|
Self::CharConstant(c, _) => (*c).into(),
|
||||||
Self::StringConstant(s, _) => s.to_string().into(),
|
Self::StringConstant(s, _) => s.clone().into(),
|
||||||
Self::True(_) => true.into(),
|
Self::True(_) => true.into(),
|
||||||
Self::False(_) => false.into(),
|
Self::False(_) => false.into(),
|
||||||
Self::Unit(_) => ().into(),
|
Self::Unit(_) => ().into(),
|
||||||
@ -382,7 +424,7 @@ 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::FunctionCall(_, _, _, pos)
|
| Self::FunctionCall(_, _, _, pos)
|
||||||
@ -408,7 +450,7 @@ 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::FunctionCall(_, _, _, pos)
|
| Self::FunctionCall(_, _, _, pos)
|
||||||
@ -439,7 +481,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(),
|
||||||
}
|
}
|
||||||
@ -498,7 +540,7 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Variable(_, _) | Self::Property(_, _) => match token {
|
Self::Variable(_, _, _) | Self::Property(_, _) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
@ -508,7 +550,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, _, pos) => Self::Property(id, pos),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -543,6 +585,7 @@ fn match_token(input: &mut Peekable<TokenIterator>, token: Token) -> Result<bool
|
|||||||
/// Parse ( expr )
|
/// Parse ( expr )
|
||||||
fn parse_paren_expr<'a>(
|
fn parse_paren_expr<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
@ -550,7 +593,7 @@ fn parse_paren_expr<'a>(
|
|||||||
return Ok(Expr::Unit(begin));
|
return Ok(Expr::Unit(begin));
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = parse_expr(input, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
// ( xxx )
|
// ( xxx )
|
||||||
@ -567,13 +610,14 @@ fn parse_paren_expr<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a function call.
|
/// Parse a function call.
|
||||||
fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
fn parse_call_expr<'a>(
|
||||||
id: S,
|
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
|
id: String,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut args_expr_list = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// id <EOF>
|
// id <EOF>
|
||||||
@ -589,19 +633,19 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
// id()
|
// id()
|
||||||
(Token::RightParen, _) => {
|
(Token::RightParen, _) => {
|
||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
return Ok(Expr::FunctionCall(id.into(), Box::new(args), None, begin));
|
||||||
}
|
}
|
||||||
// id...
|
// id...
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
args_expr_list.push(parse_expr(input, allow_stmt_expr)?);
|
args.push(parse_expr(input, stack, allow_stmt_expr)?);
|
||||||
|
|
||||||
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::FunctionCall(id.into(), args_expr_list, None, begin));
|
return Ok(Expr::FunctionCall(id.into(), Box::new(args), None, begin));
|
||||||
}
|
}
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
eat_token(input, Token::Comma);
|
eat_token(input, Token::Comma);
|
||||||
@ -630,12 +674,13 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
/// Parse an indexing chain.
|
/// Parse an indexing chain.
|
||||||
/// Indexing binds to the right, so this call parses all possible levels of indexing following in the input.
|
/// Indexing binds to the right, so this call parses all possible levels of indexing following in the input.
|
||||||
fn parse_index_chain<'a>(
|
fn parse_index_chain<'a>(
|
||||||
lhs: Expr,
|
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
|
lhs: Expr,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let idx_expr = parse_expr(input, allow_stmt_expr)?;
|
let idx_expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
// Check type of indexing - must be integer or string
|
// Check type of indexing - must be integer or string
|
||||||
match &idx_expr {
|
match &idx_expr {
|
||||||
@ -750,7 +795,8 @@ fn parse_index_chain<'a>(
|
|||||||
(Token::LeftBracket, _) => {
|
(Token::LeftBracket, _) => {
|
||||||
let follow_pos = eat_token(input, Token::LeftBracket);
|
let follow_pos = eat_token(input, Token::LeftBracket);
|
||||||
// Recursively parse the indexing chain, right-binding each
|
// Recursively parse the indexing chain, right-binding each
|
||||||
let follow = parse_index_chain(idx_expr, input, follow_pos, allow_stmt_expr)?;
|
let follow =
|
||||||
|
parse_index_chain(input, stack, idx_expr, follow_pos, allow_stmt_expr)?;
|
||||||
// Indexing binds to right
|
// Indexing binds to right
|
||||||
Ok(Expr::Index(Box::new(lhs), Box::new(follow), pos))
|
Ok(Expr::Index(Box::new(lhs), Box::new(follow), pos))
|
||||||
}
|
}
|
||||||
@ -770,6 +816,7 @@ fn parse_index_chain<'a>(
|
|||||||
/// Parse an array literal.
|
/// Parse an array literal.
|
||||||
fn parse_array_literal<'a>(
|
fn parse_array_literal<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
@ -777,7 +824,7 @@ fn parse_array_literal<'a>(
|
|||||||
|
|
||||||
if !match_token(input, Token::RightBracket)? {
|
if !match_token(input, Token::RightBracket)? {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
arr.push(parse_expr(input, allow_stmt_expr)?);
|
arr.push(parse_expr(input, stack, allow_stmt_expr)?);
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => eat_token(input, Token::Comma),
|
(Token::Comma, _) => eat_token(input, Token::Comma),
|
||||||
@ -811,6 +858,7 @@ fn parse_array_literal<'a>(
|
|||||||
/// 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,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
@ -852,7 +900,7 @@ fn parse_map_literal<'a>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = parse_expr(input, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
map.push((name, expr, pos));
|
map.push((name, expr, pos));
|
||||||
|
|
||||||
@ -898,13 +946,14 @@ fn parse_map_literal<'a>(
|
|||||||
/// Parse a primary expression.
|
/// Parse a primary expression.
|
||||||
fn parse_primary<'a>(
|
fn parse_primary<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let (token, pos) = match input.peek().unwrap() {
|
let (token, pos) = match input.peek().unwrap() {
|
||||||
// { - block statement as expression
|
// { - block statement as expression
|
||||||
(Token::LeftBrace, pos) if allow_stmt_expr => {
|
(Token::LeftBrace, pos) if allow_stmt_expr => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
return parse_block(input, false, allow_stmt_expr)
|
return parse_block(input, stack, false, allow_stmt_expr)
|
||||||
.map(|block| Expr::Stmt(Box::new(block), pos));
|
.map(|block| Expr::Stmt(Box::new(block), pos));
|
||||||
}
|
}
|
||||||
(Token::EOF, pos) => return Err(PERR::UnexpectedEOF.into_err(*pos)),
|
(Token::EOF, pos) => return Err(PERR::UnexpectedEOF.into_err(*pos)),
|
||||||
@ -916,13 +965,16 @@ fn parse_primary<'a>(
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => Expr::FloatConstant(x, pos),
|
Token::FloatConstant(x) => Expr::FloatConstant(x, pos),
|
||||||
Token::CharConstant(c) => Expr::CharConstant(c, pos),
|
Token::CharConstant(c) => Expr::CharConstant(c, pos),
|
||||||
Token::StringConst(s) => Expr::StringConstant(s.into(), pos),
|
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
||||||
Token::Identifier(s) => Expr::Variable(s.into(), pos),
|
Token::Identifier(s) => {
|
||||||
Token::LeftParen => parse_paren_expr(input, pos, allow_stmt_expr)?,
|
let index = stack.find(&s);
|
||||||
|
Expr::Variable(s, index, pos)
|
||||||
|
}
|
||||||
|
Token::LeftParen => parse_paren_expr(input, stack, pos, allow_stmt_expr)?,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Token::LeftBracket => parse_array_literal(input, pos, allow_stmt_expr)?,
|
Token::LeftBracket => parse_array_literal(input, stack, pos, allow_stmt_expr)?,
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::MapStart => parse_map_literal(input, pos, allow_stmt_expr)?,
|
Token::MapStart => parse_map_literal(input, stack, pos, allow_stmt_expr)?,
|
||||||
Token::True => Expr::True(pos),
|
Token::True => Expr::True(pos),
|
||||||
Token::False => Expr::False(pos),
|
Token::False => Expr::False(pos),
|
||||||
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
@ -943,12 +995,14 @@ fn parse_primary<'a>(
|
|||||||
|
|
||||||
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, _, pos), Token::LeftParen)
|
||||||
| (Expr::Property(id, pos), Token::LeftParen) => {
|
| (Expr::Property(id, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(id, input, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, id, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
// Indexing
|
// Indexing
|
||||||
(expr, Token::LeftBracket) => parse_index_chain(expr, input, pos, allow_stmt_expr)?,
|
(expr, Token::LeftBracket) => {
|
||||||
|
parse_index_chain(input, stack, expr, 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),
|
||||||
}
|
}
|
||||||
@ -960,6 +1014,7 @@ fn parse_primary<'a>(
|
|||||||
/// Parse a potential unary operator.
|
/// Parse a potential unary operator.
|
||||||
fn parse_unary<'a>(
|
fn parse_unary<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
@ -967,7 +1022,7 @@ fn parse_unary<'a>(
|
|||||||
(Token::If, pos) => {
|
(Token::If, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
Ok(Expr::Stmt(
|
Ok(Expr::Stmt(
|
||||||
Box::new(parse_if(input, false, allow_stmt_expr)?),
|
Box::new(parse_if(input, stack, false, allow_stmt_expr)?),
|
||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -975,7 +1030,7 @@ fn parse_unary<'a>(
|
|||||||
(Token::UnaryMinus, _) => {
|
(Token::UnaryMinus, _) => {
|
||||||
let pos = eat_token(input, Token::UnaryMinus);
|
let pos = eat_token(input, Token::UnaryMinus);
|
||||||
|
|
||||||
match parse_unary(input, allow_stmt_expr)? {
|
match parse_unary(input, stack, allow_stmt_expr)? {
|
||||||
// Negative integer
|
// Negative integer
|
||||||
Expr::IntegerConstant(i, _) => i
|
Expr::IntegerConstant(i, _) => i
|
||||||
.checked_neg()
|
.checked_neg()
|
||||||
@ -1000,49 +1055,51 @@ fn parse_unary<'a>(
|
|||||||
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
|
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
|
||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
expr => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)),
|
e => Ok(Expr::FunctionCall("-".into(), Box::new(vec![e]), None, pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// +expr
|
// +expr
|
||||||
(Token::UnaryPlus, _) => {
|
(Token::UnaryPlus, _) => {
|
||||||
eat_token(input, Token::UnaryPlus);
|
eat_token(input, Token::UnaryPlus);
|
||||||
parse_unary(input, allow_stmt_expr)
|
parse_unary(input, stack, allow_stmt_expr)
|
||||||
}
|
}
|
||||||
// !expr
|
// !expr
|
||||||
(Token::Bang, _) => {
|
(Token::Bang, _) => {
|
||||||
let pos = eat_token(input, Token::Bang);
|
let pos = eat_token(input, Token::Bang);
|
||||||
Ok(Expr::FunctionCall(
|
Ok(Expr::FunctionCall(
|
||||||
"!".into(),
|
"!".into(),
|
||||||
vec![parse_primary(input, allow_stmt_expr)?],
|
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
||||||
Some(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,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// <EOF>
|
// <EOF>
|
||||||
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
|
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => parse_primary(input, allow_stmt_expr),
|
_ => parse_primary(input, stack, allow_stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_assignment_stmt<'a>(
|
fn parse_assignment_stmt<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let pos = eat_token(input, Token::Equals);
|
let pos = eat_token(input, Token::Equals);
|
||||||
let rhs = parse_expr(input, allow_stmt_expr)?;
|
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator-assignment expression.
|
/// Parse an operator-assignment expression.
|
||||||
fn parse_op_assignment_stmt<'a>(
|
fn parse_op_assignment_stmt<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let (op, pos) = match *input.peek().unwrap() {
|
let (op, pos) = match *input.peek().unwrap() {
|
||||||
(Token::Equals, _) => return parse_assignment_stmt(input, lhs, allow_stmt_expr),
|
(Token::Equals, _) => return parse_assignment_stmt(input, stack, lhs, allow_stmt_expr),
|
||||||
(Token::PlusAssign, pos) => ("+", pos),
|
(Token::PlusAssign, pos) => ("+", pos),
|
||||||
(Token::MinusAssign, pos) => ("-", pos),
|
(Token::MinusAssign, pos) => ("-", pos),
|
||||||
(Token::MultiplyAssign, pos) => ("*", pos),
|
(Token::MultiplyAssign, pos) => ("*", pos),
|
||||||
@ -1060,10 +1117,10 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
input.next();
|
input.next();
|
||||||
|
|
||||||
let lhs_copy = lhs.clone();
|
let lhs_copy = lhs.clone();
|
||||||
let rhs = parse_expr(input, allow_stmt_expr)?;
|
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
let rhs_expr = Expr::FunctionCall(op.into(), vec![lhs_copy, rhs], None, pos);
|
let rhs_expr = Expr::FunctionCall(op.into(), Box::new(vec![lhs_copy, rhs]), None, pos);
|
||||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs_expr), pos))
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs_expr), pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,7 +1135,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position, is_index: bool) -> Expr
|
|||||||
idx_pos,
|
idx_pos,
|
||||||
),
|
),
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, rhs @ Expr::Variable(_, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
(lhs, rhs @ Expr::Variable(_, _, _)) | (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)
|
||||||
}
|
}
|
||||||
@ -1239,6 +1296,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<Pars
|
|||||||
/// Parse a binary expression.
|
/// Parse a binary expression.
|
||||||
fn parse_binary_op<'a>(
|
fn parse_binary_op<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
parent_precedence: u8,
|
parent_precedence: u8,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
@ -1261,7 +1319,7 @@ fn parse_binary_op<'a>(
|
|||||||
|
|
||||||
let (op_token, pos) = input.next().unwrap();
|
let (op_token, pos) = input.next().unwrap();
|
||||||
|
|
||||||
let rhs = parse_unary(input, allow_stmt_expr)?;
|
let rhs = parse_unary(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
let next_precedence = input.peek().unwrap().0.precedence();
|
let next_precedence = input.peek().unwrap().0.precedence();
|
||||||
|
|
||||||
@ -1270,48 +1328,90 @@ fn parse_binary_op<'a>(
|
|||||||
let rhs = if (current_precedence == next_precedence && bind_right)
|
let rhs = if (current_precedence == next_precedence && bind_right)
|
||||||
|| current_precedence < next_precedence
|
|| current_precedence < next_precedence
|
||||||
{
|
{
|
||||||
parse_binary_op(input, current_precedence, rhs, allow_stmt_expr)?
|
parse_binary_op(input, stack, current_precedence, rhs, allow_stmt_expr)?
|
||||||
} else {
|
} else {
|
||||||
// Otherwise bind to left (even if next operator has the same precedence)
|
// Otherwise bind to left (even if next operator has the same precedence)
|
||||||
rhs
|
rhs
|
||||||
};
|
};
|
||||||
|
|
||||||
current_lhs = match op_token {
|
let cmp_default = Some(Box::new(false.into()));
|
||||||
Token::Plus => Expr::FunctionCall("+".into(), vec![current_lhs, rhs], None, pos),
|
|
||||||
Token::Minus => Expr::FunctionCall("-".into(), vec![current_lhs, rhs], None, pos),
|
|
||||||
Token::Multiply => Expr::FunctionCall("*".into(), vec![current_lhs, rhs], None, pos),
|
|
||||||
Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos),
|
|
||||||
|
|
||||||
Token::LeftShift => Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos),
|
current_lhs = match op_token {
|
||||||
Token::RightShift => Expr::FunctionCall(">>".into(), vec![current_lhs, rhs], None, pos),
|
Token::Plus => {
|
||||||
Token::Modulo => Expr::FunctionCall("%".into(), vec![current_lhs, rhs], None, pos),
|
Expr::FunctionCall("+".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
Token::PowerOf => Expr::FunctionCall("~".into(), vec![current_lhs, rhs], None, pos),
|
}
|
||||||
|
Token::Minus => {
|
||||||
|
Expr::FunctionCall("-".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::Multiply => {
|
||||||
|
Expr::FunctionCall("*".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::Divide => {
|
||||||
|
Expr::FunctionCall("/".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::LeftShift => {
|
||||||
|
Expr::FunctionCall("<<".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::RightShift => {
|
||||||
|
Expr::FunctionCall(">>".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::Modulo => {
|
||||||
|
Expr::FunctionCall("%".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::PowerOf => {
|
||||||
|
Expr::FunctionCall("~".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
|
||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo => {
|
Token::EqualsTo => Expr::FunctionCall(
|
||||||
Expr::FunctionCall("==".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
"==".into(),
|
||||||
}
|
Box::new(vec![current_lhs, rhs]),
|
||||||
Token::NotEqualsTo => {
|
cmp_default,
|
||||||
Expr::FunctionCall("!=".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
pos,
|
||||||
}
|
),
|
||||||
Token::LessThan => {
|
Token::NotEqualsTo => Expr::FunctionCall(
|
||||||
Expr::FunctionCall("<".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
"!=".into(),
|
||||||
}
|
Box::new(vec![current_lhs, rhs]),
|
||||||
Token::LessThanEqualsTo => {
|
cmp_default,
|
||||||
Expr::FunctionCall("<=".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
pos,
|
||||||
}
|
),
|
||||||
Token::GreaterThan => {
|
Token::LessThan => Expr::FunctionCall(
|
||||||
Expr::FunctionCall(">".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
"<".into(),
|
||||||
}
|
Box::new(vec![current_lhs, rhs]),
|
||||||
Token::GreaterThanEqualsTo => {
|
cmp_default,
|
||||||
Expr::FunctionCall(">=".into(), vec![current_lhs, rhs], Some(false.into()), pos)
|
pos,
|
||||||
}
|
),
|
||||||
|
Token::LessThanEqualsTo => Expr::FunctionCall(
|
||||||
|
"<=".into(),
|
||||||
|
Box::new(vec![current_lhs, rhs]),
|
||||||
|
cmp_default,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
|
Token::GreaterThan => Expr::FunctionCall(
|
||||||
|
">".into(),
|
||||||
|
Box::new(vec![current_lhs, rhs]),
|
||||||
|
cmp_default,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
|
Token::GreaterThanEqualsTo => Expr::FunctionCall(
|
||||||
|
">=".into(),
|
||||||
|
Box::new(vec![current_lhs, rhs]),
|
||||||
|
cmp_default,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
|
|
||||||
Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs), pos),
|
Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs), pos),
|
||||||
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::FunctionCall("&".into(), vec![current_lhs, rhs], None, pos),
|
Token::Ampersand => {
|
||||||
Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos),
|
Expr::FunctionCall("&".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos),
|
}
|
||||||
|
Token::Pipe => {
|
||||||
|
Expr::FunctionCall("|".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
Token::XOr => {
|
||||||
|
Expr::FunctionCall("^".into(), Box::new(vec![current_lhs, rhs]), None, pos)
|
||||||
|
}
|
||||||
|
|
||||||
Token::In => make_in_expr(current_lhs, rhs, pos)?,
|
Token::In => make_in_expr(current_lhs, rhs, pos)?,
|
||||||
|
|
||||||
@ -1326,10 +1426,11 @@ fn parse_binary_op<'a>(
|
|||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
fn parse_expr<'a>(
|
fn parse_expr<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let lhs = parse_unary(input, allow_stmt_expr)?;
|
let lhs = parse_unary(input, stack, allow_stmt_expr)?;
|
||||||
parse_binary_op(input, 1, lhs, allow_stmt_expr)
|
parse_binary_op(input, stack, 1, lhs, allow_stmt_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
|
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
|
||||||
@ -1379,6 +1480,7 @@ fn ensure_not_assignment<'a>(
|
|||||||
/// Parse an if statement.
|
/// Parse an if statement.
|
||||||
fn parse_if<'a>(
|
fn parse_if<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
@ -1387,18 +1489,18 @@ fn parse_if<'a>(
|
|||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, allow_stmt_expr)?;
|
let guard = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
|
let if_body = parse_block(input, stack, breakable, allow_stmt_expr)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
||||||
Some(Box::new(if let (Token::If, _) = input.peek().unwrap() {
|
Some(Box::new(if let (Token::If, _) = input.peek().unwrap() {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
parse_if(input, breakable, allow_stmt_expr)?
|
parse_if(input, stack, breakable, allow_stmt_expr)?
|
||||||
} else {
|
} else {
|
||||||
// if guard { if_body } else { else-body }
|
// if guard { if_body } else { else-body }
|
||||||
parse_block(input, breakable, allow_stmt_expr)?
|
parse_block(input, stack, breakable, allow_stmt_expr)?
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -1414,6 +1516,7 @@ fn parse_if<'a>(
|
|||||||
/// Parse a while loop.
|
/// Parse a while loop.
|
||||||
fn parse_while<'a>(
|
fn parse_while<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
// while ...
|
// while ...
|
||||||
@ -1421,9 +1524,9 @@ fn parse_while<'a>(
|
|||||||
|
|
||||||
// while guard { body }
|
// while guard { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, allow_stmt_expr)?;
|
let guard = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let body = parse_block(input, true, allow_stmt_expr)?;
|
let body = parse_block(input, stack, true, allow_stmt_expr)?;
|
||||||
|
|
||||||
Ok(Stmt::While(Box::new(guard), Box::new(body)))
|
Ok(Stmt::While(Box::new(guard), Box::new(body)))
|
||||||
}
|
}
|
||||||
@ -1431,13 +1534,14 @@ fn parse_while<'a>(
|
|||||||
/// Parse a loop statement.
|
/// Parse a loop statement.
|
||||||
fn parse_loop<'a>(
|
fn parse_loop<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
// loop ...
|
// loop ...
|
||||||
eat_token(input, Token::Loop);
|
eat_token(input, Token::Loop);
|
||||||
|
|
||||||
// loop { body }
|
// loop { body }
|
||||||
let body = parse_block(input, true, allow_stmt_expr)?;
|
let body = parse_block(input, stack, true, allow_stmt_expr)?;
|
||||||
|
|
||||||
Ok(Stmt::Loop(Box::new(body)))
|
Ok(Stmt::Loop(Box::new(body)))
|
||||||
}
|
}
|
||||||
@ -1445,6 +1549,7 @@ fn parse_loop<'a>(
|
|||||||
/// Parse a for loop.
|
/// Parse a for loop.
|
||||||
fn parse_for<'a>(
|
fn parse_for<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
// for ...
|
// for ...
|
||||||
@ -1476,15 +1581,22 @@ fn parse_for<'a>(
|
|||||||
|
|
||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = parse_expr(input, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
let body = parse_block(input, true, allow_stmt_expr)?;
|
|
||||||
|
|
||||||
Ok(Stmt::For(name.into(), Box::new(expr), Box::new(body)))
|
let prev_len = stack.len();
|
||||||
|
stack.push(name.clone());
|
||||||
|
|
||||||
|
let body = parse_block(input, stack, true, allow_stmt_expr)?;
|
||||||
|
|
||||||
|
stack.rewind(prev_len);
|
||||||
|
|
||||||
|
Ok(Stmt::For(name, Box::new(expr), Box::new(body)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a variable definition statement.
|
/// Parse a variable definition statement.
|
||||||
fn parse_let<'a>(
|
fn parse_let<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
var_type: ScopeEntryType,
|
var_type: ScopeEntryType,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
@ -1501,14 +1613,18 @@ fn parse_let<'a>(
|
|||||||
// let name = ...
|
// let name = ...
|
||||||
if match_token(input, Token::Equals)? {
|
if match_token(input, Token::Equals)? {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
let init_value = parse_expr(input, allow_stmt_expr)?;
|
let init_value = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
ScopeEntryType::Normal => Ok(Stmt::Let(name.into(), Some(Box::new(init_value)), pos)),
|
ScopeEntryType::Normal => {
|
||||||
|
stack.push(name.clone());
|
||||||
|
Ok(Stmt::Let(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() => {
|
||||||
Ok(Stmt::Const(name.into(), Box::new(init_value), pos))
|
stack.push(name.clone());
|
||||||
|
Ok(Stmt::Const(name, Box::new(init_value), pos))
|
||||||
}
|
}
|
||||||
// const name = expr - error
|
// const name = expr - error
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
@ -1517,13 +1633,14 @@ fn parse_let<'a>(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// let name
|
// let name
|
||||||
Ok(Stmt::Let(name.into(), None, pos))
|
Ok(Stmt::Let(name, None, 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>>,
|
||||||
|
stack: &mut Stack,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
@ -1539,10 +1656,11 @@ fn parse_block<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::new();
|
||||||
|
let prev_len = stack.len();
|
||||||
|
|
||||||
while !match_token(input, Token::RightBrace)? {
|
while !match_token(input, Token::RightBrace)? {
|
||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
let stmt = parse_stmt(input, breakable, allow_stmt_expr)?;
|
let stmt = parse_stmt(input, stack, breakable, allow_stmt_expr)?;
|
||||||
|
|
||||||
// See if it needs a terminating semicolon
|
// See if it needs a terminating semicolon
|
||||||
let need_semicolon = !stmt.is_self_terminated();
|
let need_semicolon = !stmt.is_self_terminated();
|
||||||
@ -1578,22 +1696,26 @@ fn parse_block<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack.rewind(prev_len);
|
||||||
|
|
||||||
Ok(Stmt::Block(statements, pos))
|
Ok(Stmt::Block(statements, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression as a statement.
|
/// Parse an expression as a statement.
|
||||||
fn parse_expr_stmt<'a>(
|
fn parse_expr_stmt<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
let expr = parse_expr(input, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
let expr = parse_op_assignment_stmt(input, expr, allow_stmt_expr)?;
|
let expr = parse_op_assignment_stmt(input, stack, expr, allow_stmt_expr)?;
|
||||||
Ok(Stmt::Expr(Box::new(expr)))
|
Ok(Stmt::Expr(Box::new(expr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single statement.
|
/// Parse a single statement.
|
||||||
fn parse_stmt<'a>(
|
fn parse_stmt<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
@ -1606,16 +1728,16 @@ fn parse_stmt<'a>(
|
|||||||
// Semicolon - empty statement
|
// Semicolon - empty statement
|
||||||
Token::SemiColon => Ok(Stmt::Noop(*pos)),
|
Token::SemiColon => Ok(Stmt::Noop(*pos)),
|
||||||
|
|
||||||
Token::LeftBrace => parse_block(input, breakable, allow_stmt_expr),
|
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
||||||
|
|
||||||
Token::If => parse_if(input, breakable, allow_stmt_expr),
|
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
||||||
Token::While => parse_while(input, allow_stmt_expr),
|
Token::While => parse_while(input, stack, allow_stmt_expr),
|
||||||
Token::Loop => parse_loop(input, allow_stmt_expr),
|
Token::Loop => parse_loop(input, stack, allow_stmt_expr),
|
||||||
Token::For => parse_for(input, allow_stmt_expr),
|
Token::For => parse_for(input, stack, allow_stmt_expr),
|
||||||
|
|
||||||
Token::Continue if breakable => {
|
Token::Continue if breakable => {
|
||||||
let pos = eat_token(input, Token::Continue);
|
let pos = eat_token(input, Token::Continue);
|
||||||
@ -1643,23 +1765,24 @@ fn parse_stmt<'a>(
|
|||||||
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
||||||
// `return` or `throw` with expression
|
// `return` or `throw` with expression
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
let expr = parse_expr(input, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
let pos = expr.position();
|
let pos = expr.position();
|
||||||
Ok(Stmt::ReturnWithVal(Some(Box::new(expr)), return_type, pos))
|
Ok(Stmt::ReturnWithVal(Some(Box::new(expr)), return_type, pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Let => parse_let(input, ScopeEntryType::Normal, allow_stmt_expr),
|
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
|
||||||
Token::Const => parse_let(input, ScopeEntryType::Constant, allow_stmt_expr),
|
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
|
||||||
|
|
||||||
_ => parse_expr_stmt(input, allow_stmt_expr),
|
_ => parse_expr_stmt(input, stack, allow_stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a function definition.
|
/// Parse a function definition.
|
||||||
fn parse_fn<'a>(
|
fn parse_fn<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
stack: &mut Stack,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<FnDef, Box<ParseError>> {
|
) -> Result<FnDef, Box<ParseError>> {
|
||||||
let pos = input.next().expect("should be fn").1;
|
let pos = input.next().expect("should be fn").1;
|
||||||
@ -1682,7 +1805,10 @@ fn parse_fn<'a>(
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => params.push((s, pos)),
|
(Token::Identifier(s), pos) => {
|
||||||
|
stack.push(s.clone());
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
@ -1719,10 +1845,10 @@ fn parse_fn<'a>(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = match input.peek().unwrap() {
|
let body = Box::new(match input.peek().unwrap() {
|
||||||
(Token::LeftBrace, _) => parse_block(input, false, allow_stmt_expr)?,
|
(Token::LeftBrace, _) => parse_block(input, stack, false, allow_stmt_expr)?,
|
||||||
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
||||||
};
|
});
|
||||||
|
|
||||||
let params = params.into_iter().map(|(p, _)| p).collect();
|
let params = params.into_iter().map(|(p, _)| p).collect();
|
||||||
|
|
||||||
@ -1740,7 +1866,8 @@ pub fn parse_global_expr<'a>(
|
|||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, Box<ParseError>> {
|
) -> Result<AST, Box<ParseError>> {
|
||||||
let expr = parse_expr(input, false)?;
|
let mut stack = Stack::new();
|
||||||
|
let expr = parse_expr(input, &mut stack, false)?;
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::EOF, _) => (),
|
(Token::EOF, _) => (),
|
||||||
@ -1768,20 +1895,22 @@ fn parse_global_level<'a>(
|
|||||||
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), Box<ParseError>> {
|
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), Box<ParseError>> {
|
||||||
let mut statements = Vec::<Stmt>::new();
|
let mut statements = Vec::<Stmt>::new();
|
||||||
let mut functions = HashMap::<u64, FnDef>::new();
|
let mut functions = HashMap::<u64, FnDef>::new();
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
// Collect all the function definitions
|
// Collect all the function definitions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
if let (Token::Fn, _) = input.peek().unwrap() {
|
if let (Token::Fn, _) = input.peek().unwrap() {
|
||||||
let f = parse_fn(input, true)?;
|
let mut stack = Stack::new();
|
||||||
|
let f = parse_fn(input, &mut stack, true)?;
|
||||||
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual statement
|
// Actual statement
|
||||||
let stmt = parse_stmt(input, false, true)?;
|
let stmt = parse_stmt(input, &mut stack, false, true)?;
|
||||||
|
|
||||||
let need_semicolon = !stmt.is_self_terminated();
|
let need_semicolon = !stmt.is_self_terminated();
|
||||||
|
|
||||||
@ -1840,7 +1969,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
Union::Unit(_) => Some(Expr::Unit(pos)),
|
||||||
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
||||||
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
||||||
Union::Str(value) => Some(Expr::StringConstant((*value).into(), pos)),
|
Union::Str(value) => Some(Expr::StringConstant((*value).clone(), pos)),
|
||||||
Union::Bool(true) => Some(Expr::True(pos)),
|
Union::Bool(true) => Some(Expr::True(pos)),
|
||||||
Union::Bool(false) => Some(Expr::False(pos)),
|
Union::Bool(false) => Some(Expr::False(pos)),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
@ -4,7 +4,7 @@ use crate::any::{Dynamic, Variant};
|
|||||||
use crate::parser::{map_dynamic_to_expr, Expr};
|
use crate::parser::{map_dynamic_to_expr, Expr};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{borrow::Cow, iter, vec::Vec};
|
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
|
||||||
|
|
||||||
/// Type of an entry in the Scope.
|
/// Type of an entry in the Scope.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||||
@ -349,7 +349,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the Scope.
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
|
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
|
|
||||||
// assert_ne!(
|
// assert_ne!(
|
||||||
@ -358,11 +358,11 @@ impl<'a> Scope<'a> {
|
|||||||
// "get mut of constant entry"
|
// "get mut of constant entry"
|
||||||
// );
|
// );
|
||||||
|
|
||||||
&mut entry.value
|
(&mut entry.value, entry.typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Entry> {
|
pub(crate) fn iter(&self) -> impl Iterator<Item = &Entry> {
|
||||||
self.0.iter().rev() // Always search a Scope in reverse order
|
self.0.iter().rev() // Always search a Scope in reverse order
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,10 @@ fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
|||||||
script += "y += foo(y);";
|
script += "y += foo(y);";
|
||||||
script += "x + y";
|
script += "x + y";
|
||||||
|
|
||||||
eval(script)
|
eval(script) + x + y
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
42
|
84
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -54,7 +54,8 @@ fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
|||||||
32
|
32
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(!scope.contains("z"));
|
assert!(scope.contains("script"));
|
||||||
|
assert_eq!(scope.len(), 3);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user