Merge pull request #379 from schungx/master

Gate metadata methods and support shebangs in scripts.
This commit is contained in:
Stephen Chung 2021-03-28 17:16:19 +08:00 committed by GitHub
commit 6829518eb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 208 additions and 130 deletions

View File

@ -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

View File

@ -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 = []

View File

@ -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"

View File

@ -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 {

View File

@ -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());
}) })

View File

@ -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(),
); );

View File

@ -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);
} }
} }
} }

View File

@ -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(

View File

@ -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.

View File

@ -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")]

View File

@ -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, &param_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, &param_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,
}), }),
); );

View File

@ -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()
} }

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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"
"#, "#,
)?; )?;

View File

@ -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(())

View File

@ -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
); );

View File

@ -21,7 +21,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
} }
return x; return x;
" "
)?, )?,
21 21
); );

View File

@ -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(_))

View File

@ -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,

View File

@ -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
); );

View File

@ -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(())

View File

@ -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
); );