diff --git a/CHANGELOG.md b/CHANGELOG.md index 2345ed34..a83391e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,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 ------------ @@ -39,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/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/engine_api.rs b/src/engine_api.rs index 96abbbbf..131bcc6e 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1185,6 +1185,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/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