Improve AST debug display.
This commit is contained in:
parent
ff06bb98a1
commit
f1458e79e0
109
src/ast/ast.rs
109
src/ast/ast.rs
@ -1,10 +1,11 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
use super::{Expr, FnAccess, Stmt, StmtBlock, AST_OPTION_FLAGS};
|
use super::{Expr, FnAccess, Stmt, StmtBlock, AST_OPTION_FLAGS::*};
|
||||||
use crate::{Dynamic, FnNamespace, Identifier, Position, StaticVec};
|
use crate::{Dynamic, FnNamespace, Identifier, Position, StaticVec};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
ops::{Add, AddAssign},
|
ops::{Add, AddAssign},
|
||||||
};
|
};
|
||||||
@ -14,7 +15,7 @@ use std::{
|
|||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AST {
|
pub struct AST {
|
||||||
/// Source of the [`AST`].
|
/// Source of the [`AST`].
|
||||||
/// No source if string is empty.
|
/// No source if string is empty.
|
||||||
@ -23,7 +24,7 @@ pub struct AST {
|
|||||||
body: StmtBlock,
|
body: StmtBlock,
|
||||||
/// Script-defined functions.
|
/// Script-defined functions.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions: crate::Shared<crate::Module>,
|
lib: crate::Shared<crate::Module>,
|
||||||
/// Embedded module resolver, if any.
|
/// Embedded module resolver, if any.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
|
resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||||
@ -36,6 +37,31 @@ impl Default for AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AST {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut fp = f.debug_struct("AST");
|
||||||
|
|
||||||
|
if !self.source.is_empty() {
|
||||||
|
fp.field("source: ", &self.source);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
if let Some(ref resolver) = self.resolver {
|
||||||
|
fp.field("resolver: ", resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.field("body", &self.body.as_slice());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if !self.lib.is_empty() {
|
||||||
|
for (_, _, _, _, ref fn_def) in self.lib.iter_script_fn() {
|
||||||
|
let sig = fn_def.to_string();
|
||||||
|
fp.field(&sig, &fn_def.body.as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fp.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new [`AST`].
|
/// Create a new [`AST`].
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -49,7 +75,7 @@ impl AST {
|
|||||||
source: Identifier::new_const(),
|
source: Identifier::new_const(),
|
||||||
body: StmtBlock::new(statements, Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions: functions.into(),
|
lib: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
}
|
}
|
||||||
@ -67,7 +93,7 @@ impl AST {
|
|||||||
source: Identifier::new_const(),
|
source: Identifier::new_const(),
|
||||||
body: StmtBlock::new(statements, Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions: functions.into(),
|
lib: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
}
|
}
|
||||||
@ -115,7 +141,7 @@ impl AST {
|
|||||||
source: Identifier::new_const(),
|
source: Identifier::new_const(),
|
||||||
body: StmtBlock::NONE,
|
body: StmtBlock::NONE,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions: crate::Module::new().into(),
|
lib: crate::Module::new().into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
}
|
}
|
||||||
@ -140,7 +166,7 @@ impl AST {
|
|||||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
||||||
let source = source.into();
|
let source = source.into();
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::Shared::get_mut(&mut self.functions)
|
crate::Shared::get_mut(&mut self.lib)
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|m| m.set_id(source.clone()));
|
.map(|m| m.set_id(source.clone()));
|
||||||
self.source = source;
|
self.source = source;
|
||||||
@ -181,7 +207,7 @@ impl AST {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn has_functions(&self) -> bool {
|
pub fn has_functions(&self) -> bool {
|
||||||
!self.functions.is_empty()
|
!self.lib.is_empty()
|
||||||
}
|
}
|
||||||
/// Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
/// Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -189,7 +215,7 @@ impl AST {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
pub(crate) fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
||||||
&self.functions
|
&self.lib
|
||||||
}
|
}
|
||||||
/// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
/// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -200,7 +226,7 @@ impl AST {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
pub fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
||||||
&self.functions
|
&self.lib
|
||||||
}
|
}
|
||||||
/// Get the embedded [module resolver][`ModuleResolver`].
|
/// Get the embedded [module resolver][`ModuleResolver`].
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -260,12 +286,12 @@ impl AST {
|
|||||||
&self,
|
&self,
|
||||||
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut functions = crate::Module::new();
|
let mut lib = crate::Module::new();
|
||||||
functions.merge_filtered(&self.functions, &filter);
|
lib.merge_filtered(&self.lib, &filter);
|
||||||
Self {
|
Self {
|
||||||
source: self.source.clone(),
|
source: self.source.clone(),
|
||||||
body: StmtBlock::NONE,
|
body: StmtBlock::NONE,
|
||||||
functions: functions.into(),
|
lib: lib.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: self.resolver.clone(),
|
resolver: self.resolver.clone(),
|
||||||
}
|
}
|
||||||
@ -279,7 +305,7 @@ impl AST {
|
|||||||
source: self.source.clone(),
|
source: self.source.clone(),
|
||||||
body: self.body.clone(),
|
body: self.body.clone(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions: crate::Module::new().into(),
|
lib: crate::Module::new().into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: self.resolver.clone(),
|
resolver: self.resolver.clone(),
|
||||||
}
|
}
|
||||||
@ -472,24 +498,24 @@ impl AST {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let functions = {
|
let lib = {
|
||||||
let mut functions = self.functions.as_ref().clone();
|
let mut lib = self.lib.as_ref().clone();
|
||||||
functions.merge_filtered(&other.functions, &_filter);
|
lib.merge_filtered(&other.lib, &_filter);
|
||||||
functions
|
lib
|
||||||
};
|
};
|
||||||
|
|
||||||
if !other.source.is_empty() {
|
if !other.source.is_empty() {
|
||||||
Self::new_with_source(
|
Self::new_with_source(
|
||||||
merged,
|
merged,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions,
|
lib,
|
||||||
other.source.clone(),
|
other.source.clone(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::new(
|
Self::new(
|
||||||
merged,
|
merged,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions,
|
lib,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,9 +588,9 @@ impl AST {
|
|||||||
self.body.extend(other.body.into_iter());
|
self.body.extend(other.body.into_iter());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if !other.functions.is_empty() {
|
if !other.lib.is_empty() {
|
||||||
crate::func::native::shared_make_mut(&mut self.functions)
|
crate::func::native::shared_make_mut(&mut self.lib)
|
||||||
.merge_filtered(&other.functions, &_filter);
|
.merge_filtered(&other.lib, &_filter);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -599,20 +625,32 @@ impl AST {
|
|||||||
&mut self,
|
&mut self,
|
||||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if !self.functions.is_empty() {
|
if !self.lib.is_empty() {
|
||||||
crate::func::native::shared_make_mut(&mut self.functions)
|
crate::func::native::shared_make_mut(&mut self.lib).retain_script_functions(filter);
|
||||||
.retain_script_functions(filter);
|
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
/// _(internals)_ Iterate through all function definitions.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
|
pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
||||||
|
self.lib
|
||||||
|
.iter_script_fn()
|
||||||
|
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
|
||||||
|
}
|
||||||
/// Iterate through all function definitions.
|
/// Iterate through all function definitions.
|
||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
||||||
self.functions
|
self.lib
|
||||||
.iter_script_fn()
|
.iter_script_fn()
|
||||||
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
|
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
|
||||||
}
|
}
|
||||||
@ -622,7 +660,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
|
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
|
||||||
self.functions
|
self.lib
|
||||||
.iter_script_fn()
|
.iter_script_fn()
|
||||||
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into())
|
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into())
|
||||||
}
|
}
|
||||||
@ -632,7 +670,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_functions(&mut self) -> &mut Self {
|
pub fn clear_functions(&mut self) -> &mut Self {
|
||||||
self.functions = crate::Module::new().into();
|
self.lib = crate::Module::new().into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Clear all statements in the [`AST`], leaving only function definitions.
|
/// Clear all statements in the [`AST`], leaving only function definitions.
|
||||||
@ -707,16 +745,11 @@ impl AST {
|
|||||||
) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||||
self.statements().iter().filter_map(move |stmt| match stmt {
|
self.statements().iter().filter_map(move |stmt| match stmt {
|
||||||
Stmt::Var(expr, name, options, _)
|
Stmt::Var(expr, name, options, _)
|
||||||
if options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) && include_constants
|
if options.contains(AST_OPTION_CONSTANT) && include_constants
|
||||||
|| !options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT)
|
|| !options.contains(AST_OPTION_CONSTANT) && include_variables =>
|
||||||
&& include_variables =>
|
|
||||||
{
|
{
|
||||||
if let Some(value) = expr.get_literal_value() {
|
if let Some(value) = expr.get_literal_value() {
|
||||||
Some((
|
Some((name.as_str(), options.contains(AST_OPTION_CONSTANT), value))
|
||||||
name.as_str(),
|
|
||||||
options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT),
|
|
||||||
value,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -871,6 +904,6 @@ impl AST {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn lib(&self) -> &crate::Module {
|
pub fn lib(&self) -> &crate::Module {
|
||||||
&self.functions
|
&self.lib
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ impl FnCallHashes {
|
|||||||
|
|
||||||
/// _(internals)_ A function call.
|
/// _(internals)_ A function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, Clone, Default, Hash)]
|
#[derive(Clone, Default, Hash)]
|
||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Namespace of the function, if any.
|
/// Namespace of the function, if any.
|
||||||
///
|
///
|
||||||
@ -193,6 +193,24 @@ pub struct FnCallExpr {
|
|||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for FnCallExpr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut ff = f.debug_struct("FnCallExpr");
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
self.namespace.as_ref().map(|ns| ff.field("namespace", ns));
|
||||||
|
ff.field("name", &self.name)
|
||||||
|
.field("hash", &self.hashes)
|
||||||
|
.field("arg_exprs", &self.args);
|
||||||
|
if !self.constants.is_empty() {
|
||||||
|
ff.field("constant_args", &self.constants);
|
||||||
|
}
|
||||||
|
if self.capture_parent_scope {
|
||||||
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
|
}
|
||||||
|
ff.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FnCallExpr {
|
impl FnCallExpr {
|
||||||
/// Does this function call contain a qualified namespace?
|
/// Does this function call contain a qualified namespace?
|
||||||
///
|
///
|
||||||
@ -459,26 +477,12 @@ impl fmt::Debug for Expr {
|
|||||||
f.write_str(")")
|
f.write_str(")")
|
||||||
}
|
}
|
||||||
Self::Property(x, _) => write!(f, "Property({})", x.2),
|
Self::Property(x, _) => write!(f, "Property({})", x.2),
|
||||||
Self::Stack(x, _) => write!(f, "StackSlot({})", x),
|
Self::Stack(x, _) => write!(f, "ConstantArg#{}", x),
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
f.write_str("ExprStmtBlock")?;
|
f.write_str("ExprStmtBlock")?;
|
||||||
f.debug_list().entries(x.iter()).finish()
|
f.debug_list().entries(x.iter()).finish()
|
||||||
}
|
}
|
||||||
Self::FnCall(x, _) => {
|
Self::FnCall(x, _) => fmt::Debug::fmt(x, f),
|
||||||
let mut ff = f.debug_struct("FnCall");
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
x.namespace.as_ref().map(|ns| ff.field("namespace", ns));
|
|
||||||
ff.field("name", &x.name)
|
|
||||||
.field("hash", &x.hashes)
|
|
||||||
.field("args", &x.args);
|
|
||||||
if !x.constants.is_empty() {
|
|
||||||
ff.field("constants", &x.constants);
|
|
||||||
}
|
|
||||||
if x.capture_parent_scope {
|
|
||||||
ff.field("capture_parent_scope", &x.capture_parent_scope);
|
|
||||||
}
|
|
||||||
ff.finish()
|
|
||||||
}
|
|
||||||
Self::Index(x, term, pos) => {
|
Self::Index(x, term, pos) => {
|
||||||
display_pos = *pos;
|
display_pos = *pos;
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ pub use parser::ParseState;
|
|||||||
pub use ast::{
|
pub use ast::{
|
||||||
ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
ASTNode, BinaryExpr, ConditionalStmtBlock, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||||
OpAssignment, OptionFlags, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
OpAssignment, OptionFlags, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
||||||
AST_OPTION_FLAGS::*,
|
AST_OPTION_FLAGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -52,11 +52,11 @@ pub struct ParseState<'e> {
|
|||||||
pub entry_stack_len: usize,
|
pub entry_stack_len: usize,
|
||||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub external_vars: BTreeMap<Identifier, Position>,
|
pub external_vars: Vec<crate::ast::Ident>,
|
||||||
/// An indicator that disables variable capturing into externals one single time
|
/// An indicator that disables variable capturing into externals one single time
|
||||||
/// up until the nearest consumed Identifier token.
|
/// up until the nearest consumed Identifier token.
|
||||||
/// If set to false the next call to [`access_var`][ParseState::access_var] will not capture the variable.
|
/// If set to false the next call to [`access_var`][ParseState::access_var] will not capture the variable.
|
||||||
/// All consequent calls to [`access_var`][ParseState::access_var] will not be affected
|
/// All consequent calls to [`access_var`][ParseState::access_var] will not be affected.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub allow_capture: bool,
|
pub allow_capture: bool,
|
||||||
/// Encapsulates a local stack with imported [module][crate::Module] names.
|
/// Encapsulates a local stack with imported [module][crate::Module] names.
|
||||||
@ -85,7 +85,7 @@ impl<'e> ParseState<'e> {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
max_function_expr_depth: NonZeroUsize::new(engine.max_function_expr_depth()),
|
max_function_expr_depth: NonZeroUsize::new(engine.max_function_expr_depth()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
external_vars: BTreeMap::new(),
|
external_vars: Vec::new(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
interned_strings: StringsInterner::new(),
|
interned_strings: StringsInterner::new(),
|
||||||
@ -128,8 +128,11 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if self.allow_capture {
|
if self.allow_capture {
|
||||||
if index.is_none() && !self.external_vars.contains_key(name) {
|
if index.is_none() && !self.external_vars.iter().any(|v| v.name == name) {
|
||||||
self.external_vars.insert(name.into(), _pos);
|
self.external_vars.push(crate::ast::Ident {
|
||||||
|
name: name.into(),
|
||||||
|
pos: _pos,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.allow_capture = true
|
self.allow_capture = true
|
||||||
@ -1258,12 +1261,14 @@ fn parse_primary(
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
new_state.external_vars.iter().try_for_each(
|
new_state.external_vars.iter().try_for_each(
|
||||||
|(captured_var, &pos)| -> ParseResult<_> {
|
|crate::ast::Ident { name, pos }| -> ParseResult<_> {
|
||||||
let index = state.access_var(captured_var, pos);
|
let index = state.access_var(name, *pos);
|
||||||
|
|
||||||
if !settings.is_closure && settings.strict_var && index.is_none() {
|
if settings.strict_var && !settings.is_closure && index.is_none() {
|
||||||
// If the parent scope is not inside another capturing closure
|
// If the parent scope is not inside another capturing closure
|
||||||
Err(PERR::VariableUndefined(captured_var.to_string()).into_err(pos))
|
// then we can conclude that the captured variable doesn't exist.
|
||||||
|
// Under Strict Variables mode, this is not allowed.
|
||||||
|
Err(PERR::VariableUndefined(name.to_string()).into_err(*pos))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -3311,20 +3316,21 @@ fn parse_anon_fn(
|
|||||||
// External variables may need to be processed in a consistent order,
|
// External variables may need to be processed in a consistent order,
|
||||||
// so extract them into a list.
|
// so extract them into a list.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let externals: StaticVec<Identifier> = state
|
let (mut params, externals) = {
|
||||||
.external_vars
|
let externals: StaticVec<Identifier> = state
|
||||||
.iter()
|
.external_vars
|
||||||
.map(|(name, _)| name.clone())
|
.iter()
|
||||||
.collect();
|
.map(|crate::ast::Ident { name, .. }| name.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
let mut params = StaticVec::with_capacity(params_list.len() + externals.len());
|
||||||
let mut params = StaticVec::with_capacity(params_list.len() + externals.len());
|
params.extend(externals.iter().cloned());
|
||||||
|
|
||||||
|
(params, externals)
|
||||||
|
};
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
let mut params = StaticVec::with_capacity(params_list.len());
|
let mut params = StaticVec::with_capacity(params_list.len());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
params.extend(externals.iter().cloned());
|
|
||||||
|
|
||||||
params.append(&mut params_list);
|
params.append(&mut params_list);
|
||||||
|
|
||||||
// Create unique function name by hashing the script body plus the parameters.
|
// Create unique function name by hashing the script body plus the parameters.
|
||||||
|
@ -149,7 +149,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
"" => f.write_str("Malformed dot expression"),
|
"" => f.write_str("Malformed dot expression"),
|
||||||
s => f.write_str(s),
|
s => f.write_str(s),
|
||||||
}?,
|
}?,
|
||||||
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for {}", s)?,
|
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered: {}", s)?,
|
||||||
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
||||||
Self::ErrorFor(_) => f.write_str("For loop expects a type that is iterable")?,
|
Self::ErrorFor(_) => f.write_str("For loop expects a type that is iterable")?,
|
||||||
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
||||||
@ -166,7 +166,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?,
|
Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?,
|
||||||
|
|
||||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant {}", s)?,
|
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant: {}", s)?,
|
||||||
Self::ErrorMismatchOutputType(s, r, _) => match (r.as_str(), s.as_str()) {
|
Self::ErrorMismatchOutputType(s, r, _) => match (r.as_str(), s.as_str()) {
|
||||||
("", s) => write!(f, "Output type is incorrect, expecting {}", s),
|
("", s) => write!(f, "Output type is incorrect, expecting {}", s),
|
||||||
(r, "") => write!(f, "Output type is incorrect: {}", r),
|
(r, "") => write!(f, "Output type is incorrect: {}", r),
|
||||||
|
@ -78,33 +78,24 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", ast), "AST { body: [Expr(123 @ 1:53)] }");
|
||||||
format!("{:?}", ast),
|
|
||||||
"AST { source: \"\", body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
|
|
||||||
);
|
|
||||||
|
|
||||||
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", ast),
|
format!("{:?}", ast),
|
||||||
r#"AST { source: "", body: Block[Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
|
r#"AST { body: [Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", ast), "AST { body: [] }");
|
||||||
format!("{:?}", ast),
|
|
||||||
"AST { source: \"\", body: Block[], functions: Module, resolver: None }"
|
|
||||||
);
|
|
||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
|
|
||||||
let ast = engine.compile("abs(-42)")?;
|
let ast = engine.compile("abs(-42)")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", ast), "AST { body: [Expr(42 @ 1:1)] }");
|
||||||
format!("{:?}", ast),
|
|
||||||
"AST { source: \"\", body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user