More code refinements.

This commit is contained in:
Stephen Chung
2020-12-29 10:41:20 +08:00
parent e481a8019d
commit eca8212f38
12 changed files with 317 additions and 157 deletions

View File

@@ -882,7 +882,7 @@ fn parse_switch(
}
};
let stmt = parse_stmt(input, state, lib, settings.level_up()).map(Option::unwrap)?;
let stmt = parse_stmt(input, state, lib, settings.level_up())?;
let need_comma = !stmt.is_self_terminated();
@@ -1014,10 +1014,11 @@ fn parse_primary(
state.access_var(closure, *pos);
});
// Qualifiers (none) + function name + number of arguments.
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap();
lib.insert(hash, func);
lib.insert(
// Qualifiers (none) + function name + number of arguments.
calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(),
func,
);
expr
}
@@ -1372,7 +1373,28 @@ fn make_assignment_stmt<'a>(
rhs: Expr,
op_pos: Position,
) -> Result<Stmt, ParseError> {
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Position {
match expr {
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
ref e => e.position(),
},
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs {
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
},
Expr::Property(_) if parent_is_dot => Position::NONE,
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
e if parent_is_dot => e.position(),
_ => Position::NONE,
}
}
match &lhs {
// const_expr = rhs
expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
}
// var (non-indexed) = rhs
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
@@ -1392,33 +1414,36 @@ fn make_assignment_stmt<'a>(
}
}
}
// xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
let (index, _, Ident { name, pos }) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
AccessMode::ReadWrite => Ok(Stmt::Assignment(
// xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => {
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
Position::NONE => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// Constant values cannot be assigned to
AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
let (index, _, Ident { name, pos }) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
AccessMode::ReadWrite => Ok(Stmt::Assignment(
Box::new((lhs, fn_name.into(), rhs)),
op_pos,
)),
// Constant values cannot be assigned to
AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
}
}
}
}
// expr[???] = rhs, expr.??? = rhs
expr => {
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position()))
}
},
pos => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(pos)),
}
// expr[???] = rhs, expr.??? = rhs
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(x.lhs.position())),
},
// const_expr = rhs
expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
}
// ??? && ??? = rhs, ??? || ??? = rhs
Expr::And(_, _) | Expr::Or(_, _) => Err(LexError::ImproperSymbol(
@@ -2435,7 +2460,11 @@ fn parse_block(
// Parse statements inside the block
settings.is_global = false;
let stmt = parse_stmt(input, state, lib, settings.level_up()).map(Option::unwrap)?;
let stmt = parse_stmt(input, state, lib, settings.level_up())?;
if stmt.is_noop() {
continue;
}
// See if it needs a terminating semicolon
let need_semicolon = !stmt.is_self_terminated();
@@ -2502,7 +2531,7 @@ fn parse_stmt(
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Option<Stmt>, ParseError> {
) -> Result<Stmt, ParseError> {
use AccessMode::{ReadOnly, ReadWrite};
let mut comments: Vec<String> = Default::default();
@@ -2538,7 +2567,7 @@ fn parse_stmt(
}
let (token, token_pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
x => x,
};
settings.pos = *token_pos;
@@ -2548,10 +2577,10 @@ fn parse_stmt(
match token {
// ; - empty statement
Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))),
Token::SemiColon => Ok(Stmt::Noop(settings.pos)),
// { - statements block
Token::LeftBrace => Ok(Some(parse_block(input, state, lib, settings.level_up())?)),
Token::LeftBrace => Ok(parse_block(input, state, lib, settings.level_up())?),
// fn ...
#[cfg(not(feature = "no_function"))]
@@ -2591,12 +2620,13 @@ fn parse_stmt(
let func = parse_fn(input, &mut new_state, lib, access, settings, comments)?;
// Qualifiers (none) + function name + number of arguments.
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap();
lib.insert(
// Qualifiers (none) + function name + number of arguments.
calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(),
func,
);
lib.insert(hash, func);
Ok(None)
Ok(Stmt::Noop(settings.pos))
}
(_, pos) => Err(PERR::MissingToken(
@@ -2607,21 +2637,19 @@ fn parse_stmt(
}
}
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
Token::Switch => parse_switch(input, state, lib, settings.level_up()).map(Some),
Token::While | Token::Loop => {
parse_while_loop(input, state, lib, settings.level_up()).map(Some)
}
Token::Do => parse_do(input, state, lib, settings.level_up()).map(Some),
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
Token::If => parse_if(input, state, lib, settings.level_up()),
Token::Switch => parse_switch(input, state, lib, settings.level_up()),
Token::While | Token::Loop => parse_while_loop(input, state, lib, settings.level_up()),
Token::Do => parse_do(input, state, lib, settings.level_up()),
Token::For => parse_for(input, state, lib, settings.level_up()),
Token::Continue if settings.is_breakable => {
let pos = eat_token(input, Token::Continue);
Ok(Some(Stmt::Continue(pos)))
Ok(Stmt::Continue(pos))
}
Token::Break if settings.is_breakable => {
let pos = eat_token(input, Token::Break);
Ok(Some(Stmt::Break(pos)))
Ok(Stmt::Break(pos))
}
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
@@ -2645,43 +2673,35 @@ fn parse_stmt(
match input.peek().unwrap() {
// `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Some(Stmt::Return((return_type, token_pos), None, *pos))),
(Token::EOF, pos) => Ok(Stmt::Return((return_type, token_pos), None, *pos)),
// `return;` or `throw;`
(Token::SemiColon, _) => Ok(Some(Stmt::Return(
(return_type, token_pos),
None,
settings.pos,
))),
(Token::SemiColon, _) => {
Ok(Stmt::Return((return_type, token_pos), None, settings.pos))
}
// `return` or `throw` with expression
(_, _) => {
let expr = parse_expr(input, state, lib, settings.level_up())?;
let pos = expr.position();
Ok(Some(Stmt::Return(
(return_type, token_pos),
Some(expr),
pos,
)))
Ok(Stmt::Return((return_type, token_pos), Some(expr), pos))
}
}
}
Token::Try => parse_try_catch(input, state, lib, settings.level_up()).map(Some),
Token::Try => parse_try_catch(input, state, lib, settings.level_up()),
Token::Let => parse_let(input, state, lib, ReadWrite, false, settings.level_up()).map(Some),
Token::Const => {
parse_let(input, state, lib, ReadOnly, false, settings.level_up()).map(Some)
}
Token::Let => parse_let(input, state, lib, ReadWrite, false, settings.level_up()),
Token::Const => parse_let(input, state, lib, ReadOnly, false, settings.level_up()),
#[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, state, lib, settings.level_up()).map(Some),
Token::Import => parse_import(input, state, lib, settings.level_up()),
#[cfg(not(feature = "no_module"))]
Token::Export if !settings.is_global => Err(PERR::WrongExport.into_err(settings.pos)),
#[cfg(not(feature = "no_module"))]
Token::Export => parse_export(input, state, lib, settings.level_up()).map(Some),
Token::Export => parse_export(input, state, lib, settings.level_up()),
_ => parse_expr_stmt(input, state, lib, settings.level_up()).map(Some),
_ => parse_expr_stmt(input, state, lib, settings.level_up()),
}
}
@@ -2951,7 +2971,7 @@ fn parse_anon_fn(
// Parse function body
settings.is_breakable = false;
let body = parse_stmt(input, state, lib, settings.level_up()).map(Option::unwrap)?;
let body = parse_stmt(input, state, lib, settings.level_up())?;
// External variables may need to be processed in a consistent order,
// so extract them into a list.
@@ -3095,10 +3115,11 @@ impl Engine {
pos: Position::NONE,
};
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
Some(s) => s,
None => continue,
};
let stmt = parse_stmt(input, &mut state, &mut functions, settings)?;
if stmt.is_noop() {
continue;
}
let need_semicolon = !stmt.is_self_terminated();