Reduce redirections in Stmt.
This commit is contained in:
parent
8853ebf059
commit
b11b8d6d39
68
src/ast.rs
68
src/ast.rs
@ -757,6 +757,8 @@ pub struct Ident {
|
|||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
/// Declaration position.
|
/// Declaration position.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
|
/// Is this identifier public?
|
||||||
|
pub public: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Ident {
|
impl fmt::Debug for Ident {
|
||||||
@ -815,7 +817,7 @@ pub enum Stmt {
|
|||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
If(Expr, Box<(Stmt, Stmt)>, Position),
|
||||||
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(
|
||||||
Expr,
|
Expr,
|
||||||
@ -826,15 +828,15 @@ pub enum Stmt {
|
|||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Option<Expr>, Box<Stmt>, Position),
|
While(Expr, Box<Stmt>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Do(Box<Stmt>, Expr, bool, Position),
|
Do(Box<Stmt>, Expr, bool, Position),
|
||||||
/// `for` id `in` expr `{` stmt `}`
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
For(Expr, Box<(String, Stmt)>, Position),
|
For(Expr, String, Box<Stmt>, Position),
|
||||||
/// \[`export`\] `let` id `=` expr
|
/// \[`export`\] `let` id `=` expr
|
||||||
Let(Box<Ident>, Option<Expr>, bool, Position),
|
Let(Expr, Ident, Position),
|
||||||
/// \[`export`\] `const` id `=` expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Box<Ident>, Option<Expr>, bool, Position),
|
Const(Expr, Ident, Position),
|
||||||
/// expr op`=` expr
|
/// expr op`=` expr
|
||||||
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
||||||
/// `{` stmt`;` ... `}`
|
/// `{` stmt`;` ... `}`
|
||||||
@ -848,10 +850,10 @@ pub enum Stmt {
|
|||||||
/// `break`
|
/// `break`
|
||||||
Break(Position),
|
Break(Position),
|
||||||
/// `return`/`throw`
|
/// `return`/`throw`
|
||||||
Return((ReturnType, Position), Option<Expr>, Position),
|
Return(ReturnType, Option<Expr>, Position),
|
||||||
/// `import` expr `as` var
|
/// `import` expr `as` var
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import(Expr, Option<Box<Ident>>, Position),
|
Import(Expr, Option<Ident>, Position),
|
||||||
/// `export` var `as` var `,` ...
|
/// `export` var `as` var `,` ...
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Export(Vec<(Ident, Option<Ident>)>, Position),
|
Export(Vec<(Ident, Option<Ident>)>, Position),
|
||||||
@ -888,10 +890,10 @@ impl Stmt {
|
|||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Do(_, _, _, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return(_, _, pos)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, pos)
|
||||||
| Self::Const(_, _, _, pos)
|
| Self::Const(_, _, pos)
|
||||||
| Self::TryCatch(_, pos, _) => *pos,
|
| Self::TryCatch(_, pos, _) => *pos,
|
||||||
|
|
||||||
Self::Expr(x) => x.position(),
|
Self::Expr(x) => x.position(),
|
||||||
@ -917,10 +919,10 @@ impl Stmt {
|
|||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Do(_, _, _, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return(_, _, pos)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, pos)
|
||||||
| Self::Const(_, _, _, pos)
|
| Self::Const(_, _, pos)
|
||||||
| Self::TryCatch(_, pos, _) => *pos = new_pos,
|
| Self::TryCatch(_, pos, _) => *pos = new_pos,
|
||||||
|
|
||||||
Self::Expr(x) => {
|
Self::Expr(x) => {
|
||||||
@ -944,15 +946,15 @@ impl Stmt {
|
|||||||
Self::If(_, _, _)
|
Self::If(_, _, _)
|
||||||
| Self::Switch(_, _, _)
|
| Self::Switch(_, _, _)
|
||||||
| Self::While(_, _, _)
|
| Self::While(_, _, _)
|
||||||
| Self::For(_, _, _)
|
| Self::For(_, _, _, _)
|
||||||
| Self::Block(_, _)
|
| Self::Block(_, _)
|
||||||
| Self::TryCatch(_, _, _) => true,
|
| Self::TryCatch(_, _, _) => true,
|
||||||
|
|
||||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||||
Self::Noop(_) => false,
|
Self::Noop(_) => false,
|
||||||
|
|
||||||
Self::Let(_, _, _, _)
|
Self::Let(_, _, _)
|
||||||
| Self::Const(_, _, _, _)
|
| Self::Const(_, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
| Self::Do(_, _, _, _)
|
| Self::Do(_, _, _, _)
|
||||||
@ -974,22 +976,17 @@ impl Stmt {
|
|||||||
match self {
|
match self {
|
||||||
Self::Noop(_) => true,
|
Self::Noop(_) => true,
|
||||||
Self::Expr(expr) => expr.is_pure(),
|
Self::Expr(expr) => expr.is_pure(),
|
||||||
Self::If(condition, x, _) => {
|
Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure() && x.1.is_pure(),
|
||||||
condition.is_pure()
|
|
||||||
&& x.0.is_pure()
|
|
||||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
|
||||||
}
|
|
||||||
Self::Switch(expr, x, _) => {
|
Self::Switch(expr, x, _) => {
|
||||||
expr.is_pure()
|
expr.is_pure()
|
||||||
&& x.0.values().all(Stmt::is_pure)
|
&& x.0.values().all(Stmt::is_pure)
|
||||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||||
}
|
}
|
||||||
Self::While(Some(condition), block, _) | Self::Do(block, condition, _, _) => {
|
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||||
condition.is_pure() && block.is_pure()
|
condition.is_pure() && block.is_pure()
|
||||||
}
|
}
|
||||||
Self::While(None, block, _) => block.is_pure(),
|
Self::For(iterable, _, block, _) => iterable.is_pure() && block.is_pure(),
|
||||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.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, _, _) => x.0.is_pure() && x.2.is_pure(),
|
Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(),
|
||||||
@ -1010,13 +1007,11 @@ impl Stmt {
|
|||||||
on_node(path);
|
on_node(path);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Let(_, Some(e), _, _) | Self::Const(_, Some(e), _, _) => e.walk(path, on_node),
|
Self::Let(e, _, _) | Self::Const(e, _, _) => e.walk(path, on_node),
|
||||||
Self::If(e, x, _) => {
|
Self::If(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
x.0.walk(path, on_node);
|
x.0.walk(path, on_node);
|
||||||
if let Some(ref s) = x.1 {
|
x.1.walk(path, on_node);
|
||||||
s.walk(path, on_node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Self::Switch(e, x, _) => {
|
Self::Switch(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
@ -1025,14 +1020,13 @@ impl Stmt {
|
|||||||
s.walk(path, on_node);
|
s.walk(path, on_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::While(Some(e), s, _) | Self::Do(s, e, _, _) => {
|
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
s.walk(path, on_node);
|
s.walk(path, on_node);
|
||||||
}
|
}
|
||||||
Self::While(None, s, _) => s.walk(path, on_node),
|
Self::For(e, _, s, _) => {
|
||||||
Self::For(e, x, _) => {
|
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
x.1.walk(path, on_node);
|
s.walk(path, on_node);
|
||||||
}
|
}
|
||||||
Self::Assignment(x, _) => {
|
Self::Assignment(x, _) => {
|
||||||
x.0.walk(path, on_node);
|
x.0.walk(path, on_node);
|
||||||
@ -1583,8 +1577,8 @@ mod tests {
|
|||||||
assert_eq!(size_of::<Position>(), 4);
|
assert_eq!(size_of::<Position>(), 4);
|
||||||
assert_eq!(size_of::<ast::Expr>(), 16);
|
assert_eq!(size_of::<ast::Expr>(), 16);
|
||||||
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
||||||
assert_eq!(size_of::<ast::Stmt>(), 32);
|
assert_eq!(size_of::<ast::Stmt>(), 40);
|
||||||
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
|
assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
|
||||||
assert_eq!(size_of::<FnPtr>(), 32);
|
assert_eq!(size_of::<FnPtr>(), 32);
|
||||||
assert_eq!(size_of::<Scope>(), 48);
|
assert_eq!(size_of::<Scope>(), 48);
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
|
@ -946,7 +946,7 @@ impl Engine {
|
|||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(v) => match v.as_ref() {
|
Expr::Variable(v) => match v.as_ref() {
|
||||||
// Qualified variable
|
// Qualified variable
|
||||||
(_, Some((hash_var, modules)), Ident { name, pos }) => {
|
(_, Some((hash_var, modules)), Ident { name, pos, .. }) => {
|
||||||
let module = self.search_imports(mods, state, modules).ok_or_else(|| {
|
let module = self.search_imports(mods, state, modules).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorModuleNotFound(
|
EvalAltResult::ErrorModuleNotFound(
|
||||||
modules[0].name.to_string(),
|
modules[0].name.to_string(),
|
||||||
@ -985,7 +985,7 @@ impl Engine {
|
|||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
||||||
let (index, _, Ident { name, pos }) = match expr {
|
let (index, _, Ident { name, pos, .. }) = match expr {
|
||||||
Expr::Variable(v) => v.as_ref(),
|
Expr::Variable(v) => v.as_ref(),
|
||||||
_ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected, but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
@ -1181,7 +1181,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id op= ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos } = &x.4;
|
let Ident { name, pos, .. } = &x.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
||||||
@ -1194,7 +1194,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target_val.is::<Map>() => {
|
Expr::Property(x) if target_val.is::<Map>() => {
|
||||||
let Ident { name, pos } = &x.4;
|
let Ident { name, pos, .. } = &x.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
|
||||||
@ -1229,7 +1229,7 @@ impl Engine {
|
|||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
|
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
|
||||||
let mut val = match &x.lhs {
|
let mut val = match &x.lhs {
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let Ident { name, pos } = &p.4;
|
let Ident { name, pos, .. } = &p.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, false, is_ref, true,
|
mods, state, lib, target_val, index, *pos, false, is_ref, true,
|
||||||
@ -1378,6 +1378,7 @@ impl Engine {
|
|||||||
let Ident {
|
let Ident {
|
||||||
name: var_name,
|
name: var_name,
|
||||||
pos: var_pos,
|
pos: var_pos,
|
||||||
|
..
|
||||||
} = &x.2;
|
} = &x.2;
|
||||||
|
|
||||||
self.inc_operations(state, *var_pos)?;
|
self.inc_operations(state, *var_pos)?;
|
||||||
@ -2037,8 +2038,8 @@ impl Engine {
|
|||||||
.and_then(|guard_val| {
|
.and_then(|guard_val| {
|
||||||
if guard_val {
|
if guard_val {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
|
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
|
||||||
} else if let Some(stmt) = else_block {
|
} else if !else_block.is_noop() {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
self.eval_stmt(scope, mods, state, lib, this_ptr, else_block, level)
|
||||||
} else {
|
} else {
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
@ -2076,7 +2077,7 @@ impl Engine {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(expr, body, _) => loop {
|
Stmt::While(expr, body, _) => loop {
|
||||||
let condition = if let Some(expr) = expr {
|
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)?
|
||||||
.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()))?
|
||||||
@ -2125,8 +2126,8 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, name, x, _) => {
|
||||||
let (name, stmt) = x.as_ref();
|
let stmt = 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();
|
||||||
@ -2287,7 +2288,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::Return((ReturnType::Return, pos), Some(expr), _) => {
|
Stmt::Return(ReturnType::Return, Some(expr), pos) => {
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2295,12 +2296,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
Stmt::Return((ReturnType::Return, pos), None, _) => {
|
Stmt::Return(ReturnType::Return, None, pos) => {
|
||||||
EvalAltResult::Return(Default::default(), *pos).into()
|
EvalAltResult::Return(Default::default(), *pos).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::Return((ReturnType::Exception, pos), Some(expr), _) => {
|
Stmt::Return(ReturnType::Exception, Some(expr), pos) => {
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2308,38 +2309,34 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Empty throw
|
// Empty throw
|
||||||
Stmt::Return((ReturnType::Exception, pos), None, _) => {
|
Stmt::Return(ReturnType::Exception, None, pos) => {
|
||||||
EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into()
|
EvalAltResult::ErrorRuntime(Dynamic::UNIT, *pos).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Let(var_def, expr, export, _) | Stmt::Const(var_def, expr, export, _) => {
|
Stmt::Let(expr, Ident { name, public, .. }, _)
|
||||||
|
| Stmt::Const(expr, Ident { name, public, .. }, _) => {
|
||||||
let entry_type = match stmt {
|
let entry_type = match stmt {
|
||||||
Stmt::Let(_, _, _, _) => AccessMode::ReadWrite,
|
Stmt::Let(_, _, _) => AccessMode::ReadWrite,
|
||||||
Stmt::Const(_, _, _, _) => AccessMode::ReadOnly,
|
Stmt::Const(_, _, _) => AccessMode::ReadOnly,
|
||||||
_ => unreachable!("should be Stmt::Let or Stmt::Const, but gets {:?}", stmt),
|
_ => unreachable!("should be Stmt::Let or Stmt::Const, but gets {:?}", stmt),
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = if let Some(expr) = expr {
|
let value = self
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten()
|
.flatten();
|
||||||
} else {
|
|
||||||
Dynamic::UNIT
|
|
||||||
};
|
|
||||||
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
||||||
(
|
(
|
||||||
var_def.name.to_string().into(),
|
name.to_string().into(),
|
||||||
if *export {
|
if *public { Some(name.clone()) } else { None },
|
||||||
Some(var_def.name.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
} else if *export {
|
} else if *public {
|
||||||
unreachable!("exported variable not on global level");
|
unreachable!("exported variable not on global level");
|
||||||
} else {
|
} else {
|
||||||
(unsafe_cast_var_name_to_lifetime(&var_def.name).into(), None)
|
(unsafe_cast_var_name_to_lifetime(name).into(), None)
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.push_dynamic_value(var_name, entry_type, value);
|
scope.push_dynamic_value(var_name, entry_type, value);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -2400,14 +2397,13 @@ impl Engine {
|
|||||||
// Export statement
|
// Export statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(list, _) => {
|
Stmt::Export(list, _) => {
|
||||||
for (Ident { name, pos: id_pos }, rename) in list.iter() {
|
for (Ident { name, pos, .. }, rename) in list.iter() {
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
||||||
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
||||||
scope.add_entry_alias(index, alias.clone());
|
scope.add_entry_alias(index, alias.clone());
|
||||||
} else {
|
} else {
|
||||||
return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos)
|
return EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into();
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
|
@ -903,7 +903,7 @@ impl Engine {
|
|||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hashes
|
// Recalculate hashes
|
||||||
let hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
let new_hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = curry
|
let mut arg_values = curry
|
||||||
@ -914,7 +914,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, false, false, pos, None, level,
|
mods, state, lib, fn_name, new_hash, args, false, false, pos, None, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
@ -939,7 +939,7 @@ impl Engine {
|
|||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = FnHash::from_script_and_native(
|
let new_hash = FnHash::from_script_and_native(
|
||||||
calc_fn_hash(empty(), fn_name, args_len),
|
calc_fn_hash(empty(), fn_name, args_len),
|
||||||
calc_fn_hash(empty(), fn_name, args_len + 1),
|
calc_fn_hash(empty(), fn_name, args_len + 1),
|
||||||
);
|
);
|
||||||
@ -953,7 +953,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, is_ref, true, pos, None, level,
|
mods, state, lib, fn_name, new_hash, args, is_ref, true, pos, None, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CURRY => {
|
KEYWORD_FN_PTR_CURRY => {
|
||||||
@ -1096,7 +1096,6 @@ impl Engine {
|
|||||||
FnHash::from_native(calc_fn_hash(empty(), name, args_len))
|
FnHash::from_native(calc_fn_hash(empty(), name, args_len))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if args_expr.len() == 1 => {
|
KEYWORD_FN_PTR if args_expr.len() == 1 => {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
|
100
src/optimize.rs
100
src/optimize.rs
@ -1,6 +1,6 @@
|
|||||||
//! Module implementing the [`AST`] optimizer.
|
//! Module implementing the [`AST`] optimizer.
|
||||||
|
|
||||||
use crate::ast::{Expr, Stmt};
|
use crate::ast::{Expr, Ident, Stmt};
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||||
@ -181,27 +181,17 @@ fn optimize_stmt_block(
|
|||||||
statements.iter_mut().for_each(|stmt| {
|
statements.iter_mut().for_each(|stmt| {
|
||||||
match stmt {
|
match stmt {
|
||||||
// Add constant literals into the state
|
// Add constant literals into the state
|
||||||
Stmt::Const(var_def, Some(value_expr), _, _) => {
|
Stmt::Const(value_expr, Ident { name, .. }, _) => {
|
||||||
optimize_expr(value_expr, state);
|
optimize_expr(value_expr, state);
|
||||||
|
|
||||||
if value_expr.is_constant() {
|
if value_expr.is_constant() {
|
||||||
state.push_var(&var_def.name, AccessMode::ReadOnly, value_expr.clone());
|
state.push_var(name, AccessMode::ReadOnly, value_expr.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Const(var_def, None, _, _) => {
|
|
||||||
state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos));
|
|
||||||
}
|
|
||||||
// Add variables into the state
|
// Add variables into the state
|
||||||
Stmt::Let(var_def, expr, _, _) => {
|
Stmt::Let(value_expr, Ident { name, pos, .. }, _) => {
|
||||||
if let Some(value_expr) = expr {
|
optimize_expr(value_expr, state);
|
||||||
optimize_expr(value_expr, state);
|
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
||||||
}
|
|
||||||
|
|
||||||
state.push_var(
|
|
||||||
&var_def.name,
|
|
||||||
AccessMode::ReadWrite,
|
|
||||||
Expr::Unit(var_def.pos),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
@ -228,9 +218,7 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
while let Some(expr) = statements.pop() {
|
while let Some(expr) = statements.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
Stmt::Let(_, expr, _, _) | Stmt::Const(_, expr, _, _) => {
|
Stmt::Let(expr, _, _) | Stmt::Const(expr, _, _) => removed = expr.is_pure(),
|
||||||
removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true)
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
||||||
_ => {
|
_ => {
|
||||||
@ -286,9 +274,9 @@ fn optimize_stmt_block(
|
|||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
}
|
}
|
||||||
// Only one let statement - leave it alone
|
// Only one let statement - leave it alone
|
||||||
[x] if matches!(x, Stmt::Let(_, _, _, _)) => Stmt::Block(statements, pos),
|
[x] if matches!(x, Stmt::Let(_, _, _)) => Stmt::Block(statements, pos),
|
||||||
// Only one const statement - leave it alone
|
// Only one const statement - leave it alone
|
||||||
[x] if matches!(x, Stmt::Const(_, _, _, _)) => Stmt::Block(statements, pos),
|
[x] if matches!(x, Stmt::Const(_, _, _)) => Stmt::Block(statements, pos),
|
||||||
// Only one import statement - leave it alone
|
// Only one import statement - leave it alone
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
[x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos),
|
[x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos),
|
||||||
@ -316,17 +304,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// if false { if_block } -> Noop
|
// if false { if_block } -> Noop
|
||||||
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_none() => {
|
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_noop() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos);
|
*stmt = Stmt::Noop(*pos);
|
||||||
}
|
}
|
||||||
// if true { if_block } -> if_block
|
// if true { if_block } -> if_block
|
||||||
Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_none() => {
|
Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_noop() => {
|
||||||
*stmt = mem::take(&mut x.0);
|
*stmt = mem::take(&mut x.0);
|
||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if expr { Noop }
|
// if expr { Noop }
|
||||||
Stmt::If(condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => {
|
Stmt::If(condition, x, _) if x.1.is_noop() && x.0.is_noop() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = condition.position();
|
let pos = condition.position();
|
||||||
@ -342,13 +330,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if expr { if_block }
|
// if expr { if_block }
|
||||||
Stmt::If(condition, x, _) if x.1.is_none() => {
|
Stmt::If(condition, x, _) if x.1.is_noop() => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
}
|
}
|
||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Stmt::If(Expr::BoolConstant(false, _), x, _) if x.1.is_some() => {
|
Stmt::If(Expr::BoolConstant(false, _), x, _) if !x.1.is_noop() => {
|
||||||
*stmt = mem::take(x.1.as_mut().unwrap());
|
*stmt = mem::take(&mut x.1);
|
||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
@ -360,13 +348,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
Stmt::If(condition, x, _) => {
|
Stmt::If(condition, x, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
if let Some(else_block) = x.1.as_mut() {
|
optimize_stmt(&mut x.1, state, true);
|
||||||
optimize_stmt(else_block, state, true);
|
|
||||||
match else_block {
|
|
||||||
Stmt::Noop(_) => x.1 = None, // Noop -> no else block
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
@ -406,24 +388,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
Stmt::While(Some(Expr::BoolConstant(false, pos)), _, _) => {
|
Stmt::While(Expr::BoolConstant(false, pos), _, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos)
|
*stmt = Stmt::Noop(*pos)
|
||||||
}
|
}
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(condition, block, _) => {
|
Stmt::While(condition, block, _) => {
|
||||||
optimize_stmt(block, state, false);
|
optimize_stmt(block, state, false);
|
||||||
|
optimize_expr(condition, state);
|
||||||
if let Some(condition) = condition {
|
|
||||||
optimize_expr(condition, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
match **block {
|
match **block {
|
||||||
// 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
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if let Some(condition) = condition {
|
if !condition.is_unit() {
|
||||||
let mut statements = vec![Stmt::Expr(mem::take(condition))];
|
let mut statements = vec![Stmt::Expr(mem::take(condition))];
|
||||||
if preserve_result {
|
if preserve_result {
|
||||||
statements.push(Stmt::Noop(pos))
|
statements.push(Stmt::Noop(pos))
|
||||||
@ -449,14 +428,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(iterable, x, _) => {
|
Stmt::For(iterable, _, block, _) => {
|
||||||
optimize_expr(iterable, state);
|
optimize_expr(iterable, state);
|
||||||
optimize_stmt(&mut x.1, state, false);
|
optimize_stmt(block, state, false);
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state),
|
Stmt::Let(expr, _, _) => optimize_expr(expr, state),
|
||||||
// let id;
|
|
||||||
Stmt::Let(_, None, _, _) => (),
|
|
||||||
// import expr as var;
|
// import expr as var;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
||||||
@ -789,40 +766,23 @@ fn optimize_top_level(
|
|||||||
|
|
||||||
statements.iter_mut().enumerate().for_each(|(i, stmt)| {
|
statements.iter_mut().enumerate().for_each(|(i, stmt)| {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Const(var_def, expr, _, _) if expr.is_some() => {
|
Stmt::Const(value_expr, Ident { name, .. }, _) => {
|
||||||
// Load constants
|
// Load constants
|
||||||
let value_expr = expr.as_mut().unwrap();
|
|
||||||
optimize_expr(value_expr, &mut state);
|
optimize_expr(value_expr, &mut state);
|
||||||
|
|
||||||
if value_expr.is_constant() {
|
if value_expr.is_constant() {
|
||||||
state.push_var(&var_def.name, AccessMode::ReadOnly, value_expr.clone());
|
state.push_var(name, AccessMode::ReadOnly, value_expr.clone());
|
||||||
}
|
|
||||||
|
|
||||||
// Keep it in the global scope
|
|
||||||
if value_expr.is_unit() {
|
|
||||||
state.set_dirty();
|
|
||||||
*expr = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Const(var_def, None, _, _) => {
|
Stmt::Let(value_expr, Ident { name, pos, .. }, _) => {
|
||||||
state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos));
|
optimize_expr(value_expr, &mut state);
|
||||||
}
|
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos));
|
||||||
Stmt::Let(var_def, expr, _, _) => {
|
|
||||||
if let Some(value_expr) = expr {
|
|
||||||
optimize_expr(value_expr, &mut state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.push_var(
|
|
||||||
&var_def.name,
|
|
||||||
AccessMode::ReadWrite,
|
|
||||||
Expr::Unit(var_def.pos),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Keep all variable declarations at this level
|
// Keep all variable declarations at this level
|
||||||
// and always keep the last return value
|
// and always keep the last return value
|
||||||
let keep = match stmt {
|
let keep = match stmt {
|
||||||
Stmt::Let(_, _, _, _) | Stmt::Const(_, _, _, _) => true,
|
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => true,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(_, _, _) => true,
|
Stmt::Import(_, _, _) => true,
|
||||||
_ => i == num_statements - 1,
|
_ => i == num_statements - 1,
|
||||||
@ -911,11 +871,11 @@ pub fn optimize_into_ast(
|
|||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
// { return val; } -> val
|
// { return val; } -> val
|
||||||
Stmt::Return((crate::ast::ReturnType::Return, _), Some(expr), _) => {
|
Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => {
|
||||||
Stmt::Expr(expr)
|
Stmt::Expr(expr)
|
||||||
}
|
}
|
||||||
// { return; } -> ()
|
// { return; } -> ()
|
||||||
Stmt::Return((crate::ast::ReturnType::Return, pos), None, _) => {
|
Stmt::Return(crate::ast::ReturnType::Return, None, pos) => {
|
||||||
Stmt::Expr(Expr::Unit(pos))
|
Stmt::Expr(Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// All others
|
// All others
|
||||||
|
@ -755,7 +755,8 @@ fn parse_map_literal(
|
|||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
let name = state.get_interned_string(name);
|
let name = state.get_interned_string(name);
|
||||||
map.push((Ident { name, pos }, expr));
|
let public = false;
|
||||||
|
map.push((Ident { name, pos, public }, expr));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -1033,6 +1034,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1047,6 +1049,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1056,6 +1059,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((index, None, var_name_def)))
|
Expr::Variable(Box::new((index, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1075,6 +1079,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1083,6 +1088,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((None, None, var_name_def)))
|
Expr::Variable(Box::new((None, None, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1147,14 +1153,14 @@ fn parse_primary(
|
|||||||
.into_err(pos));
|
.into_err(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, namespace, Ident { name, pos }) = *x;
|
let (_, namespace, Ident { name, pos, .. }) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let ns = namespace.map(|(_, ns)| ns);
|
let ns = namespace.map(|(_, ns)| ns);
|
||||||
parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
|
parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
let (_, namespace, Ident { name, pos }) = *x;
|
let (_, namespace, Ident { name, pos, .. }) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let ns = namespace.map(|(_, ns)| ns);
|
let ns = namespace.map(|(_, ns)| ns);
|
||||||
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
|
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
|
||||||
@ -1176,6 +1182,7 @@ fn parse_primary(
|
|||||||
let var_name_def = Ident {
|
let var_name_def = Ident {
|
||||||
name: state.get_interned_string(id2),
|
name: state.get_interned_string(id2),
|
||||||
pos: pos2,
|
pos: pos2,
|
||||||
|
public: false,
|
||||||
};
|
};
|
||||||
Expr::Variable(Box::new((index, namespace, var_name_def)))
|
Expr::Variable(Box::new((index, namespace, var_name_def)))
|
||||||
}
|
}
|
||||||
@ -1395,7 +1402,7 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos }) = x.as_ref();
|
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
@ -1416,7 +1423,7 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos }) = x.as_ref();
|
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
@ -1846,7 +1853,8 @@ fn parse_custom_syntax(
|
|||||||
let name = state.get_interned_string(s);
|
let name = state.get_interned_string(s);
|
||||||
segments.push(name.clone());
|
segments.push(name.clone());
|
||||||
tokens.push(state.get_interned_string(MARKER_IDENT));
|
tokens.push(state.get_interned_string(MARKER_IDENT));
|
||||||
let var_name_def = Ident { name, pos };
|
let public = false;
|
||||||
|
let var_name_def = Ident { name, pos, public };
|
||||||
keywords.push(Expr::Variable(Box::new((None, None, var_name_def))));
|
keywords.push(Expr::Variable(Box::new((None, None, var_name_def))));
|
||||||
}
|
}
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
@ -1995,15 +2003,15 @@ fn parse_if(
|
|||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).0 {
|
let else_body = if match_token(input, Token::Else).0 {
|
||||||
Some(if let (Token::If, _) = input.peek().unwrap() {
|
if let (Token::If, _) = input.peek().unwrap() {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
parse_if(input, state, lib, settings.level_up())?
|
parse_if(input, state, lib, settings.level_up())?
|
||||||
} else {
|
} else {
|
||||||
// if guard { if_body } else { else-body }
|
// if guard { if_body } else { else-body }
|
||||||
parse_block(input, state, lib, settings.level_up())?
|
parse_block(input, state, lib, settings.level_up())?
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
Stmt::Noop(Position::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Stmt::If(
|
Ok(Stmt::If(
|
||||||
@ -2028,9 +2036,9 @@ fn parse_while_loop(
|
|||||||
(Token::While, pos) => {
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
(Some(expr), pos)
|
(expr, pos)
|
||||||
}
|
}
|
||||||
(Token::Loop, pos) => (None, pos),
|
(Token::Loop, pos) => (Expr::Unit(Position::NONE), pos),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
@ -2130,7 +2138,7 @@ fn parse_for(
|
|||||||
|
|
||||||
state.stack.truncate(prev_stack_len);
|
state.stack.truncate(prev_stack_len);
|
||||||
|
|
||||||
Ok(Stmt::For(expr, Box::new((name, body)), settings.pos))
|
Ok(Stmt::For(expr, name, Box::new(body), settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a variable definition statement.
|
/// Parse a variable definition statement.
|
||||||
@ -2162,23 +2170,24 @@ fn parse_let(
|
|||||||
let var_def = Ident {
|
let var_def = Ident {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
pos,
|
pos,
|
||||||
|
public: export,
|
||||||
};
|
};
|
||||||
|
|
||||||
// let name = ...
|
// let name = ...
|
||||||
let expr = if match_token(input, Token::Equals).0 {
|
let expr = if match_token(input, Token::Equals).0 {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
Some(parse_expr(input, state, lib, settings.level_up())?)
|
parse_expr(input, state, lib, settings.level_up())?
|
||||||
} else {
|
} else {
|
||||||
None
|
Expr::Unit(Position::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
state.stack.push((name, var_type));
|
state.stack.push((name, var_type));
|
||||||
|
|
||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
AccessMode::ReadWrite => Ok(Stmt::Let(Box::new(var_def), expr, export, settings.pos)),
|
AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def, settings.pos)),
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
AccessMode::ReadOnly => Ok(Stmt::Const(Box::new(var_def), expr, export, settings.pos)),
|
AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def, settings.pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2219,10 +2228,11 @@ fn parse_import(
|
|||||||
|
|
||||||
Ok(Stmt::Import(
|
Ok(Stmt::Import(
|
||||||
expr,
|
expr,
|
||||||
Some(Box::new(Ident {
|
Some(Ident {
|
||||||
name,
|
name,
|
||||||
pos: name_pos,
|
pos: name_pos,
|
||||||
})),
|
public: false,
|
||||||
|
}),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2273,6 +2283,7 @@ fn parse_export(
|
|||||||
(Token::Identifier(s), pos) => Some(Ident {
|
(Token::Identifier(s), pos) => Some(Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos,
|
pos,
|
||||||
|
public: false,
|
||||||
}),
|
}),
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
@ -2288,6 +2299,7 @@ fn parse_export(
|
|||||||
Ident {
|
Ident {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
pos: id_pos,
|
pos: id_pos,
|
||||||
|
public: false,
|
||||||
},
|
},
|
||||||
rename,
|
rename,
|
||||||
));
|
));
|
||||||
@ -2565,16 +2577,13 @@ fn parse_stmt(
|
|||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// `return`/`throw` at <EOF>
|
// `return`/`throw` at <EOF>
|
||||||
(Token::EOF, pos) => Ok(Stmt::Return((return_type, token_pos), None, *pos)),
|
(Token::EOF, _) => Ok(Stmt::Return(return_type, None, token_pos)),
|
||||||
// `return;` or `throw;`
|
// `return;` or `throw;`
|
||||||
(Token::SemiColon, _) => {
|
(Token::SemiColon, _) => Ok(Stmt::Return(return_type, None, token_pos)),
|
||||||
Ok(Stmt::Return((return_type, token_pos), None, settings.pos))
|
|
||||||
}
|
|
||||||
// `return` or `throw` with expression
|
// `return` or `throw` with expression
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
let pos = expr.position();
|
Ok(Stmt::Return(return_type, Some(expr), token_pos))
|
||||||
Ok(Stmt::Return((return_type, token_pos), Some(expr), pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2629,6 +2638,7 @@ fn parse_try_catch(
|
|||||||
(Token::Identifier(s), pos) => Ident {
|
(Token::Identifier(s), pos) => Ident {
|
||||||
name: state.get_interned_string(s),
|
name: state.get_interned_string(s),
|
||||||
pos,
|
pos,
|
||||||
|
public: false,
|
||||||
},
|
},
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2861,6 +2871,7 @@ fn parse_anon_fn(
|
|||||||
.map(|(name, &pos)| Ident {
|
.map(|(name, &pos)| Ident {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
pos,
|
pos,
|
||||||
|
public: false,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,9 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||||
|
|
||||||
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
||||||
|
println!("{:?}", ast);
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(Ident("DECISION" @ 1:9), Some(BoolConstant(false, 1:20)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
|
assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(BoolConstant(false, 1:20), Ident("DECISION" @ 1:9), 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)], functions: Module("#));
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user