Merge pull request #379 from schungx/master
Gate metadata methods and support shebangs in scripts.
This commit is contained in:
commit
6829518eb9
@ -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
|
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
|
||||||
existing code.
|
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
|
Breaking changes
|
||||||
@ -30,6 +32,8 @@ Breaking changes
|
|||||||
* `protected`, `super` are now reserved keywords.
|
* `protected`, `super` are now reserved keywords.
|
||||||
* The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into<String>`.
|
* The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into<String>`.
|
||||||
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
|
* 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
|
Enhancements
|
||||||
------------
|
------------
|
||||||
@ -37,6 +41,7 @@ Enhancements
|
|||||||
* Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
|
* 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.
|
* `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<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
||||||
|
* Rhai scripts can now start with a shebang `#!`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.14
|
Version 0.19.14
|
||||||
|
@ -19,7 +19,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
|||||||
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
||||||
ahash = { version = "0.7", default-features = false }
|
ahash = { version = "0.7", default-features = false }
|
||||||
num-traits = { version = "0.2", 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]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -11,6 +11,10 @@ license = "MIT OR Apache-2.0"
|
|||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
metadata = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rhai = { path = "..", version = "^0.19.15" }
|
rhai = { path = "..", version = "^0.19.15" }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
@ -650,6 +650,7 @@ impl ExportedFn {
|
|||||||
|
|
||||||
let mut unpack_statements: Vec<syn::Stmt> = Vec::new();
|
let mut unpack_statements: Vec<syn::Stmt> = Vec::new();
|
||||||
let mut unpack_exprs: Vec<syn::Expr> = Vec::new();
|
let mut unpack_exprs: Vec<syn::Expr> = Vec::new();
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
let mut input_type_names: Vec<String> = Vec::new();
|
let mut input_type_names: Vec<String> = Vec::new();
|
||||||
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
||||||
|
|
||||||
@ -671,6 +672,7 @@ impl ExportedFn {
|
|||||||
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
||||||
match first_arg {
|
match first_arg {
|
||||||
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
|
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty));
|
let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty));
|
||||||
let arg_type = match flatten_type_groups(ty.as_ref()) {
|
let arg_type = match flatten_type_groups(ty.as_ref()) {
|
||||||
syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
||||||
@ -699,6 +701,7 @@ impl ExportedFn {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
input_type_names.push(arg_name);
|
input_type_names.push(arg_name);
|
||||||
input_type_exprs.push(
|
input_type_exprs.push(
|
||||||
syn::parse2::<syn::Expr>(quote_spanned!(arg_type.span() =>
|
syn::parse2::<syn::Expr>(quote_spanned!(arg_type.span() =>
|
||||||
@ -726,6 +729,7 @@ impl ExportedFn {
|
|||||||
let is_ref;
|
let is_ref;
|
||||||
match arg {
|
match arg {
|
||||||
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
|
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty));
|
let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty));
|
||||||
let arg_type = ty.as_ref();
|
let arg_type = ty.as_ref();
|
||||||
let downcast_span = match flatten_type_groups(arg_type) {
|
let downcast_span = match flatten_type_groups(arg_type) {
|
||||||
@ -765,6 +769,7 @@ impl ExportedFn {
|
|||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
input_type_names.push(arg_name);
|
input_type_names.push(arg_name);
|
||||||
if !is_string {
|
if !is_string {
|
||||||
input_type_exprs.push(
|
input_type_exprs.push(
|
||||||
@ -817,9 +822,17 @@ impl ExportedFn {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
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! {
|
quote! {
|
||||||
impl #type_name {
|
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),*] }
|
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
|
||||||
}
|
}
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
|
@ -330,9 +330,17 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||||||
match crate::register::parse_register_macro(args) {
|
match crate::register::parse_register_macro(args) {
|
||||||
Ok((module_expr, export_name, rust_mod_path)) => {
|
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||||
let gen_mod_path = crate::register::generated_module_path(&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! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
|
#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::param_types(),
|
||||||
#gen_mod_path::Token().into());
|
#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) {
|
match crate::register::parse_register_macro(args) {
|
||||||
Ok((module_expr, export_name, rust_mod_path)) => {
|
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||||
let gen_mod_path = crate::register::generated_module_path(&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! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
|
#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::param_types(),
|
||||||
#gen_mod_path::Token().into());
|
#gen_mod_path::Token().into());
|
||||||
})
|
})
|
||||||
|
@ -154,11 +154,18 @@ pub fn generate_body(
|
|||||||
},
|
},
|
||||||
fn_literal.span(),
|
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(
|
set_fn_statements.push(
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
||||||
Some(#fn_token_name::PARAM_NAMES), &[#(#fn_input_types),*],
|
#param_names, &[#(#fn_input_types),*], #fn_token_name().into());
|
||||||
#fn_token_name().into());
|
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
@ -58,13 +58,20 @@ fn main() {
|
|||||||
exit(1);
|
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!("{:=<1$}", "", filename.len());
|
||||||
eprintln!("{}", filename);
|
eprintln!("{}", filename);
|
||||||
eprintln!("{:=<1$}", "", filename.len());
|
eprintln!("{:=<1$}", "", filename.len());
|
||||||
eprintln!("");
|
eprintln!("");
|
||||||
|
|
||||||
eprint_error(&contents, *err);
|
eprint_error(contents, *err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,33 +426,35 @@ impl Hash for Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||||
if name == type_name::<String>() {
|
if name == type_name::<String>() {
|
||||||
"string"
|
return "string";
|
||||||
} else if name == type_name::<ImmutableString>() {
|
|
||||||
"string"
|
|
||||||
} else if name == type_name::<&str>() {
|
|
||||||
"string"
|
|
||||||
} else if name == type_name::<FnPtr>() {
|
|
||||||
"Fn"
|
|
||||||
} else {
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
if name == type_name::<Decimal>() {
|
|
||||||
return "decimal";
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
if name == type_name::<Array>() {
|
|
||||||
return "array";
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
if name == type_name::<Map>() {
|
|
||||||
return "map";
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
if name == type_name::<Instant>() {
|
|
||||||
return "timestamp";
|
|
||||||
}
|
|
||||||
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
|
if name == type_name::<ImmutableString>() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
if name == type_name::<&str>() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
if name == type_name::<FnPtr>() {
|
||||||
|
return "Fn";
|
||||||
|
}
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
if name == type_name::<Decimal>() {
|
||||||
|
return "decimal";
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if name == type_name::<Array>() {
|
||||||
|
return "array";
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if name == type_name::<Map>() {
|
||||||
|
return "map";
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
if name == type_name::<Instant>() {
|
||||||
|
return "timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Dynamic {
|
impl fmt::Display for Dynamic {
|
||||||
@ -905,7 +907,7 @@ impl Dynamic {
|
|||||||
value = match unsafe_try_cast::<_, Map>(value) {
|
value = match unsafe_try_cast::<_, Map>(value) {
|
||||||
Ok(map) => return (map).into(),
|
Ok(map) => return (map).into(),
|
||||||
Err(val) => val,
|
Err(val) => val,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
value = match unsafe_try_cast::<_, FnPtr>(value) {
|
value = match unsafe_try_cast::<_, FnPtr>(value) {
|
||||||
@ -918,7 +920,7 @@ impl Dynamic {
|
|||||||
value = match unsafe_try_cast::<_, Instant>(value) {
|
value = match unsafe_try_cast::<_, Instant>(value) {
|
||||||
Ok(timestamp) => return (timestamp).into(),
|
Ok(timestamp) => return (timestamp).into(),
|
||||||
Err(val) => val,
|
Err(val) => val,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Self(Union::Variant(
|
Self(Union::Variant(
|
||||||
|
@ -859,12 +859,10 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register a shared [`Module`] into the global namespace of [`Engine`].
|
/// Register a shared [`Module`] into the global namespace of [`Engine`].
|
||||||
///
|
/// This function is deprecated and will be removed in the future.
|
||||||
/// # Deprecated
|
|
||||||
///
|
|
||||||
/// Use [`register_global_module`][Engine::register_global_module] instead.
|
/// Use [`register_global_module`][Engine::register_global_module] instead.
|
||||||
#[inline(always)]
|
#[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<Shared<Module>>) -> &mut Self {
|
pub fn load_package(&mut self, module: impl Into<Shared<Module>>) -> &mut Self {
|
||||||
self.register_global_module(module.into())
|
self.register_global_module(module.into())
|
||||||
}
|
}
|
||||||
@ -947,13 +945,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register a shared [`Module`] as a static module namespace with the [`Engine`].
|
/// Register a shared [`Module`] as a static module namespace with the [`Engine`].
|
||||||
///
|
/// This function is deprecated and will be removed in the future.
|
||||||
/// # Deprecated
|
|
||||||
///
|
|
||||||
/// Use [`register_static_module`][Engine::register_static_module] instead.
|
/// Use [`register_static_module`][Engine::register_static_module] instead.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[deprecated = "use `register_static_module` instead"]
|
#[deprecated(since = "0.19.9", note = "use `register_static_module` instead")]
|
||||||
pub fn register_module(
|
pub fn register_module(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<ImmutableString>,
|
name: impl AsRef<str> + Into<ImmutableString>,
|
||||||
@ -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)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
/// Compile a script file into an [`AST`], which can be used later for evaluation.
|
/// Compile a script file into an [`AST`], which can be used later for evaluation.
|
||||||
|
28
src/lib.rs
28
src/lib.rs
@ -135,6 +135,22 @@ pub use syntax::Expression;
|
|||||||
pub use token::Position;
|
pub use token::Position;
|
||||||
pub use utils::ImmutableString;
|
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.
|
/// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag.
|
||||||
pub use fn_native::Shared;
|
pub use fn_native::Shared;
|
||||||
|
|
||||||
@ -208,9 +224,9 @@ pub use engine::Limits;
|
|||||||
pub use module::NamespaceRef;
|
pub use module::NamespaceRef;
|
||||||
|
|
||||||
/// Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), which is a
|
/// 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`
|
/// 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."
|
/// 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
|
/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a
|
||||||
/// large number of external variables.
|
/// 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
|
/// 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).
|
/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long).
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
|
|
||||||
/// _(INTERNALS)_ Alias to [`smallvec`](https://crates.io/crates/smallvec), which is a specialized
|
/// _(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.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # Background
|
/// # History
|
||||||
///
|
///
|
||||||
/// And Saint Attila raised the `SmallVec` up on high, saying, "O Lord, bless this Thy `SmallVec`
|
/// 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."
|
/// that, with it, Thou mayest blow Thine allocation costs to tiny bits in Thy mercy."
|
||||||
@ -270,7 +286,7 @@ type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
|||||||
/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a
|
/// in that matter) contain fewer than 5 arguments, the exception being closures that capture a
|
||||||
/// large number of external variables.
|
/// 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
|
/// 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).
|
/// (e.g. `std::collections::map::HashMap` is 4 levels, and that's already quite long).
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -644,6 +644,24 @@ impl Module {
|
|||||||
self
|
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::<ImmutableString>();
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<String>() {
|
||||||
|
// Map String to ImmutableString
|
||||||
|
return TypeId::of::<ImmutableString>();
|
||||||
|
}
|
||||||
|
|
||||||
|
type_id
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a Rust function into the [`Module`], returning a hash key.
|
/// 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.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
@ -663,31 +681,12 @@ impl Module {
|
|||||||
) -> u64 {
|
) -> u64 {
|
||||||
let is_method = func.is_method();
|
let is_method = func.is_method();
|
||||||
|
|
||||||
let param_types = arg_types
|
let param_types: StaticVec<_> = arg_types
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, type_id)| {
|
.map(|(i, type_id)| Self::map_type(!is_method || i > 0, type_id))
|
||||||
if !is_method || i > 0 {
|
.collect();
|
||||||
if type_id == TypeId::of::<&str>() {
|
|
||||||
// Map &str to ImmutableString
|
|
||||||
TypeId::of::<ImmutableString>()
|
|
||||||
} else if type_id == TypeId::of::<String>() {
|
|
||||||
// Map String to ImmutableString
|
|
||||||
TypeId::of::<ImmutableString>()
|
|
||||||
} else {
|
|
||||||
type_id
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Do not map &mut parameter
|
|
||||||
type_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<StaticVec<_>>();
|
|
||||||
|
|
||||||
let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types);
|
|
||||||
|
|
||||||
let name = self.interned_strings.get(name);
|
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
let param_names = _arg_names
|
let param_names = _arg_names
|
||||||
@ -696,17 +695,19 @@ impl Module {
|
|||||||
.map(|&arg| self.interned_strings.get(arg))
|
.map(|&arg| self.interned_strings.get(arg))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types);
|
||||||
|
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_fn,
|
hash_fn,
|
||||||
Box::new(FuncInfo {
|
Box::new(FuncInfo {
|
||||||
name,
|
name: self.interned_strings.get(name),
|
||||||
namespace,
|
namespace,
|
||||||
access,
|
access,
|
||||||
params: param_types.len(),
|
params: param_types.len(),
|
||||||
param_types,
|
param_types,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
param_names,
|
param_names,
|
||||||
func: func.into(),
|
func,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -40,11 +40,9 @@ pub trait Package {
|
|||||||
fn as_shared_module(&self) -> Shared<Module>;
|
fn as_shared_module(&self) -> Shared<Module>;
|
||||||
|
|
||||||
/// Retrieve the generic package library from this package.
|
/// Retrieve the generic package library from this package.
|
||||||
///
|
/// This method is deprecated and will be removed in the future.
|
||||||
/// # Deprecated
|
|
||||||
///
|
|
||||||
/// Use [`as_shared_module`][Package::as_shared_module] instead.
|
/// 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<Module> {
|
fn get(&self) -> Shared<Module> {
|
||||||
self.as_shared_module()
|
self.as_shared_module()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ mod serialize;
|
|||||||
mod str;
|
mod str;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
mod metadata;
|
mod metadata;
|
||||||
|
|
||||||
pub use de::from_dynamic;
|
pub use de::from_dynamic;
|
||||||
|
15
src/token.rs
15
src/token.rs
@ -587,11 +587,13 @@ impl Token {
|
|||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public"
|
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!"
|
||||||
| "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static"
|
| "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
||||||
| "begin" | "end" | "shared" | "with" | "each" | "then" | "goto" | "unless"
|
| "static" | "begin" | "end" | "shared" | "with" | "each" | "then" | "goto"
|
||||||
| "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn"
|
| "unless" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil"
|
||||||
| "thread" | "go" | "sync" | "async" | "await" | "yield" => Reserved(syntax.into()),
|
| "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
||||||
|
Reserved(syntax.into())
|
||||||
|
}
|
||||||
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
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 => {
|
| 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);
|
eat_next(stream, pos);
|
||||||
return Some((Token::MapStart, start_pos));
|
return Some((Token::MapStart, start_pos));
|
||||||
}
|
}
|
||||||
|
// Shebang
|
||||||
|
('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)),
|
||||||
|
|
||||||
('#', _) => return Some((Token::Reserved("#".into()), start_pos)),
|
('#', _) => return Some((Token::Reserved("#".into()), start_pos)),
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
@ -324,7 +324,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
|||||||
r#"
|
r#"
|
||||||
let test = "hello";
|
let test = "hello";
|
||||||
|x| test + x
|
|x| test + x
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Save the function pointer together with captured variables
|
// Save the function pointer together with captured variables
|
||||||
|
@ -67,7 +67,7 @@ fn test_expressions_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let result: bool = engine.eval_expression_with_scope(
|
let result: bool = engine.eval_expression_with_scope(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
r#"
|
r#"
|
||||||
agent.age > 10 && agent.gender == "male"
|
agent.age > 10 && agent.gender == "male"
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -242,19 +242,19 @@ fn test_function_is_def() -> Result<(), Box<EvalAltResult>> {
|
|||||||
r#"
|
r#"
|
||||||
fn foo(x) { x + 1 }
|
fn foo(x) { x + 1 }
|
||||||
is_def_fn("foo", 1)
|
is_def_fn("foo", 1)
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
assert!(!engine.eval::<bool>(
|
assert!(!engine.eval::<bool>(
|
||||||
r#"
|
r#"
|
||||||
fn foo(x) { x + 1 }
|
fn foo(x) { x + 1 }
|
||||||
is_def_fn("bar", 1)
|
is_def_fn("bar", 1)
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
assert!(!engine.eval::<bool>(
|
assert!(!engine.eval::<bool>(
|
||||||
r#"
|
r#"
|
||||||
fn foo(x) { x + 1 }
|
fn foo(x) { x + 1 }
|
||||||
is_def_fn("foo", 0)
|
is_def_fn("foo", 0)
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -20,7 +20,7 @@ fn test_if() -> Result<(), Box<EvalAltResult>> {
|
|||||||
else if false { 77 }
|
else if false { 77 }
|
||||||
else if false { 88 }
|
else if false { 88 }
|
||||||
else { 44 }
|
else { 44 }
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
44
|
44
|
||||||
);
|
);
|
||||||
@ -38,7 +38,7 @@ fn test_if_expr() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let x = 42;
|
let x = 42;
|
||||||
let y = 1 + if x > 40 { 100 } else { 0 } / x;
|
let y = 1 + if x > 40 { 100 } else { 0 } / x;
|
||||||
y
|
y
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
21
|
21
|
||||||
);
|
);
|
||||||
|
@ -43,24 +43,24 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
print("Test1");
|
print("Test1");
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
||||||
while x < 28 {
|
while x < 28 {
|
||||||
print(x);
|
print(x);
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
print("Test2");
|
print("Test2");
|
||||||
fn inc(x) { x + 1 }
|
fn inc(x) { x + 1 }
|
||||||
let x = 0;
|
let x = 0;
|
||||||
while x < 20 { x = inc(x); }
|
while x < 20 { x = inc(x); }
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -76,7 +76,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
print(x);
|
print(x);
|
||||||
x = inc(x);
|
x = inc(x);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTooManyOperations(_)
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
@ -103,7 +103,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
r#"
|
r#"
|
||||||
let script = "for x in range(0, 500) {}";
|
let script = "for x in range(0, 500) {}";
|
||||||
eval(script);
|
eval(script);
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_))
|
EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_))
|
||||||
|
@ -65,24 +65,24 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
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
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile(
|
.compile(
|
||||||
r"
|
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 + 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
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.0,
|
.0,
|
||||||
|
@ -23,7 +23,7 @@ fn test_timestamp() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let x = 10_000;
|
let x = 10_000;
|
||||||
while x > 0 { x -= 1; }
|
while x > 0 { x -= 1; }
|
||||||
elapsed(time)
|
elapsed(time)
|
||||||
"#
|
"#
|
||||||
)? < 10.0
|
)? < 10.0
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ fn test_timestamp() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let x = 10_000;
|
let x = 10_000;
|
||||||
while x > 0 { x -= 1; }
|
while x > 0 { x -= 1; }
|
||||||
elapsed(time)
|
elapsed(time)
|
||||||
"#
|
"#
|
||||||
)? < 10
|
)? < 10
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ fn test_timestamp_op() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let time1 = timestamp();
|
let time1 = timestamp();
|
||||||
let time2 = time1 + 123.45;
|
let time2 = time1 + 123.45;
|
||||||
time2 - time1
|
time2 - time1
|
||||||
"#
|
"#
|
||||||
)? - 123.45)
|
)? - 123.45)
|
||||||
.abs()
|
.abs()
|
||||||
< 0.001
|
< 0.001
|
||||||
@ -75,7 +75,7 @@ fn test_timestamp_op() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let time1 = timestamp();
|
let time1 = timestamp();
|
||||||
let time2 = time1 - 123.45;
|
let time2 = time1 - 123.45;
|
||||||
time1 - time2
|
time1 - time2
|
||||||
"#
|
"#
|
||||||
)? - 123.45)
|
)? - 123.45)
|
||||||
.abs()
|
.abs()
|
||||||
< 0.001
|
< 0.001
|
||||||
@ -88,7 +88,7 @@ fn test_timestamp_op() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let time1 = timestamp();
|
let time1 = timestamp();
|
||||||
let time2 = time1 + 42;
|
let time2 = time1 + 42;
|
||||||
time2 - time1
|
time2 - time1
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
@ -100,7 +100,7 @@ fn test_timestamp_op() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let time1 = timestamp();
|
let time1 = timestamp();
|
||||||
let time2 = time1 - 42;
|
let time2 = time1 - 42;
|
||||||
time1 - time2
|
time1 - time2
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
@ -27,19 +27,19 @@ fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
|
|||||||
r#"
|
r#"
|
||||||
let x = 42;
|
let x = 42;
|
||||||
is_def_var("x")
|
is_def_var("x")
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
assert!(!engine.eval::<bool>(
|
assert!(!engine.eval::<bool>(
|
||||||
r#"
|
r#"
|
||||||
let x = 42;
|
let x = 42;
|
||||||
is_def_var("y")
|
is_def_var("y")
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
assert!(engine.eval::<bool>(
|
assert!(engine.eval::<bool>(
|
||||||
r#"
|
r#"
|
||||||
const x = 42;
|
const x = 42;
|
||||||
is_def_var("x")
|
is_def_var("x")
|
||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -17,7 +17,7 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
x
|
x
|
||||||
",
|
",
|
||||||
)?,
|
)?,
|
||||||
6
|
6
|
||||||
);
|
);
|
||||||
@ -42,7 +42,7 @@ fn test_do() -> Result<(), Box<EvalAltResult>> {
|
|||||||
} while x < 10;
|
} while x < 10;
|
||||||
|
|
||||||
x
|
x
|
||||||
",
|
",
|
||||||
)?,
|
)?,
|
||||||
6
|
6
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user