diff --git a/CHANGELOG.md b/CHANGELOG.md index 91efdb6c..a83391e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,9 @@ an object map is small. `HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break existing code. -All function signature/metadata methods are now grouped under the umbrella `metadata` feature. +Im addition, all function signature/metadata methods are now grouped under the umbrella `metadata` feature. +This avoids spending precious resources maintaining metadata for functions for the vast majority of +use cases where such information is not required. Breaking changes @@ -30,6 +32,8 @@ Breaking changes * `protected`, `super` are now reserved keywords. * The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into`. * The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate. +* The shebang `#!` is now a reserved symbol. +* Shebangs at the very beginning of script files are skipped when loading them. Enhancements ------------ @@ -37,6 +41,7 @@ Enhancements * Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny. * `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type. * `#[rhai_fn(return_raw)]` can now return `Result>` where `T` is any clonable type instead of `Result>`. +* Rhai scripts can now start with a shebang `#!`. Version 0.19.14 diff --git a/Cargo.toml b/Cargo.toml index b7ecc4f3..b16375d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"] smallvec = { version = "1.6", default-features = false, features = ["union"] } ahash = { version = "0.7", default-features = false } num-traits = { version = "0.2", default_features = false } -rhai_codegen = { version = "0.3.4", path = "codegen" } +rhai_codegen = { version = "0.3.4", path = "codegen", features = ["metadata"] } [features] default = [] diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 1170ea08..94b0b2cc 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -11,6 +11,10 @@ license = "MIT OR Apache-2.0" [lib] proc-macro = true +[features] +default = [] +metadata = [] + [dev-dependencies] rhai = { path = "..", version = "^0.19.15" } trybuild = "1" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 03d42400..c8c336e6 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -650,6 +650,7 @@ impl ExportedFn { let mut unpack_statements: Vec = Vec::new(); let mut unpack_exprs: Vec = Vec::new(); + #[cfg(feature = "metadata")] let mut input_type_names: Vec = Vec::new(); let mut input_type_exprs: Vec = Vec::new(); @@ -671,6 +672,7 @@ impl ExportedFn { let var = syn::Ident::new("arg0", proc_macro2::Span::call_site()); match first_arg { syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { + #[cfg(feature = "metadata")] let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty)); let arg_type = match flatten_type_groups(ty.as_ref()) { syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(), @@ -699,6 +701,7 @@ impl ExportedFn { .unwrap(), ); } + #[cfg(feature = "metadata")] input_type_names.push(arg_name); input_type_exprs.push( syn::parse2::(quote_spanned!(arg_type.span() => @@ -726,6 +729,7 @@ impl ExportedFn { let is_ref; match arg { syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { + #[cfg(feature = "metadata")] let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty)); let arg_type = ty.as_ref(); let downcast_span = match flatten_type_groups(arg_type) { @@ -765,6 +769,7 @@ impl ExportedFn { }) .unwrap(), ); + #[cfg(feature = "metadata")] input_type_names.push(arg_name); if !is_string { input_type_exprs.push( @@ -817,9 +822,17 @@ impl ExportedFn { }; let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site()); + + #[cfg(feature = "metadata")] + let param_names = quote! { + pub const PARAM_NAMES: &'static [&'static str] = &[#(#input_type_names,)* #return_type]; + }; + #[cfg(not(feature = "metadata"))] + let param_names = quote! {}; + quote! { impl #type_name { - pub const PARAM_NAMES: &'static [&'static str] = &[#(#input_type_names,)* #return_type]; + #param_names #[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] } } impl PluginFunction for #type_name { diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index e6af8fe2..d985757b 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -330,9 +330,17 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream match crate::register::parse_register_macro(args) { Ok((module_expr, export_name, rust_mod_path)) => { let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); + + #[cfg(feature = "metadata")] + let param_names = quote! { + Some(#gen_mod_path::Token::PARAM_NAMES) + }; + #[cfg(not(feature = "metadata"))] + let param_names = quote! { None }; + proc_macro::TokenStream::from(quote! { #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, - Some(#gen_mod_path::Token::PARAM_NAMES), + #param_names, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into()); }) @@ -371,9 +379,17 @@ pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::Toke match crate::register::parse_register_macro(args) { Ok((module_expr, export_name, rust_mod_path)) => { let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); + + #[cfg(feature = "metadata")] + let param_names = quote! { + Some(#gen_mod_path::Token::PARAM_NAMES) + }; + #[cfg(not(feature = "metadata"))] + let param_names = quote! { None }; + proc_macro::TokenStream::from(quote! { #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, - Some(#gen_mod_path::Token::PARAM_NAMES), + #param_names, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into()); }) diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 11c8d1de..1fafd99e 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -154,11 +154,18 @@ pub fn generate_body( }, fn_literal.span(), ); + + #[cfg(feature = "metadata")] + let param_names = quote! { + Some(#fn_token_name::PARAM_NAMES) + }; + #[cfg(not(feature = "metadata"))] + let param_names = quote! { None }; + set_fn_statements.push( syn::parse2::(quote! { m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, - Some(#fn_token_name::PARAM_NAMES), &[#(#fn_input_types),*], - #fn_token_name().into()); + #param_names, &[#(#fn_input_types),*], #fn_token_name().into()); }) .unwrap(), ); diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index 2ffe8423..268390a1 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -58,13 +58,20 @@ fn main() { exit(1); } - if let Err(err) = engine.consume(&contents) { + let contents = if contents.starts_with("#!") { + // Skip shebang + &contents[contents.find('\n').unwrap_or(0)..] + } else { + &contents[..] + }; + + if let Err(err) = engine.consume(contents) { eprintln!("{:=<1$}", "", filename.len()); eprintln!("{}", filename); eprintln!("{:=<1$}", "", filename.len()); eprintln!(""); - eprint_error(&contents, *err); + eprint_error(contents, *err); } } } diff --git a/src/dynamic.rs b/src/dynamic.rs index 6c3f9dbf..7bde7ba2 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -426,33 +426,35 @@ impl Hash for Dynamic { #[inline(always)] pub(crate) fn map_std_type_name(name: &str) -> &str { if name == type_name::() { - "string" - } else if name == type_name::() { - "string" - } else if name == type_name::<&str>() { - "string" - } else if name == type_name::() { - "Fn" - } else { - #[cfg(feature = "decimal")] - if name == type_name::() { - return "decimal"; - } - #[cfg(not(feature = "no_index"))] - if name == type_name::() { - return "array"; - } - #[cfg(not(feature = "no_object"))] - if name == type_name::() { - return "map"; - } - #[cfg(not(feature = "no_std"))] - if name == type_name::() { - return "timestamp"; - } - - name + return "string"; } + if name == type_name::() { + return "string"; + } + if name == type_name::<&str>() { + return "string"; + } + if name == type_name::() { + return "Fn"; + } + #[cfg(feature = "decimal")] + if name == type_name::() { + return "decimal"; + } + #[cfg(not(feature = "no_index"))] + if name == type_name::() { + return "array"; + } + #[cfg(not(feature = "no_object"))] + if name == type_name::() { + return "map"; + } + #[cfg(not(feature = "no_std"))] + if name == type_name::() { + return "timestamp"; + } + + name } impl fmt::Display for Dynamic { @@ -905,7 +907,7 @@ impl Dynamic { value = match unsafe_try_cast::<_, Map>(value) { Ok(map) => return (map).into(), Err(val) => val, - } + }; } value = match unsafe_try_cast::<_, FnPtr>(value) { @@ -918,7 +920,7 @@ impl Dynamic { value = match unsafe_try_cast::<_, Instant>(value) { Ok(timestamp) => return (timestamp).into(), Err(val) => val, - } + }; } Self(Union::Variant( diff --git a/src/engine_api.rs b/src/engine_api.rs index 96abbbbf..8703d3d0 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -859,12 +859,10 @@ impl Engine { self } /// Register a shared [`Module`] into the global namespace of [`Engine`]. - /// - /// # Deprecated - /// + /// This function is deprecated and will be removed in the future. /// Use [`register_global_module`][Engine::register_global_module] instead. #[inline(always)] - #[deprecated = "use `register_global_module` instead"] + #[deprecated(since = "0.19.9", note = "use `register_global_module` instead")] pub fn load_package(&mut self, module: impl Into>) -> &mut Self { self.register_global_module(module.into()) } @@ -947,13 +945,11 @@ impl Engine { } /// Register a shared [`Module`] as a static module namespace with the [`Engine`]. - /// - /// # Deprecated - /// + /// This function is deprecated and will be removed in the future. /// Use [`register_static_module`][Engine::register_static_module] instead. #[cfg(not(feature = "no_module"))] #[inline(always)] - #[deprecated = "use `register_static_module` instead"] + #[deprecated(since = "0.19.9", note = "use `register_static_module` instead")] pub fn register_module( &mut self, name: impl AsRef + Into, @@ -1185,6 +1181,15 @@ impl Engine { ) })?; + if contents.starts_with("#!") { + // Remove shebang + if let Some(n) = contents.find('\n') { + contents.drain(0..n).count(); + } else { + contents.clear(); + } + }; + Ok(contents) } /// Compile a script file into an [`AST`], which can be used later for evaluation. diff --git a/src/lib.rs b/src/lib.rs index 3a880f4d..87996279 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,6 +135,22 @@ pub use syntax::Expression; pub use token::Position; pub use utils::ImmutableString; +/// A trait to enable registering Rust functions. +/// This trait is no longer needed and will be removed in the future. +#[deprecated( + since = "0.19.15", + note = "this trait is no longer needed and will be removed in the future" +)] +pub trait RegisterFn {} + +/// A trait to enable registering Rust functions. +/// This trait is no longer needed and will be removed in the future. +#[deprecated( + since = "0.19.15", + note = "this trait is no longer needed and will be removed in the future" +)] +pub trait RegisterResultFn {} + /// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag. pub use fn_native::Shared; @@ -208,9 +224,9 @@ pub use engine::Limits; pub use module::NamespaceRef; /// Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), which is a -/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. +/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 4 items stored. /// -/// # Background +/// # History /// /// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` /// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." @@ -235,17 +251,17 @@ pub use module::NamespaceRef; /// in that matter) contain fewer than 5 arguments, the exception being closures that capture a /// large number of external variables. /// -/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// In addition, most script blocks either contain many statements, or just a few lines; /// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels /// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(not(feature = "internals"))] type StaticVec = smallvec::SmallVec<[T; 4]>; /// _(INTERNALS)_ Alias to [`smallvec`](https://crates.io/crates/smallvec), which is a specialized -/// [`Vec`] backed by a small, inline, fixed-size array when there are <= 4 items stored. +/// [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 4 items stored. /// Exported under the `internals` feature only. /// -/// # Background +/// # History /// /// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec` /// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy." @@ -270,7 +286,7 @@ type StaticVec = smallvec::SmallVec<[T; 4]>; /// in that matter) contain fewer than 5 arguments, the exception being closures that capture a /// large number of external variables. /// -/// In addition, most scripts blocks either contain many statements, or just a few lines; +/// In addition, most script blocks either contain many statements, or just a few lines; /// most scripts load fewer than 5 external modules; most module paths contain fewer than 5 levels /// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long). #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index e0d9a60b..472e4319 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -644,6 +644,24 @@ impl Module { self } + /// Remap type ID. + #[inline(always)] + fn map_type(map: bool, type_id: TypeId) -> TypeId { + if !map { + return type_id; + } + if type_id == TypeId::of::<&str>() { + // Map &str to ImmutableString + return TypeId::of::(); + } + if type_id == TypeId::of::() { + // Map String to ImmutableString + return TypeId::of::(); + } + + type_id + } + /// Set a Rust function into the [`Module`], returning a hash key. /// /// If there is an existing Rust function of the same hash, it is replaced. @@ -663,31 +681,12 @@ impl Module { ) -> u64 { let is_method = func.is_method(); - let param_types = arg_types + let param_types: StaticVec<_> = arg_types .iter() .cloned() .enumerate() - .map(|(i, type_id)| { - if !is_method || i > 0 { - if type_id == TypeId::of::<&str>() { - // Map &str to ImmutableString - TypeId::of::() - } else if type_id == TypeId::of::() { - // Map String to ImmutableString - TypeId::of::() - } else { - type_id - } - } else { - // Do not map &mut parameter - type_id - } - }) - .collect::>(); - - let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types); - - let name = self.interned_strings.get(name); + .map(|(i, type_id)| Self::map_type(!is_method || i > 0, type_id)) + .collect(); #[cfg(feature = "metadata")] let param_names = _arg_names @@ -696,17 +695,19 @@ impl Module { .map(|&arg| self.interned_strings.get(arg)) .collect(); + let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types); + self.functions.insert( hash_fn, Box::new(FuncInfo { - name, + name: self.interned_strings.get(name), namespace, access, params: param_types.len(), param_types, #[cfg(feature = "metadata")] param_names, - func: func.into(), + func, }), ); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 7524ec3d..145eabf0 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -40,11 +40,9 @@ pub trait Package { fn as_shared_module(&self) -> Shared; /// Retrieve the generic package library from this package. - /// - /// # Deprecated - /// + /// This method is deprecated and will be removed in the future. /// Use [`as_shared_module`][Package::as_shared_module] instead. - #[deprecated = "use `as_shared_module` instead"] + #[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")] fn get(&self) -> Shared { self.as_shared_module() } diff --git a/src/serde/mod.rs b/src/serde/mod.rs index adb605de..a3819556 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -8,7 +8,6 @@ mod serialize; mod str; #[cfg(feature = "metadata")] -#[cfg(feature = "serde")] mod metadata; pub use de::from_dynamic; diff --git a/src/token.rs b/src/token.rs index dba17351..16087267 100644 --- a/src/token.rs +++ b/src/token.rs @@ -587,11 +587,13 @@ impl Token { #[cfg(feature = "no_module")] "import" | "export" | "as" => Reserved(syntax.into()), - "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public" - | "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static" - | "begin" | "end" | "shared" | "with" | "each" | "then" | "goto" | "unless" - | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" - | "thread" | "go" | "sync" | "async" | "await" | "yield" => Reserved(syntax.into()), + "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" + | "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" + | "static" | "begin" | "end" | "shared" | "with" | "each" | "then" | "goto" + | "unless" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" + | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => { + Reserved(syntax.into()) + } KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { @@ -1323,6 +1325,9 @@ fn get_next_token_inner( eat_next(stream, pos); return Some((Token::MapStart, start_pos)); } + // Shebang + ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), + ('#', _) => return Some((Token::Reserved("#".into()), start_pos)), // Operators diff --git a/tests/closures.rs b/tests/closures.rs index ff4cc431..cfa1f355 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -324,7 +324,7 @@ fn test_closures_external() -> Result<(), Box> { r#" let test = "hello"; |x| test + x - "#, + "#, )?; // Save the function pointer together with captured variables diff --git a/tests/expressions.rs b/tests/expressions.rs index e6c09b5c..13588d0d 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -67,7 +67,7 @@ fn test_expressions_eval() -> Result<(), Box> { let result: bool = engine.eval_expression_with_scope( &mut scope, r#" - agent.age > 10 && agent.gender == "male" + agent.age > 10 && agent.gender == "male" "#, )?; diff --git a/tests/functions.rs b/tests/functions.rs index d2db1128..5cbdcf3f 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -242,19 +242,19 @@ fn test_function_is_def() -> Result<(), Box> { r#" fn foo(x) { x + 1 } is_def_fn("foo", 1) - "# + "# )?); assert!(!engine.eval::( r#" fn foo(x) { x + 1 } is_def_fn("bar", 1) - "# + "# )?); assert!(!engine.eval::( r#" fn foo(x) { x + 1 } is_def_fn("foo", 0) - "# + "# )?); Ok(()) diff --git a/tests/if_block.rs b/tests/if_block.rs index a5d5125a..eeb345a1 100644 --- a/tests/if_block.rs +++ b/tests/if_block.rs @@ -20,7 +20,7 @@ fn test_if() -> Result<(), Box> { else if false { 77 } else if false { 88 } else { 44 } - " + " )?, 44 ); @@ -38,7 +38,7 @@ fn test_if_expr() -> Result<(), Box> { let x = 42; let y = 1 + if x > 40 { 100 } else { 0 } / x; y - " + " )?, 3 ); diff --git a/tests/looping.rs b/tests/looping.rs index 23f87bb0..097297f7 100644 --- a/tests/looping.rs +++ b/tests/looping.rs @@ -21,7 +21,7 @@ fn test_loop() -> Result<(), Box> { } return x; - " + " )?, 21 ); diff --git a/tests/operations.rs b/tests/operations.rs index 6b812958..bcc9019f 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -43,24 +43,24 @@ fn test_max_operations_functions() -> Result<(), Box> { engine.eval::<()>( r#" - print("Test1"); - let x = 0; + print("Test1"); + let x = 0; - while x < 28 { - print(x); - x += 1; - } - "#, + while x < 28 { + print(x); + x += 1; + } + "#, )?; #[cfg(not(feature = "no_function"))] engine.eval::<()>( r#" - print("Test2"); - fn inc(x) { x + 1 } - let x = 0; - while x < 20 { x = inc(x); } - "#, + print("Test2"); + fn inc(x) { x + 1 } + let x = 0; + while x < 20 { x = inc(x); } + "#, )?; #[cfg(not(feature = "no_function"))] @@ -76,7 +76,7 @@ fn test_max_operations_functions() -> Result<(), Box> { print(x); x = inc(x); } - "#, + "#, ) .expect_err("should error"), EvalAltResult::ErrorTooManyOperations(_) @@ -103,7 +103,7 @@ fn test_max_operations_eval() -> Result<(), Box> { r#" let script = "for x in range(0, 500) {}"; eval(script); - "# + "# ) .expect_err("should error"), EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_)) diff --git a/tests/stack.rs b/tests/stack.rs index 03f1c083..281c2d87 100644 --- a/tests/stack.rs +++ b/tests/stack.rs @@ -65,24 +65,24 @@ fn test_stack_overflow_parsing() -> Result<(), Box> { 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 - ", + ", )?; assert_eq!( *engine .compile( r" - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + - 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 - " + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + " ) .expect_err("should error") .0, diff --git a/tests/time.rs b/tests/time.rs index 54240db8..94de7933 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -23,7 +23,7 @@ fn test_timestamp() -> Result<(), Box> { let x = 10_000; while x > 0 { x -= 1; } elapsed(time) - "# + "# )? < 10.0 ); @@ -35,7 +35,7 @@ fn test_timestamp() -> Result<(), Box> { let x = 10_000; while x > 0 { x -= 1; } elapsed(time) - "# + "# )? < 10 ); @@ -62,7 +62,7 @@ fn test_timestamp_op() -> Result<(), Box> { let time1 = timestamp(); let time2 = time1 + 123.45; time2 - time1 - "# + "# )? - 123.45) .abs() < 0.001 @@ -75,7 +75,7 @@ fn test_timestamp_op() -> Result<(), Box> { let time1 = timestamp(); let time2 = time1 - 123.45; time1 - time2 - "# + "# )? - 123.45) .abs() < 0.001 @@ -88,7 +88,7 @@ fn test_timestamp_op() -> Result<(), Box> { let time1 = timestamp(); let time2 = time1 + 42; time2 - time1 - "# + "# )?, 42 ); @@ -100,7 +100,7 @@ fn test_timestamp_op() -> Result<(), Box> { let time1 = timestamp(); let time2 = time1 - 42; time1 - time2 - "# + "# )?, 42 ); diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 09cf8730..082a294b 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -27,19 +27,19 @@ fn test_var_is_def() -> Result<(), Box> { r#" let x = 42; is_def_var("x") - "# + "# )?); assert!(!engine.eval::( r#" let x = 42; is_def_var("y") - "# + "# )?); assert!(engine.eval::( r#" const x = 42; is_def_var("x") - "# + "# )?); Ok(()) diff --git a/tests/while_loop.rs b/tests/while_loop.rs index f9b74846..35a71dae 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -17,7 +17,7 @@ fn test_while() -> Result<(), Box> { } x - ", + ", )?, 6 ); @@ -42,7 +42,7 @@ fn test_do() -> Result<(), Box> { } while x < 10; x - ", + ", )?, 6 );