Add switch case conditions.
This commit is contained in:
parent
c5128e15d5
commit
980a13ca42
@ -35,6 +35,7 @@ New features
|
|||||||
* String interpolation support is added via the `` `... ${`` ... ``} ...` `` syntax.
|
* String interpolation support is added via the `` `... ${`` ... ``} ...` `` syntax.
|
||||||
* `FileModuleResolver` resolves relative paths under the parent path (i.e. the path holding the script that does the loading). This allows seamless cross-loading of scripts from a directory hierarchy instead of having all relative paths load from the current working directory.
|
* `FileModuleResolver` resolves relative paths under the parent path (i.e. the path holding the script that does the loading). This allows seamless cross-loading of scripts from a directory hierarchy instead of having all relative paths load from the current working directory.
|
||||||
* Negative index to an array or string yields the appropriate element/character counting from the _end_.
|
* Negative index to an array or string yields the appropriate element/character counting from the _end_.
|
||||||
|
* `switch` statement cases can now have an optional `if` clause.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.15
|
Version 0.19.15
|
||||||
|
114
src/ast.rs
114
src/ast.rs
@ -187,10 +187,7 @@ impl AST {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: None,
|
source: None,
|
||||||
body: StmtBlock {
|
body: StmtBlock(statements.into_iter().collect(), Position::NONE),
|
||||||
statements: statements.into_iter().collect(),
|
|
||||||
pos: Position::NONE,
|
|
||||||
},
|
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
@ -205,10 +202,7 @@ impl AST {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Some(source.into()),
|
source: Some(source.into()),
|
||||||
body: StmtBlock {
|
body: StmtBlock(statements.into_iter().collect(), Position::NONE),
|
||||||
statements: statements.into_iter().collect(),
|
|
||||||
pos: Position::NONE,
|
|
||||||
},
|
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
@ -245,7 +239,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn statements(&self) -> &[Stmt] {
|
pub(crate) fn statements(&self) -> &[Stmt] {
|
||||||
&self.body.statements
|
&self.body.0
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Get the statements.
|
/// _(INTERNALS)_ Get the statements.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -259,7 +253,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
|
pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
|
||||||
&mut self.body.statements
|
&mut self.body.0
|
||||||
}
|
}
|
||||||
/// Get the internal shared [`Module`] containing all script-defined functions.
|
/// Get the internal shared [`Module`] containing all script-defined functions.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -535,8 +529,7 @@ impl AST {
|
|||||||
let merged = match (body.is_empty(), other.body.is_empty()) {
|
let merged = match (body.is_empty(), other.body.is_empty()) {
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
let mut body = body.clone();
|
let mut body = body.clone();
|
||||||
body.statements
|
body.0.extend(other.body.0.iter().cloned());
|
||||||
.extend(other.body.statements.iter().cloned());
|
|
||||||
body
|
body
|
||||||
}
|
}
|
||||||
(false, true) => body.clone(),
|
(false, true) => body.clone(),
|
||||||
@ -550,9 +543,9 @@ impl AST {
|
|||||||
functions.merge_filtered(&other.functions, &filter);
|
functions.merge_filtered(&other.functions, &filter);
|
||||||
|
|
||||||
if let Some(source) = source {
|
if let Some(source) = source {
|
||||||
Self::new_with_source(merged.statements, functions, source)
|
Self::new_with_source(merged.0, functions, source)
|
||||||
} else {
|
} else {
|
||||||
Self::new(merged.statements, functions)
|
Self::new(merged.0, functions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
||||||
@ -612,9 +605,7 @@ impl AST {
|
|||||||
other: Self,
|
other: Self,
|
||||||
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.body
|
self.body.0.extend(other.body.0.into_iter());
|
||||||
.statements
|
|
||||||
.extend(other.body.statements.into_iter());
|
|
||||||
|
|
||||||
if !other.functions.is_empty() {
|
if !other.functions.is_empty() {
|
||||||
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter);
|
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter);
|
||||||
@ -705,7 +696,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
for stmt in self.iter_fn_def().flat_map(|f| f.body.statements.iter()) {
|
for stmt in self.iter_fn_def().flat_map(|f| f.body.0.iter()) {
|
||||||
if !stmt.walk(path, on_node) {
|
if !stmt.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -833,30 +824,27 @@ impl<'a> From<&'a Expr> for ASTNode<'a> {
|
|||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Clone, Hash, Default)]
|
#[derive(Clone, Hash, Default)]
|
||||||
pub struct StmtBlock {
|
pub struct StmtBlock(pub StaticVec<Stmt>, pub Position);
|
||||||
pub statements: StaticVec<Stmt>,
|
|
||||||
pub pos: Position,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StmtBlock {
|
impl StmtBlock {
|
||||||
/// Is this statements block empty?
|
/// Is this statements block empty?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.statements.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
/// Number of statements in this statements block.
|
/// Number of statements in this statements block.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.statements.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for StmtBlock {
|
impl fmt::Debug for StmtBlock {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.statements, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
if !self.pos.is_none() {
|
if !self.1.is_none() {
|
||||||
write!(f, " @ {:?}", self.pos)?;
|
write!(f, " @ {:?}", self.1)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -874,10 +862,10 @@ pub enum Stmt {
|
|||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
|
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
|
||||||
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(
|
||||||
Expr,
|
Expr,
|
||||||
Box<(BTreeMap<u64, Box<StmtBlock>>, StmtBlock)>,
|
Box<(BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>, StmtBlock)>,
|
||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
@ -930,18 +918,11 @@ impl From<Stmt> for StmtBlock {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(stmt: Stmt) -> Self {
|
fn from(stmt: Stmt) -> Self {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Block(block, pos) => Self {
|
Stmt::Block(block, pos) => Self(block.into(), pos),
|
||||||
statements: block.into(),
|
Stmt::Noop(pos) => Self(Default::default(), pos),
|
||||||
pos,
|
|
||||||
},
|
|
||||||
Stmt::Noop(pos) => Self {
|
|
||||||
statements: Default::default(),
|
|
||||||
pos,
|
|
||||||
},
|
|
||||||
_ => {
|
_ => {
|
||||||
let pos = stmt.position();
|
let pos = stmt.position();
|
||||||
let statements = vec![stmt].into();
|
Self(vec![stmt].into(), pos)
|
||||||
Self { statements, pos }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1081,28 +1062,26 @@ impl Stmt {
|
|||||||
Self::Expr(expr) => expr.is_pure(),
|
Self::Expr(expr) => expr.is_pure(),
|
||||||
Self::If(condition, x, _) => {
|
Self::If(condition, x, _) => {
|
||||||
condition.is_pure()
|
condition.is_pure()
|
||||||
&& x.0.statements.iter().all(Stmt::is_pure)
|
&& (x.0).0.iter().all(Stmt::is_pure)
|
||||||
&& x.1.statements.iter().all(Stmt::is_pure)
|
&& (x.1).0.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
Self::Switch(expr, x, _) => {
|
Self::Switch(expr, x, _) => {
|
||||||
expr.is_pure()
|
expr.is_pure()
|
||||||
&& x.0
|
&& x.0.values().all(|block| {
|
||||||
.values()
|
block.0.as_ref().map(Expr::is_pure).unwrap_or(true)
|
||||||
.flat_map(|block| block.statements.iter())
|
&& (block.1).0.iter().all(Stmt::is_pure)
|
||||||
.all(Stmt::is_pure)
|
})
|
||||||
&& x.1.statements.iter().all(Stmt::is_pure)
|
&& (x.1).0.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||||
condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
|
condition.is_pure() && block.0.iter().all(Stmt::is_pure)
|
||||||
}
|
|
||||||
Self::For(iterable, x, _) => {
|
|
||||||
iterable.is_pure() && x.1.statements.iter().all(Stmt::is_pure)
|
|
||||||
}
|
}
|
||||||
|
Self::For(iterable, x, _) => iterable.is_pure() && (x.1).0.iter().all(Stmt::is_pure),
|
||||||
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
||||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||||
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
|
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
|
||||||
Self::TryCatch(x, _, _) => {
|
Self::TryCatch(x, _, _) => {
|
||||||
x.0.statements.iter().all(Stmt::is_pure) && x.2.statements.iter().all(Stmt::is_pure)
|
(x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1168,12 +1147,12 @@ impl Stmt {
|
|||||||
if !e.walk(path, on_node) {
|
if !e.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in &x.0.statements {
|
for s in &(x.0).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in &x.1.statements {
|
for s in &(x.1).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1183,12 +1162,17 @@ impl Stmt {
|
|||||||
if !e.walk(path, on_node) {
|
if !e.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in x.0.values().flat_map(|block| block.statements.iter()) {
|
for b in x.0.values() {
|
||||||
|
if !b.0.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for s in &(b.1).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in &x.1.statements {
|
}
|
||||||
|
for s in &(x.1).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1198,7 +1182,7 @@ impl Stmt {
|
|||||||
if !e.walk(path, on_node) {
|
if !e.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in &s.statements {
|
for s in &s.0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1208,7 +1192,7 @@ impl Stmt {
|
|||||||
if !e.walk(path, on_node) {
|
if !e.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in &x.1.statements {
|
for s in &(x.1).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1230,12 +1214,12 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::TryCatch(x, _, _) => {
|
Self::TryCatch(x, _, _) => {
|
||||||
for s in &x.0.statements {
|
for s in &(x.0).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in &x.2.statements {
|
for s in &(x.2).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1681,8 +1665,8 @@ impl fmt::Debug for Expr {
|
|||||||
Self::Property(x) => write!(f, "Property({:?} @ {:?})", x.2.name, x.2.pos),
|
Self::Property(x) => write!(f, "Property({:?} @ {:?})", x.2.name, x.2.pos),
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
f.write_str("Stmt")?;
|
f.write_str("Stmt")?;
|
||||||
f.debug_list().entries(x.statements.iter()).finish()?;
|
f.debug_list().entries(x.0.iter()).finish()?;
|
||||||
write!(f, " @ {:?}", x.pos)
|
write!(f, " @ {:?}", x.1)
|
||||||
}
|
}
|
||||||
Self::FnCall(x, pos) => {
|
Self::FnCall(x, pos) => {
|
||||||
let mut ff = f.debug_struct("FnCall");
|
let mut ff = f.debug_struct("FnCall");
|
||||||
@ -1796,7 +1780,7 @@ impl Expr {
|
|||||||
Self::Array(_, pos) => *pos,
|
Self::Array(_, pos) => *pos,
|
||||||
Self::Map(_, pos) => *pos,
|
Self::Map(_, pos) => *pos,
|
||||||
Self::Property(x) => (x.2).pos,
|
Self::Property(x) => (x.2).pos,
|
||||||
Self::Stmt(x) => x.pos,
|
Self::Stmt(x) => x.1,
|
||||||
Self::Variable(_, pos, _) => *pos,
|
Self::Variable(_, pos, _) => *pos,
|
||||||
Self::FnCall(_, pos) => *pos,
|
Self::FnCall(_, pos) => *pos,
|
||||||
|
|
||||||
@ -1829,7 +1813,7 @@ impl Expr {
|
|||||||
Self::Map(_, pos) => *pos = new_pos,
|
Self::Map(_, pos) => *pos = new_pos,
|
||||||
Self::Variable(_, pos, _) => *pos = new_pos,
|
Self::Variable(_, pos, _) => *pos = new_pos,
|
||||||
Self::Property(x) => (x.2).pos = new_pos,
|
Self::Property(x) => (x.2).pos = new_pos,
|
||||||
Self::Stmt(x) => x.pos = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
Self::FnCall(_, pos) => *pos = new_pos,
|
||||||
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
|
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
|
||||||
Self::Unit(pos) => *pos = new_pos,
|
Self::Unit(pos) => *pos = new_pos,
|
||||||
@ -1853,7 +1837,7 @@ impl Expr {
|
|||||||
x.lhs.is_pure() && x.rhs.is_pure()
|
x.lhs.is_pure() && x.rhs.is_pure()
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure),
|
Self::Stmt(x) => x.0.iter().all(Stmt::is_pure),
|
||||||
|
|
||||||
Self::Variable(_, _, _) => true,
|
Self::Variable(_, _, _) => true,
|
||||||
|
|
||||||
@ -1959,7 +1943,7 @@ impl Expr {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
for s in &x.statements {
|
for s in &x.0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1733,8 +1733,7 @@ impl Engine {
|
|||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
||||||
Expr::Stmt(x) => {
|
Expr::Stmt(x) => {
|
||||||
let statements = &x.statements;
|
self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.0, true, level)
|
||||||
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
@ -2135,16 +2134,7 @@ impl Engine {
|
|||||||
|
|
||||||
// If statement
|
// If statement
|
||||||
Stmt::If(expr, x, _) => {
|
Stmt::If(expr, x, _) => {
|
||||||
let (
|
let (StmtBlock(if_stmt, _), StmtBlock(else_stmt, _)) = x.as_ref();
|
||||||
StmtBlock {
|
|
||||||
statements: if_stmt,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
StmtBlock {
|
|
||||||
statements: else_stmt,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) = x.as_ref();
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
|
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||||
@ -2180,16 +2170,33 @@ impl Engine {
|
|||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
table.get(&hash).map(|t| {
|
table.get(&hash).and_then(|t| {
|
||||||
let statements = &t.statements;
|
if let Some(condition) = &t.0 {
|
||||||
|
match self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, &condition, level)
|
||||||
|
.and_then(|v| {
|
||||||
|
v.as_bool().map_err(|err| {
|
||||||
|
self.make_type_mismatch_err::<bool>(
|
||||||
|
err,
|
||||||
|
condition.position(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
Ok(true) => (),
|
||||||
|
Ok(false) => return None,
|
||||||
|
Err(err) => return Some(Err(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !statements.is_empty() {
|
let statements = &(t.1).0;
|
||||||
|
|
||||||
|
Some(if !statements.is_empty() {
|
||||||
self.eval_stmt_block(
|
self.eval_stmt_block(
|
||||||
scope, mods, state, lib, this_ptr, statements, true, level,
|
scope, mods, state, lib, this_ptr, statements, true, level,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Non-hashable values never match any specific clause
|
// Non-hashable values never match any specific clause
|
||||||
@ -2197,7 +2204,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
// Default match clause
|
// Default match clause
|
||||||
let def_stmt = &def_stmt.statements;
|
let def_stmt = &def_stmt.0;
|
||||||
if !def_stmt.is_empty() {
|
if !def_stmt.is_empty() {
|
||||||
self.eval_stmt_block(
|
self.eval_stmt_block(
|
||||||
scope, mods, state, lib, this_ptr, def_stmt, true, level,
|
scope, mods, state, lib, this_ptr, def_stmt, true, level,
|
||||||
@ -2210,7 +2217,7 @@ impl Engine {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(expr, body, _) => {
|
Stmt::While(expr, body, _) => {
|
||||||
let body = &body.statements;
|
let body = &body.0;
|
||||||
loop {
|
loop {
|
||||||
let condition = if !expr.is_unit() {
|
let condition = if !expr.is_unit() {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
@ -2243,7 +2250,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Do loop
|
// Do loop
|
||||||
Stmt::Do(body, expr, is_while, _) => {
|
Stmt::Do(body, expr, is_while, _) => {
|
||||||
let body = &body.statements;
|
let body = &body.0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if !body.is_empty() {
|
if !body.is_empty() {
|
||||||
@ -2277,7 +2284,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, x, _) => {
|
||||||
let (Ident { name, .. }, StmtBlock { statements, pos }) = x.as_ref();
|
let (Ident { name, .. }, StmtBlock(statements, pos)) = x.as_ref();
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2360,17 +2367,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
Stmt::TryCatch(x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
let (
|
let (StmtBlock(try_body, _), err_var, StmtBlock(catch_body, _)) = x.as_ref();
|
||||||
StmtBlock {
|
|
||||||
statements: try_body,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
err_var,
|
|
||||||
StmtBlock {
|
|
||||||
statements: catch_body,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) = x.as_ref();
|
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt_block(scope, mods, state, lib, this_ptr, try_body, true, level)
|
.eval_stmt_block(scope, mods, state, lib, this_ptr, try_body, true, level)
|
||||||
|
@ -540,7 +540,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the function
|
// Evaluate the function
|
||||||
let body = &fn_def.body.statements;
|
let body = &fn_def.body.0;
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
|
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
|
||||||
|
167
src/optimize.rs
167
src/optimize.rs
@ -416,73 +416,107 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Stmt::If(Expr::BoolConstant(false, _), x, _) => {
|
Stmt::If(Expr::BoolConstant(false, _), x, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let else_block = mem::take(&mut x.1.statements).into_vec();
|
let else_block = mem::take(&mut (x.1).0).into_vec();
|
||||||
*stmt = match optimize_stmt_block(else_block, state, preserve_result, true, false) {
|
*stmt = match optimize_stmt_block(else_block, state, preserve_result, true, false) {
|
||||||
statements if statements.is_empty() => Stmt::Noop(x.1.pos),
|
statements if statements.is_empty() => Stmt::Noop((x.1).1),
|
||||||
statements => Stmt::Block(statements, x.1.pos),
|
statements => Stmt::Block(statements, (x.1).1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
Stmt::If(Expr::BoolConstant(true, _), x, _) => {
|
Stmt::If(Expr::BoolConstant(true, _), x, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let if_block = mem::take(&mut x.0.statements).into_vec();
|
let if_block = mem::take(&mut (x.0).0).into_vec();
|
||||||
*stmt = match optimize_stmt_block(if_block, state, preserve_result, true, false) {
|
*stmt = match optimize_stmt_block(if_block, state, preserve_result, true, false) {
|
||||||
statements if statements.is_empty() => Stmt::Noop(x.0.pos),
|
statements if statements.is_empty() => Stmt::Noop((x.0).1),
|
||||||
statements => Stmt::Block(statements, x.0.pos),
|
statements => Stmt::Block(statements, (x.0).1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::If(condition, x, _) => {
|
Stmt::If(condition, x, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
let if_block = mem::take(&mut x.0.statements).into_vec();
|
let if_block = mem::take(&mut (x.0).0).into_vec();
|
||||||
x.0.statements =
|
(x.0).0 = optimize_stmt_block(if_block, state, preserve_result, true, false).into();
|
||||||
optimize_stmt_block(if_block, state, preserve_result, true, false).into();
|
let else_block = mem::take(&mut (x.1).0).into_vec();
|
||||||
let else_block = mem::take(&mut x.1.statements).into_vec();
|
(x.1).0 = optimize_stmt_block(else_block, state, preserve_result, true, false).into();
|
||||||
x.1.statements =
|
|
||||||
optimize_stmt_block(else_block, state, preserve_result, true, false).into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
Stmt::Switch(expr, x, pos) if expr.is_constant() => {
|
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
|
||||||
let value = expr.get_constant_value().unwrap();
|
let value = match_expr.get_constant_value().unwrap();
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let table = &mut x.0;
|
let table = &mut x.0;
|
||||||
|
|
||||||
let (statements, new_pos) = if let Some(block) = table.get_mut(&hash) {
|
if let Some(block) = table.get_mut(&hash) {
|
||||||
let match_block = mem::take(&mut block.statements).into_vec();
|
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||||
(
|
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||||
optimize_stmt_block(match_block, state, true, true, false).into(),
|
optimize_expr(&mut condition, state);
|
||||||
block.pos,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let def_block = mem::take(&mut x.1.statements).into_vec();
|
|
||||||
(
|
|
||||||
optimize_stmt_block(def_block, state, true, true, false).into(),
|
|
||||||
if x.1.pos.is_none() { *pos } else { x.1.pos },
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
*expr = Expr::Stmt(Box::new(StmtBlock {
|
let def_block = mem::take(&mut (x.1).0).into_vec();
|
||||||
statements,
|
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||||
pos: new_pos,
|
let def_pos = if (x.1).1.is_none() { *pos } else { (x.1).1 };
|
||||||
}));
|
|
||||||
|
*stmt = Stmt::If(
|
||||||
|
condition,
|
||||||
|
Box::new((
|
||||||
|
mem::take(&mut block.1),
|
||||||
|
Stmt::Block(def_stmt, def_pos).into(),
|
||||||
|
)),
|
||||||
|
match_expr.position(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Promote the matched case
|
||||||
|
let StmtBlock(statements, new_pos) = mem::take(&mut block.1);
|
||||||
|
let statements =
|
||||||
|
optimize_stmt_block(statements.into_vec(), state, true, true, false);
|
||||||
|
*stmt = Stmt::Block(statements, new_pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Promote the default case
|
||||||
|
let def_block = mem::take(&mut (x.1).0).into_vec();
|
||||||
|
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||||
|
let def_pos = if (x.1).1.is_none() { *pos } else { (x.1).1 };
|
||||||
|
*stmt = Stmt::Block(def_stmt, def_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// switch
|
// switch
|
||||||
Stmt::Switch(expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
optimize_expr(expr, state);
|
optimize_expr(match_expr, state);
|
||||||
x.0.values_mut().for_each(|block| {
|
x.0.values_mut().for_each(|block| {
|
||||||
let match_block = mem::take(&mut block.statements).into_vec();
|
let condition = if let Some(mut condition) = mem::take(&mut block.0) {
|
||||||
block.statements =
|
optimize_expr(&mut condition, state);
|
||||||
optimize_stmt_block(match_block, state, preserve_result, true, false).into()
|
condition
|
||||||
|
} else {
|
||||||
|
Expr::Unit(Position::NONE)
|
||||||
|
};
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
|
||||||
|
_ => {
|
||||||
|
block.0 = Some(condition);
|
||||||
|
|
||||||
|
let match_block = mem::take(&mut (block.1).0).into_vec();
|
||||||
|
(block.1).0 =
|
||||||
|
optimize_stmt_block(match_block, state, preserve_result, true, false)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let def_block = mem::take(&mut x.1.statements).into_vec();
|
|
||||||
x.1.statements =
|
// Remove false cases
|
||||||
optimize_stmt_block(def_block, state, preserve_result, true, false).into()
|
while let Some((&key, _)) = x.0.iter().find(|(_, block)| match block.0 {
|
||||||
|
Some(Expr::BoolConstant(false, _)) => true,
|
||||||
|
_ => false,
|
||||||
|
}) {
|
||||||
|
state.set_dirty();
|
||||||
|
x.0.remove(&key);
|
||||||
|
}
|
||||||
|
|
||||||
|
let def_block = mem::take(&mut (x.1).0).into_vec();
|
||||||
|
(x.1).0 = optimize_stmt_block(def_block, state, preserve_result, true, false).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
@ -494,11 +528,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
Stmt::While(condition, body, _) => {
|
Stmt::While(condition, body, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
|
|
||||||
let block = mem::take(&mut body.statements).into_vec();
|
let block = mem::take(&mut body.0).into_vec();
|
||||||
body.statements = optimize_stmt_block(block, state, false, true, false).into();
|
body.0 = optimize_stmt_block(block, state, false, true, false).into();
|
||||||
|
|
||||||
if body.len() == 1 {
|
if body.len() == 1 {
|
||||||
match body.statements[0] {
|
match body.0[0] {
|
||||||
// while expr { break; } -> { expr; }
|
// while expr { break; } -> { expr; }
|
||||||
Stmt::Break(pos) => {
|
Stmt::Break(pos) => {
|
||||||
// Only a single break statement - turn into running the guard expression once
|
// Only a single break statement - turn into running the guard expression once
|
||||||
@ -521,23 +555,23 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
Stmt::Do(body, Expr::BoolConstant(true, _), false, _)
|
Stmt::Do(body, Expr::BoolConstant(true, _), false, _)
|
||||||
| Stmt::Do(body, Expr::BoolConstant(false, _), true, _) => {
|
| Stmt::Do(body, Expr::BoolConstant(false, _), true, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let block = mem::take(&mut body.statements).into_vec();
|
let block = mem::take(&mut body.0).into_vec();
|
||||||
*stmt = Stmt::Block(
|
*stmt = Stmt::Block(
|
||||||
optimize_stmt_block(block, state, false, true, false),
|
optimize_stmt_block(block, state, false, true, false),
|
||||||
body.pos,
|
body.1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// do { block } while|until expr
|
// do { block } while|until expr
|
||||||
Stmt::Do(body, condition, _, _) => {
|
Stmt::Do(body, condition, _, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
let block = mem::take(&mut body.statements).into_vec();
|
let block = mem::take(&mut body.0).into_vec();
|
||||||
body.statements = optimize_stmt_block(block, state, false, true, false).into();
|
body.0 = optimize_stmt_block(block, state, false, true, false).into();
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(iterable, x, _) => {
|
Stmt::For(iterable, x, _) => {
|
||||||
optimize_expr(iterable, state);
|
optimize_expr(iterable, state);
|
||||||
let body = mem::take(&mut x.1.statements).into_vec();
|
let body = mem::take(&mut (x.1).0).into_vec();
|
||||||
x.1.statements = optimize_stmt_block(body, state, false, true, false).into();
|
(x.1).0 = optimize_stmt_block(body, state, false, true, false).into();
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(expr, _, _, _) => optimize_expr(expr, state),
|
Stmt::Let(expr, _, _, _) => optimize_expr(expr, state),
|
||||||
@ -563,31 +597,31 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
||||||
Stmt::TryCatch(x, _, _) if x.0.statements.iter().all(Stmt::is_pure) => {
|
Stmt::TryCatch(x, _, _) if (x.0).0.iter().all(Stmt::is_pure) => {
|
||||||
// If try block is pure, there will never be any exceptions
|
// If try block is pure, there will never be any exceptions
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let try_block = mem::take(&mut x.0.statements).into_vec();
|
let try_block = mem::take(&mut (x.0).0).into_vec();
|
||||||
*stmt = Stmt::Block(
|
*stmt = Stmt::Block(
|
||||||
optimize_stmt_block(try_block, state, false, true, false),
|
optimize_stmt_block(try_block, state, false, true, false),
|
||||||
x.0.pos,
|
(x.0).1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
Stmt::TryCatch(x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
let try_block = mem::take(&mut x.0.statements).into_vec();
|
let try_block = mem::take(&mut (x.0).0).into_vec();
|
||||||
x.0.statements = optimize_stmt_block(try_block, state, false, true, false).into();
|
(x.0).0 = optimize_stmt_block(try_block, state, false, true, false).into();
|
||||||
let catch_block = mem::take(&mut x.2.statements).into_vec();
|
let catch_block = mem::take(&mut (x.2).0).into_vec();
|
||||||
x.2.statements = optimize_stmt_block(catch_block, state, false, true, false).into();
|
(x.2).0 = optimize_stmt_block(catch_block, state, false, true, false).into();
|
||||||
}
|
}
|
||||||
// {}
|
// {}
|
||||||
Stmt::Expr(Expr::Stmt(x)) if x.statements.is_empty() => {
|
Stmt::Expr(Expr::Stmt(x)) if x.0.is_empty() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(x.pos);
|
*stmt = Stmt::Noop(x.1);
|
||||||
}
|
}
|
||||||
// {...};
|
// {...};
|
||||||
Stmt::Expr(Expr::Stmt(x)) => {
|
Stmt::Expr(Expr::Stmt(x)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Block(mem::take(&mut x.statements).into_vec(), x.pos);
|
*stmt = Stmt::Block(mem::take(&mut x.0).into_vec(), x.1);
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
Stmt::Expr(expr) => optimize_expr(expr, state),
|
Stmt::Expr(expr) => optimize_expr(expr, state),
|
||||||
@ -610,13 +644,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
// {}
|
// {}
|
||||||
Expr::Stmt(x) if x.statements.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.pos) }
|
Expr::Stmt(x) if x.0.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.1) }
|
||||||
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
|
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
|
||||||
Expr::Stmt(x) => {
|
Expr::Stmt(x) => {
|
||||||
x.statements = optimize_stmt_block(mem::take(&mut x.statements).into_vec(), state, true, true, false).into();
|
x.0 = optimize_stmt_block(mem::take(&mut x.0).into_vec(), state, true, true, false).into();
|
||||||
|
|
||||||
// { Stmt(Expr) } - promote
|
// { Stmt(Expr) } - promote
|
||||||
match x.statements.as_mut() {
|
match x.0.as_mut() {
|
||||||
[ Stmt::Expr(e) ] => { state.set_dirty(); *expr = mem::take(e); }
|
[ Stmt::Expr(e) ] => { state.set_dirty(); *expr = mem::take(e); }
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
@ -1034,19 +1068,14 @@ pub fn optimize_into_ast(
|
|||||||
.map(|fn_def| {
|
.map(|fn_def| {
|
||||||
let mut fn_def = crate::fn_native::shared_take_or_clone(fn_def);
|
let mut fn_def = crate::fn_native::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
let pos = fn_def.body.pos;
|
let mut body = fn_def.body.0.into_vec();
|
||||||
|
|
||||||
let mut body = fn_def.body.statements.into_vec();
|
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let state = &mut State::new(engine, lib2, level);
|
let state = &mut State::new(engine, lib2, level);
|
||||||
|
|
||||||
body = optimize_stmt_block(body, state, true, true, true);
|
body = optimize_stmt_block(body, state, true, true, true);
|
||||||
|
|
||||||
fn_def.body = StmtBlock {
|
fn_def.body.0 = body.into();
|
||||||
statements: body.into(),
|
|
||||||
pos,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn_def
|
fn_def
|
||||||
})
|
})
|
||||||
|
@ -808,14 +808,14 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = BTreeMap::<u64, Box<StmtBlock>>::new();
|
let mut table = BTreeMap::<u64, Box<(Option<Expr>, StmtBlock)>>::new();
|
||||||
let mut def_pos = Position::NONE;
|
let mut def_pos = Position::NONE;
|
||||||
let mut def_stmt = None;
|
let mut def_stmt = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
const MISSING_RBRACE: &str = "to end this switch block";
|
const MISSING_RBRACE: &str = "to end this switch block";
|
||||||
|
|
||||||
let expr = match input.peek().unwrap() {
|
let (expr, condition) = match input.peek().unwrap() {
|
||||||
(Token::RightBrace, _) => {
|
(Token::RightBrace, _) => {
|
||||||
eat_token(input, Token::RightBrace);
|
eat_token(input, Token::RightBrace);
|
||||||
break;
|
break;
|
||||||
@ -829,11 +829,21 @@ fn parse_switch(
|
|||||||
(Token::Underscore, pos) if def_stmt.is_none() => {
|
(Token::Underscore, pos) if def_stmt.is_none() => {
|
||||||
def_pos = *pos;
|
def_pos = *pos;
|
||||||
eat_token(input, Token::Underscore);
|
eat_token(input, Token::Underscore);
|
||||||
None
|
(None, None)
|
||||||
}
|
}
|
||||||
(Token::Underscore, pos) => return Err(PERR::DuplicatedSwitchCase.into_err(*pos)),
|
(Token::Underscore, pos) => return Err(PERR::DuplicatedSwitchCase.into_err(*pos)),
|
||||||
_ if def_stmt.is_some() => return Err(PERR::WrongSwitchDefaultCase.into_err(def_pos)),
|
_ if def_stmt.is_some() => return Err(PERR::WrongSwitchDefaultCase.into_err(def_pos)),
|
||||||
_ => Some(parse_expr(input, state, lib, settings.level_up())?),
|
|
||||||
|
_ => {
|
||||||
|
let case_expr = Some(parse_expr(input, state, lib, settings.level_up())?);
|
||||||
|
|
||||||
|
let condition = if match_token(input, Token::If).0 {
|
||||||
|
Some(parse_expr(input, state, lib, settings.level_up())?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
(case_expr, condition)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash = if let Some(expr) = expr {
|
let hash = if let Some(expr) = expr {
|
||||||
@ -871,7 +881,7 @@ fn parse_switch(
|
|||||||
let need_comma = !stmt.is_self_terminated();
|
let need_comma = !stmt.is_self_terminated();
|
||||||
|
|
||||||
def_stmt = if let Some(hash) = hash {
|
def_stmt = if let Some(hash) = hash {
|
||||||
table.insert(hash, Box::new(stmt.into()));
|
table.insert(hash, Box::new((condition, stmt.into())));
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(stmt.into())
|
Some(stmt.into())
|
||||||
@ -2878,7 +2888,7 @@ fn make_curry_from_externals(
|
|||||||
let mut statements: StaticVec<_> = Default::default();
|
let mut statements: StaticVec<_> = Default::default();
|
||||||
statements.extend(externals.into_iter().map(Stmt::Share));
|
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||||
statements.push(Stmt::Expr(expr));
|
statements.push(Stmt::Expr(expr));
|
||||||
Expr::Stmt(Box::new(StmtBlock { statements, pos }))
|
Expr::Stmt(Box::new(StmtBlock(statements, pos)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an anonymous function definition.
|
/// Parse an anonymous function definition.
|
||||||
|
@ -67,6 +67,45 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_switch_condition() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
scope.push("x", 42 as INT);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
r"
|
||||||
|
switch x / 2 {
|
||||||
|
21 if x > 40 => 1,
|
||||||
|
0 if x < 100 => 2,
|
||||||
|
1 => 3,
|
||||||
|
_ => 9
|
||||||
|
}
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
r"
|
||||||
|
switch x / 2 {
|
||||||
|
21 if x < 40 => 1,
|
||||||
|
0 if x < 100 => 2,
|
||||||
|
1 => 3,
|
||||||
|
_ => 9
|
||||||
|
}
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
9
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
mod test_switch_enum {
|
mod test_switch_enum {
|
||||||
|
Loading…
Reference in New Issue
Block a user