Expr::Switch -> Stmt::Switch.
This commit is contained in:
parent
0182117759
commit
a63f14b59c
33
src/ast.rs
33
src/ast.rs
@ -583,6 +583,12 @@ pub enum Stmt {
|
||||
Noop(Position),
|
||||
/// if expr { stmt } else { stmt }
|
||||
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
||||
/// switch expr { literal or _ => stmt, ... }
|
||||
Switch(
|
||||
Expr,
|
||||
Box<(HashMap<u64, Stmt, StraightHasherBuilder>, Option<Stmt>)>,
|
||||
Position,
|
||||
),
|
||||
/// while expr { stmt }
|
||||
While(Expr, Box<Stmt>, Position),
|
||||
/// loop { stmt }
|
||||
@ -642,6 +648,7 @@ impl Stmt {
|
||||
| Self::Block(_, pos)
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::If(_, _, pos)
|
||||
| Self::Switch(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::For(_, _, pos)
|
||||
@ -670,6 +677,7 @@ impl Stmt {
|
||||
| Self::Block(_, pos)
|
||||
| Self::Assignment(_, pos)
|
||||
| Self::If(_, _, pos)
|
||||
| Self::Switch(_, _, pos)
|
||||
| Self::While(_, _, pos)
|
||||
| Self::Loop(_, pos)
|
||||
| Self::For(_, _, pos)
|
||||
@ -697,6 +705,7 @@ impl Stmt {
|
||||
pub fn is_self_terminated(&self) -> bool {
|
||||
match self {
|
||||
Self::If(_, _, _)
|
||||
| Self::Switch(_, _, _)
|
||||
| Self::While(_, _, _)
|
||||
| Self::Loop(_, _)
|
||||
| Self::For(_, _, _)
|
||||
@ -726,10 +735,16 @@ impl Stmt {
|
||||
match self {
|
||||
Self::Noop(_) => true,
|
||||
Self::Expr(expr) => expr.is_pure(),
|
||||
Self::If(condition, x, _) if x.1.is_some() => {
|
||||
condition.is_pure() && x.0.is_pure() && x.1.as_ref().unwrap().is_pure()
|
||||
Self::If(condition, x, _) => {
|
||||
condition.is_pure()
|
||||
&& x.0.is_pure()
|
||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||
}
|
||||
Self::Switch(expr, x, _) => {
|
||||
expr.is_pure()
|
||||
&& x.0.values().all(Stmt::is_pure)
|
||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||
}
|
||||
Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure(),
|
||||
Self::While(condition, block, _) => condition.is_pure() && block.is_pure(),
|
||||
Self::Loop(block, _) => block.is_pure(),
|
||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
||||
@ -872,15 +887,6 @@ pub enum Expr {
|
||||
Dot(Box<BinaryExpr>, Position),
|
||||
/// expr[expr]
|
||||
Index(Box<BinaryExpr>, Position),
|
||||
/// switch expr { literal or _ => stmt, ... }
|
||||
Switch(
|
||||
Box<(
|
||||
Expr,
|
||||
HashMap<u64, Stmt, StraightHasherBuilder>,
|
||||
Option<Stmt>,
|
||||
)>,
|
||||
Position,
|
||||
),
|
||||
/// lhs in rhs
|
||||
In(Box<BinaryExpr>, Position),
|
||||
/// lhs && rhs
|
||||
@ -963,7 +969,6 @@ impl Expr {
|
||||
Self::Stmt(_, pos) => *pos,
|
||||
Self::Variable(x) => (x.3).pos,
|
||||
Self::FnCall(_, pos) => *pos,
|
||||
Self::Switch(_, pos) => *pos,
|
||||
|
||||
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
||||
|
||||
@ -995,7 +1000,6 @@ impl Expr {
|
||||
Self::Property(x) => (x.1).pos = new_pos,
|
||||
Self::Stmt(_, pos) => *pos = new_pos,
|
||||
Self::FnCall(_, pos) => *pos = new_pos,
|
||||
Self::Switch(_, pos) => *pos = new_pos,
|
||||
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
||||
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos,
|
||||
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
||||
@ -1089,7 +1093,6 @@ impl Expr {
|
||||
Self::StringConstant(_, _)
|
||||
| Self::Stmt(_, _)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Switch(_, _)
|
||||
| Self::Dot(_, _)
|
||||
| Self::Index(_, _)
|
||||
| Self::Array(_, _)
|
||||
|
@ -1803,27 +1803,6 @@ impl Engine {
|
||||
Expr::False(_) => Ok(false.into()),
|
||||
Expr::Unit(_) => Ok(().into()),
|
||||
|
||||
Expr::Switch(x, _) => {
|
||||
let (match_expr, table, def_stmt) = x.as_ref();
|
||||
|
||||
let hasher = &mut get_hasher();
|
||||
self.eval_expr_as_target(
|
||||
scope, mods, state, lib, this_ptr, match_expr, false, level,
|
||||
)?
|
||||
.0
|
||||
.as_ref()
|
||||
.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
if let Some(stmt) = table.get(&hash) {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
} else if let Some(def_stmt) = def_stmt {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||
} else {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Custom(custom, _) => {
|
||||
let expressions = custom
|
||||
.keywords()
|
||||
@ -2063,7 +2042,7 @@ impl Engine {
|
||||
self.eval_statements(scope, mods, state, lib, this_ptr, statements, level)
|
||||
}
|
||||
|
||||
// If-else statement
|
||||
// If statement
|
||||
Stmt::If(expr, x, _) => {
|
||||
let (if_block, else_block) = x.as_ref();
|
||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||
@ -2080,6 +2059,28 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
|
||||
// Switch statement
|
||||
Stmt::Switch(match_expr, x, _) => {
|
||||
let (table, def_stmt) = x.as_ref();
|
||||
|
||||
let hasher = &mut get_hasher();
|
||||
self.eval_expr_as_target(
|
||||
scope, mods, state, lib, this_ptr, match_expr, false, level,
|
||||
)?
|
||||
.0
|
||||
.as_ref()
|
||||
.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
if let Some(stmt) = table.get(&hash) {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
} else if let Some(def_stmt) = def_stmt {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||
} else {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
|
||||
// While loop
|
||||
Stmt::While(expr, body, _) => loop {
|
||||
match self
|
||||
|
@ -291,6 +291,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
optimize_expr(&mut x.2, state);
|
||||
}
|
||||
},
|
||||
|
||||
// if false { if_block } -> Noop
|
||||
Stmt::If(Expr::False(pos), x, _) if x.1.is_none() => {
|
||||
state.set_dirty();
|
||||
@ -348,6 +349,42 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// switch const { ... }
|
||||
Stmt::Switch(expr, x, pos) if expr.is_constant() => {
|
||||
let value = expr.get_constant_value().unwrap();
|
||||
let hasher = &mut get_hasher();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
state.set_dirty();
|
||||
|
||||
let table = &mut x.0;
|
||||
|
||||
if let Some(stmt) = table.get_mut(&hash) {
|
||||
optimize_stmt(stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos);
|
||||
} else if let Some(def_stmt) = x.1.as_mut() {
|
||||
optimize_stmt(def_stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos);
|
||||
} else {
|
||||
*expr = Expr::Unit(*pos);
|
||||
}
|
||||
}
|
||||
// switch
|
||||
Stmt::Switch(expr, x, _) => {
|
||||
optimize_expr(expr, state);
|
||||
x.0.values_mut()
|
||||
.for_each(|stmt| optimize_stmt(stmt, state, true));
|
||||
if let Some(def_stmt) = x.1.as_mut() {
|
||||
optimize_stmt(def_stmt, state, true);
|
||||
|
||||
match def_stmt {
|
||||
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// while false { block } -> Noop
|
||||
Stmt::While(Expr::False(pos), _, _) => {
|
||||
state.set_dirty();
|
||||
@ -717,42 +754,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
||||
*expr = result;
|
||||
}
|
||||
|
||||
// switch const { ... }
|
||||
Expr::Switch(x, pos) if x.0.is_constant() => {
|
||||
let value = x.0.get_constant_value().unwrap();
|
||||
let hasher = &mut get_hasher();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
state.set_dirty();
|
||||
|
||||
let table = &mut x.1;
|
||||
|
||||
if let Some(stmt) = table.get_mut(&hash) {
|
||||
optimize_stmt(stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos);
|
||||
} else if let Some(def_stmt) = x.2.as_mut() {
|
||||
optimize_stmt(def_stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos);
|
||||
} else {
|
||||
*expr = Expr::Unit(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
// switch
|
||||
Expr::Switch(x, _) => {
|
||||
optimize_expr(&mut x.0, state);
|
||||
x.1.values_mut().for_each(|stmt| optimize_stmt(stmt, state, true));
|
||||
if let Some(def_stmt) = x.2.as_mut() {
|
||||
optimize_stmt(def_stmt, state, true);
|
||||
|
||||
match def_stmt {
|
||||
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.2 = None,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom syntax
|
||||
Expr::Custom(x, _) => x.keywords.iter_mut().for_each(|expr| optimize_expr(expr, state)),
|
||||
|
||||
|
@ -192,6 +192,8 @@ struct ParseSettings {
|
||||
allow_anonymous_fn: bool,
|
||||
/// Is if-expression allowed?
|
||||
allow_if_expr: bool,
|
||||
/// Is switch expression allowed?
|
||||
allow_switch_expr: bool,
|
||||
/// Is statement-expression allowed?
|
||||
allow_stmt_expr: bool,
|
||||
/// Current expression nesting level.
|
||||
@ -793,8 +795,12 @@ fn parse_switch(
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
lib: &mut FunctionsLib,
|
||||
settings: ParseSettings,
|
||||
) -> Result<Expr, ParseError> {
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Stmt, ParseError> {
|
||||
// switch ...
|
||||
let token_pos = eat_token(input, Token::Switch);
|
||||
settings.pos = token_pos;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
@ -901,8 +907,9 @@ fn parse_switch(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Expr::Switch(
|
||||
Box::new((item, table, def_stmt)),
|
||||
Ok(Stmt::Switch(
|
||||
item,
|
||||
Box::new((table, def_stmt)),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -1004,7 +1011,6 @@ fn parse_primary(
|
||||
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
|
||||
Token::Switch => parse_switch(input, state, lib, settings.level_up())?,
|
||||
Token::True => Expr::True(settings.pos),
|
||||
Token::False => Expr::False(settings.pos),
|
||||
Token::LexError(err) => return Err(err.into_err(settings.pos)),
|
||||
@ -1136,6 +1142,12 @@ fn parse_unary(
|
||||
block.push(parse_if(input, state, lib, settings.level_up())?);
|
||||
Ok(Expr::Stmt(Box::new(block), settings.pos))
|
||||
}
|
||||
// Switch statement is allowed to act as expressions
|
||||
Token::Switch if settings.allow_switch_expr => {
|
||||
let mut block: StaticVec<_> = Default::default();
|
||||
block.push(parse_switch(input, state, lib, settings.level_up())?);
|
||||
Ok(Expr::Stmt(Box::new(block), settings.pos))
|
||||
}
|
||||
// -expr
|
||||
Token::UnaryMinus => {
|
||||
let pos = eat_token(input, Token::UnaryMinus);
|
||||
@ -1219,6 +1231,7 @@ fn parse_unary(
|
||||
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: true,
|
||||
allow_switch_expr: true,
|
||||
allow_stmt_expr: true,
|
||||
allow_anonymous_fn: true,
|
||||
is_global: false,
|
||||
@ -2388,6 +2401,7 @@ fn parse_stmt(
|
||||
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: true,
|
||||
allow_switch_expr: true,
|
||||
allow_stmt_expr: true,
|
||||
allow_anonymous_fn: true,
|
||||
is_global: false,
|
||||
@ -2827,6 +2841,7 @@ impl Engine {
|
||||
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: false,
|
||||
allow_switch_expr: false,
|
||||
allow_stmt_expr: false,
|
||||
allow_anonymous_fn: false,
|
||||
is_global: true,
|
||||
@ -2879,6 +2894,7 @@ impl Engine {
|
||||
while !input.peek().unwrap().0.is_eof() {
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: true,
|
||||
allow_switch_expr: true,
|
||||
allow_stmt_expr: true,
|
||||
allow_anonymous_fn: true,
|
||||
is_global: true,
|
||||
|
@ -1,14 +1,25 @@
|
||||
use rhai::{Engine, INT};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_comments() {
|
||||
fn test_comments() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert!(engine
|
||||
.eval::<INT>("let x = 5; x // I am a single line comment, yay!")
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let x = 42; x // I am a single line comment, yay!")?,
|
||||
42
|
||||
);
|
||||
|
||||
assert!(engine
|
||||
.eval::<INT>("let /* I am a multi-line comment, yay! */ x = 5; x")
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"
|
||||
let /* I am a
|
||||
multi-line
|
||||
comment, yay!
|
||||
*/ x = 42; x
|
||||
"#
|
||||
)?,
|
||||
42
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, Scope, INT};
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
||||
|
Loading…
Reference in New Issue
Block a user