Streamline.

This commit is contained in:
Stephen Chung 2020-04-29 16:11:54 +08:00
parent 304c658f89
commit 21c3edb31e
3 changed files with 33 additions and 20 deletions

View File

@ -189,7 +189,7 @@ impl StaticVec {
} }
/// A type that holds all the current states of the Engine. /// A type that holds all the current states of the Engine.
#[derive(Debug, Clone, Hash, Copy)] #[derive(Debug, Clone, Copy)]
pub struct State { pub struct State {
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
/// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned. /// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned.
@ -198,6 +198,7 @@ pub struct State {
} }
impl State { impl State {
/// Create a new `State`.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
always_search: false, always_search: false,
@ -914,10 +915,9 @@ impl Engine {
match dot_lhs { match dot_lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(id, index, pos) => { Expr::Variable(id, index, pos) => {
let (target, typ) = if !state.always_search && *index > 0 { let (target, typ) = match index {
scope.get_mut(scope.len() - *index) Some(i) if !state.always_search => scope.get_mut(scope.len() - i.get()),
} else { _ => search_scope(scope, id, *pos)?,
search_scope(scope, id, *pos)?
}; };
// Constants cannot be modified // Constants cannot be modified
@ -1156,8 +1156,8 @@ impl Engine {
Expr::FloatConstant(f, _) => Ok((*f).into()), Expr::FloatConstant(f, _) => Ok((*f).into()),
Expr::StringConstant(s, _) => Ok(s.to_string().into()), Expr::StringConstant(s, _) => Ok(s.to_string().into()),
Expr::CharConstant(c, _) => Ok((*c).into()), Expr::CharConstant(c, _) => Ok((*c).into()),
Expr::Variable(_, index, _) if !state.always_search && *index > 0 => { Expr::Variable(_, Some(index), _) if !state.always_search => {
Ok(scope.get_mut(scope.len() - *index).0.clone()) Ok(scope.get_mut(scope.len() - index.get()).0.clone())
} }
Expr::Variable(id, _, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()), Expr::Variable(id, _, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()),
Expr::Property(_, _) => panic!("unexpected property."), Expr::Property(_, _) => panic!("unexpected property."),
@ -1272,13 +1272,17 @@ impl Engine {
&& args.len() == 1 && args.len() == 1
&& !self.has_override(fn_lib, KEYWORD_EVAL) && !self.has_override(fn_lib, KEYWORD_EVAL)
{ {
let prev_len = scope.len();
// Evaluate the text string as a script // Evaluate the text string as a script
let result = let result =
self.eval_script_expr(scope, fn_lib, args[0], arg_exprs[0].position()); self.eval_script_expr(scope, fn_lib, args[0], arg_exprs[0].position());
// IMPORTANT! The eval may define new variables in the current scope! if scope.len() != prev_len {
// We can no longer trust the variable offsets. // IMPORTANT! If the eval defines new variables in the current scope,
// all variable offsets from this point on will be mis-aligned.
state.always_search = true; state.always_search = true;
}
return result; return result;
} }

View File

@ -729,10 +729,10 @@ pub fn optimize_into_ast(
// Optimize the function body // Optimize the function body
let mut body = let mut body =
optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level); optimize(vec![*fn_def.body], engine, &Scope::new(), &fn_lib, level);
// {} -> Noop // {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { fn_def.body = Box::new(match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val // { return val; } -> val
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val), Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val),
// { return; } -> () // { return; } -> ()
@ -741,7 +741,7 @@ pub fn optimize_into_ast(
} }
// All others // All others
stmt => stmt, stmt => stmt,
}; });
} }
fn_def fn_def
}) })

View File

@ -14,6 +14,7 @@ use crate::stdlib::{
collections::HashMap, collections::HashMap,
format, format,
iter::Peekable, iter::Peekable,
num::NonZeroUsize,
ops::Add, ops::Add,
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
@ -169,7 +170,7 @@ pub struct FnDef {
/// Names of function parameters. /// Names of function parameters.
pub params: Vec<String>, pub params: Vec<String>,
/// Function body. /// Function body.
pub body: Stmt, pub body: Box<Stmt>,
/// Position of the function definition. /// Position of the function definition.
pub pos: Position, pub pos: Position,
} }
@ -184,29 +185,37 @@ pub enum ReturnType {
} }
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope. /// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
#[derive(Debug, Clone)]
struct Stack(Vec<String>); struct Stack(Vec<String>);
impl Stack { impl Stack {
/// Create a new `Stack`.
pub fn new() -> Self { pub fn new() -> Self {
Self(Vec::new()) Self(Vec::new())
} }
/// Get the number of variables in the `Stack`.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
/// Push (add) a new variable onto the `Stack`.
pub fn push(&mut self, name: String) { pub fn push(&mut self, name: String) {
self.0.push(name); self.0.push(name);
} }
/// Rewind the stack to a previous size.
pub fn rewind(&mut self, len: usize) { pub fn rewind(&mut self, len: usize) {
self.0.truncate(len); self.0.truncate(len);
} }
pub fn find(&self, name: &str) -> usize { /// Find a variable by name in the `Stack`, searching in reverse.
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `Stack` is offset 1.
/// Return zero when the variable name is not found in the `Stack`.
pub fn find(&self, name: &str) -> Option<NonZeroUsize> {
self.0 self.0
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, n)| *n == name) .find(|(_, n)| *n == name)
.map(|(i, _)| i + 1) .and_then(|(i, _)| NonZeroUsize::new(i + 1))
.unwrap_or(0)
} }
} }
@ -308,7 +317,7 @@ pub enum Expr {
/// String constant. /// String constant.
StringConstant(String, Position), StringConstant(String, Position),
/// Variable access. /// Variable access.
Variable(String, usize, Position), Variable(String, Option<NonZeroUsize>, Position),
/// Property access. /// Property access.
Property(String, Position), Property(String, Position),
/// { stmt } /// { stmt }
@ -1836,10 +1845,10 @@ fn parse_fn<'a>(
})?; })?;
// Parse function body // Parse function body
let body = match input.peek().unwrap() { let body = Box::new(match input.peek().unwrap() {
(Token::LeftBrace, _) => parse_block(input, stack, false, allow_stmt_expr)?, (Token::LeftBrace, _) => parse_block(input, stack, false, allow_stmt_expr)?,
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)), (_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
}; });
let params = params.into_iter().map(|(p, _)| p).collect(); let params = params.into_iter().map(|(p, _)| p).collect();