2020-11-20 09:52:28 +01:00
//! Module implementing custom syntax for [`Engine`].
2022-07-05 16:59:03 +02:00
#![ cfg(not(feature = " no_custom_syntax " )) ]
2020-07-23 12:40:42 +02:00
2020-10-29 04:37:51 +01:00
use crate ::ast ::Expr ;
2022-06-26 08:10:09 +02:00
use crate ::func ::SendSync ;
2021-12-26 05:16:48 +01:00
use crate ::parser ::ParseResult ;
2021-11-13 15:36:23 +01:00
use crate ::tokenizer ::{ is_valid_identifier , Token } ;
use crate ::types ::dynamic ::Variant ;
2020-11-16 16:10:14 +01:00
use crate ::{
2022-02-06 16:02:59 +01:00
reify , Engine , EvalContext , Identifier , ImmutableString , LexError , Position , RhaiResult ,
2022-05-07 09:54:44 +02:00
StaticVec ,
2020-11-16 16:10:14 +01:00
} ;
2022-02-06 16:02:59 +01:00
use std ::ops ::Deref ;
2021-04-17 09:15:54 +02:00
#[ cfg(feature = " no_std " ) ]
use std ::prelude ::v1 ::* ;
2020-07-09 13:54:28 +02:00
2021-07-10 09:50:31 +02:00
/// Collection of special markers for custom syntax definition.
pub mod markers {
/// Special marker for matching an expression.
pub const CUSTOM_SYNTAX_MARKER_EXPR : & str = " $expr$ " ;
/// Special marker for matching a statements block.
pub const CUSTOM_SYNTAX_MARKER_BLOCK : & str = " $block$ " ;
/// Special marker for matching an identifier.
pub const CUSTOM_SYNTAX_MARKER_IDENT : & str = " $ident$ " ;
/// Special marker for matching a single symbol.
pub const CUSTOM_SYNTAX_MARKER_SYMBOL : & str = " $symbol$ " ;
/// Special marker for matching a string literal.
pub const CUSTOM_SYNTAX_MARKER_STRING : & str = " $string$ " ;
/// Special marker for matching an integer number.
pub const CUSTOM_SYNTAX_MARKER_INT : & str = " $int$ " ;
/// Special marker for matching a floating-point number.
#[ cfg(not(feature = " no_float " )) ]
pub const CUSTOM_SYNTAX_MARKER_FLOAT : & str = " $float$ " ;
/// Special marker for matching a boolean value.
pub const CUSTOM_SYNTAX_MARKER_BOOL : & str = " $bool$ " ;
2021-10-25 16:41:42 +02:00
/// Special marker for identifying the custom syntax variant.
pub const CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT : & str = " $$ " ;
2021-07-10 09:50:31 +02:00
}
2020-12-13 07:31:24 +01:00
2020-07-23 09:49:09 +02:00
/// A general expression evaluation trait object.
2020-07-09 13:54:28 +02:00
#[ cfg(not(feature = " sync " )) ]
2021-03-02 08:02:28 +01:00
pub type FnCustomSyntaxEval = dyn Fn ( & mut EvalContext , & [ Expression ] ) -> RhaiResult ;
2020-07-23 09:49:09 +02:00
/// A general expression evaluation trait object.
2020-07-09 13:54:28 +02:00
#[ cfg(feature = " sync " ) ]
2021-03-02 08:02:28 +01:00
pub type FnCustomSyntaxEval = dyn Fn ( & mut EvalContext , & [ Expression ] ) -> RhaiResult + Send + Sync ;
2020-07-09 13:54:28 +02:00
2020-10-25 14:57:18 +01:00
/// A general expression parsing trait object.
#[ cfg(not(feature = " sync " )) ]
2020-12-13 07:31:24 +01:00
pub type FnCustomSyntaxParse =
2021-12-26 05:16:48 +01:00
dyn Fn ( & [ ImmutableString ] , & str ) -> ParseResult < Option < ImmutableString > > ;
2020-10-25 14:57:18 +01:00
/// A general expression parsing trait object.
#[ cfg(feature = " sync " ) ]
pub type FnCustomSyntaxParse =
2021-12-26 05:16:48 +01:00
dyn Fn ( & [ ImmutableString ] , & str ) -> ParseResult < Option < ImmutableString > > + Send + Sync ;
2020-10-25 14:57:18 +01:00
2020-11-20 09:52:28 +01:00
/// An expression sub-tree in an [`AST`][crate::AST].
2020-11-13 11:32:18 +01:00
#[ derive(Debug, Clone) ]
2020-07-22 11:05:13 +02:00
pub struct Expression < ' a > ( & ' a Expr ) ;
impl < ' a > From < & ' a Expr > for Expression < ' a > {
2020-10-08 16:25:50 +02:00
#[ inline(always) ]
2020-07-22 11:05:13 +02:00
fn from ( expr : & ' a Expr ) -> Self {
Self ( expr )
}
}
impl Expression < '_ > {
2022-05-07 09:54:44 +02:00
/// Evaluate this [expression tree][Expression] within an [evaluation context][`EvalContext`].
///
/// # WARNING - Low Level API
///
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
#[ inline(always) ]
pub fn eval_with_context ( & self , context : & mut EvalContext ) -> RhaiResult {
context . eval_expression_tree ( self )
}
2022-07-06 06:56:15 +02:00
/// Evaluate this [expression tree][Expression] within an [evaluation context][`EvalContext`].
///
/// The following option is available:
///
2022-07-18 17:01:03 +02:00
/// * whether to rewind the [`Scope`][crate::Scope] after evaluation if the expression is a [`StmtBlock`][crate::ast::StmtBlock]
2022-07-06 06:56:15 +02:00
///
/// # WARNING - Unstable API
///
/// This API is volatile and may change in the future.
///
/// # WARNING - Low Level API
///
/// This function is _extremely_ low level. It evaluates an expression from an [`AST`][crate::AST].
#[ inline(always) ]
pub fn eval_with_context_raw (
& self ,
context : & mut EvalContext ,
rewind_scope : bool ,
) -> RhaiResult {
#[ allow(deprecated) ]
context . eval_expression_tree_raw ( self , rewind_scope )
}
2021-12-16 11:01:49 +01:00
/// Get the value of this expression if it is a variable name or a string constant.
///
/// Returns [`None`] also if the constant is not of the specified type.
2020-10-08 16:25:50 +02:00
#[ inline(always) ]
2021-06-12 16:47:43 +02:00
#[ must_use ]
2021-12-16 11:01:49 +01:00
pub fn get_string_value ( & self ) -> Option < & str > {
match self . 0 {
2022-01-29 04:09:43 +01:00
#[ cfg(not(feature = " no_module " )) ]
2022-03-05 10:57:23 +01:00
Expr ::Variable ( x , .. ) if ! x . 1. is_empty ( ) = > None ,
Expr ::Variable ( x , .. ) = > Some ( x . 3. as_str ( ) ) ,
2022-02-08 02:02:15 +01:00
Expr ::StringConstant ( x , .. ) = > Some ( x . as_str ( ) ) ,
2021-12-16 11:01:49 +01:00
_ = > None ,
}
2020-07-22 11:05:13 +02:00
}
/// Get the position of this expression.
2020-10-08 16:25:50 +02:00
#[ inline(always) ]
2021-06-12 16:47:43 +02:00
#[ must_use ]
2021-06-29 12:41:03 +02:00
pub const fn position ( & self ) -> Position {
2020-07-22 11:05:13 +02:00
self . 0. position ( )
}
2021-06-10 04:16:39 +02:00
/// Get the value of this expression if it is a literal constant.
2022-02-24 03:36:20 +01:00
///
2021-06-16 06:24:36 +02:00
/// Supports [`INT`][crate::INT], [`FLOAT`][crate::FLOAT], `()`, `char`, `bool` and
/// [`ImmutableString`][crate::ImmutableString].
///
/// Returns [`None`] also if the constant is not of the specified type.
2021-08-14 09:10:37 +02:00
#[ inline ]
2021-06-12 16:47:43 +02:00
#[ must_use ]
2021-06-16 06:24:36 +02:00
pub fn get_literal_value < T : Variant > ( & self ) -> Option < T > {
// Coded this way in order to maximally leverage potentials for dead-code removal.
2022-02-06 01:29:05 +01:00
match self . 0 {
2022-02-08 02:25:53 +01:00
Expr ::IntegerConstant ( x , .. ) = > reify! ( * x = > Option < T > ) ,
2021-06-16 06:24:36 +02:00
2022-02-06 01:29:05 +01:00
#[ cfg(not(feature = " no_float " )) ]
2022-02-08 02:25:53 +01:00
Expr ::FloatConstant ( x , .. ) = > reify! ( * x = > Option < T > ) ,
2022-02-06 01:29:05 +01:00
2022-02-08 02:25:53 +01:00
Expr ::CharConstant ( x , .. ) = > reify! ( * x = > Option < T > ) ,
Expr ::StringConstant ( x , .. ) = > reify! ( x . clone ( ) = > Option < T > ) ,
2022-03-05 10:57:23 +01:00
Expr ::Variable ( x , .. ) = > {
let x : ImmutableString = x . 3. clone ( ) . into ( ) ;
2022-02-08 02:25:53 +01:00
reify! ( x = > Option < T > )
2022-02-06 01:29:05 +01:00
}
2022-02-08 02:25:53 +01:00
Expr ::BoolConstant ( x , .. ) = > reify! ( * x = > Option < T > ) ,
Expr ::Unit ( .. ) = > reify! ( ( ) = > Option < T > ) ,
2022-02-06 01:29:05 +01:00
_ = > None ,
2021-06-16 06:24:36 +02:00
}
2021-06-10 04:16:39 +02:00
}
2020-07-22 11:05:13 +02:00
}
2021-12-06 11:50:37 +01:00
impl AsRef < Expr > for Expression < '_ > {
#[ inline(always) ]
fn as_ref ( & self ) -> & Expr {
2022-07-20 14:16:35 +02:00
self . 0
2021-12-06 11:50:37 +01:00
}
}
impl Deref for Expression < '_ > {
type Target = Expr ;
#[ inline(always) ]
fn deref ( & self ) -> & Self ::Target {
2022-07-20 14:16:35 +02:00
self . 0
2021-12-06 11:50:37 +01:00
}
}
2020-11-06 09:27:40 +01:00
/// Definition of a custom syntax definition.
2020-07-09 13:54:28 +02:00
pub struct CustomSyntax {
2021-08-26 17:58:41 +02:00
/// A parsing function to return the next token in a custom syntax based on the
/// symbols parsed so far.
2020-10-25 14:57:18 +01:00
pub parse : Box < FnCustomSyntaxParse > ,
2020-11-06 09:27:40 +01:00
/// Custom syntax implementation function.
2022-05-07 09:54:44 +02:00
pub func : Box < FnCustomSyntaxEval > ,
2021-05-11 04:58:28 +02:00
/// Any variables added/removed in the scope?
2021-07-04 10:31:01 +02:00
pub scope_may_be_changed : bool ,
2020-07-09 13:54:28 +02:00
}
impl Engine {
2020-11-20 09:52:28 +01:00
/// Register a custom syntax with the [`Engine`].
2020-10-11 15:58:11 +02:00
///
2022-07-05 16:59:03 +02:00
/// Not available under `no_custom_syntax`.
///
2022-07-20 14:16:35 +02:00
/// * `symbols` holds a slice of strings that define the custom syntax.
2021-07-04 10:40:15 +02:00
/// * `scope_may_be_changed` specifies variables _may_ be added/removed by this custom syntax.
2020-10-11 15:58:11 +02:00
/// * `func` is the implementation function.
2021-01-14 12:07:03 +01:00
///
2021-08-26 17:58:41 +02:00
/// ## Note on `symbols`
///
/// * Whitespaces around symbols are stripped.
/// * Symbols that are all-whitespace or empty are ignored.
/// * If `symbols` does not contain at least one valid token, then the custom syntax registration
/// is simply ignored.
///
2021-07-04 10:40:15 +02:00
/// ## Note on `scope_may_be_changed`
2021-01-14 12:07:03 +01:00
///
2021-07-04 10:40:15 +02:00
/// If `scope_may_be_changed` is `true`, then _size_ of the current [`Scope`][crate::Scope]
/// _may_ be modified by this custom syntax.
2021-01-14 12:07:03 +01:00
///
2021-07-04 10:40:15 +02:00
/// Adding new variables and/or removing variables count.
2021-05-11 04:58:28 +02:00
///
2021-07-04 10:40:15 +02:00
/// Simply modifying the values of existing variables does NOT count, as the _size_ of the
/// current [`Scope`][crate::Scope] is unchanged, so `false` should be passed.
2021-05-11 04:58:28 +02:00
///
2021-07-04 10:40:15 +02:00
/// Replacing one variable with another (i.e. adding a new variable and removing one variable at
/// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also
/// does NOT count, so `false` should be passed.
2022-01-04 08:22:48 +01:00
pub fn register_custom_syntax < S : AsRef < str > + Into < Identifier > > (
2020-07-09 13:54:28 +02:00
& mut self ,
2022-01-04 08:22:48 +01:00
symbols : impl AsRef < [ S ] > ,
2021-07-04 10:31:01 +02:00
scope_may_be_changed : bool ,
2021-03-02 08:02:28 +01:00
func : impl Fn ( & mut EvalContext , & [ Expression ] ) -> RhaiResult + SendSync + 'static ,
2021-12-26 05:16:48 +01:00
) -> ParseResult < & mut Self > {
2021-07-10 09:50:31 +02:00
use markers ::* ;
2021-09-11 13:40:40 +02:00
let mut segments = StaticVec ::< ImmutableString > ::new ( ) ;
2020-07-09 13:54:28 +02:00
2022-01-04 08:22:48 +01:00
for s in symbols . as_ref ( ) {
2020-07-22 11:05:13 +02:00
let s = s . as_ref ( ) . trim ( ) ;
2021-08-26 17:58:41 +02:00
// Skip empty symbols
2020-07-22 11:05:13 +02:00
if s . is_empty ( ) {
continue ;
}
2020-12-26 16:21:09 +01:00
let token = Token ::lookup_from_syntax ( s ) ;
2020-07-22 11:05:13 +02:00
let seg = match s {
2020-07-09 13:54:28 +02:00
// Markers not in first position
2021-06-28 12:06:05 +02:00
CUSTOM_SYNTAX_MARKER_IDENT
2021-07-10 09:50:31 +02:00
| CUSTOM_SYNTAX_MARKER_SYMBOL
2021-06-28 12:06:05 +02:00
| CUSTOM_SYNTAX_MARKER_EXPR
| CUSTOM_SYNTAX_MARKER_BLOCK
| CUSTOM_SYNTAX_MARKER_BOOL
| CUSTOM_SYNTAX_MARKER_INT
| CUSTOM_SYNTAX_MARKER_STRING
2021-06-10 04:16:39 +02:00
if ! segments . is_empty ( ) = >
{
s . into ( )
}
// Markers not in first position
#[ cfg(not(feature = " no_float " )) ]
2021-06-28 12:06:05 +02:00
CUSTOM_SYNTAX_MARKER_FLOAT if ! segments . is_empty ( ) = > s . into ( ) ,
2020-10-25 14:57:18 +01:00
// Standard or reserved keyword/symbol not in first position
2021-08-26 17:58:41 +02:00
_ if ! segments . is_empty ( ) & & token . is_some ( ) = > {
2020-12-26 16:21:09 +01:00
// Make it a custom keyword/symbol if it is disabled or reserved
2022-03-03 06:02:57 +01:00
if ( ( ! self . disabled_symbols . is_empty ( ) & & self . disabled_symbols . contains ( s ) )
2021-08-26 17:58:41 +02:00
| | token . map_or ( false , | v | v . is_reserved ( ) ) )
2022-03-03 06:02:57 +01:00
& & ( self . custom_keywords . is_empty ( )
| | ! self . custom_keywords . contains_key ( s ) )
2020-12-26 16:21:09 +01:00
{
2020-10-25 14:57:18 +01:00
self . custom_keywords . insert ( s . into ( ) , None ) ;
2020-07-10 16:01:47 +02:00
}
s . into ( )
}
2021-08-13 13:23:20 +02:00
// Standard keyword in first position but not disabled
_ if segments . is_empty ( )
2022-07-27 12:04:59 +02:00
& & token . as_ref ( ) . map_or ( false , Token ::is_standard_keyword )
2022-03-03 06:02:57 +01:00
& & ( self . disabled_symbols . is_empty ( ) | | ! self . disabled_symbols . contains ( s ) ) = >
2020-10-25 14:57:18 +01:00
{
2020-11-21 08:08:18 +01:00
return Err ( LexError ::ImproperSymbol (
s . to_string ( ) ,
format! (
2022-08-11 13:01:23 +02:00
" Improper symbol for custom syntax at position #{}: '{s}' " ,
2020-11-21 08:08:18 +01:00
segments . len ( ) + 1 ,
) ,
)
2021-07-24 08:11:16 +02:00
. into_err ( Position ::NONE ) ) ;
2020-10-25 14:57:18 +01:00
}
// Identifier in first position
2021-08-13 13:23:20 +02:00
_ if segments . is_empty ( ) & & is_valid_identifier ( s . chars ( ) ) = > {
2020-12-26 16:21:09 +01:00
// Make it a custom keyword/symbol if it is disabled or reserved
2022-03-03 06:02:57 +01:00
if ( ! self . disabled_symbols . is_empty ( ) & & self . disabled_symbols . contains ( s ) )
| | token . map_or ( false , | v | v . is_reserved ( ) )
2022-07-27 10:04:24 +02:00
& & self . custom_keywords . is_empty ( )
| | ! self . custom_keywords . contains_key ( s )
2020-12-26 16:21:09 +01:00
{
2022-07-27 10:04:24 +02:00
self . custom_keywords . insert ( s . into ( ) , None ) ;
2020-07-09 13:54:28 +02:00
}
s . into ( )
}
// Anything else is an error
2020-07-22 11:05:13 +02:00
_ = > {
2020-11-21 08:08:18 +01:00
return Err ( LexError ::ImproperSymbol (
s . to_string ( ) ,
format! (
2022-08-11 13:01:23 +02:00
" Improper symbol for custom syntax at position #{}: '{s}' " ,
2020-11-21 08:08:18 +01:00
segments . len ( ) + 1 ,
) ,
)
2021-07-24 08:11:16 +02:00
. into_err ( Position ::NONE ) ) ;
2020-07-22 11:05:13 +02:00
}
2020-07-09 13:54:28 +02:00
} ;
2020-10-27 02:56:37 +01:00
segments . push ( seg ) ;
2020-07-09 13:54:28 +02:00
}
2021-08-26 17:58:41 +02:00
// If the syntax has no symbols, just ignore the registration
2020-07-22 11:05:13 +02:00
if segments . is_empty ( ) {
return Ok ( self ) ;
}
2020-10-25 14:57:18 +01:00
// The first keyword is the discriminator
let key = segments [ 0 ] . clone ( ) ;
self . register_custom_syntax_raw (
key ,
// Construct the parsing function
2020-12-15 12:23:30 +01:00
move | stream , _ | {
2020-10-25 14:57:18 +01:00
if stream . len ( ) > = segments . len ( ) {
Ok ( None )
} else {
Ok ( Some ( segments [ stream . len ( ) ] . clone ( ) ) )
}
} ,
2021-07-04 10:31:01 +02:00
scope_may_be_changed ,
2020-10-25 14:57:18 +01:00
func ,
) ;
Ok ( self )
}
2020-11-20 09:52:28 +01:00
/// Register a custom syntax with the [`Engine`].
2020-10-25 14:57:18 +01:00
///
2022-07-05 16:59:03 +02:00
/// Not available under `no_custom_syntax`.
///
2021-01-02 16:30:10 +01:00
/// # WARNING - Low Level API
2020-10-25 14:57:18 +01:00
///
/// This function is very low level.
///
2021-07-04 10:31:01 +02:00
/// * `scope_may_be_changed` specifies variables have been added/removed by this custom syntax.
2020-10-25 14:57:18 +01:00
/// * `parse` is the parsing function.
/// * `func` is the implementation function.
///
2021-08-26 17:58:41 +02:00
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
/// Otherwise, they won't be recognized.
2021-09-25 09:57:38 +02:00
///
/// # Implementation Function Signature
///
/// The implementation function has the following signature:
///
2022-05-02 06:14:53 +02:00
/// `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
2021-09-25 09:57:38 +02:00
///
/// where:
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
/// `$ident$` and other literal markers are replaced by the actual text
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
///
/// ## Return value
///
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
2021-12-31 16:01:34 +01:00
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
2020-10-25 14:57:18 +01:00
pub fn register_custom_syntax_raw (
& mut self ,
2021-03-29 05:36:02 +02:00
key : impl Into < Identifier > ,
2021-12-26 05:16:48 +01:00
parse : impl Fn ( & [ ImmutableString ] , & str ) -> ParseResult < Option < ImmutableString > >
2020-12-13 07:31:24 +01:00
+ SendSync
+ 'static ,
2021-07-04 10:31:01 +02:00
scope_may_be_changed : bool ,
2021-03-02 08:02:28 +01:00
func : impl Fn ( & mut EvalContext , & [ Expression ] ) -> RhaiResult + SendSync + 'static ,
2020-10-25 14:57:18 +01:00
) -> & mut Self {
2021-05-03 07:45:41 +02:00
self . custom_syntax . insert (
key . into ( ) ,
2021-06-29 12:25:20 +02:00
CustomSyntax {
2021-05-03 07:45:41 +02:00
parse : Box ::new ( parse ) ,
2022-05-07 09:54:44 +02:00
func : Box ::new ( func ) ,
2021-07-04 10:31:01 +02:00
scope_may_be_changed ,
2022-07-20 14:28:17 +02:00
} ,
2021-05-03 07:45:41 +02:00
) ;
2020-10-25 14:57:18 +01:00
self
2020-07-09 13:54:28 +02:00
}
}