Add custom operators.

This commit is contained in:
Stephen Chung
2020-07-05 17:41:45 +08:00
parent 936a3ff44a
commit e390dd73e6
13 changed files with 326 additions and 139 deletions

View File

@@ -332,36 +332,26 @@ pub enum ReturnType {
Exception,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
struct ParseState {
#[derive(Clone)]
struct ParseState<'e> {
/// Reference to the scripting `Engine`.
engine: &'e Engine,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
pub stack: Vec<(String, ScopeEntryType)>,
stack: Vec<(String, ScopeEntryType)>,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
pub modules: Vec<String>,
modules: Vec<String>,
/// Maximum levels of expression nesting.
pub max_expr_depth: usize,
/// Maximum length of a string.
pub max_string_size: usize,
/// Maximum length of an array.
pub max_array_size: usize,
/// Maximum number of properties in a map.
pub max_map_size: usize,
max_expr_depth: usize,
}
impl ParseState {
impl<'e> ParseState<'e> {
/// Create a new `ParseState`.
pub fn new(
max_expr_depth: usize,
max_string_size: usize,
max_array_size: usize,
max_map_size: usize,
) -> Self {
pub fn new(engine: &'e Engine, max_expr_depth: usize) -> Self {
Self {
engine,
max_expr_depth,
max_string_size,
max_array_size,
max_map_size,
..Default::default()
stack: Default::default(),
modules: Default::default(),
}
}
/// Find a variable by name in the `ParseState`, searching in reverse.
@@ -1206,10 +1196,10 @@ fn parse_array_literal(
let mut arr = StaticVec::new();
while !input.peek().unwrap().0.is_eof() {
if state.max_array_size > 0 && arr.len() >= state.max_array_size {
if state.engine.max_array_size > 0 && arr.len() >= state.engine.max_array_size {
return Err(PERR::LiteralTooLarge(
"Size of array literal".to_string(),
state.max_array_size,
state.engine.max_array_size,
)
.into_err(input.peek().unwrap().1));
}
@@ -1306,10 +1296,10 @@ fn parse_map_literal(
}
};
if state.max_map_size > 0 && map.len() >= state.max_map_size {
if state.engine.max_map_size > 0 && map.len() >= state.engine.max_map_size {
return Err(PERR::LiteralTooLarge(
"Number of properties in object map literal".to_string(),
state.max_map_size,
state.engine.max_map_size,
)
.into_err(input.peek().unwrap().1));
}
@@ -1866,7 +1856,8 @@ fn parse_binary_op(
loop {
let (current_op, _) = input.peek().unwrap();
let precedence = current_op.precedence();
let custom = state.engine.custom_keywords.as_ref();
let precedence = current_op.precedence(custom);
let bind_right = current_op.is_bind_right();
// Bind left to the parent lhs expression if precedence is higher
@@ -1879,7 +1870,7 @@ fn parse_binary_op(
let rhs = parse_unary(input, state, settings)?;
let next_precedence = input.peek().unwrap().0.precedence();
let next_precedence = input.peek().unwrap().0.precedence(custom);
// Bind to right if the next operator has higher precedence
// If same precedence, then check if the operator binds right
@@ -1949,6 +1940,19 @@ fn parse_binary_op(
make_dot_expr(current_lhs, rhs, pos)?
}
Token::Custom(s)
if state
.engine
.custom_keywords
.as_ref()
.map(|c| c.contains_key(&s))
.unwrap_or(false) =>
{
// Accept non-native functions for custom operators
let op = (op.0, false, op.2);
Expr::FnCall(Box::new((op, None, hash, args, None)))
}
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
};
}
@@ -2467,7 +2471,7 @@ fn parse_fn(
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let name = match input.next().unwrap() {
(Token::Identifier(s), _) => s,
(Token::Identifier(s), _) | (Token::Custom(s), _) => s,
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
};
@@ -2555,12 +2559,7 @@ impl Engine {
scope: &Scope,
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let mut state = ParseState::new(
self.max_expr_depth,
self.max_string_size,
self.max_array_size,
self.max_map_size,
);
let mut state = ParseState::new(self, self.max_expr_depth);
let settings = ParseSettings {
allow_if_expr: false,
allow_stmt_expr: false,
@@ -2596,12 +2595,7 @@ impl Engine {
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements = Vec::<Stmt>::new();
let mut functions = HashMap::<u64, ScriptFnDef, _>::with_hasher(StraightHasherBuilder);
let mut state = ParseState::new(
self.max_expr_depth,
self.max_string_size,
self.max_array_size,
self.max_map_size,
);
let mut state = ParseState::new(self, self.max_expr_depth);
while !input.peek().unwrap().0.is_eof() {
// Collect all the function definitions
@@ -2615,12 +2609,7 @@ impl Engine {
match input.peek().unwrap() {
(Token::Fn, pos) => {
let mut state = ParseState::new(
self.max_function_expr_depth,
self.max_string_size,
self.max_array_size,
self.max_map_size,
);
let mut state = ParseState::new(self, self.max_function_expr_depth);
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,