Change parser output to String.

This commit is contained in:
Stephen Chung 2020-10-27 09:56:37 +08:00
parent 54d68c1061
commit 4add90b215
4 changed files with 63 additions and 64 deletions

View File

@ -116,16 +116,19 @@ The function signature of an implementation is:
where: where:
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_, exposing the following: | Parameter | Type | Description |
* `context.scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to/removed from it. | ----------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------- |
* `context.engine(): &Engine` - reference to the current [`Engine`]. | `context` | `&mut EvalContext` | mutable reference to the current evaluation _context_ |
* `context.iter_namespaces(): impl Iterator<Item = &Module>` - iterator of the namespaces (as [modules]) containing all script-defined functions. | - `context.scope` | `&mut Scope` | mutable reference to the current [`Scope`]; variables can be added to/removed from it |
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any. | - `context.engine()` | `&Engine` | reference to the current [`Engine`] |
* `context.call_level(): usize` - the current nesting level of function calls. | - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
| - `context.this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
| - `context.call_level()` | `usize` | the current nesting level of function calls |
| `inputs` | `&[Expression]` | a list of input expression trees |
* `inputs: &[Expression]` - a list of input expression trees. ### Return Value
* Return value - result of evaluating the custom syntax expression. Return value is the result of evaluating the custom syntax expression.
### Access Arguments ### Access Arguments
@ -283,12 +286,12 @@ engine.register_custom_syntax_raw(
"perform", "perform",
|stream| match stream.len() { |stream| match stream.len() {
// perform ... // perform ...
1 => Ok(Some("$ident$".into())), 1 => Ok(Some("$ident$".to_string())),
// perform command ... // perform command ...
2 => match stream[1].as_str() { 2 => match stream[1].as_str() {
"action" => Ok(Some("$expr$".into())), "action" => Ok(Some("$expr$".to_string())),
"hello" => Ok(Some("world".into())), "hello" => Ok(Some("world".to_string())),
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".into())), "update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
"cleanup" => Ok(None), "cleanup" => Ok(None),
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput( cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
format!("Improper command: {}", cmd))), format!("Improper command: {}", cmd))),
@ -320,21 +323,20 @@ engine.register_custom_syntax_raw(
The custom syntax parser has the following signature: The custom syntax parser has the following signature:
> `Fn(stream: &[String]) -> Result<Option<ImmutableString>, ParseError>` > `Fn(stream: &[String]) -> Result<Option<String>, ParseError>`
where: where:
* `stream: &[String]` - a slice of symbols that have been parsed so far, perhaps containing the following: | Parameter | Type | Description |
* `$expr$` - an expression | --------- | :---------: | -------------------------------------------------------------------------------------------------- |
* `$block$` - a statement block | `stream` | `&[String]` | a slice of symbols that have been parsed so far, possibly containing `"$expr$"` and/or `"$block$"` |
### Return Value ### Return Value
The return value is `Result<Option<ImmutableString>, ParseError>` where: The return value is `Result<Option<String>, ParseError>` where:
* `Ok(None)` - parsing complete and there are no more symbols to match. | Value | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
* `Ok(Some(symbol))` - next symbol to match. | `Ok(None)` | parsing complete and there are no more symbols to match |
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
* `Err(ParseError)` - error is reflected back to the [`Engine`]. | `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(message), Position::none())` to indicate that there is a syntax error, but it can be any `ParseError`. |
Normally this is `ParseErrorType::BadInput` to indicate that there is a syntax error, but it can be any error.

View File

@ -67,27 +67,23 @@ The function signature passed to `Engine::on_var` takes the following form:
where: where:
* `name: &str` - variable name. | Parameter | Type | Description |
| ----------------------------- | :-----------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
* `index: usize` - an offset from the bottom of the current [`Scope`] that the variable is supposed to reside. | `name` | `&str` | variable name |
Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`. | `index` | `usize` | an offset from the bottom of the current [`Scope`] that the variable is supposed to reside.<br/>Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`.<br/>If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. |
| `context` | `&EvalContext` | reference to the current evaluation _context_ |
If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. | - `context.scope` | `&Scope` | reference to the current [`Scope`] containing all variables up to the current evaluation position |
| - `context.engine()` | `&Engine` | reference to the current [`Engine`] |
* `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields: | - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
* `context.scope: &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position. | - `context.this_ptr()` | `Option<&Dynamic>` | reference to the current bound [`this`] pointer, if any |
* `context.engine(): &Engine` - reference to the current [`Engine`]. | - `context.call_level()` | `usize` | the current nesting level of function calls |
* `context.iter_namespaces(): impl Iterator<Item = &Module>` - iterator of the namespaces (as [modules]) containing all script-defined functions.
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any.
* `context.call_level(): usize` - the current nesting level of function calls.
### Return Value ### Return Value
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where: The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
* `Ok(None)` - normal variable resolution process should continue, meaning to continue searching through the [`Scope`]. | Value | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
* `Ok(Some(Dynamic))` - wrapped [`Dynamic`] is taken as the value of the variable, which is treated as a constant. | `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
* `Err(Box<EvalAltResult>)` - error is reflected back to the [`Engine`]. | `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::none())` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
Normally this is `EvalAltResult::ErrorVariableNotFound` to indicate that the variable does not exist, but it can be any error.

View File

@ -65,18 +65,17 @@ The function signature passed to `Engine::register_raw_fn` takes the following f
where: where:
* `T: Clone` - return type of the function. | Parameter | Type | Description |
| ----------------------------- | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `T` | `impl Clone` | return type of the function |
| `context` | `NativeCallContext` | the current _native call context_ |
| - `context.engine()` | `&Engine` | the current [`Engine`], with all configurations and settings.<br/>This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer]. |
| - `context.iter_namespaces()` | `impl Iterator<Item = &Module>` | iterator of the namespaces (as [modules]) containing all script-defined functions |
| `args` | `&mut [&mut Dynamic]` | a slice containing `&mut` references to [`Dynamic`] values.<br/>The slice is guaranteed to contain enough arguments _of the correct types_. |
* `context: NativeCallContext` - the current _native call context_, which exposes the following: ### Return value
* `context.engine(): &Engine` - the current [`Engine`], with all configurations and settings. The return value is the result of the function call.
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`], or calling a [function pointer].
* `context.iter_namespaces(): impl Iterator<Item = &Module>` - iterator of the namespaces (as [modules]) containing all script-defined functions.
* `args: &mut [&mut Dynamic]` - a slice containing `&mut` references to [`Dynamic`] values.
The slice is guaranteed to contain enough arguments _of the correct types_.
* Return value - result of the function call.
Remember, in Rhai, all arguments _except_ the _first_ one are always passed by _value_ (i.e. cloned). Remember, in Rhai, all arguments _except_ the _first_ one are always passed by _value_ (i.e. cloned).
Therefore, it is unnecessary to ever mutate any argument except the first one, as all mutations Therefore, it is unnecessary to ever mutate any argument except the first one, as all mutations
@ -118,16 +117,16 @@ let mut engine = Engine::new();
engine.register_raw_fn( engine.register_raw_fn(
"bar", "bar",
&[ &[
std::any::TypeId::of::<i64>(), // parameter types std::any::TypeId::of::<i64>(), // parameter types
std::any::TypeId::of::<FnPtr>(), std::any::TypeId::of::<FnPtr>(),
std::any::TypeId::of::<i64>(), std::any::TypeId::of::<i64>(),
], ],
|context, args| { |context, args| {
// 'args' is guaranteed to contain enough arguments of the correct types // 'args' is guaranteed to contain enough arguments of the correct types
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
let value = args[2].clone(); // 3rd argument - function argument let value = args[2].clone(); // 3rd argument - function argument
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
// Use 'FnPtr::call_dynamic' to call the function pointer. // Use 'FnPtr::call_dynamic' to call the function pointer.
// Beware, private script-defined functions will not be found. // Beware, private script-defined functions will not be found.
@ -135,13 +134,14 @@ engine.register_raw_fn(
}, },
); );
let result = engine.eval::<i64>(r#" let result = engine.eval::<i64>(
r#"
fn foo(x) { this += x; } // script-defined function 'foo' fn foo(x) { this += x; } // script-defined function 'foo'
let x = 41; // object let x = 41; // object
x.bar(Fn("foo"), 1); // pass 'foo' as function pointer x.bar(Fn("foo"), 1); // pass 'foo' as function pointer
x x
"#)?; "#)?;
``` ```
@ -158,7 +158,8 @@ Shared values are implemented as `Rc<RefCell<Dynamic>>` (`Arc<RwLock<Dynamic>>`
If the value is _not_ a shared value, or if running under [`no_closure`] where there is If the value is _not_ a shared value, or if running under [`no_closure`] where there is
no [capturing][automatic currying], this API de-sugars to a simple `Dynamic::downcast_ref` and no [capturing][automatic currying], this API de-sugars to a simple `Dynamic::downcast_ref` and
`Dynamic::downcast_mut`. `Dynamic::downcast_mut`. In other words, there is no locking and reference counting overhead
for the vast majority of non-shared values.
If the value is a shared value, then it is first locked and the returned lock guard If the value is a shared value, then it is first locked and the returned lock guard
then allows access to the underlying value in the specified type. then allows access to the underlying value in the specified type.

View File

@ -27,11 +27,11 @@ pub type FnCustomSyntaxEval =
/// A general expression parsing trait object. /// A general expression parsing trait object.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxParse = dyn Fn(&[String]) -> Result<Option<ImmutableString>, ParseError>; pub type FnCustomSyntaxParse = dyn Fn(&[String]) -> Result<Option<String>, ParseError>;
/// A general expression parsing trait object. /// A general expression parsing trait object.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type FnCustomSyntaxParse = pub type FnCustomSyntaxParse =
dyn Fn(&[String]) -> Result<Option<ImmutableString>, ParseError> + Send + Sync; dyn Fn(&[String]) -> Result<Option<String>, ParseError> + Send + Sync;
/// An expression sub-tree in an AST. /// An expression sub-tree in an AST.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@ -107,7 +107,7 @@ impl Engine {
) -> Result<&mut Self, ParseError> { ) -> Result<&mut Self, ParseError> {
let keywords = keywords.as_ref(); let keywords = keywords.as_ref();
let mut segments: StaticVec<ImmutableString> = Default::default(); let mut segments: StaticVec<_> = Default::default();
for s in keywords { for s in keywords {
let s = s.as_ref().trim(); let s = s.as_ref().trim();
@ -161,7 +161,7 @@ impl Engine {
} }
}; };
segments.push(seg.into()); segments.push(seg);
} }
// If the syntax has no keywords, just ignore the registration // If the syntax has no keywords, just ignore the registration
@ -204,7 +204,7 @@ impl Engine {
pub fn register_custom_syntax_raw( pub fn register_custom_syntax_raw(
&mut self, &mut self,
key: impl Into<ImmutableString>, key: impl Into<ImmutableString>,
parse: impl Fn(&[String]) -> Result<Option<ImmutableString>, ParseError> + SendSync + 'static, parse: impl Fn(&[String]) -> Result<Option<String>, ParseError> + SendSync + 'static,
new_vars: isize, new_vars: isize,
func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
+ SendSync + SendSync