From 99118fe2c377314563eb41a3b3bf3c3cb874397d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Mar 2022 21:58:43 +0800 Subject: [PATCH 01/13] Minor refactors. --- CHANGELOG.md | 5 +++++ codegen/src/lib.rs | 8 +++---- codegen/src/module.rs | 30 ++++++++++++--------------- src/api/compile.rs | 45 ++++++++++++++++++++-------------------- src/api/events.rs | 4 ++-- src/api/mod.rs | 2 +- src/ast/ast.rs | 7 ++++--- src/ast/stmt.rs | 20 ++++++++++++++++++ src/engine.rs | 33 ++++++++++++++--------------- src/eval/eval_context.rs | 7 ++++--- src/eval/eval_state.rs | 2 +- src/eval/global_state.rs | 7 ++++--- src/eval/stmt.rs | 7 ++++--- src/func/call.rs | 35 +++++++++++++------------------ 14 files changed, 114 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9eac99..2fb11d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ Bug fixes * Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error. * Full optimization is now skipped for method calls. +New features +------------ + +* [Type aliases](https://doc.rust-lang.org/reference/items/type-aliases.html) in plugin modules are now used as friendly names for custom types. This makes plugin modules more self-contained when they are used to define a custom type's API. + Enhancements ------------ diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3be27449..915b6741 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -269,7 +269,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream { match crate::register::parse_register_macro(args) { Ok((module_expr, _export_name, module_path)) => proc_macro::TokenStream::from(quote! { - #module_path::rhai_generate_into_module(#module_expr, true); + #module_path::rhai_generate_into_module(#module_expr, true) }), Err(e) => e.to_compile_error().into(), } @@ -303,7 +303,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS Ok((engine_expr, export_name, rust_mod_path)) => { let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); proc_macro::TokenStream::from(quote! { - #engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn); + #engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn) }) } Err(e) => e.to_compile_error().into(), @@ -352,7 +352,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, #param_names, &#gen_mod_path::Token::param_types(), - #gen_mod_path::Token().into()); + #gen_mod_path::Token().into()) }) } Err(e) => e.to_compile_error().into(), @@ -401,7 +401,7 @@ pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::Toke #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, #param_names, &#gen_mod_path::Token::param_types(), - #gen_mod_path::Token().into()); + #gen_mod_path::Token().into()) }) } Err(e) => e.to_compile_error().into(), diff --git a/codegen/src/module.rs b/codegen/src/module.rs index bafd1b33..52b9716c 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -140,20 +140,18 @@ impl Parse for Module { for item in content.iter() { match item { syn::Item::Const(syn::ItemConst { - vis, + vis: syn::Visibility::Public(..), ref expr, ident, attrs, ty, .. - }) if matches!(vis, syn::Visibility::Public(..)) => { - consts.push(ExportedConst { - name: ident.to_string(), - typ: ty.clone(), - expr: expr.as_ref().clone(), - cfg_attrs: crate::attrs::collect_cfg_attr(&attrs), - }) - } + }) => consts.push(ExportedConst { + name: ident.to_string(), + typ: ty.clone(), + expr: expr.as_ref().clone(), + cfg_attrs: crate::attrs::collect_cfg_attr(&attrs), + }), _ => {} } } @@ -161,18 +159,16 @@ impl Parse for Module { for item in content.iter() { match item { syn::Item::Type(syn::ItemType { - vis, + vis: syn::Visibility::Public(..), ident, attrs, ty, .. - }) if matches!(vis, syn::Visibility::Public(..)) => { - custom_types.push(ExportedType { - name: ident.to_string(), - typ: ty.clone(), - cfg_attrs: crate::attrs::collect_cfg_attr(&attrs), - }) - } + }) => custom_types.push(ExportedType { + name: ident.to_string(), + typ: ty.clone(), + cfg_attrs: crate::attrs::collect_cfg_attr(&attrs), + }), _ => {} } } diff --git a/src/api/compile.rs b/src/api/compile.rs index 39a6ed79..4a3c0490 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -115,33 +115,34 @@ impl Engine { let mut ast = self.compile_scripts_with_scope(scope, &[script])?; - if let Some(ref module_resolver) = self.module_resolver { - let mut resolver = StaticModuleResolver::new(); - let mut imports = BTreeSet::new(); + let mut resolver = StaticModuleResolver::new(); + let mut imports = BTreeSet::new(); - collect_imports(&ast, &resolver, &mut imports); + collect_imports(&ast, &resolver, &mut imports); - if !imports.is_empty() { - while let Some(path) = imports.iter().next() { - let path = path.clone(); + if !imports.is_empty() { + while let Some(path) = imports.iter().next() { + let path = path.clone(); - match module_resolver.resolve_ast(self, None, &path, crate::Position::NONE) { - Some(Ok(module_ast)) => { - collect_imports(&module_ast, &resolver, &mut imports) - } - Some(err) => return err, - None => (), - } - - let module = - module_resolver.resolve(self, None, &path, crate::Position::NONE)?; - let module = shared_take_or_clone(module); - - imports.remove(&path); - resolver.insert(path, module); + match self + .module_resolver + .resolve_ast(self, None, &path, crate::Position::NONE) + { + Some(Ok(module_ast)) => collect_imports(&module_ast, &resolver, &mut imports), + Some(err) => return err, + None => (), } - ast.set_resolver(resolver); + + let module = + self.module_resolver + .resolve(self, None, &path, crate::Position::NONE)?; + + let module = shared_take_or_clone(module); + + imports.remove(&path); + resolver.insert(path, module); } + ast.set_resolver(resolver); } Ok(ast) diff --git a/src/api/events.rs b/src/api/events.rs index 0f0061d4..782d211c 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -282,7 +282,7 @@ impl Engine { /// ``` #[inline(always)] pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self { - self.print = Some(Box::new(callback)); + self.print = Box::new(callback); self } /// Override default action of `debug` (print to stdout using [`println!`]) @@ -332,7 +332,7 @@ impl Engine { &mut self, callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static, ) -> &mut Self { - self.debug = Some(Box::new(callback)); + self.debug = Box::new(callback); self } /// _(debugging)_ Register a callback for debugging. diff --git a/src/api/mod.rs b/src/api/mod.rs index 2b94013a..25431175 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -71,7 +71,7 @@ impl Engine { &mut self, resolver: impl crate::ModuleResolver + 'static, ) -> &mut Self { - self.module_resolver = Some(Box::new(resolver)); + self.module_resolver = Box::new(resolver); self } diff --git a/src/ast/ast.rs b/src/ast/ast.rs index 7c0115e5..7e6f56c7 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -150,9 +150,10 @@ impl AST { #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - match self.source.as_str() { - "" => None, - s => Some(s), + if self.source.is_empty() { + None + } else { + Some(self.source.as_str()) } } /// Get a reference to the source. diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 815c0f8b..33fbd75a 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -89,6 +89,26 @@ pub struct ConditionalStmtBlock { pub statements: StmtBlock, } +impl> From for ConditionalStmtBlock { + #[inline(always)] + fn from(value: B) -> Self { + Self { + condition: None, + statements: value.into(), + } + } +} + +impl> From<(Expr, B)> for ConditionalStmtBlock { + #[inline(always)] + fn from(value: (Expr, B)) -> Self { + Self { + condition: Some(value.0), + statements: value.1.into(), + } + } +} + impl> From<(Option, B)> for ConditionalStmtBlock { #[inline(always)] fn from(value: (Option, B)) -> Self { diff --git a/src/engine.rs b/src/engine.rs index f180f624..34218fda 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,7 @@ //! Main module defining the script evaluation [`Engine`]. use crate::api::custom_syntax::CustomSyntax; +use crate::api::options::LanguageOptions; use crate::func::native::{ OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback, }; @@ -102,7 +103,7 @@ pub struct Engine { /// A module resolution service. #[cfg(not(feature = "no_module"))] - pub(crate) module_resolver: Option>, + pub(crate) module_resolver: Box, /// A map mapping type names to pretty-print names. pub(crate) custom_types: CustomTypesCollection, @@ -124,9 +125,9 @@ pub struct Engine { pub(crate) token_mapper: Option>, /// Callback closure for implementing the `print` command. - pub(crate) print: Option>, + pub(crate) print: Box, /// Callback closure for implementing the `debug` command. - pub(crate) debug: Option>, + pub(crate) debug: Box, /// Callback closure for progress reporting. #[cfg(not(feature = "unchecked"))] pub(crate) progress: Option>, @@ -135,7 +136,7 @@ pub struct Engine { pub(crate) optimization_level: OptimizationLevel, /// Language options. - pub(crate) options: crate::api::options::LanguageOptions, + pub(crate) options: LanguageOptions, /// Max limits. #[cfg(not(feature = "unchecked"))] @@ -157,8 +158,7 @@ impl fmt::Debug for Engine { f.field("global_modules", &self.global_modules); #[cfg(not(feature = "no_module"))] - f.field("global_sub_modules", &self.global_sub_modules) - .field("module_resolver", &self.module_resolver.is_some()); + f.field("global_sub_modules", &self.global_sub_modules); f.field("type_names", &self.custom_types) .field("disabled_symbols", &self.disabled_symbols) @@ -166,9 +166,7 @@ impl fmt::Debug for Engine { .field("custom_syntax", &(!self.custom_syntax.is_empty())) .field("def_var_filter", &self.def_var_filter.is_some()) .field("resolve_var", &self.resolve_var.is_some()) - .field("token_mapper", &self.token_mapper.is_some()) - .field("print", &self.print.is_some()) - .field("debug", &self.debug.is_some()); + .field("token_mapper", &self.token_mapper.is_some()); #[cfg(not(feature = "unchecked"))] f.field("progress", &self.progress.is_some()); @@ -226,16 +224,15 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.module_resolver = - Some(Box::new(crate::module::resolvers::FileModuleResolver::new())); + engine.module_resolver = Box::new(crate::module::resolvers::FileModuleResolver::new()); } // default print/debug implementations #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] { - engine.print = Some(Box::new(|s| println!("{}", s))); - engine.debug = Some(Box::new(|s, source, pos| { + engine.print = Box::new(|s| println!("{}", s)); + engine.debug = Box::new(|s, source, pos| { if let Some(source) = source { println!("{} @ {:?} | {}", source, pos, s); } else if pos.is_none() { @@ -243,7 +240,7 @@ impl Engine { } else { println!("{:?} | {}", pos, s); } - })); + }); } #[cfg(any(feature = "no_std", target_family = "wasm"))] { @@ -269,7 +266,7 @@ impl Engine { global_sub_modules: BTreeMap::new(), #[cfg(not(feature = "no_module"))] - module_resolver: None, + module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()), custom_types: CustomTypesCollection::new(), empty_string: ImmutableString::new(), @@ -281,15 +278,15 @@ impl Engine { resolve_var: None, token_mapper: None, - print: None, - debug: None, + print: Box::new(|_| {}), + debug: Box::new(|_, _, _| {}), #[cfg(not(feature = "unchecked"))] progress: None, optimization_level: OptimizationLevel::default(), - options: crate::api::options::LanguageOptions::new(), + options: LanguageOptions::new(), #[cfg(not(feature = "unchecked"))] limits: crate::api::limits::Limits::new(), diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index 2191bb44..f378076b 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -35,9 +35,10 @@ impl<'x, 'px, 'm, 'pm, 'pt> EvalContext<'_, 'x, 'px, 'm, 'pm, '_, '_, '_, '_, 'p #[inline(always)] #[must_use] pub fn source(&self) -> Option<&str> { - match self.global.source.as_str() { - "" => None, - s => Some(s), + if self.global.source.is_empty() { + None + } else { + Some(self.global.source.as_str()) } } /// The current [`Scope`]. diff --git a/src/eval/eval_state.rs b/src/eval/eval_state.rs index 943831b7..5f8a7c3a 100644 --- a/src/eval/eval_state.rs +++ b/src/eval/eval_state.rs @@ -28,7 +28,7 @@ pub struct EvalState<'a> { /// Stack of function resolution caches. fn_resolution_caches: StaticVec, /// Take care of the lifetime parameter - dummy: PhantomData>, + dummy: PhantomData<&'a ()>, } impl EvalState<'_> { diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 1363f108..a7d6aeb6 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -244,9 +244,10 @@ impl GlobalRuntimeState<'_> { #[inline] #[must_use] pub fn source(&self) -> Option<&str> { - match self.source.as_str() { - "" => None, - s => Some(s), + if self.source.is_empty() { + None + } else { + Some(self.source.as_str()) } } /// Get the pre-calculated index getter hash. diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 1a1244fd..4e33f28a 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -952,9 +952,10 @@ impl Engine { result => Some(result), }) .or_else(|| { - self.module_resolver - .as_ref() - .map(|r| r.resolve_raw(self, global, &path, path_pos)) + Some( + self.module_resolver + .resolve_raw(self, global, &path, path_pos), + ) }) .unwrap_or_else(|| { Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) diff --git a/src/func/call.rs b/src/func/call.rs index 1a5e9960..3b256349 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -464,30 +464,23 @@ impl Engine { // See if the function match print/debug (which requires special processing) return Ok(match name { KEYWORD_PRINT => { - if let Some(ref print) = self.print { - let text = result.into_immutable_string().map_err(|typ| { - let t = self.map_type_name(type_name::()).into(); - ERR::ErrorMismatchOutputType(t, typ.into(), pos) - })?; - (print(&text).into(), false) - } else { - (Dynamic::UNIT, false) - } + let text = result.into_immutable_string().map_err(|typ| { + let t = self.map_type_name(type_name::()).into(); + ERR::ErrorMismatchOutputType(t, typ.into(), pos) + })?; + ((&*self.print)(&text).into(), false) } KEYWORD_DEBUG => { - if let Some(ref debug) = self.debug { - let text = result.into_immutable_string().map_err(|typ| { - let t = self.map_type_name(type_name::()).into(); - ERR::ErrorMismatchOutputType(t, typ.into(), pos) - })?; - let source = match global.source.as_str() { - "" => None, - s => Some(s), - }; - (debug(&text, source, pos).into(), false) + let text = result.into_immutable_string().map_err(|typ| { + let t = self.map_type_name(type_name::()).into(); + ERR::ErrorMismatchOutputType(t, typ.into(), pos) + })?; + let source = if global.source.is_empty() { + None } else { - (Dynamic::UNIT, false) - } + Some(global.source.as_str()) + }; + ((&*self.debug)(&text, source, pos).into(), false) } _ => (result, is_method), }); From cad4c7a942692c12e26fdcdafa1096c34a1c532d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Mar 2022 14:28:45 +0800 Subject: [PATCH 02/13] Add starts_with and ends_with for strings. --- CHANGELOG.md | 1 + src/packages/string_more.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fb11d78..74aba840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Enhancements * Separation of constants in function calls is removed as its performance benefit is dubious. * A function `sleep` is added to block the current thread by a specified number of seconds. * `Scope::set_alias` is added to export a variable under a particular alias name. +* `starts_with` and `ends_with` are added for strings. Version 1.5.0 diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 182ee743..6112b5a2 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -458,6 +458,35 @@ mod string_functions { *character = to_lower_char(*character) } + /// Return `true` if the string starts with a specified string. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.starts_with("hello")); // prints true + /// + /// print(text.starts_with("world")); // prints false + /// ``` + pub fn starts_with(string: &str, match_string: &str) -> bool { + string.starts_with(match_string) + } + /// Return `true` if the string ends with a specified string. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.ends_with("world!")); // prints true + /// + /// print(text.ends_with("hello")); // prints false + /// ``` + pub fn ends_with(string: &str, match_string: &str) -> bool { + string.ends_with(match_string) + } + /// Find the specified `character` in the string, starting from the specified `start` position, /// and return the first index where it is found. /// If the `character` is not found, `-1` is returned. From 77c06b94a2fc1194ab181c0c3bad6fc3a7a044f4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 25 Mar 2022 08:52:53 +0800 Subject: [PATCH 03/13] Use SmartString for comments. --- src/tokenizer.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5e8cd23a..98456c47 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -5,7 +5,7 @@ use crate::engine::{ KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::func::native::OnParseTokenCallback; -use crate::{Engine, LexError, SmartString, StaticVec, INT, UNSIGNED_INT}; +use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -363,7 +363,7 @@ pub enum Token { #[cfg(feature = "decimal")] DecimalConstant(rust_decimal::Decimal), /// An identifier. - Identifier(SmartString), + Identifier(Identifier), /// A character constant. CharConstant(char), /// A string constant. @@ -1113,8 +1113,8 @@ pub fn parse_string_literal( allow_line_continuation: bool, allow_interpolation: bool, ) -> Result<(SmartString, bool, Position), (LexError, Position)> { - let mut result = SmartString::new(); - let mut escape = SmartString::new(); + let mut result = SmartString::new_const(); + let mut escape = SmartString::new_const(); let start = *pos; let mut first_char = Position::NONE; @@ -1323,7 +1323,7 @@ fn scan_block_comment( stream: &mut impl InputStream, level: usize, pos: &mut Position, - comment: Option<&mut String>, + comment: Option<&mut SmartString>, ) -> usize { let mut level = level; let mut comment = comment; @@ -1418,7 +1418,7 @@ fn get_next_token_inner( if state.comment_level > 0 { let start_pos = *pos; let mut comment = if state.include_comments { - Some(String::new()) + Some(SmartString::new_const()) } else { None }; @@ -1796,7 +1796,7 @@ fn get_next_token_inner( ('/', '/') => { eat_next(stream, pos); - let mut comment = match stream.peek_next() { + let mut comment: Option = match stream.peek_next() { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] Some('/') => { @@ -1805,10 +1805,10 @@ fn get_next_token_inner( // Long streams of `///...` are not doc-comments match stream.peek_next() { Some('/') => None, - _ => Some("///".to_string()), + _ => Some("///".into()), } } - _ if state.include_comments => Some("//".to_string()), + _ if state.include_comments => Some("//".into()), _ => None, }; @@ -1832,14 +1832,14 @@ fn get_next_token_inner( } if let Some(comment) = comment { - return Some((Token::Comment(comment.into()), start_pos)); + return Some((Token::Comment(comment), start_pos)); } } ('/', '*') => { state.comment_level = 1; eat_next(stream, pos); - let mut comment = match stream.peek_next() { + let mut comment: Option = match stream.peek_next() { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] Some('*') => { @@ -1848,10 +1848,10 @@ fn get_next_token_inner( // Long streams of `/****...` are not doc-comments match stream.peek_next() { Some('*') => None, - _ => Some("/**".to_string()), + _ => Some("/**".into()), } } - _ if state.include_comments => Some("/*".to_string()), + _ if state.include_comments => Some("/*".into()), _ => None, }; @@ -1859,7 +1859,7 @@ fn get_next_token_inner( scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); if let Some(comment) = comment { - return Some((Token::Comment(comment.into()), start_pos)); + return Some((Token::Comment(comment), start_pos)); } } From 2b10c33b8156b11f1aaa6df05015876028df7ddd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 26 Mar 2022 18:19:42 +0800 Subject: [PATCH 04/13] Write JSON metadata to file for repl. --- src/bin/rhai-repl.rs | 60 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index a9c775e6..87494edf 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -1,3 +1,4 @@ +use rhai::plugin::*; use rhai::{Dynamic, Engine, EvalAltResult, Module, Scope, AST, INT}; use rustyline::config::Builder; use rustyline::error::ReadlineError; @@ -58,7 +59,7 @@ fn print_help() { #[cfg(feature = "metadata")] println!("functions => print all functions defined"); #[cfg(feature = "metadata")] - println!("json => output all functions in JSON format"); + println!("json => output all functions to `metadata.json`"); println!("ast => print the last AST (optimized)"); #[cfg(not(feature = "no_optimize"))] println!("astu => print the last raw, un-optimized AST"); @@ -267,6 +268,41 @@ fn setup_editor() -> Editor<()> { rl } +#[export_module] +mod sample_functions { + /// This is a sample function. + /// + /// It takes two numbers and prints them to a string. + /// + /// # Example + /// + /// ```rhai + /// let result = test(42, 123); + /// + /// print(result); // prints "42 123" + /// ``` + pub fn test(x: INT, y: INT) -> String { + format!("{} {}", x, y) + } + + /// This is a sample method for integers. + /// + /// # Example + /// + /// ```rhai + /// let x = 42; + /// + /// x.test(123, "hello"); + /// + /// print(x); // prints 170 + /// ``` + #[rhai_fn(name = "test")] + pub fn test2(x: &mut INT, y: INT, z: &str) { + *x += y + (z.len() as INT); + println!("{} {} {}", x, y, z); + } +} + fn main() { let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION")); println!("{}", title); @@ -296,12 +332,7 @@ fn main() { } // Register sample functions - engine - .register_fn("test", |x: INT, y: INT| format!("{} {}", x, y)) - .register_fn("test", |x: &mut INT, y: INT, z: &str| { - *x += y; - println!("{} {} {}", x, y, z); - }); + engine.register_global_module(exported_module!(sample_functions).into()); // Create scope let mut scope = Scope::new(); @@ -463,12 +494,15 @@ fn main() { } #[cfg(feature = "metadata")] "json" => { - println!( - "{}", - engine - .gen_fn_metadata_with_ast_to_json(&main_ast, true) - .unwrap() - ); + use std::io::Write; + + let json = engine + .gen_fn_metadata_with_ast_to_json(&main_ast, false) + .unwrap(); + let mut f = std::fs::File::create("metadata.json") + .expect("Unable to create `metadata.json`"); + f.write_all(json.as_bytes()).expect("Unable to write data"); + println!("Functions metadata written to `metadata.json`."); continue; } "!!" => { From 5caf20d26b46c9332389a81b3d8547e5b2934636 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 27 Mar 2022 21:49:34 +0800 Subject: [PATCH 05/13] Search for global variables in global modules. --- CHANGELOG.md | 2 ++ src/eval/expr.rs | 23 ++++++++++++++--------- src/types/dynamic.rs | 7 +++++++ tests/plugins.rs | 2 ++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74aba840..e94439f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ Enhancements * A function `sleep` is added to block the current thread by a specified number of seconds. * `Scope::set_alias` is added to export a variable under a particular alias name. * `starts_with` and `ends_with` are added for strings. +* Variables in modules registered via `register_global_module` can now be accessed in the global namespace. +* `Dynamic::into_read_only` is added to convert a `Dynamic` value into constant. Version 1.5.0 diff --git a/src/eval/expr.rs b/src/eval/expr.rs index f824ac7b..c2d35b32 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -165,11 +165,8 @@ impl Engine { this_ptr, level, }; - match resolve_var( - expr.get_variable_name(true).expect("`Expr::Variable`"), - index, - &context, - ) { + let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); + match resolve_var(var_name, index, &context) { Ok(Some(mut result)) => { result.set_access_mode(AccessMode::ReadOnly); return Ok((result.into(), var_pos)); @@ -184,10 +181,18 @@ impl Engine { } else { // Find the variable in the scope let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); - scope - .get_index(var_name) - .ok_or_else(|| ERR::ErrorVariableNotFound(var_name.to_string(), var_pos))? - .0 + + match scope.get_index(var_name) { + Some((index, _)) => index, + None => { + return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { + Some(val) => Ok((val.into(), var_pos)), + None => { + Err(ERR::ErrorVariableNotFound(var_name.to_string(), var_pos).into()) + } + } + } + } }; let val = scope.get_mut_by_index(index); diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index afeb3d7c..e8a8a07a 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -1051,6 +1051,13 @@ impl Dynamic { } self } + /// Make this [`Dynamic`] read-only (i.e. a constant). + #[inline(always)] + pub fn into_read_only(self) -> Self { + let mut value = self; + value.set_access_mode(AccessMode::ReadOnly); + value + } /// Is this [`Dynamic`] read-only? /// /// Constant [`Dynamic`] values are read-only. diff --git a/tests/plugins.rs b/tests/plugins.rs index e0a0d648..5327595c 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -85,6 +85,8 @@ fn test_plugins_package() -> Result<(), Box> { reg_functions!(engine += greet::single(INT, bool, char)); + assert_eq!(engine.eval::("MYSTIC_NUMBER")?, 42); + #[cfg(not(feature = "no_object"))] { assert_eq!(engine.eval::("let a = [1, 2, 3]; a.foo")?, 1); From 6369fa5c65cd7f99243d150dde84db39c82f2cce Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 27 Mar 2022 21:53:50 +0800 Subject: [PATCH 06/13] Minor refactor. --- README.md | 1 + src/api/call_fn.rs | 6 ++---- src/api/eval.rs | 4 ++-- src/eval/eval_state.rs | 2 +- src/optimizer.rs | 8 ++++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a9c52df4..23a28ba1 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Protected against attacks * [Sand-boxed](https://rhai.rs/book/safety/sandbox.html) - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://rhai.rs/book/patterns/control.html). * Rugged - protected against malicious attacks (such as [stack-overflow](https://rhai.rs/book/safety/max-call-stack.html), [over-sized data](https://rhai.rs/book/safety/max-string-size.html), and [runaway scripts](https://rhai.rs/book/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts. * Track script evaluation [progress](https://rhai.rs/book/safety/progress.html) and manually terminate a script run. +* Passes Miri. For those who actually want their own language diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 80a0d193..cfad57f9 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -178,7 +178,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] crate::func::call::ensure_no_data_race(name, &mut args, false)?; - let result = self.call_script_fn( + self.call_script_fn( scope, global, state, @@ -189,8 +189,6 @@ impl Engine { rewind_scope, Position::NONE, 0, - ); - - result + ) } } diff --git a/src/api/eval.rs b/src/api/eval.rs index dc4a4336..20d4655b 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -227,9 +227,9 @@ impl Engine { ast.as_ref(), ]; let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) { - &lib[0..0] + &[] } else { - &lib + &lib[..] }; self.eval_global_statements(scope, global, &mut state, statements, lib, level) } diff --git a/src/eval/eval_state.rs b/src/eval/eval_state.rs index 5f8a7c3a..b826ffd9 100644 --- a/src/eval/eval_state.rs +++ b/src/eval/eval_state.rs @@ -40,7 +40,7 @@ impl EvalState<'_> { always_search_scope: false, scope_level: 0, fn_resolution_caches: StaticVec::new_const(), - dummy: PhantomData::default(), + dummy: Default::default(), } } /// Get the number of function resolution cache(s) in the stack. diff --git a/src/optimizer.rs b/src/optimizer.rs index a9952ce0..5e19726e 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -9,8 +9,8 @@ use crate::func::hashing::get_hasher; use crate::tokenizer::{Span, Token}; use crate::types::dynamic::AccessMode; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope, - StaticVec, AST, INT, INT_BITS, + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier, + Position, Scope, StaticVec, AST, INT, INT_BITS, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -46,7 +46,7 @@ struct OptimizerState<'a> { /// Has the [`AST`] been changed during this pass? changed: bool, /// Collection of constants to use for eager function evaluations. - variables: StaticVec<(String, AccessMode, Option)>, + variables: StaticVec<(Identifier, AccessMode, Option)>, /// Activate constants propagation? propagate_constants: bool, /// An [`Engine`] instance for eager function evaluation. @@ -100,7 +100,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub fn push_var( &mut self, - name: impl Into, + name: impl Into, access: AccessMode, value: Option, ) { From 42b67962009186663d0e5d6b562eb83c65a305ea Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Mar 2022 12:53:49 +0800 Subject: [PATCH 07/13] Fix no-std build. --- src/engine.rs | 5 ----- src/packages/lang_core.rs | 5 +---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 34218fda..76577c7d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -242,11 +242,6 @@ impl Engine { } }); } - #[cfg(any(feature = "no_std", target_family = "wasm"))] - { - engine.print = None; - engine.debug = None; - } engine.register_global_module(StandardPackage::new().as_shared_module()); diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 41d91c01..4126b5ae 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -5,9 +5,6 @@ use crate::{Dynamic, RhaiResultOf, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - def_package! { /// Package of core language features. pub LanguageCorePackage(lib) { @@ -84,7 +81,7 @@ mod core_functions { #[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_std"))] #[rhai_fn(name = "sleep")] - pub fn sleep_float(seconds: FLOAT) { + pub fn sleep_float(seconds: crate::FLOAT) { if seconds <= 0.0 { return; } From 95753bb9c39441c0f41b25db068d91f1c6c659e3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Mar 2022 12:53:52 +0800 Subject: [PATCH 08/13] Use global module constants in optimization. --- src/optimizer.rs | 10 ++++++++++ tests/optimizer.rs | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/optimizer.rs b/src/optimizer.rs index 5e19726e..2fcd06bb 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1236,6 +1236,16 @@ fn optimize_top_level( optimization_level, ); + // Add constants from global modules + for (name, value) in engine + .global_modules + .iter() + .rev() + .flat_map(|m| m.iter_var()) + { + state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); + } + // Add constants and variables from the scope for (name, constant, value) in scope.iter() { if !constant { diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 351d91e5..3a203ac2 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_optimize"))] -use rhai::{Engine, EvalAltResult, OptimizationLevel, Scope, INT}; +use rhai::{Engine, EvalAltResult, Module, OptimizationLevel, Scope, INT}; #[test] fn test_optimizer() -> Result<(), Box> { @@ -74,6 +74,7 @@ fn test_optimizer_run() -> Result<(), Box> { #[test] fn test_optimizer_parse() -> Result<(), Box> { let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::Simple); let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?; @@ -97,6 +98,22 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!(format!("{:?}", ast), "AST { body: [Expr(42 @ 1:1)] }"); + let ast = engine.compile("NUMBER")?; + + assert_eq!( + format!("{:?}", ast), + "AST { body: [Expr(Variable(NUMBER) @ 1:1)] }" + ); + + let mut module = Module::new(); + module.set_var("NUMBER", 42 as INT); + + engine.register_global_module(module.into()); + + let ast = engine.compile("NUMBER")?; + + assert_eq!(format!("{:?}", ast), "AST { body: [Expr(42 @ 1:1)] }"); + Ok(()) } From f9250fb0530b292c59492b6e6db7d7c4eef67c80 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Mar 2022 23:22:17 +0800 Subject: [PATCH 09/13] Remove unnecessary call site resolution. --- codegen/src/function.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index e276cfa8..3f593c7c 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -651,8 +651,7 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(Span::call_site) - .resolved_at(Span::call_site()); + .unwrap_or_else(Span::call_site); if self.params.return_raw.is_some() { quote_spanned! { return_span => pub #dynamic_signature { @@ -835,8 +834,7 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(Span::call_site) - .resolved_at(Span::call_site()); + .unwrap_or_else(Span::call_site); let return_expr = if self.params.return_raw.is_none() { quote_spanned! { return_span => Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*))) From 2b9b84a764d92bf3a1c388c10274dbd3fe5a8bb3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Mar 2022 23:24:56 +0800 Subject: [PATCH 10/13] Bump Rhai version requirement. --- codegen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 67baf653..2cb1c662 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -16,7 +16,7 @@ default = [] metadata = [] [dev-dependencies] -rhai = { path = "..", version = "1.4", features = ["metadata"] } +rhai = { path = "..", version = "1.6", features = ["metadata"] } trybuild = "1" [dependencies] From 1b6de25007d3c8a11eff1c17151590b2bfc60fca Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Mar 2022 08:18:20 +0800 Subject: [PATCH 11/13] Remove Engine::custom_types. --- CHANGELOG.md | 1 + src/api/register.rs | 5 +++-- src/api/type_names.rs | 15 ++++++++++++--- src/engine.rs | 9 ++------- src/module/mod.rs | 9 +++++++++ src/types/custom_types.rs | 11 ++++++++--- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94439f0..b5f152ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Enhancements * `starts_with` and `ends_with` are added for strings. * Variables in modules registered via `register_global_module` can now be accessed in the global namespace. * `Dynamic::into_read_only` is added to convert a `Dynamic` value into constant. +* `Module` now holds a collection of custom types with an API. Version 1.5.0 diff --git a/src/api/register.rs b/src/api/register.rs index 1d5220d5..8d69f7d6 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -273,7 +273,7 @@ impl Engine { /// ``` #[inline(always)] pub fn register_type_with_name(&mut self, name: &str) -> &mut Self { - self.custom_types.add_type::(name); + self.global_namespace_mut().set_custom_type::(name); self } /// Register a custom type for use with the [`Engine`], with a pretty-print name @@ -289,7 +289,8 @@ impl Engine { name: impl Into, ) -> &mut Self { // Add the pretty-print type name into the map - self.custom_types.add(fully_qualified_type_path, name); + self.global_namespace_mut() + .set_custom_type_raw(fully_qualified_type_path, name); self } /// Register an type iterator for an iterable type with the [`Engine`]. diff --git a/src/api/type_names.rs b/src/api/type_names.rs index 9c4ca5ea..0b36603e 100644 --- a/src/api/type_names.rs +++ b/src/api/type_names.rs @@ -97,7 +97,6 @@ impl Engine { #[cfg(feature = "no_module")] return None; }) - .or_else(|| self.custom_types.get(name)) .unwrap_or_else(|| map_std_type_name(name, true)) } @@ -119,8 +118,18 @@ impl Engine { }; } - self.custom_types - .get(name) + self.global_modules + .iter() + .find_map(|m| m.get_custom_type(name)) + .or_else(|| { + #[cfg(not(feature = "no_module"))] + return self + .global_sub_modules + .iter() + .find_map(|(_, m)| m.get_custom_type(name)); + #[cfg(feature = "no_module")] + return None; + }) .unwrap_or_else(|| match name { "INT" => return type_name::(), #[cfg(not(feature = "no_float"))] diff --git a/src/engine.rs b/src/engine.rs index 76577c7d..6b7614af 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,7 +7,7 @@ use crate::func::native::{ }; use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; -use crate::types::{dynamic::Union, CustomTypesCollection}; +use crate::types::dynamic::Union; use crate::{ Dynamic, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiResult, Shared, StaticVec, @@ -105,9 +105,6 @@ pub struct Engine { #[cfg(not(feature = "no_module"))] pub(crate) module_resolver: Box, - /// A map mapping type names to pretty-print names. - pub(crate) custom_types: CustomTypesCollection, - /// An empty [`ImmutableString`] for cloning purposes. pub(crate) empty_string: ImmutableString, @@ -160,8 +157,7 @@ impl fmt::Debug for Engine { #[cfg(not(feature = "no_module"))] f.field("global_sub_modules", &self.global_sub_modules); - f.field("type_names", &self.custom_types) - .field("disabled_symbols", &self.disabled_symbols) + f.field("disabled_symbols", &self.disabled_symbols) .field("custom_keywords", &self.custom_keywords) .field("custom_syntax", &(!self.custom_syntax.is_empty())) .field("def_var_filter", &self.def_var_filter.is_some()) @@ -263,7 +259,6 @@ impl Engine { #[cfg(not(feature = "no_module"))] module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()), - custom_types: CustomTypesCollection::new(), empty_string: ImmutableString::new(), disabled_symbols: BTreeSet::new(), custom_keywords: BTreeMap::new(), diff --git a/src/module/mod.rs b/src/module/mod.rs index a70ee8e9..34c2887b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -421,6 +421,15 @@ impl Module { pub fn set_custom_type(&mut self, name: &str) { self.custom_types.add_type::(name) } + /// Map a custom type to a friendly display name. + #[inline(always)] + pub fn set_custom_type_raw( + &mut self, + type_name: impl Into, + name: impl Into, + ) { + self.custom_types.add(type_name, name) + } /// Get the display name of a registered custom type. #[inline(always)] pub fn get_custom_type(&self, key: &str) -> Option<&str> { diff --git a/src/types/custom_types.rs b/src/types/custom_types.rs index e46aaddc..34759d1f 100644 --- a/src/types/custom_types.rs +++ b/src/types/custom_types.rs @@ -27,13 +27,18 @@ impl CustomTypesCollection { } /// Register a custom type. #[inline(always)] - pub fn add(&mut self, key: impl Into, name: impl Into) { - self.0.insert(key.into(), name.into()); + pub fn add(&mut self, type_name: impl Into, name: impl Into) { + self.add_raw(type_name, name.into()); } /// Register a custom type. #[inline(always)] pub fn add_type(&mut self, name: &str) { - self.0.insert(type_name::().into(), name.into()); + self.add_raw(type_name::(), name.into()); + } + /// Register a custom type. + #[inline(always)] + pub fn add_raw(&mut self, type_name: impl Into, custom_type: CustomType) { + self.0.insert(type_name.into(), custom_type); } /// Find a custom type. #[inline(always)] From 56f6b181dbe1856fb5bfb211a4797248572a0283 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Mar 2022 08:26:42 +0800 Subject: [PATCH 12/13] Move optimization_level into Options. --- src/api/compile.rs | 13 +++++++++++-- src/api/eval.rs | 2 +- src/api/optimize.rs | 4 ++-- src/api/options.rs | 9 ++++++++- src/api/run.rs | 2 +- src/engine.rs | 11 +---------- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index 4a3c0490..53402446 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -201,7 +201,11 @@ impl Engine { scope: &Scope, scripts: impl AsRef<[S]>, ) -> ParseResult { - self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level) + self.compile_with_scope_and_optimization_level( + scope, + scripts, + self.options.optimization_level, + ) } /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level. /// @@ -297,7 +301,12 @@ impl Engine { let mut peekable = stream.peekable(); let mut state = ParseState::new(self, tokenizer_control); - self.parse_global_expr(&mut peekable, &mut state, scope, self.optimization_level) + self.parse_global_expr( + &mut peekable, + &mut state, + scope, + self.options.optimization_level, + ) } /// Parse a JSON string into an [object map][crate::Map]. /// This is a light-weight alternative to using, say, diff --git a/src/api/eval.rs b/src/api/eval.rs index 20d4655b..f83da77f 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -67,7 +67,7 @@ impl Engine { let ast = self.compile_with_scope_and_optimization_level( scope, &[script], - self.optimization_level, + self.options.optimization_level, )?; self.eval_ast_with_scope(scope, &ast) } diff --git a/src/api/optimize.rs b/src/api/optimize.rs index 7589c2fd..dd0ca118 100644 --- a/src/api/optimize.rs +++ b/src/api/optimize.rs @@ -9,7 +9,7 @@ impl Engine { /// Not available under `no_optimize`. #[inline(always)] pub fn set_optimization_level(&mut self, optimization_level: OptimizationLevel) -> &mut Self { - self.optimization_level = optimization_level; + self.options.optimization_level = optimization_level; self } @@ -20,7 +20,7 @@ impl Engine { #[inline(always)] #[must_use] pub const fn optimization_level(&self) -> OptimizationLevel { - self.optimization_level + self.options.optimization_level } /// Optimize the [`AST`] with constants defined in an external Scope. diff --git a/src/api/options.rs b/src/api/options.rs index 619be34c..dba11f4b 100644 --- a/src/api/options.rs +++ b/src/api/options.rs @@ -1,12 +1,14 @@ //! Settings for [`Engine`]'s language options. -use crate::Engine; +use crate::{Engine, OptimizationLevel}; #[cfg(feature = "no_std")] use std::prelude::v1::*; /// A type containing all language options for the [`Engine`]. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct LanguageOptions { + /// Script optimization level. + pub optimization_level: OptimizationLevel, /// Is `if`-expression allowed? pub allow_if_expr: bool, /// Is `switch` expression allowed? @@ -33,6 +35,11 @@ impl LanguageOptions { #[inline(always)] pub const fn new() -> Self { Self { + #[cfg(not(feature = "no_optimize"))] + optimization_level: OptimizationLevel::Simple, + #[cfg(feature = "no_optimize")] + optimization_level: (), + allow_if_expr: true, allow_switch_expr: true, allow_stmt_expr: true, diff --git a/src/api/run.rs b/src/api/run.rs index bad510a7..25fd69c7 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -30,7 +30,7 @@ impl Engine { &mut stream.peekable(), &mut state, scope, - self.optimization_level, + self.options.optimization_level, )?; self.run_ast_with_scope(scope, &ast) diff --git a/src/engine.rs b/src/engine.rs index 6b7614af..192416d0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,8 +9,7 @@ use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; use crate::types::dynamic::Union; use crate::{ - Dynamic, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiResult, Shared, - StaticVec, + Dynamic, Identifier, ImmutableString, Module, Position, RhaiResult, Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -129,9 +128,6 @@ pub struct Engine { #[cfg(not(feature = "unchecked"))] pub(crate) progress: Option>, - /// Optimize the [`AST`][crate::AST] after compilation. - pub(crate) optimization_level: OptimizationLevel, - /// Language options. pub(crate) options: LanguageOptions, @@ -167,9 +163,6 @@ impl fmt::Debug for Engine { #[cfg(not(feature = "unchecked"))] f.field("progress", &self.progress.is_some()); - #[cfg(not(feature = "no_optimize"))] - f.field("optimization_level", &self.optimization_level); - f.field("options", &self.options); #[cfg(not(feature = "unchecked"))] @@ -274,8 +267,6 @@ impl Engine { #[cfg(not(feature = "unchecked"))] progress: None, - optimization_level: OptimizationLevel::default(), - options: LanguageOptions::new(), #[cfg(not(feature = "unchecked"))] From a2681053544aaf44adbc634d1618f3b893842f02 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Mar 2022 08:34:02 +0800 Subject: [PATCH 13/13] Revert "Remove unnecessary call site resolution." This reverts commit f9250fb0530b292c59492b6e6db7d7c4eef67c80. --- codegen/src/function.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 3f593c7c..e276cfa8 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -651,7 +651,8 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(Span::call_site); + .unwrap_or_else(Span::call_site) + .resolved_at(Span::call_site()); if self.params.return_raw.is_some() { quote_spanned! { return_span => pub #dynamic_signature { @@ -834,7 +835,8 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(Span::call_site); + .unwrap_or_else(Span::call_site) + .resolved_at(Span::call_site()); let return_expr = if self.params.return_raw.is_none() { quote_spanned! { return_span => Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))