rhai/src/syntax.rs

203 lines
6.3 KiB
Rust
Raw Normal View History

//! Module implementing custom syntax for `Engine`.
2020-07-09 13:54:28 +02:00
use crate::any::Dynamic;
2020-10-11 15:58:11 +02:00
use crate::engine::{Engine, EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
2020-07-22 15:32:56 +02:00
use crate::error::{LexError, ParseError};
2020-07-09 13:54:28 +02:00
use crate::fn_native::{SendSync, Shared};
use crate::parser::Expr;
2020-07-09 13:54:28 +02:00
use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position, Token};
2020-10-10 07:41:55 +02:00
use crate::StaticVec;
2020-07-09 13:54:28 +02:00
use crate::stdlib::{
boxed::Box,
fmt, format,
2020-07-09 13:54:28 +02:00
string::{String, ToString},
};
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"))]
2020-10-11 15:58:11 +02:00
pub type FnCustomSyntaxEval =
dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>;
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")]
2020-10-11 15:58:11 +02:00
pub type FnCustomSyntaxEval = dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
2020-07-09 13:54:28 +02:00
+ Send
+ Sync;
2020-07-23 09:49:09 +02:00
/// An expression sub-tree in an AST.
#[derive(Debug, Clone, Hash)]
pub struct Expression<'a>(&'a Expr);
impl<'a> From<&'a Expr> for Expression<'a> {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn from(expr: &'a Expr) -> Self {
Self(expr)
}
}
impl Expression<'_> {
/// If this expression is a variable name, return it. Otherwise `None`.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn get_variable_name(&self) -> Option<&str> {
match self.0 {
Expr::Variable(x) => Some((x.0).0.as_str()),
_ => None,
}
}
/// Get the expression.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub(crate) fn expr(&self) -> &Expr {
&self.0
}
/// Get the position of this expression.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn position(&self) -> Position {
self.0.position()
}
}
2020-10-11 15:58:11 +02:00
impl EvalContext<'_, '_, '_, '_, '_, '_> {
/// Evaluate an expression tree.
///
/// ## WARNING - Low Level API
///
/// This function is very low level. It evaluates an expression from an AST.
#[inline(always)]
pub fn eval_expression_tree(
&mut self,
scope: &mut Scope,
expr: &Expression,
) -> Result<Dynamic, Box<EvalAltResult>> {
self.engine.eval_expr(
scope,
self.mods,
self.state,
self.lib,
self.this_ptr,
expr.expr(),
self.level,
)
}
}
2020-07-09 13:54:28 +02:00
#[derive(Clone)]
pub struct CustomSyntax {
pub segments: StaticVec<String>,
pub func: Shared<FnCustomSyntaxEval>,
pub scope_delta: isize,
}
impl fmt::Debug for CustomSyntax {
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-09 13:54:28 +02:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.segments, f)
}
}
impl Engine {
2020-10-11 15:58:11 +02:00
/// Register a custom syntax with the `Engine`.
///
/// * `keywords` holds a slice of strings that define the custom syntax.
/// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative).
/// * `func` is the implementation function.
2020-07-10 16:01:47 +02:00
pub fn register_custom_syntax<S: AsRef<str> + ToString>(
2020-07-09 13:54:28 +02:00
&mut self,
keywords: &[S],
2020-10-11 15:58:11 +02:00
new_vars: isize,
func: impl Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
2020-07-09 13:54:28 +02:00
+ SendSync
+ 'static,
) -> Result<&mut Self, ParseError> {
2020-07-09 13:54:28 +02:00
let mut segments: StaticVec<_> = Default::default();
for s in keywords {
let s = s.as_ref().trim();
// skip empty keywords
if s.is_empty() {
continue;
}
let seg = match s {
2020-07-09 13:54:28 +02:00
// Markers not in first position
2020-07-10 16:01:47 +02:00
MARKER_EXPR | MARKER_BLOCK | MARKER_IDENT if !segments.is_empty() => s.to_string(),
2020-07-09 13:54:28 +02:00
// Standard symbols not in first position
2020-07-10 16:01:47 +02:00
s if !segments.is_empty() && Token::lookup_from_syntax(s).is_some() => {
// Make it a custom keyword/operator if it is a disabled standard keyword/operator
// or a reserved keyword/operator.
2020-07-10 16:01:47 +02:00
if self
.disabled_symbols
.as_ref()
.map(|d| d.contains(s))
.unwrap_or(false)
|| Token::lookup_from_syntax(s)
.map(|token| token.is_reserved())
.unwrap_or(false)
2020-07-10 16:01:47 +02:00
{
// If symbol is disabled, make it a custom keyword
if self.custom_keywords.is_none() {
self.custom_keywords = Some(Default::default());
}
if !self.custom_keywords.as_ref().unwrap().contains_key(s) {
self.custom_keywords.as_mut().unwrap().insert(s.into(), 0);
}
}
s.into()
}
// Identifier
2020-07-09 13:54:28 +02:00
s if is_valid_identifier(s.chars()) => {
if self.custom_keywords.is_none() {
self.custom_keywords = Some(Default::default());
}
if !self.custom_keywords.as_ref().unwrap().contains_key(s) {
self.custom_keywords.as_mut().unwrap().insert(s.into(), 0);
}
s.into()
}
// Anything else is an error
_ => {
return Err(LexError::ImproperSymbol(format!(
"Improper symbol for custom syntax: '{}'",
s
))
.into_err(Position::none())
.into());
}
2020-07-09 13:54:28 +02:00
};
segments.push(seg);
}
// If the syntax has no keywords, just ignore the registration
if segments.is_empty() {
return Ok(self);
}
// Remove the first keyword as the discriminator
2020-07-09 13:54:28 +02:00
let key = segments.remove(0);
let syntax = CustomSyntax {
segments,
func: (Box::new(func) as Box<FnCustomSyntaxEval>).into(),
2020-10-11 15:58:11 +02:00
scope_delta: new_vars,
2020-07-09 13:54:28 +02:00
};
if self.custom_syntax.is_none() {
self.custom_syntax = Some(Default::default());
}
self.custom_syntax
.as_mut()
.unwrap()
.insert(key, syntax.into());
2020-07-12 05:46:53 +02:00
Ok(self)
2020-07-09 13:54:28 +02:00
}
}