Use bitflags.

This commit is contained in:
Stephen Chung 2022-11-23 11:36:30 +08:00
parent d911327242
commit 4e27039521
33 changed files with 294 additions and 259 deletions

View File

@ -313,7 +313,7 @@ impl Parse for ExportedFn {
} }
} }
let skip_slots = if pass_context { 1 } else { 0 }; let skip_slots = usize::from(pass_context);
// Determine whether function generates a special calling convention for a mutable receiver. // Determine whether function generates a special calling convention for a mutable receiver.
let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) { let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) {
@ -485,12 +485,12 @@ impl ExportedFn {
} }
pub fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> { pub fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
let skip = if self.pass_context { 1 } else { 0 }; let skip = usize::from(self.pass_context);
self.signature.inputs.iter().skip(skip) self.signature.inputs.iter().skip(skip)
} }
pub fn arg_count(&self) -> usize { pub fn arg_count(&self) -> usize {
let skip = if self.pass_context { 1 } else { 0 }; let skip = usize::from(self.pass_context);
self.signature.inputs.len() - skip self.signature.inputs.len() - skip
} }

View File

@ -8,7 +8,7 @@ pub fn main() {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn main() { pub fn main() {
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Map, Scope, AST}; use rhai::{CallFnOptions, Dynamic, Engine, Map, Scope, AST};
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
const SCRIPT_FILE: &str = "event_handler_js/script.rhai"; const SCRIPT_FILE: &str = "event_handler_js/script.rhai";

View File

@ -2,7 +2,7 @@
#![cfg(feature = "internals")] #![cfg(feature = "internals")]
#![cfg(feature = "metadata")] #![cfg(feature = "metadata")]
use crate::module::FuncInfo; use crate::module::{FuncInfo, ModuleFlags};
use crate::tokenizer::{is_valid_function_name, Token}; use crate::tokenizer::{is_valid_function_name, Token};
use crate::{Engine, FnAccess, FnPtr, Module, Scope, INT}; use crate::{Engine, FnAccess, FnPtr, Module, Scope, INT};
@ -308,10 +308,16 @@ impl Definitions<'_> {
String::new() String::new()
}; };
let exclude_flags = if !self.config.include_standard_packages {
ModuleFlags::STANDARD_LIB
} else {
ModuleFlags::empty()
};
self.engine self.engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| self.config.include_standard_packages || !m.standard) .filter(|m| !m.flags.contains(exclude_flags))
.enumerate() .enumerate()
.for_each(|(i, m)| { .for_each(|(i, m)| {
if i > 0 { if i > 0 {

View File

@ -1,7 +1,7 @@
//! Module that defines JSON manipulation functions for [`Engine`]. //! Module that defines JSON manipulation functions for [`Engine`].
#![cfg(not(feature = "no_object"))] #![cfg(not(feature = "no_object"))]
use crate::parser::ParseState; use crate::parser::{ParseSettingFlags, ParseState};
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf, Scope}; use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf, Scope};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -122,7 +122,7 @@ impl Engine {
let ast = self.parse_global_expr( let ast = self.parse_global_expr(
&mut stream.peekable(), &mut stream.peekable(),
&mut state, &mut state,
|s| s.allow_unquoted_map_properties = false, |s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]

View File

@ -1,6 +1,7 @@
//! Module that defines the public function/module registration API of [`Engine`]. //! Module that defines the public function/module registration API of [`Engine`].
use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync};
use crate::module::ModuleFlags;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared, Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared,
@ -741,11 +742,17 @@ impl Engine {
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}"))); signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
} }
let exclude_flags = if !include_packages {
ModuleFlags::INTERNAL | ModuleFlags::STANDARD_LIB
} else {
ModuleFlags::INTERNAL
};
signatures.extend( signatures.extend(
self.global_modules self.global_modules
.iter() .iter()
.skip(1) .skip(1)
.filter(|m| !m.internal && (include_packages || !m.standard)) .filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.gen_fn_signatures()), .flat_map(|m| m.gen_fn_signatures()),
); );

View File

@ -5,6 +5,7 @@ use crate::func::native::{
locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback, locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
OnVarCallback, OnVarCallback,
}; };
use crate::module::ModuleFlags;
use crate::packages::{Package, StandardPackage}; use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::StringsInterner; use crate::types::StringsInterner;
@ -303,7 +304,7 @@ impl Engine {
// Add the global namespace module // Add the global namespace module
let mut global_namespace = Module::with_capacity(0); let mut global_namespace = Module::with_capacity(0);
global_namespace.internal = true; global_namespace.flags |= ModuleFlags::INTERNAL;
engine.global_modules.push(global_namespace.into()); engine.global_modules.push(global_namespace.into());
engine engine

View File

@ -764,6 +764,8 @@ impl Engine {
// Import statement // Import statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x, _pos) => { Stmt::Import(x, _pos) => {
use crate::ModuleResolver;
let (expr, export) = &**x; let (expr, export) = &**x;
// Guard against too many modules // Guard against too many modules
@ -777,8 +779,6 @@ impl Engine {
self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position()) self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position())
})?; })?;
use crate::ModuleResolver;
let path_pos = expr.start_position(); let path_pos = expr.start_position();
let resolver = global.embedded_module_resolver.clone(); let resolver = global.embedded_module_resolver.clone();

View File

@ -53,8 +53,7 @@ pub fn calc_index<E>(
negative_count_from_end: bool, negative_count_from_end: bool,
err_func: impl FnOnce() -> Result<usize, E>, err_func: impl FnOnce() -> Result<usize, E>,
) -> Result<usize, E> { ) -> Result<usize, E> {
if start < 0 { if start < 0 && negative_count_from_end {
if negative_count_from_end {
let abs_start = start.unsigned_abs() as usize; let abs_start = start.unsigned_abs() as usize;
// Count from end if negative // Count from end if negative
@ -62,7 +61,6 @@ pub fn calc_index<E>(
return Ok(length - abs_start); return Ok(length - abs_start);
} }
} }
}
if start <= crate::MAX_USIZE_INT && (start as usize) < length { if start <= crate::MAX_USIZE_INT && (start as usize) < length {
return Ok(start as usize); return Ok(start as usize);
} }

View File

