Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung
2020-07-26 22:38:35 +08:00
11 changed files with 196 additions and 250 deletions

View File

@@ -19,6 +19,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET};
use crate::{
engine::{make_getter, make_setter, Map},
fn_register::RegisterFn,
token::Token,
};
#[cfg(not(feature = "no_function"))]
@@ -62,147 +63,6 @@ impl Engine {
self
}
/// Register a function of no parameters with the `Engine`.
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
#[deprecated(note = "this function is volatile and may change")]
pub fn register_raw_fn_0<T: Variant + Clone>(
&mut self,
name: &str,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> &mut Self {
self.global_module.set_raw_fn(name, &[], func);
self
}
/// Register a function of one parameter with the `Engine`.
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
///
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
/// which is guaranteed to contain enough arguments of the correct types.
///
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
///
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated(note = "this function is volatile and may change")]
pub fn register_raw_fn_1<A: Variant + Clone, T: Variant + Clone>(
&mut self,
name: &str,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> &mut Self {
self.global_module
.set_raw_fn(name, &[TypeId::of::<A>()], func);
self
}
/// Register a function of two parameters with the `Engine`.
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
///
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
/// which is guaranteed to contain enough arguments of the correct types.
///
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
///
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated(note = "this function is volatile and may change")]
pub fn register_raw_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
name: &str,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> &mut Self {
self.global_module
.set_raw_fn(name, &[TypeId::of::<A>(), TypeId::of::<B>()], func);
self
}
/// Register a function of three parameters with the `Engine`.
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
///
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
/// which is guaranteed to contain enough arguments of the correct types.
///
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
///
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated(note = "this function is volatile and may change")]
pub fn register_raw_fn_3<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
T: Variant + Clone,
>(
&mut self,
name: &str,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> &mut Self {
self.global_module.set_raw_fn(
name,
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
func,
);
self
}
/// Register a function of four parameters with the `Engine`.
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
///
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
/// which is guaranteed to contain enough arguments of the correct types.
///
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
///
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated(note = "this function is volatile and may change")]
pub fn register_raw_fn_4<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
D: Variant + Clone,
T: Variant + Clone,
>(
&mut self,
name: &str,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> &mut Self {
self.global_module.set_raw_fn(
name,
&[
TypeId::of::<A>(),
TypeId::of::<B>(),
TypeId::of::<C>(),
TypeId::of::<D>(),
],
func,
);
self
}
/// Register a custom type for use with the `Engine`.
/// The type must implement `Clone`.
///
@@ -731,7 +591,7 @@ impl Engine {
scripts: &[&str],
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let stream = lex(scripts, self);
let stream = lex(scripts, None, self);
self.parse(&mut stream.peekable(), scope, optimization_level)
}
@@ -856,7 +716,19 @@ impl Engine {
// Trims the JSON string and add a '#' in front
let scripts = ["#", json.trim()];
let stream = lex(&scripts, self);
let stream = lex(
&scripts,
if has_null {
Some(Box::new(|token| match token {
// If `null` is present, make sure `null` is treated as a variable
Token::Reserved(s) if s == "null" => Token::Identifier(s),
_ => token,
}))
} else {
None
},
self,
);
let ast =
self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
@@ -937,7 +809,7 @@ impl Engine {
script: &str,
) -> Result<AST, ParseError> {
let scripts = [script];
let stream = lex(&scripts, self);
let stream = lex(&scripts, None, self);
{
let mut peekable = stream.peekable();
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
@@ -1092,7 +964,7 @@ impl Engine {
script: &str,
) -> Result<T, Box<EvalAltResult>> {
let scripts = [script];
let stream = lex(&scripts, self);
let stream = lex(&scripts, None, self);
// No need to optimize a lone expression
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
@@ -1225,7 +1097,7 @@ impl Engine {
script: &str,
) -> Result<(), Box<EvalAltResult>> {
let scripts = [script];
let stream = lex(&scripts, self);
let stream = lex(&scripts, None, self);
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast)
}

View File

@@ -172,7 +172,7 @@ impl ParseErrorType {
Self::VariableExpected => "Expecting name of a variable",
Self::Reserved(_) => "Invalid use of reserved keyword",
Self::ExprExpected(_) => "Expecting an expression",
Self::FnMissingName => "Expecting name in function declaration",
Self::FnMissingName => "Expecting function name in function declaration",
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",

View File

@@ -18,6 +18,8 @@ use num_traits::{
CheckedShr, CheckedSub,
};
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
use crate::stdlib::ops::{BitAnd, BitOr, BitXor};
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
@@ -145,12 +147,18 @@ where
}
}
// Bit operators
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
fn binary_and<T: BitAnd>(x: T, y: T) -> FuncReturn<<T as BitAnd>::Output> {
Ok(x & y)
}
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
fn binary_or<T: BitOr>(x: T, y: T) -> FuncReturn<<T as BitOr>::Output> {
Ok(x | y)
}
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
fn binary_xor<T: BitXor>(x: T, y: T) -> FuncReturn<<T as BitXor>::Output> {
Ok(x ^ y)
}

View File

@@ -9,7 +9,7 @@ use crate::module::{Module, ModuleRef};
use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::FnCustomSyntaxEval;
use crate::token::{is_valid_identifier, Position, Token, TokenStream};
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_function"))]
@@ -1044,7 +1044,7 @@ fn parse_paren_expr(
}
/// Parse a function call.
fn parse_call_expr(
fn parse_fn_call(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
@@ -1553,8 +1553,12 @@ fn parse_primary(
Expr::Variable(Box::new(((s, settings.pos), None, 0, index)))
}
// Function call is allowed to have reserved keyword
Token::Reserved(s) if s != KEYWORD_THIS && input.peek().unwrap().0 == Token::LeftParen => {
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
Token::Reserved(s) if input.peek().unwrap().0 == Token::LeftParen => {
if is_keyword_function(&s) {
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
} else {
return Err(PERR::Reserved(s).into_err(settings.pos));
}
}
// Access to `this` as a variable is OK
Token::Reserved(s) if s == KEYWORD_THIS && input.peek().unwrap().0 != Token::LeftParen => {
@@ -1601,7 +1605,7 @@ fn parse_primary(
(Expr::Variable(x), Token::LeftParen) => {
let ((name, pos), modules, _, _) = *x;
settings.pos = pos;
parse_call_expr(input, state, lib, name, modules, settings.level_up())?
parse_fn_call(input, state, lib, name, modules, settings.level_up())?
}
(Expr::Property(_), _) => unreachable!(),
// module access
@@ -2882,14 +2886,12 @@ fn parse_fn(
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let name = match input.next().unwrap() {
(Token::Identifier(s), _) | (Token::Custom(s), _) | (Token::Reserved(s), _)
if s != KEYWORD_THIS && is_valid_identifier(s.chars()) =>
{
s
}
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
};
let (token, pos) = input.next().unwrap();
let name = token.into_function_name().map_err(|t| match t {
Token::Reserved(s) => PERR::Reserved(s).into_err(pos),
_ => PERR::FnMissingName.into_err(pos),
})?;
match input.peek().unwrap() {
(Token::LeftParen, _) => eat_token(input, Token::LeftParen),

View File

@@ -492,9 +492,12 @@ impl Token {
#[cfg(feature = "no_module")]
"import" | "export" | "as" => Reserved(syntax.into()),
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" => {
Reserved(syntax.into())
}
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
| "new" | "use" | "module" | "package" | "var" | "static" | "with" | "do" | "each"
| "then" | "goto" | "exit" | "switch" | "match" | "case" | "try" | "catch"
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "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 => Reserved(syntax.into()),
@@ -666,6 +669,15 @@ impl Token {
}
}
/// Convert a token into a function name, if possible.
pub fn into_function_name(self) -> Result<String, Self> {
match self {
Self::Reserved(s) if is_keyword_function(&s) => Ok(s),
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
_ => Err(self),
}
}
/// Is this token a custom keyword?
pub fn is_custom(&self) -> bool {
match self {
@@ -714,6 +726,16 @@ pub trait InputStream {
fn peek_next(&mut self) -> Option<char>;
}
pub fn is_keyword_function(name: &str) -> bool {
name == KEYWORD_PRINT
|| name == KEYWORD_DEBUG
|| name == KEYWORD_TYPE_OF
|| name == KEYWORD_EVAL
|| name == KEYWORD_FN_PTR
|| name == KEYWORD_FN_PTR_CALL
|| name == KEYWORD_FN_PTR_CURRY
}
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
let mut first_alphabetic = false;
@@ -1452,13 +1474,15 @@ pub struct TokenIterator<'a, 'e> {
pos: Position,
/// Input character stream.
stream: MultiInputsStream<'a>,
/// A processor function (if any) that maps a token to another.
map: Option<Box<dyn Fn(Token) -> Token>>,
}
impl<'a> Iterator for TokenIterator<'a, '_> {
type Item = (Token, Position);
fn next(&mut self) -> Option<Self::Item> {
match (
let token = match (
get_next_token(&mut self.stream, &mut self.state, &mut self.pos),
self.engine.disabled_symbols.as_ref(),
self.engine.custom_keywords.as_ref(),
@@ -1540,12 +1564,27 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
Some((Token::Reserved(token.syntax().into()), pos))
}
(r, _, _) => r,
};
match token {
None => None,
Some((token, pos)) => {
if let Some(ref map) = self.map {
Some((map(token), pos))
} else {
Some((token, pos))
}
}
}
}
}
/// Tokenize an input text stream.
pub fn lex<'a, 'e>(input: &'a [&'a str], engine: &'e Engine) -> TokenIterator<'a, 'e> {
pub fn lex<'a, 'e>(
input: &'a [&'a str],
map: Option<Box<dyn Fn(Token) -> Token>>,
engine: &'e Engine,
) -> TokenIterator<'a, 'e> {
TokenIterator {
engine,
state: TokenizeState {
@@ -1563,5 +1602,6 @@ pub fn lex<'a, 'e>(input: &'a [&'a str], engine: &'e Engine) -> TokenIterator<'a
streams: input.iter().map(|s| s.chars().peekable()).collect(),
index: 0,
},
map,
}
}