Streamline tokens reading and reformat code for easier reading.
This commit is contained in:
parent
691541c176
commit
78cd53db09
418
src/parser.rs
418
src/parser.rs
@ -556,7 +556,9 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Stmt(stmt, _) => stmt.is_pure(),
|
Self::Stmt(stmt, _) => stmt.is_pure(),
|
||||||
|
|
||||||
expr => expr.is_constant() || matches!(expr, Self::Variable(_, _)),
|
Self::Variable(_, _) => true,
|
||||||
|
|
||||||
|
expr => expr.is_constant(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1435,14 +1437,45 @@ pub fn lex<'a>(input: &'a [&'a str]) -> TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consume a particular token, checking that it is the expected one.
|
||||||
|
fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) {
|
||||||
|
if let Some((t, pos)) = input.next() {
|
||||||
|
if t != token {
|
||||||
|
panic!(
|
||||||
|
"expecting {} (found {}) at {}",
|
||||||
|
token.syntax(),
|
||||||
|
t.syntax(),
|
||||||
|
pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("expecting {} but already EOF", token.syntax());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match a particular token, consuming it if matched.
|
||||||
|
fn match_token(input: &mut Peekable<TokenIterator>, token: Token) -> Result<bool, ParseError> {
|
||||||
|
if let Some((t, _)) = input.peek() {
|
||||||
|
if *t == token {
|
||||||
|
eat_token(input, token);
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(PERR::UnexpectedEOF.into_err_eof())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse ( expr )
|
/// Parse ( expr )
|
||||||
fn parse_paren_expr<'a>(
|
fn parse_paren_expr<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if matches!(input.peek(), Some((Token::RightParen, _))) {
|
const MISSING_RPAREN: &str = "for a matching ( in this expression";
|
||||||
input.next();
|
|
||||||
|
if match_token(input, Token::RightParen)? {
|
||||||
return Ok(Expr::Unit(begin));
|
return Ok(Expr::Unit(begin));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1452,16 +1485,9 @@ fn parse_paren_expr<'a>(
|
|||||||
// ( xxx )
|
// ( xxx )
|
||||||
Some((Token::RightParen, _)) => Ok(expr),
|
Some((Token::RightParen, _)) => Ok(expr),
|
||||||
// ( xxx ???
|
// ( xxx ???
|
||||||
Some((_, pos)) => Err(PERR::MissingToken(
|
Some((_, pos)) => Err(PERR::MissingToken(")".into(), MISSING_RPAREN.into()).into_err(pos)),
|
||||||
")".into(),
|
|
||||||
"for a matching ( in this expression".into(),
|
|
||||||
)
|
|
||||||
.into_err(pos)),
|
|
||||||
// ( xxx
|
// ( xxx
|
||||||
None => Err(
|
None => Err(PERR::MissingToken(")".into(), MISSING_RPAREN.into()).into_err_eof()),
|
||||||
PERR::MissingToken(")".into(), "for a matching ( in this expression".into())
|
|
||||||
.into_err_eof(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1485,7 +1511,7 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
}
|
}
|
||||||
// id()
|
// id()
|
||||||
Some((Token::RightParen, _)) => {
|
Some((Token::RightParen, _)) => {
|
||||||
input.next();
|
eat_token(input, Token::RightParen);
|
||||||
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
||||||
}
|
}
|
||||||
// id...
|
// id...
|
||||||
@ -1504,10 +1530,10 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
.into_err_eof())
|
.into_err_eof())
|
||||||
}
|
}
|
||||||
Some((Token::RightParen, _)) => {
|
Some((Token::RightParen, _)) => {
|
||||||
input.next();
|
eat_token(input, Token::RightParen);
|
||||||
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
||||||
}
|
}
|
||||||
Some((Token::Comma, _)) => (),
|
Some((Token::Comma, _)) => eat_token(input, Token::Comma),
|
||||||
Some((_, pos)) => {
|
Some((_, pos)) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
",".into(),
|
||||||
@ -1516,8 +1542,6 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.next();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1633,22 +1657,17 @@ fn parse_index_expr<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is a closing bracket
|
// Check if there is a closing bracket
|
||||||
match input.peek().ok_or_else(|| {
|
const MISSING_RBRACKET: &str = "for a matching [ in this index expression";
|
||||||
PERR::MissingToken(
|
|
||||||
"]".into(),
|
match input.peek() {
|
||||||
"for a matching [ in this index expression".into(),
|
Some((Token::RightBracket, _)) => {
|
||||||
)
|
eat_token(input, Token::RightBracket);
|
||||||
.into_err_eof()
|
|
||||||
})? {
|
|
||||||
(Token::RightBracket, _) => {
|
|
||||||
input.next();
|
|
||||||
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
||||||
}
|
}
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
Some((_, pos)) => {
|
||||||
"]".into(),
|
Err(PERR::MissingToken("]".into(), MISSING_RBRACKET.into()).into_err(*pos))
|
||||||
"for a matching [ in this index expression".into(),
|
}
|
||||||
)
|
None => Err(PERR::MissingToken("]".into(), MISSING_RBRACKET.into()).into_err_eof()),
|
||||||
.into_err(*pos)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1662,14 +1681,14 @@ fn parse_ident_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
match input.peek() {
|
match input.peek() {
|
||||||
// id(...) - function call
|
// id(...) - function call
|
||||||
Some((Token::LeftParen, _)) => {
|
Some((Token::LeftParen, _)) => {
|
||||||
input.next();
|
eat_token(input, Token::LeftParen);
|
||||||
parse_call_expr(id, input, begin, allow_stmt_expr)
|
parse_call_expr(id, input, begin, allow_stmt_expr)
|
||||||
}
|
}
|
||||||
// id[...] - indexing
|
// id[...] - indexing
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Some((Token::LeftBracket, pos)) => {
|
Some((Token::LeftBracket, pos)) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
eat_token(input, Token::LeftBracket);
|
||||||
parse_index_expr(
|
parse_index_expr(
|
||||||
Box::new(Expr::Variable(id.into(), begin)),
|
Box::new(Expr::Variable(id.into(), begin)),
|
||||||
input,
|
input,
|
||||||
@ -1679,7 +1698,7 @@ fn parse_ident_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
}
|
}
|
||||||
// id - variable
|
// id - variable
|
||||||
Some(_) => Ok(Expr::Variable(id.into(), begin)),
|
Some(_) => Ok(Expr::Variable(id.into(), begin)),
|
||||||
// EOF
|
// {EOF}
|
||||||
None => Ok(Expr::Variable(id.into(), begin)),
|
None => Ok(Expr::Variable(id.into(), begin)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1692,38 +1711,35 @@ fn parse_array_literal<'a>(
|
|||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let mut arr = Vec::new();
|
let mut arr = Vec::new();
|
||||||
|
|
||||||
if !matches!(input.peek(), Some((Token::RightBracket, _))) {
|
if !match_token(input, Token::RightBracket)? {
|
||||||
while input.peek().is_some() {
|
while input.peek().is_some() {
|
||||||
arr.push(parse_expr(input, allow_stmt_expr)?);
|
arr.push(parse_expr(input, allow_stmt_expr)?);
|
||||||
|
|
||||||
match input.peek().ok_or_else(|| {
|
match input.peek() {
|
||||||
PERR::MissingToken("]".into(), "to end this array literal".into()).into_err_eof()
|
Some((Token::Comma, _)) => eat_token(input, Token::Comma),
|
||||||
})? {
|
Some((Token::RightBracket, _)) => {
|
||||||
(Token::Comma, _) => input.next(),
|
eat_token(input, Token::RightBracket);
|
||||||
(Token::RightBracket, _) => break,
|
break;
|
||||||
(_, pos) => {
|
}
|
||||||
|
Some((_, pos)) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
",".into(),
|
||||||
"to separate the items of this array literal".into(),
|
"to separate the items of this array literal".into(),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken("]".into(), "to end this array literal".into())
|
||||||
|
.into_err_eof(),
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.peek().ok_or_else(|| {
|
|
||||||
PERR::MissingToken("]".into(), "to end this array literal".into()).into_err_eof()
|
|
||||||
})? {
|
|
||||||
(Token::RightBracket, _) => {
|
|
||||||
input.next();
|
|
||||||
Ok(Expr::Array(arr, begin))
|
Ok(Expr::Array(arr, begin))
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
|
||||||
Err(PERR::MissingToken("]".into(), "to end this array literal".into()).into_err(*pos))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a map literal.
|
/// Parse a map literal.
|
||||||
fn parse_map_literal<'a>(
|
fn parse_map_literal<'a>(
|
||||||
@ -1733,36 +1749,35 @@ fn parse_map_literal<'a>(
|
|||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let mut map = Vec::new();
|
let mut map = Vec::new();
|
||||||
|
|
||||||
if !matches!(input.peek(), Some((Token::RightBrace, _))) {
|
if !match_token(input, Token::RightBrace)? {
|
||||||
while input.peek().is_some() {
|
while input.peek().is_some() {
|
||||||
let (name, pos) = match input.next().ok_or_else(|| {
|
const MISSING_RBRACE: &str = "to end this object map literal";
|
||||||
PERR::MissingToken("}".into(), "to end this object map literal".into())
|
|
||||||
.into_err_eof()
|
let (name, pos) = match input.next() {
|
||||||
})? {
|
Some((Token::Identifier(s), pos)) => (s, pos),
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
Some((Token::StringConst(s), pos)) => (s, pos),
|
||||||
(Token::StringConst(s), pos) => (s, pos),
|
Some((_, pos)) if map.is_empty() => {
|
||||||
(_, pos) if map.is_empty() => {
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
|
||||||
|
}
|
||||||
|
Some((_, pos)) => return Err(PERR::PropertyExpected.into_err(pos)),
|
||||||
|
None => {
|
||||||
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err_eof())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match input.next() {
|
||||||
|
Some((Token::Colon, _)) => (),
|
||||||
|
Some((_, pos)) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
"}".into(),
|
":".into(),
|
||||||
"to end this object map literal".into(),
|
format!(
|
||||||
|
"to follow the property '{}' in this object map literal",
|
||||||
|
name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.into_err(pos))
|
.into_err(pos))
|
||||||
}
|
}
|
||||||
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
None => {
|
||||||
};
|
|
||||||
|
|
||||||
match input.next().ok_or_else(|| {
|
|
||||||
PERR::MissingToken(
|
|
||||||
":".into(),
|
|
||||||
format!(
|
|
||||||
"to follow the property '{}' in this object map literal",
|
|
||||||
name
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_err_eof()
|
|
||||||
})? {
|
|
||||||
(Token::Colon, _) => (),
|
|
||||||
(_, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
":".into(),
|
":".into(),
|
||||||
format!(
|
format!(
|
||||||
@ -1770,7 +1785,7 @@ fn parse_map_literal<'a>(
|
|||||||
name
|
name
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into_err(pos))
|
.into_err_eof())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1778,27 +1793,24 @@ fn parse_map_literal<'a>(
|
|||||||
|
|
||||||
map.push((name, expr, pos));
|
map.push((name, expr, pos));
|
||||||
|
|
||||||
match input.peek().ok_or_else(|| {
|
match input.peek() {
|
||||||
PERR::MissingToken("}".into(), "to end this object map literal".into())
|
Some((Token::Comma, _)) => eat_token(input, Token::Comma),
|
||||||
.into_err_eof()
|
Some((Token::RightBrace, _)) => {
|
||||||
})? {
|
eat_token(input, Token::RightBrace);
|
||||||
(Token::Comma, _) => {
|
break;
|
||||||
input.next();
|
|
||||||
}
|
}
|
||||||
(Token::RightBrace, _) => break,
|
Some((Token::Identifier(_), pos)) => {
|
||||||
(Token::Identifier(_), pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
",".into(),
|
",".into(),
|
||||||
"to separate the items of this object map literal".into(),
|
"to separate the items of this object map literal".into(),
|
||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
Some((_, pos)) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(*pos))
|
||||||
"}".into(),
|
}
|
||||||
"to end this object map literal".into(),
|
None => {
|
||||||
)
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err_eof())
|
||||||
.into_err(*pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1815,36 +1827,23 @@ fn parse_map_literal<'a>(
|
|||||||
})
|
})
|
||||||
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
// Ending brace
|
|
||||||
match input.peek().ok_or_else(|| {
|
|
||||||
PERR::MissingToken("}".into(), "to end this object map literal".into()).into_err_eof()
|
|
||||||
})? {
|
|
||||||
(Token::RightBrace, _) => {
|
|
||||||
input.next();
|
|
||||||
Ok(Expr::Map(map, begin))
|
Ok(Expr::Map(map, begin))
|
||||||
}
|
}
|
||||||
(_, pos) => Err(
|
|
||||||
PERR::MissingToken("]".into(), "to end this object map literal".into()).into_err(*pos),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a primary expression.
|
/// Parse a primary expression.
|
||||||
fn parse_primary<'a>(
|
fn parse_primary<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let token = match input
|
let token = match input.peek() {
|
||||||
.peek()
|
|
||||||
.ok_or_else(|| PERR::UnexpectedEOF.into_err_eof())?
|
|
||||||
{
|
|
||||||
// { - block statement as expression
|
// { - block statement as expression
|
||||||
(Token::LeftBrace, pos) if allow_stmt_expr => {
|
Some((Token::LeftBrace, pos)) if allow_stmt_expr => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
return parse_block(input, false, allow_stmt_expr)
|
return parse_block(input, false, allow_stmt_expr)
|
||||||
.map(|block| Expr::Stmt(Box::new(block), pos));
|
.map(|block| Expr::Stmt(Box::new(block), pos));
|
||||||
}
|
}
|
||||||
_ => input.next().expect("should be a token"),
|
Some(_) => input.next().expect("should be a token"),
|
||||||
|
None => return Err(PERR::UnexpectedEOF.into_err_eof()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut can_be_indexed = false;
|
let mut can_be_indexed = false;
|
||||||
@ -1890,7 +1889,7 @@ fn parse_primary<'a>(
|
|||||||
// Tail processing all possible indexing
|
// Tail processing all possible indexing
|
||||||
while let Some((Token::LeftBracket, pos)) = input.peek() {
|
while let Some((Token::LeftBracket, pos)) = input.peek() {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
eat_token(input, Token::LeftBracket);
|
||||||
root_expr = parse_index_expr(Box::new(root_expr), input, pos, allow_stmt_expr)?;
|
root_expr = parse_index_expr(Box::new(root_expr), input, pos, allow_stmt_expr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1903,12 +1902,9 @@ fn parse_unary<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
match input
|
match input.peek() {
|
||||||
.peek()
|
|
||||||
.ok_or_else(|| PERR::UnexpectedEOF.into_err_eof())?
|
|
||||||
{
|
|
||||||
// If statement is allowed to act as expressions
|
// If statement is allowed to act as expressions
|
||||||
(Token::If, pos) => {
|
Some((Token::If, pos)) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
Ok(Expr::Stmt(
|
Ok(Expr::Stmt(
|
||||||
Box::new(parse_if(input, false, allow_stmt_expr)?),
|
Box::new(parse_if(input, false, allow_stmt_expr)?),
|
||||||
@ -1916,10 +1912,9 @@ fn parse_unary<'a>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
// -expr
|
// -expr
|
||||||
(Token::UnaryMinus, pos) => {
|
Some((Token::UnaryMinus, pos)) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
eat_token(input, Token::UnaryMinus);
|
||||||
input.next();
|
|
||||||
|
|
||||||
match parse_unary(input, allow_stmt_expr)? {
|
match parse_unary(input, allow_stmt_expr)? {
|
||||||
// Negative integer
|
// Negative integer
|
||||||
@ -1950,16 +1945,14 @@ fn parse_unary<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// +expr
|
// +expr
|
||||||
(Token::UnaryPlus, _) => {
|
Some((Token::UnaryPlus, _)) => {
|
||||||
input.next();
|
eat_token(input, Token::UnaryPlus);
|
||||||
parse_unary(input, allow_stmt_expr)
|
parse_unary(input, allow_stmt_expr)
|
||||||
}
|
}
|
||||||
// !expr
|
// !expr
|
||||||
(Token::Bang, pos) => {
|
Some((Token::Bang, pos)) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
eat_token(input, Token::Bang);
|
||||||
input.next();
|
|
||||||
|
|
||||||
Ok(Expr::FunctionCall(
|
Ok(Expr::FunctionCall(
|
||||||
"!".into(),
|
"!".into(),
|
||||||
vec![parse_primary(input, allow_stmt_expr)?],
|
vec![parse_primary(input, allow_stmt_expr)?],
|
||||||
@ -1968,7 +1961,9 @@ fn parse_unary<'a>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => parse_primary(input, allow_stmt_expr),
|
Some(_) => parse_primary(input, allow_stmt_expr),
|
||||||
|
// {EOF}
|
||||||
|
None => Err(PERR::UnexpectedEOF.into_err_eof()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1988,45 +1983,41 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// idx_lhs[...]
|
||||||
|
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
|
||||||
// var[...]
|
// var[...]
|
||||||
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) => {
|
Expr::Variable(_, _) => {
|
||||||
assert!(is_top, "property expected but gets variable");
|
assert!(is_top, "property expected but gets variable");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
// property[...]
|
// property[...]
|
||||||
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) => {
|
Expr::Property(_, _) => {
|
||||||
assert!(!is_top, "variable expected but gets property");
|
assert!(!is_top, "variable expected but gets property");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
// ???[...][...]
|
||||||
// idx_lhs[...]
|
|
||||||
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
|
|
||||||
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
||||||
|
// idx_lhs[...]
|
||||||
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
||||||
},
|
},
|
||||||
|
|
||||||
// dot_lhs.dot_rhs
|
// dot_lhs.dot_rhs
|
||||||
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
|
Expr::Dot(dot_lhs, dot_rhs, pos) => match dot_lhs.as_ref() {
|
||||||
// var.dot_rhs
|
// var.dot_rhs
|
||||||
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
// property.dot_rhs
|
// property.dot_rhs
|
||||||
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
// var[...]
|
// idx_lhs[...].dot_rhs
|
||||||
Expr::Index(idx_lhs, _, _)
|
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
||||||
if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) && is_top =>
|
// var[...].dot_rhs
|
||||||
{
|
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
valid_assignment_chain(dot_rhs, false)
|
// property[...].dot_rhs
|
||||||
}
|
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
// property[...]
|
// ???[...][...].dot_rhs
|
||||||
Expr::Index(idx_lhs, _, _)
|
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
||||||
if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) && !is_top =>
|
// idx_lhs[...].dot_rhs
|
||||||
{
|
_ => Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position())),
|
||||||
valid_assignment_chain(dot_rhs, false)
|
},
|
||||||
}
|
|
||||||
// idx_lhs[...]
|
|
||||||
Expr::Index(idx_lhs, _, _) => {
|
|
||||||
Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position()))
|
|
||||||
}
|
|
||||||
|
|
||||||
expr => panic!("unexpected dot expression {:#?}", expr),
|
expr => panic!("unexpected dot expression {:#?}", expr),
|
||||||
},
|
},
|
||||||
@ -2211,8 +2202,6 @@ fn parse_binary_op<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some((op_token, pos)) = input.next() {
|
if let Some((op_token, pos)) = input.next() {
|
||||||
input.peek();
|
|
||||||
|
|
||||||
let rhs = parse_unary(input, allow_stmt_expr)?;
|
let rhs = parse_unary(input, allow_stmt_expr)?;
|
||||||
|
|
||||||
let next_precedence = if let Some((next_op, _)) = input.peek() {
|
let next_precedence = if let Some((next_op, _)) = input.peek() {
|
||||||
@ -2375,7 +2364,7 @@ fn parse_if<'a>(
|
|||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// if ...
|
// if ...
|
||||||
input.next();
|
eat_token(input, Token::If);
|
||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
@ -2383,9 +2372,7 @@ fn parse_if<'a>(
|
|||||||
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
|
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if matches!(input.peek(), Some((Token::Else, _))) {
|
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
||||||
input.next();
|
|
||||||
|
|
||||||
Some(Box::new(if matches!(input.peek(), Some((Token::If, _))) {
|
Some(Box::new(if matches!(input.peek(), Some((Token::If, _))) {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
parse_if(input, breakable, allow_stmt_expr)?
|
parse_if(input, breakable, allow_stmt_expr)?
|
||||||
@ -2410,7 +2397,7 @@ fn parse_while<'a>(
|
|||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// while ...
|
// while ...
|
||||||
input.next();
|
eat_token(input, Token::While);
|
||||||
|
|
||||||
// while guard { body }
|
// while guard { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
@ -2426,7 +2413,7 @@ fn parse_loop<'a>(
|
|||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// loop ...
|
// loop ...
|
||||||
input.next();
|
eat_token(input, Token::Loop);
|
||||||
|
|
||||||
// loop { body }
|
// loop { body }
|
||||||
let body = parse_block(input, true, allow_stmt_expr)?;
|
let body = parse_block(input, true, allow_stmt_expr)?;
|
||||||
@ -2440,7 +2427,7 @@ fn parse_for<'a>(
|
|||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// for ...
|
// for ...
|
||||||
input.next();
|
eat_token(input, Token::For);
|
||||||
|
|
||||||
// for name ...
|
// for name ...
|
||||||
let name = match input
|
let name = match input
|
||||||
@ -2456,16 +2443,14 @@ fn parse_for<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// for name in ...
|
// for name in ...
|
||||||
match input.next().ok_or_else(|| {
|
const ERROR_MSG: &str = "after the iteration variable";
|
||||||
PERR::MissingToken("in".into(), "after the iteration variable".into()).into_err_eof()
|
|
||||||
})? {
|
match input.next() {
|
||||||
(Token::In, _) => (),
|
Some((Token::In, _)) => (),
|
||||||
(_, pos) => {
|
Some((_, pos)) => {
|
||||||
return Err(
|
return Err(PERR::MissingToken("in".into(), ERROR_MSG.into()).into_err(pos))
|
||||||
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
|
||||||
.into_err(pos),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
None => return Err(PERR::MissingToken("in".into(), ERROR_MSG.into()).into_err_eof()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
@ -2496,9 +2481,7 @@ fn parse_let<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// let name = ...
|
// let name = ...
|
||||||
if matches!(input.peek(), Some((Token::Equals, _))) {
|
if match_token(input, Token::Equals)? {
|
||||||
input.next();
|
|
||||||
|
|
||||||
// let name = expr
|
// let name = expr
|
||||||
let init_value = parse_expr(input, allow_stmt_expr)?;
|
let init_value = parse_expr(input, allow_stmt_expr)?;
|
||||||
|
|
||||||
@ -2541,7 +2524,7 @@ fn parse_block<'a>(
|
|||||||
|
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
while !matches!(input.peek(), Some((Token::RightBrace, _))) {
|
while !match_token(input, Token::RightBrace)? {
|
||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
let stmt = parse_stmt(input, breakable, allow_stmt_expr)?;
|
let stmt = parse_stmt(input, breakable, allow_stmt_expr)?;
|
||||||
|
|
||||||
@ -2551,13 +2534,14 @@ fn parse_block<'a>(
|
|||||||
statements.push(stmt);
|
statements.push(stmt);
|
||||||
|
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
// EOF
|
|
||||||
None => break,
|
|
||||||
// { ... stmt }
|
// { ... stmt }
|
||||||
Some((Token::RightBrace, _)) => break,
|
Some((Token::RightBrace, _)) => {
|
||||||
|
eat_token(input, Token::RightBrace);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// { ... stmt;
|
// { ... stmt;
|
||||||
Some((Token::SemiColon, _)) if need_semicolon => {
|
Some((Token::SemiColon, _)) if need_semicolon => {
|
||||||
input.next();
|
eat_token(input, Token::SemiColon);
|
||||||
}
|
}
|
||||||
// { ... { stmt } ;
|
// { ... { stmt } ;
|
||||||
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
||||||
@ -2571,21 +2555,18 @@ fn parse_block<'a>(
|
|||||||
.into_err(*pos),
|
.into_err(*pos),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// {EOF}
|
||||||
|
None => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken("}".into(), "to end this statement block".into())
|
||||||
|
.into_err_eof(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.peek().ok_or_else(|| {
|
|
||||||
PERR::MissingToken("}".into(), "to end this statement block".into()).into_err_eof()
|
|
||||||
})? {
|
|
||||||
(Token::RightBrace, _) => {
|
|
||||||
input.next();
|
|
||||||
Ok(Stmt::Block(statements, pos))
|
Ok(Stmt::Block(statements, pos))
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
|
||||||
Err(PERR::MissingToken("}".into(), "to end this statement block".into()).into_err(*pos))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse an expression as a statement.
|
/// Parse an expression as a statement.
|
||||||
fn parse_expr_stmt<'a>(
|
fn parse_expr_stmt<'a>(
|
||||||
@ -2623,28 +2604,27 @@ fn parse_stmt<'a>(
|
|||||||
|
|
||||||
(Token::Continue, pos) if breakable => {
|
(Token::Continue, pos) if breakable => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
eat_token(input, Token::Continue);
|
||||||
Ok(Stmt::Continue(pos))
|
Ok(Stmt::Continue(pos))
|
||||||
}
|
}
|
||||||
(Token::Break, pos) if breakable => {
|
(Token::Break, pos) if breakable => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
eat_token(input, Token::Break);
|
||||||
Ok(Stmt::Break(pos))
|
Ok(Stmt::Break(pos))
|
||||||
}
|
}
|
||||||
(Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),
|
(Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),
|
||||||
|
|
||||||
(token @ Token::Return, pos) | (token @ Token::Throw, pos) => {
|
(Token::Return, pos) | (Token::Throw, pos) => {
|
||||||
let return_type = match token {
|
let pos = *pos;
|
||||||
Token::Return => ReturnType::Return,
|
|
||||||
Token::Throw => ReturnType::Exception,
|
let return_type = match input.next() {
|
||||||
|
Some((Token::Return, _)) => ReturnType::Return,
|
||||||
|
Some((Token::Throw, _)) => ReturnType::Exception,
|
||||||
_ => panic!("token should be return or throw"),
|
_ => panic!("token should be return or throw"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pos = *pos;
|
|
||||||
input.next();
|
|
||||||
|
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
// `return`/`throw` at EOF
|
// `return`/`throw` at {EOF}
|
||||||
None => Ok(Stmt::ReturnWithVal(None, return_type, Position::eof())),
|
None => Ok(Stmt::ReturnWithVal(None, return_type, Position::eof())),
|
||||||
// `return;` or `throw;`
|
// `return;` or `throw;`
|
||||||
Some((Token::SemiColon, _)) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
Some((Token::SemiColon, _)) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
||||||
@ -2671,49 +2651,43 @@ fn parse_fn<'a>(
|
|||||||
) -> Result<FnDef, ParseError> {
|
) -> Result<FnDef, ParseError> {
|
||||||
let pos = input.next().expect("should be fn").1;
|
let pos = input.next().expect("should be fn").1;
|
||||||
|
|
||||||
let name = match input
|
let name = match input.next() {
|
||||||
.next()
|
Some((Token::Identifier(s), _)) => s,
|
||||||
.ok_or_else(|| PERR::FnMissingName.into_err_eof())?
|
Some((_, pos)) => return Err(PERR::FnMissingName.into_err(pos)),
|
||||||
{
|
None => return Err(PERR::FnMissingName.into_err_eof()),
|
||||||
(Token::Identifier(s), _) => s,
|
|
||||||
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match input
|
match input.peek() {
|
||||||
.peek()
|
Some((Token::LeftParen, _)) => eat_token(input, Token::LeftParen),
|
||||||
.ok_or_else(|| PERR::FnMissingParams(name.clone()).into_err_eof())?
|
Some((_, pos)) => return Err(PERR::FnMissingParams(name).into_err(*pos)),
|
||||||
{
|
None => return Err(PERR::FnMissingParams(name.clone()).into_err_eof()),
|
||||||
(Token::LeftParen, _) => input.next(),
|
|
||||||
(_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
if matches!(input.peek(), Some((Token::RightParen, _))) {
|
if !match_token(input, Token::RightParen)? {
|
||||||
input.next();
|
|
||||||
} else {
|
|
||||||
let end_err = format!("to close the parameters list of function '{}'", name);
|
let end_err = format!("to close the parameters list of function '{}'", name);
|
||||||
let sep_err = format!("to separate the parameters of function '{}'", name);
|
let sep_err = format!("to separate the parameters of function '{}'", name);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match input
|
match input.next() {
|
||||||
.next()
|
Some((Token::Identifier(s), pos)) => params.push((s, pos)),
|
||||||
.ok_or_else(|| PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())?
|
Some((_, pos)) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
||||||
{
|
None => {
|
||||||
(Token::Identifier(s), pos) => params.push((s, pos)),
|
return Err(PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())
|
||||||
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input
|
match input.next() {
|
||||||
.next()
|
Some((Token::RightParen, _)) => break,
|
||||||
.ok_or_else(|| PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())?
|
Some((Token::Comma, _)) => (),
|
||||||
{
|
Some((Token::Identifier(_), pos)) => {
|
||||||
(Token::RightParen, _) => break,
|
|
||||||
(Token::Comma, _) => (),
|
|
||||||
(Token::Identifier(_), pos) => {
|
|
||||||
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
||||||
}
|
}
|
||||||
(_, pos) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
|
Some((_, pos)) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
|
||||||
|
None => {
|
||||||
|
return Err(PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2811,7 +2785,7 @@ fn parse_global_level<'a>(
|
|||||||
None => break,
|
None => break,
|
||||||
// stmt ;
|
// stmt ;
|
||||||
Some((Token::SemiColon, _)) if need_semicolon => {
|
Some((Token::SemiColon, _)) if need_semicolon => {
|
||||||
input.next();
|
eat_token(input, Token::SemiColon);
|
||||||
}
|
}
|
||||||
// stmt ;
|
// stmt ;
|
||||||
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
||||||
|
Loading…
Reference in New Issue
Block a user