@ -529,25 +529,21 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
} }
// Handle ranges here because ranges are implemented as custom type // Handle ranges here because ranges are implemented as custom type
if type1 == TypeId::of::<ExclusiveRange>() { if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
if type1 == type2 {
return match op { return match op {
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
_ => None, _ => None,
}; };
} }
}
if type1 == TypeId::of::<InclusiveRange>() { if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
if type1 == type2 {
return match op { return match op {
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
_ => None, _ => None,
}; };
} }
}
// One of the operands is a custom type, so it is never built-in // One of the operands is a custom type, so it is never built-in
if x.is_variant() || y.is_variant() { if x.is_variant() || y.is_variant() {

View File

@ -58,11 +58,11 @@
#![cfg_attr(feature = "no_std", no_std)] #![cfg_attr(feature = "no_std", no_std)]
#![deny(missing_docs)] #![deny(missing_docs)]
// #![warn(clippy::all)] #![warn(clippy::all)]
// #![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
// #![warn(clippy::nursery)] #![warn(clippy::nursery)]
// #![warn(clippy::cargo)] #![warn(clippy::cargo)]
// #![warn(clippy::undocumented_unsafe_blocks)] #![warn(clippy::undocumented_unsafe_blocks)]
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
#![allow(clippy::missing_errors_doc)] #![allow(clippy::missing_errors_doc)]
#![allow(clippy::used_underscore_binding)] #![allow(clippy::used_underscore_binding)]

View File

@ -5,13 +5,14 @@ use crate::api::type_names::format_type;
use crate::ast::FnAccess; use crate::ast::FnAccess;
use crate::func::{ use crate::func::{
shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction,
SendSync, SendSync, StraightHashMap,
}; };
use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection}; use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection};
use crate::{ use crate::{
calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext, calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext,
RhaiResultOf, Shared, SharedModule, SmartString, StaticVec, RhaiResultOf, Shared, SharedModule, SmartString, StaticVec,
}; };
use bitflags::bitflags;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
@ -24,8 +25,6 @@ use std::{
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
use crate::func::register::Mut; use crate::func::register::Mut;
use crate::func::StraightHashMap;
/// A type representing the namespace of a function. /// A type representing the namespace of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "metadata", derive(serde::Serialize))] #[cfg_attr(feature = "metadata", derive(serde::Serialize))]
@ -94,44 +93,44 @@ impl FuncInfo {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use] #[must_use]
pub fn gen_signature(&self) -> String { pub fn gen_signature(&self) -> String {
let mut sig = format!("{}(", self.name); let mut signature = format!("{}(", self.name);
let return_type = format_type(&self.return_type, true); let return_type = format_type(&self.return_type, true);
if self.params_info.is_empty() { if self.params_info.is_empty() {
for x in 0..self.num_params { for x in 0..self.num_params {
sig.push('_'); signature.push('_');
if x < self.num_params - 1 { if x < self.num_params - 1 {
sig.push_str(", "); signature.push_str(", ");
} }
} }
} else { } else {
let params: StaticVec<_> = self let params: StaticVec<_> = self
.params_info .params_info
.iter() .iter()
.map(|s| { .map(|param| {
let mut seg = s.splitn(2, ':'); let mut segment = param.splitn(2, ':');
let name = match seg.next().unwrap().trim() { let name = match segment.next().unwrap().trim() {
"" => "_", "" => "_",
s => s, s => s,
}; };
let result: std::borrow::Cow<str> = match seg.next() { let result: std::borrow::Cow<str> = match segment.next() {
Some(typ) => format!("{name}: {}", format_type(typ, false)).into(), Some(typ) => format!("{name}: {}", format_type(typ, false)).into(),
None => name.into(), None => name.into(),
}; };
result result
}) })
.collect(); .collect();
sig.push_str(&params.join(", ")); signature.push_str(&params.join(", "));
} }
sig.push(')'); signature.push(')');
if !self.func.is_script() && !return_type.is_empty() { if !self.func.is_script() && !return_type.is_empty() {
sig.push_str(" -> "); signature.push_str(" -> ");
sig.push_str(&return_type); signature.push_str(&return_type);
} }
sig signature
} }
} }
@ -156,6 +155,20 @@ pub fn calc_native_fn_hash<'a>(
) )
} }
bitflags! {
/// Bit-flags containing all status for [`Module`].
pub struct ModuleFlags: u8 {
/// Is the [`Module`] internal?
const INTERNAL = 0b0000_0001;
/// Is the [`Module`] part of a standard library?
const STANDARD_LIB = 0b0000_0010;
/// Is the [`Module`] indexed?
const INDEXED = 0b0000_0100;
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
const INDEXED_GLOBAL_FUNCTIONS = 0b0000_1000;
}
}
/// A module which may contain variables, sub-modules, external Rust functions, /// A module which may contain variables, sub-modules, external Rust functions,
/// and/or script-defined functions. /// and/or script-defined functions.
#[derive(Clone)] #[derive(Clone)]
@ -165,10 +178,6 @@ pub struct Module {
/// Module documentation. /// Module documentation.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
doc: crate::SmartString, doc: crate::SmartString,
/// Is this module internal?
pub(crate) internal: bool,
/// Is this module part of a standard library?
pub(crate) standard: bool,
/// Custom types. /// Custom types.
custom_types: Option<CustomTypesCollection>, custom_types: Option<CustomTypesCollection>,
/// Sub-modules. /// Sub-modules.
@ -188,10 +197,8 @@ pub struct Module {
type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>, type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
/// Flattened collection of iterator functions, including those in sub-modules. /// Flattened collection of iterator functions, including those in sub-modules.
all_type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>, all_type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
/// Is the [`Module`] indexed? /// Flags.
indexed: bool, pub(crate) flags: ModuleFlags,
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
contains_indexed_global_functions: bool,
} }
impl Default for Module { impl Default for Module {
@ -295,8 +302,6 @@ impl Module {
id: None, id: None,
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
doc: crate::SmartString::new_const(), doc: crate::SmartString::new_const(),
internal: false,
standard: false,
custom_types: None, custom_types: None,
modules: None, modules: None,
variables: None, variables: None,
@ -306,8 +311,7 @@ impl Module {
dynamic_functions_filter: BloomFilterU64::new(), dynamic_functions_filter: BloomFilterU64::new(),
type_iterators: None, type_iterators: None,
all_type_iterators: None, all_type_iterators: None,
indexed: true, flags: ModuleFlags::empty(),
contains_indexed_global_functions: false,
} }
} }
@ -437,11 +441,8 @@ impl Module {
/// Clear the [`Module`]. /// Clear the [`Module`].
#[inline(always)] #[inline(always)]
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.id = None;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
self.doc.clear(); self.doc.clear();
self.internal = false;
self.standard = false;
self.custom_types = None; self.custom_types = None;
self.modules = None; self.modules = None;
self.variables = None; self.variables = None;
@ -451,8 +452,7 @@ impl Module {
self.dynamic_functions_filter.clear(); self.dynamic_functions_filter.clear();
self.type_iterators = None; self.type_iterators = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
} }
/// Map a custom type to a friendly display name. /// Map a custom type to a friendly display name.
@ -543,7 +543,7 @@ impl Module {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
!self.contains_indexed_global_functions !self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
&& self.functions.is_empty() && self.functions.is_empty()
&& self.variables.as_ref().map_or(true, |m| m.is_empty()) && self.variables.as_ref().map_or(true, |m| m.is_empty())
&& self.modules.as_ref().map_or(true, |m| m.is_empty()) && self.modules.as_ref().map_or(true, |m| m.is_empty())
@ -579,7 +579,7 @@ impl Module {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_indexed(&self) -> bool { pub const fn is_indexed(&self) -> bool {
self.indexed self.flags.contains(ModuleFlags::INDEXED)
} }
/// _(metadata)_ Generate signatures for all the non-private functions in the [`Module`]. /// _(metadata)_ Generate signatures for all the non-private functions in the [`Module`].
@ -666,7 +666,7 @@ impl Module {
let ident = name.into(); let ident = name.into();
let value = Dynamic::from(value); let value = Dynamic::from(value);
if self.indexed { if self.is_indexed() {
let hash_var = crate::calc_var_hash(Some(""), &ident); let hash_var = crate::calc_var_hash(Some(""), &ident);
self.all_variables self.all_variables
.get_or_insert_with(|| Default::default()) .get_or_insert_with(|| Default::default())
@ -717,8 +717,7 @@ impl Module {
func: fn_def.into(), func: fn_def.into(),
}, },
); );
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
hash_script hash_script
} }
@ -759,8 +758,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
self.modules.get_or_insert_with(|| Default::default()) self.modules.get_or_insert_with(|| Default::default())
} }
@ -826,8 +824,7 @@ impl Module {
self.modules self.modules
.get_or_insert_with(|| Default::default()) .get_or_insert_with(|| Default::default())
.insert(name.into(), sub_module.into()); .insert(name.into(), sub_module.into());
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
self self
} }
@ -942,8 +939,7 @@ impl Module {
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.namespace = namespace; f.namespace = namespace;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
} }
self self
} }
@ -1051,8 +1047,7 @@ impl Module {
}, },
); );
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
hash_fn hash_fn
} }
@ -1607,8 +1602,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
if !other.doc.is_empty() { if !other.doc.is_empty() {
@ -1650,8 +1644,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
if !other.doc.is_empty() { if !other.doc.is_empty() {
@ -1702,8 +1695,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
if !other.doc.is_empty() { if !other.doc.is_empty() {
@ -1775,8 +1767,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
if !other.doc.is_empty() { if !other.doc.is_empty() {
@ -1811,8 +1802,7 @@ impl Module {
self.all_functions = None; self.all_functions = None;
self.all_variables = None; self.all_variables = None;
self.all_type_iterators = None; self.all_type_iterators = None;
self.indexed = false; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
self.contains_indexed_global_functions = false;
self self
} }
@ -2108,7 +2098,7 @@ impl Module {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn contains_indexed_global_functions(&self) -> bool { pub fn contains_indexed_global_functions(&self) -> bool {
self.contains_indexed_global_functions self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
} }
/// Scan through all the sub-modules in the [`Module`] and build a hash index of all /// Scan through all the sub-modules in the [`Module`] and build a hash index of all
@ -2181,7 +2171,7 @@ impl Module {
contains_indexed_global_functions contains_indexed_global_functions
} }
if !self.indexed { if !self.is_indexed() {
let mut path = Vec::with_capacity(4); let mut path = Vec::with_capacity(4);
let mut variables = StraightHashMap::default(); let mut variables = StraightHashMap::default();
let mut functions = StraightHashMap::default(); let mut functions = StraightHashMap::default();
@ -2189,7 +2179,7 @@ impl Module {
path.push(""); path.push("");
self.contains_indexed_global_functions = index_module( let r = index_module(
self, self,
&mut path, &mut path,
&mut variables, &mut variables,
@ -2197,6 +2187,10 @@ impl Module {
&mut type_iterators, &mut type_iterators,
); );
if r {
self.flags |= ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
}
self.all_variables = if variables.is_empty() { self.all_variables = if variables.is_empty() {
None None
} else { } else {
@ -2213,7 +2207,7 @@ impl Module {
Some(type_iterators) Some(type_iterators)
}; };
self.indexed = true; self.flags |= ModuleFlags::INDEXED;
} }
self self
@ -2257,7 +2251,7 @@ impl Module {
func: impl Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + SendSync + 'static, func: impl Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
let func = Shared::new(func); let func = Shared::new(func);
if self.indexed { if self.is_indexed() {
self.all_type_iterators self.all_type_iterators
.get_or_insert_with(|| Default::default()) .get_or_insert_with(|| Default::default())
.insert(type_id, func.clone()); .insert(type_id, func.clone());

View File

@ -8,6 +8,7 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::builtin::get_builtin_binary_op_fn;
use crate::func::hashing::get_hasher; use crate::func::hashing::get_hasher;
use crate::module::ModuleFlags;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{ use crate::{
@ -168,7 +169,7 @@ fn has_native_fn_override(
if engine if engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| !m.standard) .filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
.any(|m| m.contains_fn(hash)) .any(|m| m.contains_fn(hash))
{ {
return true; return true;

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT}; use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -192,7 +193,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Basic arithmetic package. /// Basic arithmetic package.
pub ArithmeticPackage(lib) { pub ArithmeticPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);
reg_functions!(lib += signed_basic; INT); reg_functions!(lib += signed_basic; INT);

View File

@ -2,6 +2,7 @@
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::eval::{calc_index, calc_offset_len}; use crate::eval::{calc_index, calc_offset_len};
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
@ -14,7 +15,7 @@ use std::{any::TypeId, cmp::Ordering, mem};
def_package! { def_package! {
/// Package of basic array utilities. /// Package of basic array utilities.
pub BasicArrayPackage(lib) { pub BasicArrayPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "array", array_functions); combine_with_exported_module!(lib, "array", array_functions);

View File

@ -1,4 +1,5 @@
use crate::eval::calc_index; use crate::eval::calc_index;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS, def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS,
@ -10,7 +11,7 @@ use std::prelude::v1::*;
def_package! { def_package! {
/// Package of basic bit-field utilities. /// Package of basic bit-field utilities.
pub BitFieldPackage(lib) { pub BitFieldPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "bit_field", bit_field_functions); combine_with_exported_module!(lib, "bit_field", bit_field_functions);
} }

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_index"))] #![cfg(not(feature = "no_index"))]
use crate::eval::{calc_index, calc_offset_len}; use crate::eval::{calc_index, calc_offset_len};
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
@ -16,7 +17,7 @@ use crate::{FLOAT, FLOAT_BYTES};
def_package! { def_package! {
/// Package of basic BLOB utilities. /// Package of basic BLOB utilities.
pub BasicBlobPackage(lib) { pub BasicBlobPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "blob", blob_functions); combine_with_exported_module!(lib, "blob", blob_functions);
combine_with_exported_module!(lib, "parse_int", parse_int_functions); combine_with_exported_module!(lib, "parse_int", parse_int_functions);

View File

@ -1,6 +1,7 @@
#![cfg(feature = "debugging")] #![cfg(feature = "debugging")]
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -17,7 +18,7 @@ use crate::Map;
def_package! { def_package! {
/// Package of basic debugging utilities. /// Package of basic debugging utilities.
pub DebuggingPackage(lib) { pub DebuggingPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "debugging", debugging_functions); combine_with_exported_module!(lib, "debugging", debugging_functions);
} }

View File

@ -1,3 +1,4 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext}; use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -6,7 +7,7 @@ use std::prelude::v1::*;
def_package! { def_package! {
/// Package of basic function pointer utilities. /// Package of basic function pointer utilities.
pub BasicFnPackage(lib) { pub BasicFnPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
} }

View File

@ -1,4 +1,5 @@
use crate::eval::calc_index; use crate::eval::calc_index;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS, MAX_USIZE_INT, def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS, MAX_USIZE_INT,
@ -329,7 +330,7 @@ macro_rules! reg_range {
def_package! { def_package! {
/// Package of basic range iterators /// Package of basic range iterators
pub BasicIteratorPackage(lib) { pub BasicIteratorPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
reg_range!(lib | "range" => INT); reg_range!(lib | "range" => INT);

View File

@ -1,4 +1,5 @@
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::types::dynamic::Tag; use crate::types::dynamic::Tag;
use crate::{Dynamic, RhaiResultOf, ERR, INT}; use crate::{Dynamic, RhaiResultOf, ERR, INT};
@ -8,7 +9,7 @@ use std::prelude::v1::*;
def_package! { def_package! {
/// Package of core language features. /// Package of core language features.
pub LanguageCorePackage(lib) { pub LanguageCorePackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "core", core_functions); combine_with_exported_module!(lib, "core", core_functions);
@ -277,7 +278,8 @@ fn collect_fn_metadata(
&mut ns, &mut ns,
"{namespace}{}{name}", "{namespace}{}{name}",
crate::tokenizer::Token::DoubleColon.literal_syntax() crate::tokenizer::Token::DoubleColon.literal_syntax()
); )
.unwrap();
scan_module(dict, list, &ns, &**m, filter); scan_module(dict, list, &ns, &**m, filter);
} }
} }

View File

@ -1,4 +1,5 @@
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -38,7 +39,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Package of basic logic operators. /// Package of basic logic operators.
pub LogicPackage(lib) { pub LogicPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_object"))] #![cfg(not(feature = "no_object"))]
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT}; use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -12,7 +13,7 @@ use crate::Array;
def_package! { def_package! {
/// Package of basic object map utilities. /// Package of basic object map utilities.
pub BasicMapPackage(lib) { pub BasicMapPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "map", map_functions); combine_with_exported_module!(lib, "map", map_functions);
} }

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Position, RhaiResultOf, ERR, INT}; use crate::{def_package, Position, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -54,7 +55,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Basic mathematical package. /// Basic mathematical package.
pub BasicMathPackage(lib) { pub BasicMathPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
// Integer functions // Integer functions
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);

View File

@ -3,6 +3,7 @@ use std::prelude::v1::*;
use super::*; use super::*;
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
def_package! { def_package! {
/// Core package containing basic facilities. /// Core package containing basic facilities.
@ -23,6 +24,6 @@ def_package! {
BasicFnPackage, BasicFnPackage,
#[cfg(feature = "debugging")] DebuggingPackage #[cfg(feature = "debugging")] DebuggingPackage
{ {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
} }
} }

View File

@ -3,6 +3,7 @@ use std::prelude::v1::*;
use super::*; use super::*;
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
def_package! { def_package! {
/// Standard package containing all built-in features. /// Standard package containing all built-in features.
@ -29,6 +30,6 @@ def_package! {
#[cfg(not(feature = "no_time"))] BasicTimePackage, #[cfg(not(feature = "no_time"))] BasicTimePackage,
MoreStringPackage MoreStringPackage
{ {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
} }
} }

View File

@ -1,3 +1,4 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, SmartString, INT}; use crate::{def_package, FnPtr, SmartString, INT};
use std::any::TypeId; use std::any::TypeId;
@ -17,7 +18,7 @@ pub const FUNC_TO_DEBUG: &str = "to_debug";
def_package! { def_package! {
/// Package of basic string utilities (e.g. printing) /// Package of basic string utilities (e.g. printing)
pub BasicStringPackage(lib) { pub BasicStringPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "print_debug", print_debug_functions); combine_with_exported_module!(lib, "print_debug", print_debug_functions);
combine_with_exported_module!(lib, "number_formatting", number_formatting); combine_with_exported_module!(lib, "number_formatting", number_formatting);

View File

@ -1,3 +1,4 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT, def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT,
@ -12,7 +13,7 @@ use super::string_basic::{print_with_func, FUNC_TO_STRING};
def_package! { def_package! {
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage] /// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
pub MoreStringPackage(lib) { pub MoreStringPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "string", string_functions); combine_with_exported_module!(lib, "string", string_functions);
} }

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_time"))] #![cfg(not(feature = "no_time"))]
use super::arithmetic::make_err as make_arithmetic_err; use super::arithmetic::make_err as make_arithmetic_err;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT}; use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
@ -16,7 +17,7 @@ use instant::{Duration, Instant};
def_package! { def_package! {
/// Package of basic timing utilities. /// Package of basic timing utilities.
pub BasicTimePackage(lib) { pub BasicTimePackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
// Register date/time functions // Register date/time functions
combine_with_exported_module!(lib, "time", time_functions); combine_with_exported_module!(lib, "time", time_functions);

View File

@ -21,6 +21,7 @@ use crate::{
ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position, Scope, ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position, Scope,
Shared, SmartString, StaticVec, AST, INT, PERR, Shared, SmartString, StaticVec, AST, INT, PERR,
}; };
use bitflags::bitflags;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
@ -280,24 +281,32 @@ impl<'e> ParseState<'e> {
} }
} }
/// A type that encapsulates all the settings for a particular parsing function. bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] /// Bit-flags containing all status for [`ParseSettings`].
pub(crate) struct ParseSettings { pub struct ParseSettingFlags: u8 {
/// Is the construct being parsed located at global level? /// Is the construct being parsed located at global level?
pub at_global_level: bool, const GLOBAL_LEVEL = 0b0000_0001;
/// Is the construct being parsed located inside a function definition? /// Is the construct being parsed located inside a function definition?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub in_fn_scope: bool, const FN_SCOPE = 0b0000_0010;
/// Is the construct being parsed located inside a closure definition? /// Is the construct being parsed located inside a closure definition?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
pub in_closure: bool, const CLOSURE_SCOPE = 0b0000_0100;
/// Is the construct being parsed located inside a breakable loop? /// Is the construct being parsed located inside a breakable loop?
pub is_breakable: bool, const BREAKABLE = 0b0000_1000;
/// Allow statements in blocks? /// Disallow statements in blocks?
pub allow_statements: bool, const DISALLOW_STATEMENTS_IN_BLOCKS = 0b0001_0000;
/// Allow unquoted map properties? /// Disallow unquoted map properties?
pub allow_unquoted_map_properties: bool, const DISALLOW_UNQUOTED_MAP_PROPERTIES = 0b0010_0000;
}
}
/// A type that encapsulates all the settings for a particular parsing function.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ParseSettings {
/// Flags.
pub flags: ParseSettingFlags,
/// Language options in effect (overrides Engine options). /// Language options in effect (overrides Engine options).
pub options: LangOptions, pub options: LangOptions,
/// Current expression nesting level. /// Current expression nesting level.
@ -307,6 +316,18 @@ pub(crate) struct ParseSettings {
} }
impl ParseSettings { impl ParseSettings {
/// Is a particular flag on?
#[inline(always)]
#[must_use]
pub const fn has_flag(&self, flag: ParseSettingFlags) -> bool {
self.flags.contains(flag)
}
/// Is a particular language option on?
#[inline(always)]
#[must_use]
pub const fn has_option(&self, option: LangOptions) -> bool {
self.options.contains(option)
}
/// Create a new `ParseSettings` with one higher expression level. /// Create a new `ParseSettings` with one higher expression level.
#[inline] #[inline]
#[must_use] #[must_use]
@ -585,7 +606,7 @@ impl Engine {
#[cfg(any(feature = "no_function", feature = "no_module"))] #[cfg(any(feature = "no_function", feature = "no_module"))]
let is_global = false; let is_global = false;
if settings.options.contains(LangOptions::STRICT_VAR) if settings.has_option(LangOptions::STRICT_VAR)
&& index.is_none() && index.is_none()
&& !is_global && !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root) && !state.global_imports.iter().any(|m| m.as_str() == root)
@ -653,7 +674,7 @@ impl Engine {
#[cfg(any(feature = "no_function", feature = "no_module"))] #[cfg(any(feature = "no_function", feature = "no_module"))]
let is_global = false; let is_global = false;
if settings.options.contains(LangOptions::STRICT_VAR) if settings.has_option(LangOptions::STRICT_VAR)
&& index.is_none() && index.is_none()
&& !is_global && !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root) && !state.global_imports.iter().any(|m| m.as_str() == root)
@ -995,7 +1016,9 @@ impl Engine {
} }
let (name, pos) = match input.next().expect(NEVER_ENDS) { let (name, pos) = match input.next().expect(NEVER_ENDS) {
(Token::Identifier(..), pos) if !settings.allow_unquoted_map_properties => { (Token::Identifier(..), pos)
if settings.has_flag(ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES) =>
{
return Err(PERR::PropertyExpected.into_err(pos)) return Err(PERR::PropertyExpected.into_err(pos))
} }
(Token::Identifier(s) | Token::StringConstant(s), pos) => { (Token::Identifier(s) | Token::StringConstant(s), pos) => {
@ -1196,7 +1219,8 @@ impl Engine {
} }
}; };
let (action_expr, need_comma) = if settings.allow_statements { let (action_expr, need_comma) =
if !settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
let stmt = self.parse_stmt(input, state, lib, settings.level_up())?; let stmt = self.parse_stmt(input, state, lib, settings.level_up())?;
let need_comma = !stmt.is_self_terminated(); let need_comma = !stmt.is_self_terminated();
@ -1358,7 +1382,7 @@ impl Engine {
} }
// { - block statement as expression // { - block statement as expression
Token::LeftBrace if settings.options.contains(LangOptions::STMT_EXPR) => { Token::LeftBrace if settings.has_option(LangOptions::STMT_EXPR) => {
match self.parse_block(input, state, lib, settings.level_up())? { match self.parse_block(input, state, lib, settings.level_up())? {
block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())), block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())),
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
@ -1369,38 +1393,34 @@ impl Engine {
Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up())?, Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up())?,
// If statement is allowed to act as expressions // If statement is allowed to act as expressions
Token::If if settings.options.contains(LangOptions::IF_EXPR) => Expr::Stmt(Box::new( Token::If if settings.has_option(LangOptions::IF_EXPR) => Expr::Stmt(Box::new(
self.parse_if(input, state, lib, settings.level_up())? self.parse_if(input, state, lib, settings.level_up())?
.into(), .into(),
)), )),
// Loops are allowed to act as expressions // Loops are allowed to act as expressions
Token::While | Token::Loop if settings.options.contains(LangOptions::LOOP_EXPR) => { Token::While | Token::Loop if settings.has_option(LangOptions::LOOP_EXPR) => {
Expr::Stmt(Box::new( Expr::Stmt(Box::new(
self.parse_while_loop(input, state, lib, settings.level_up())? self.parse_while_loop(input, state, lib, settings.level_up())?
.into(), .into(),
)) ))
} }
Token::Do if settings.options.contains(LangOptions::LOOP_EXPR) => Expr::Stmt(Box::new( Token::Do if settings.has_option(LangOptions::LOOP_EXPR) => Expr::Stmt(Box::new(
self.parse_do(input, state, lib, settings.level_up())? self.parse_do(input, state, lib, settings.level_up())?
.into(), .into(),
)), )),
Token::For if settings.options.contains(LangOptions::LOOP_EXPR) => { Token::For if settings.has_option(LangOptions::LOOP_EXPR) => Expr::Stmt(Box::new(
Expr::Stmt(Box::new(
self.parse_for(input, state, lib, settings.level_up())? self.parse_for(input, state, lib, settings.level_up())?
.into(), .into(),
)) )),
}
// Switch statement is allowed to act as expressions // Switch statement is allowed to act as expressions
Token::Switch if settings.options.contains(LangOptions::SWITCH_EXPR) => { Token::Switch if settings.has_option(LangOptions::SWITCH_EXPR) => Expr::Stmt(Box::new(
Expr::Stmt(Box::new(
self.parse_switch(input, state, lib, settings.level_up())? self.parse_switch(input, state, lib, settings.level_up())?
.into(), .into(),
)) )),
}
// | ... // | ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or if settings.options.contains(LangOptions::ANON_FN) => { Token::Pipe | Token::Or if settings.has_option(LangOptions::ANON_FN) => {
// Build new parse state // Build new parse state
let interned_strings = std::mem::take(&mut state.interned_strings); let interned_strings = std::mem::take(&mut state.interned_strings);
@ -1429,24 +1449,24 @@ impl Engine {
new_state.max_expr_depth = self.max_function_expr_depth(); new_state.max_expr_depth = self.max_function_expr_depth();
} }
let mut options = self.options; #[cfg(not(feature = "no_closure"))]
options.set( let options = self.options & !LangOptions::STRICT_VAR; // A capturing closure can access variables not defined locally
LangOptions::STRICT_VAR, #[cfg(feature = "no_closure")]
if cfg!(feature = "no_closure") { let options = self.options | (settings.options & LangOptions::STRICT_VAR);
settings.options.contains(LangOptions::STRICT_VAR)
} else { let mut flags = (settings.flags
// A capturing closure can access variables not defined locally & !ParseSettingFlags::GLOBAL_LEVEL
false & ParseSettingFlags::BREAKABLE)
}, | ParseSettingFlags::FN_SCOPE;
);
#[cfg(not(feature = "no_closure"))]
{
flags |= ParseSettingFlags::CLOSURE_SCOPE;
}
let new_settings = ParseSettings { let new_settings = ParseSettings {
at_global_level: false,
in_fn_scope: true,
#[cfg(not(feature = "no_closure"))]
in_closure: true,
is_breakable: false,
level: 0, level: 0,
flags,
options, options,
..settings ..settings
}; };
@ -1465,8 +1485,8 @@ impl Engine {
if !is_func if !is_func
&& index.is_none() && index.is_none()
&& !settings.in_closure && !settings.has_flag(ParseSettingFlags::CLOSURE_SCOPE)
&& settings.options.contains(LangOptions::STRICT_VAR) && settings.has_option(LangOptions::STRICT_VAR)
&& !state.scope.contains(name) && !state.scope.contains(name)
{ {
// If the parent scope is not inside another capturing closure // If the parent scope is not inside another capturing closure
@ -1612,7 +1632,7 @@ impl Engine {
if !is_property if !is_property
&& !is_func && !is_func
&& index.is_none() && index.is_none()
&& settings.options.contains(LangOptions::STRICT_VAR) && settings.has_option(LangOptions::STRICT_VAR)
&& !state.scope.contains(&s) && !state.scope.contains(&s)
{ {
return Err( return Err(
@ -1656,11 +1676,13 @@ impl Engine {
} }
// Access to `this` as a variable is OK within a function scope // Access to `this` as a variable is OK within a function scope
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
_ if &*s == KEYWORD_THIS && settings.in_fn_scope => Expr::Variable( _ if &*s == KEYWORD_THIS && settings.has_flag(ParseSettingFlags::FN_SCOPE) => {
Expr::Variable(
(None, ns, 0, state.get_interned_string(*s)).into(), (None, ns, 0, state.get_interned_string(*s)).into(),
None, None,
settings.pos, settings.pos,
), )
}
// Cannot access to `this` as a variable not in a function scope // Cannot access to `this` as a variable not in a function scope
_ if &*s == KEYWORD_THIS => { _ if &*s == KEYWORD_THIS => {
let msg = format!("'{s}' can only be used in functions"); let msg = format!("'{s}' can only be used in functions");
@ -1852,7 +1874,7 @@ impl Engine {
#[cfg(any(feature = "no_function", feature = "no_module"))] #[cfg(any(feature = "no_function", feature = "no_module"))]
let is_global = false; let is_global = false;
if settings.options.contains(LangOptions::STRICT_VAR) if settings.has_option(LangOptions::STRICT_VAR)
&& index.is_none() && index.is_none()
&& !is_global && !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root) && !state.global_imports.iter().any(|m| m.as_str() == root)
@ -2399,12 +2421,12 @@ impl Engine {
Token::Or => { Token::Or => {
let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?;
let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?;
Expr::Or(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) Expr::Or(BinaryExpr { lhs, rhs }.into(), pos)
} }
Token::And => { Token::And => {
let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?;
let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?;
Expr::And(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) Expr::And(BinaryExpr { lhs, rhs }.into(), pos)
} }
Token::DoubleQuestion => { Token::DoubleQuestion => {
let rhs = op_base.args.pop().unwrap(); let rhs = op_base.args.pop().unwrap();
@ -2590,9 +2612,7 @@ impl Engine {
}, },
s => match input.next().expect(NEVER_ENDS) { s => match input.next().expect(NEVER_ENDS) {
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
(Token::Identifier(t), ..) (Token::Identifier(t) | Token::Reserved(t) | Token::Custom(t), ..)
| (Token::Reserved(t), ..)
| (Token::Custom(t), ..)
if *t == s => if *t == s =>
{ {
segments.push(required_token.clone()); segments.push(required_token.clone());
@ -2726,7 +2746,7 @@ impl Engine {
token => unreachable!("Token::While or Token::Loop expected but gets {:?}", token), token => unreachable!("Token::While or Token::Loop expected but gets {:?}", token),
}; };
settings.pos = token_pos; settings.pos = token_pos;
settings.is_breakable = true; settings.flags |= ParseSettingFlags::BREAKABLE;
let body = self.parse_block(input, state, lib, settings.level_up())?; let body = self.parse_block(input, state, lib, settings.level_up())?;
@ -2749,7 +2769,7 @@ impl Engine {
settings.pos = eat_token(input, Token::Do); settings.pos = eat_token(input, Token::Do);
// do { body } [while|until] guard // do { body } [while|until] guard
settings.is_breakable = true; settings.flags |= ParseSettingFlags::BREAKABLE;
let body = self.parse_block(input, state, lib, settings.level_up())?; let body = self.parse_block(input, state, lib, settings.level_up())?;
let negated = match input.next().expect(NEVER_ENDS) { let negated = match input.next().expect(NEVER_ENDS) {
@ -2763,7 +2783,7 @@ impl Engine {
} }
}; };
settings.is_breakable = false; settings.flags &= !ParseSettingFlags::BREAKABLE;
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = self let guard = self
@ -2859,7 +2879,7 @@ impl Engine {
pos: name_pos, pos: name_pos,
}; };
settings.is_breakable = true; settings.flags |= ParseSettingFlags::BREAKABLE;
let body = self.parse_block(input, state, lib, settings.level_up())?; let body = self.parse_block(input, state, lib, settings.level_up())?;
state.stack.rewind(prev_stack_len); state.stack.rewind(prev_stack_len);
@ -3089,7 +3109,7 @@ impl Engine {
let mut statements = StaticVec::new_const(); let mut statements = StaticVec::new_const();
if !settings.allow_statements { if settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
let stmt = self.parse_expr_stmt(input, state, lib, settings.level_up())?; let stmt = self.parse_expr_stmt(input, state, lib, settings.level_up())?;
statements.push(stmt); statements.push(stmt);
@ -3126,7 +3146,7 @@ impl Engine {
} }
// Parse statements inside the block // Parse statements inside the block
settings.at_global_level = false; settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
let stmt = self.parse_stmt(input, state, lib, settings.level_up())?; let stmt = self.parse_stmt(input, state, lib, settings.level_up())?;
@ -3222,7 +3242,7 @@ impl Engine {
unreachable!("doc-comment expected but gets {:?}", comment); unreachable!("doc-comment expected but gets {:?}", comment);
} }
if !settings.at_global_level { if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) {
return Err(PERR::WrongDocComment.into_err(comments_pos)); return Err(PERR::WrongDocComment.into_err(comments_pos));
} }
@ -3264,7 +3284,7 @@ impl Engine {
// fn ... // fn ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Token::Fn if !settings.at_global_level => { Token::Fn if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) => {
Err(PERR::WrongFnDefinition.into_err(token_pos)) Err(PERR::WrongFnDefinition.into_err(token_pos))
} }
@ -3307,20 +3327,14 @@ impl Engine {
new_state.max_expr_depth = self.max_function_expr_depth(); new_state.max_expr_depth = self.max_function_expr_depth();
} }
let mut options = self.options; let options = self.options | (settings.options & LangOptions::STRICT_VAR);
options.set(
LangOptions::STRICT_VAR, let flags = ParseSettingFlags::FN_SCOPE
settings.options.contains(LangOptions::STRICT_VAR), | (settings.flags
); & ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES);
let new_settings = ParseSettings { let new_settings = ParseSettings {
at_global_level: false, flags,
in_fn_scope: true,
#[cfg(not(feature = "no_closure"))]
in_closure: false,
is_breakable: false,
allow_statements: true,
allow_unquoted_map_properties: settings.allow_unquoted_map_properties,
level: 0, level: 0,
options, options,
pos, pos,
@ -3377,11 +3391,15 @@ impl Engine {
self.parse_for(input, state, lib, settings.level_up()) self.parse_for(input, state, lib, settings.level_up())
} }
Token::Continue if self.allow_looping() && settings.is_breakable => { Token::Continue
if self.allow_looping() && settings.has_flag(ParseSettingFlags::BREAKABLE) =>
{
let pos = eat_token(input, Token::Continue); let pos = eat_token(input, Token::Continue);
Ok(Stmt::BreakLoop(None, ASTFlags::NONE, pos)) Ok(Stmt::BreakLoop(None, ASTFlags::NONE, pos))
} }
Token::Break if self.allow_looping() && settings.is_breakable => { Token::Break
if self.allow_looping() && settings.has_flag(ParseSettingFlags::BREAKABLE) =>
{
let pos = eat_token(input, Token::Break); let pos = eat_token(input, Token::Break);
let expr = match input.peek().expect(NEVER_ENDS) { let expr = match input.peek().expect(NEVER_ENDS) {
@ -3424,7 +3442,9 @@ impl Engine {
// `return`/`throw` at <EOF> // `return`/`throw` at <EOF>
(Token::EOF, ..) => Ok(Stmt::Return(None, return_type, token_pos)), (Token::EOF, ..) => Ok(Stmt::Return(None, return_type, token_pos)),
// `return`/`throw` at end of block // `return`/`throw` at end of block
(Token::RightBrace, ..) if !settings.at_global_level => { (Token::RightBrace, ..)
if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) =>
{
Ok(Stmt::Return(None, return_type, token_pos)) Ok(Stmt::Return(None, return_type, token_pos))
} }
// `return;` or `throw;` // `return;` or `throw;`
@ -3446,7 +3466,7 @@ impl Engine {
Token::Import => self.parse_import(input, state, lib, settings.level_up()), Token::Import => self.parse_import(input, state, lib, settings.level_up()),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Token::Export if !settings.at_global_level => { Token::Export if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) => {
Err(PERR::WrongExport.into_err(token_pos)) Err(PERR::WrongExport.into_err(token_pos))
} }
@ -3607,7 +3627,7 @@ impl Engine {
// Parse function body // Parse function body
let body = match input.peek().expect(NEVER_ENDS) { let body = match input.peek().expect(NEVER_ENDS) {
(Token::LeftBrace, ..) => { (Token::LeftBrace, ..) => {
settings.is_breakable = false; settings.flags &= !ParseSettingFlags::BREAKABLE;
self.parse_block(input, state, lib, settings.level_up())? self.parse_block(input, state, lib, settings.level_up())?
} }
(.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)), (.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
@ -3759,7 +3779,7 @@ impl Engine {
} }
// Parse function body // Parse function body
settings.is_breakable = false; settings.flags &= !ParseSettingFlags::BREAKABLE;
let body = self.parse_stmt(input, state, lib, settings.level_up())?; let body = self.parse_stmt(input, state, lib, settings.level_up())?;
// External variables may need to be processed in a consistent order, // External variables may need to be processed in a consistent order,
@ -3822,22 +3842,16 @@ impl Engine {
) -> ParseResult<AST> { ) -> ParseResult<AST> {
let mut functions = StraightHashMap::default(); let mut functions = StraightHashMap::default();
let mut options = self.options; let mut options = self.options & !LangOptions::STMT_EXPR & !LangOptions::LOOP_EXPR;
options.remove(LangOptions::STMT_EXPR | LangOptions::LOOP_EXPR);
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
options.remove(LangOptions::ANON_FN); {
options &= !LangOptions::ANON_FN;
}
let mut settings = ParseSettings { let mut settings = ParseSettings {
at_global_level: true,
#[cfg(not(feature = "no_function"))]
in_fn_scope: false,
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_closure"))]
in_closure: false,
is_breakable: false,
allow_statements: false,
allow_unquoted_map_properties: true,
level: 0, level: 0,
flags: ParseSettingFlags::GLOBAL_LEVEL
| ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS,
options, options,
pos: Position::START, pos: Position::START,
}; };
@ -3883,18 +3897,11 @@ impl Engine {
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> { ) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
let mut statements = StmtBlockContainer::new_const(); let mut statements = StmtBlockContainer::new_const();
let mut functions = StraightHashMap::default(); let mut functions = StraightHashMap::default();
let mut settings = ParseSettings { let mut settings = ParseSettings {
at_global_level: true,
#[cfg(not(feature = "no_function"))]
in_fn_scope: false,
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_closure"))]
in_closure: false,
is_breakable: false,
allow_statements: true,
allow_unquoted_map_properties: true,
options: self.options,
level: 0, level: 0,
flags: ParseSettingFlags::GLOBAL_LEVEL,
options: self.options,
pos: Position::START, pos: Position::START,
}; };
process_settings(&mut settings); process_settings(&mut settings);

View File

@ -2,7 +2,7 @@
#![cfg(feature = "metadata")] #![cfg(feature = "metadata")]
use crate::api::type_names::format_type; use crate::api::type_names::format_type;
use crate::module::{calc_native_fn_hash, FuncInfo}; use crate::module::{calc_native_fn_hash, FuncInfo, ModuleFlags};
use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST}; use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST};
use serde::Serialize; use serde::Serialize;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -174,10 +174,16 @@ pub fn gen_metadata_to_json(
global.modules.insert(name, m.as_ref().into()); global.modules.insert(name, m.as_ref().into());
} }
let exclude_flags = if !include_standard_packages {
ModuleFlags::STANDARD_LIB
} else {
ModuleFlags::empty()
};
engine engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| include_standard_packages || !m.standard) .filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.iter_fn()) .flat_map(|m| m.iter_fn())
.for_each(|f| { .for_each(|f| {
#[allow(unused_mut)] #[allow(unused_mut)]

View File

@ -966,18 +966,16 @@ impl Token {
use Token::*; use Token::*;
match self { match self {
LexError(..) |
SemiColon | // ; - is unary SemiColon | // ; - is unary
Colon | // #{ foo: - is unary Colon | // #{ foo: - is unary
Comma | // ( ... , -expr ) - is unary Comma | // ( ... , -expr ) - is unary
//Period | //Period |
//Elvis | //Elvis |
//DoubleQuestion | DoubleQuestion | // ?? - is unary
//QuestionBracket |
ExclusiveRange | // .. - is unary ExclusiveRange | // .. - is unary
InclusiveRange | // ..= - is unary InclusiveRange | // ..= - is unary
LeftBrace | // { -expr } - is unary LeftBrace | // { -expr } - is unary
// RightBrace | { expr } - expr not unary & is closing // RightBrace | // { expr } - expr not unary & is closing
LeftParen | // ( -expr ) - is unary LeftParen | // ( -expr ) - is unary
// RightParen | // ( expr ) - expr not unary & is closing // RightParen | // ( expr ) - expr not unary & is closing
LeftBracket | // [ -expr ] - is unary LeftBracket | // [ -expr ] - is unary
@ -1024,6 +1022,11 @@ impl Token {
Return | Return |
Throw => true, Throw => true,
#[cfg(not(feature = "no_index"))]
QuestionBracket => true, // ?[ - is unary
LexError(..) => true,
_ => false, _ => false,
} }
} }

View File

@ -14,7 +14,7 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
engine.register_raw_fn( engine.register_raw_fn(
"call_with_arg", "call_with_arg",
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()], [TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|context, args| { |context, args| {
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>(); let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
fn_ptr.call_raw(&context, None, [std::mem::take(args[1])]) fn_ptr.call_raw(&context, None, [std::mem::take(args[1])])
@ -165,7 +165,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
engine.register_raw_fn( engine.register_raw_fn(
"custom_call", "custom_call",
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()], [TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|context, args| { |context, args| {
let func = take(args[1]).cast::<FnPtr>(); let func = take(args[1]).cast::<FnPtr>();
@ -348,7 +348,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
let p1 = Rc::new(RefCell::new(41)); let p1 = Rc::new(RefCell::new(41));
let p2 = Rc::new(RefCell::new(1)); let p2 = Rc::new(RefCell::new(1));
f(p1.clone(), p2.clone())?; f(p1.clone(), p2)?;
assert_eq!(*p1.borrow(), 42); assert_eq!(*p1.borrow(), 42);

View File

@ -274,8 +274,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
Ok(None) Ok(None)
} }
s => Err(LexError::ImproperSymbol(s.to_string(), String::new()) s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
.into_err(Position::NONE) .into_err(Position::NONE)),
.into()),
}, },
_ => unreachable!(), _ => unreachable!(),
}, },