mirror of https://github.com/boa-dev/boa.git
Iban Eguia
5 years ago
committed by
GitHub
71 changed files with 6631 additions and 3049 deletions
@ -0,0 +1,106 @@ |
|||||||
|
//! Error and result implementation for the parser.
|
||||||
|
use crate::syntax::ast::{ |
||||||
|
keyword::Keyword, |
||||||
|
node::Node, |
||||||
|
pos::Position, |
||||||
|
token::{Token, TokenKind}, |
||||||
|
}; |
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
/// Result of a parsing operation.
|
||||||
|
pub type ParseResult = Result<Node, ParseError>; |
||||||
|
|
||||||
|
/// `ParseError` is an enum which represents errors encounted during parsing an expression
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub enum ParseError { |
||||||
|
/// When it expected a certain kind of token, but got another as part of something
|
||||||
|
Expected(Vec<TokenKind>, Token, &'static str), |
||||||
|
/// When it expected a certain expression, but got another
|
||||||
|
ExpectedExpr(&'static str, Node, Position), |
||||||
|
/// When it didn't expect this keyword
|
||||||
|
UnexpectedKeyword(Keyword, Position), |
||||||
|
/// When a token is unexpected
|
||||||
|
Unexpected(Token, Option<&'static str>), |
||||||
|
/// When there is an abrupt end to the parsing
|
||||||
|
AbruptEnd, |
||||||
|
/// Out of range error, attempting to set a position where there is no token
|
||||||
|
RangeError, |
||||||
|
/// Catch all General Error
|
||||||
|
General(&'static str, Option<Position>), |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for ParseError { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
match self { |
||||||
|
Self::Expected(expected, actual, routine) => write!( |
||||||
|
f, |
||||||
|
"Expected {}, got '{}' in {} at line {}, col {}", |
||||||
|
if expected.len() == 1 { |
||||||
|
format!( |
||||||
|
"token '{}'", |
||||||
|
expected.first().map(TokenKind::to_string).unwrap() |
||||||
|
) |
||||||
|
} else { |
||||||
|
format!( |
||||||
|
"one of {}", |
||||||
|
expected |
||||||
|
.iter() |
||||||
|
.enumerate() |
||||||
|
.map(|(i, t)| { |
||||||
|
format!( |
||||||
|
"{}'{}'", |
||||||
|
if i == 0 { |
||||||
|
"" |
||||||
|
} else if i == expected.len() - 1 { |
||||||
|
" or " |
||||||
|
} else { |
||||||
|
", " |
||||||
|
}, |
||||||
|
t |
||||||
|
) |
||||||
|
}) |
||||||
|
.collect::<String>() |
||||||
|
) |
||||||
|
}, |
||||||
|
actual, |
||||||
|
routine, |
||||||
|
actual.pos.line_number, |
||||||
|
actual.pos.column_number |
||||||
|
), |
||||||
|
Self::ExpectedExpr(expected, actual, pos) => write!( |
||||||
|
f, |
||||||
|
"Expected expression '{}', got '{}' at line {}, col {}", |
||||||
|
expected, actual, pos.line_number, pos.column_number |
||||||
|
), |
||||||
|
Self::UnexpectedKeyword(keyword, pos) => write!( |
||||||
|
f, |
||||||
|
"Unexpected keyword: '{}' at line {}, col {}", |
||||||
|
keyword, pos.line_number, pos.column_number |
||||||
|
), |
||||||
|
Self::Unexpected(tok, msg) => write!( |
||||||
|
f, |
||||||
|
"Unexpected Token '{}'{} at line {}, col {}", |
||||||
|
tok, |
||||||
|
if let Some(m) = msg { |
||||||
|
format!(", {}", m) |
||||||
|
} else { |
||||||
|
String::new() |
||||||
|
}, |
||||||
|
tok.pos.line_number, |
||||||
|
tok.pos.column_number |
||||||
|
), |
||||||
|
Self::AbruptEnd => write!(f, "Abrupt End"), |
||||||
|
Self::General(msg, pos) => write!( |
||||||
|
f, |
||||||
|
"{}{}", |
||||||
|
msg, |
||||||
|
if let Some(pos) = pos { |
||||||
|
format!(" at line {}, col {}", pos.line_number, pos.column_number) |
||||||
|
} else { |
||||||
|
String::new() |
||||||
|
} |
||||||
|
), |
||||||
|
Self::RangeError => write!(f, "RangeError!"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
//! Arrow function parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions
|
||||||
|
|
||||||
|
use super::AssignmentExpression; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{ |
||||||
|
node::{FormalParameter, Node}, |
||||||
|
punc::Punctuator, |
||||||
|
token::TokenKind, |
||||||
|
}, |
||||||
|
parser::{ |
||||||
|
function::{FormalParameters, FunctionBody}, |
||||||
|
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Arrow function parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct ArrowFunction { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ArrowFunction { |
||||||
|
/// Creates a new `ArrowFunction` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<I, Y, A>( |
||||||
|
allow_in: I, |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ArrowFunction { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let params = match &next_token.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
let params = |
||||||
|
FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseParen, "arrow function")?; |
||||||
|
params |
||||||
|
} |
||||||
|
TokenKind::Identifier(param_name) => vec![FormalParameter { |
||||||
|
init: None, |
||||||
|
name: param_name.clone(), |
||||||
|
is_rest_param: false, |
||||||
|
}], |
||||||
|
_ => { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen), |
||||||
|
TokenKind::identifier("identifier"), |
||||||
|
], |
||||||
|
next_token.clone(), |
||||||
|
"arrow function", |
||||||
|
)) |
||||||
|
} |
||||||
|
}; |
||||||
|
cursor.peek_expect_no_lineterminator(0, "arrow function")?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::Arrow, "arrow function")?; |
||||||
|
|
||||||
|
let body = ConciseBody::new(self.allow_in).parse(cursor)?; |
||||||
|
|
||||||
|
Ok(Node::arrow_function_decl(params, body)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <https://tc39.es/ecma262/#prod-ConciseBody>
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct ConciseBody { |
||||||
|
allow_in: AllowIn, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConciseBody { |
||||||
|
/// Creates a new `ConcideBody` parser.
|
||||||
|
fn new<I>(allow_in: I) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ConciseBody { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
match cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||||
|
let _ = cursor.next(); |
||||||
|
let body = FunctionBody::new(false, false) |
||||||
|
.parse(cursor) |
||||||
|
.map(Node::StatementList)?; |
||||||
|
cursor.expect(Punctuator::CloseBlock, "arrow function")?; |
||||||
|
Ok(body) |
||||||
|
} |
||||||
|
_ => Ok(Node::return_node( |
||||||
|
ExpressionBody::new(self.allow_in, false).parse(cursor)?, |
||||||
|
)), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <https://tc39.es/ecma262/#prod-ExpressionBody>
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct ExpressionBody { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ExpressionBody { |
||||||
|
/// Creates a new `ExpressionBody` parser.
|
||||||
|
fn new<I, A>(allow_in: I, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ExpressionBody { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
AssignmentExpression::new(self.allow_in, false, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
//! Conditional operator parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-conditional-operator
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::{AssignmentExpression, LogicalORExpression}, |
||||||
|
AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Conditional expression parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::expression) struct ConditionalExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConditionalExpression { |
||||||
|
/// Creates a new `ConditionalExpression` parser.
|
||||||
|
pub(in crate::syntax::parser::expression) fn new<I, Y, A>( |
||||||
|
allow_in: I, |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ConditionalExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TODO: coalesce expression
|
||||||
|
let lhs = LogicalORExpression::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
|
||||||
|
if let Some(tok) = cursor.next() { |
||||||
|
if tok.kind == TokenKind::Punctuator(Punctuator::Question) { |
||||||
|
let then_clause = |
||||||
|
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::Colon, "conditional expression")?; |
||||||
|
|
||||||
|
let else_clause = |
||||||
|
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
return Ok(Node::conditional_op(lhs, then_clause, else_clause)); |
||||||
|
} else { |
||||||
|
cursor.back(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
//! Exponentiation operator parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-exp-operator
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{ |
||||||
|
keyword::Keyword, |
||||||
|
node::Node, |
||||||
|
op::{BinOp, NumOp}, |
||||||
|
punc::Punctuator, |
||||||
|
token::TokenKind, |
||||||
|
}, |
||||||
|
parser::{ |
||||||
|
expression::{unary::UnaryExpression, update::UpdateExpression}, |
||||||
|
AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses an exponentiation expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ExponentiationExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::expression) struct ExponentiationExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ExponentiationExpression { |
||||||
|
/// Creates a new `ExponentiationExpression` parser.
|
||||||
|
pub(in crate::syntax::parser::expression) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ExponentiationExpression { |
||||||
|
/// Checks by looking at the next token to see whether it's a unary operator or not.
|
||||||
|
fn is_unary_expression(cursor: &mut Cursor<'_>) -> bool { |
||||||
|
if let Some(tok) = cursor.peek(0) { |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Delete) |
||||||
|
| TokenKind::Keyword(Keyword::Void) |
||||||
|
| TokenKind::Keyword(Keyword::TypeOf) |
||||||
|
| TokenKind::Punctuator(Punctuator::Add) |
||||||
|
| TokenKind::Punctuator(Punctuator::Sub) |
||||||
|
| TokenKind::Punctuator(Punctuator::Not) |
||||||
|
| TokenKind::Punctuator(Punctuator::Neg) => true, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} else { |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ExponentiationExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
if Self::is_unary_expression(cursor) { |
||||||
|
return UnaryExpression::new(self.allow_yield, self.allow_await).parse(cursor); |
||||||
|
} |
||||||
|
|
||||||
|
let lhs = UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
if let Some(tok) = cursor.next() { |
||||||
|
if let TokenKind::Punctuator(Punctuator::Exp) = tok.kind { |
||||||
|
return Ok(Node::bin_op( |
||||||
|
BinOp::Num(NumOp::Exp), |
||||||
|
lhs, |
||||||
|
self.parse(cursor)?, |
||||||
|
)); |
||||||
|
} else { |
||||||
|
cursor.back(); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
//! Assignment operator parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Assignment
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-assignment-operators
|
||||||
|
|
||||||
|
mod arrow_function; |
||||||
|
mod conditional; |
||||||
|
mod exponentiation; |
||||||
|
|
||||||
|
use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression}; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
pub(super) use exponentiation::ExponentiationExpression; |
||||||
|
|
||||||
|
/// Assignment expression parsing.
|
||||||
|
///
|
||||||
|
/// This can be one of the following:
|
||||||
|
///
|
||||||
|
/// - [`ConditionalExpression`](../conditional_operator/struct.ConditionalExpression.html)
|
||||||
|
/// - `YieldExpression`
|
||||||
|
/// - [`ArrowFunction`](../../function/arrow_function/struct.ArrowFunction.html)
|
||||||
|
/// - `AsyncArrowFunction`
|
||||||
|
/// - [`LeftHandSideExpression`][lhs] `=` `AssignmentExpression`
|
||||||
|
/// - [`LeftHandSideExpression`][lhs] `AssignmentOperator` `AssignmentExpression`
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Assignment
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||||
|
/// [lhs]: ../lhs_expression/struct.LeftHandSideExpression.html
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct AssignmentExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl AssignmentExpression { |
||||||
|
/// Creates a new `AssignmentExpression` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<I, Y, A>( |
||||||
|
allow_in: I, |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for AssignmentExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// Arrow function
|
||||||
|
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
match next_token.kind { |
||||||
|
// a=>{}
|
||||||
|
TokenKind::Identifier(_) |
||||||
|
if cursor |
||||||
|
.peek_expect_no_lineterminator(1, "arrow function") |
||||||
|
.is_ok() => |
||||||
|
{ |
||||||
|
if let Some(tok) = cursor.peek(1) { |
||||||
|
if tok.kind == TokenKind::Punctuator(Punctuator::Arrow) { |
||||||
|
return ArrowFunction::new( |
||||||
|
self.allow_in, |
||||||
|
self.allow_yield, |
||||||
|
self.allow_await, |
||||||
|
) |
||||||
|
.parse(cursor); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// (a,b)=>{}
|
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
if let Some(node) = |
||||||
|
ArrowFunction::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.try_parse(cursor) |
||||||
|
{ |
||||||
|
return Ok(node); |
||||||
|
} |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
let mut lhs = ConditionalExpression::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
// let mut lhs = self.read_block()?;
|
||||||
|
|
||||||
|
if let Some(tok) = cursor.next() { |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::Assign) => { |
||||||
|
lhs = Node::assign(lhs, self.parse(cursor)?) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(p) if p.as_binop().is_some() => { |
||||||
|
let expr = self.parse(cursor)?; |
||||||
|
let binop = p.as_binop().expect("binop disappeared"); |
||||||
|
lhs = Node::bin_op(binop, lhs, expr); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
cursor.back(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
//! Argument parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-Arguments
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a list of arguments.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Arguments
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::expression) struct Arguments { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl Arguments { |
||||||
|
/// Creates a new `Arguments` parser.
|
||||||
|
pub(in crate::syntax::parser::expression) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for Arguments { |
||||||
|
type Output = Vec<Node>; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Vec<Node>, ParseError> { |
||||||
|
cursor.expect(Punctuator::OpenParen, "arguments")?; |
||||||
|
let mut args = Vec::new(); |
||||||
|
loop { |
||||||
|
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
match next_token.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::CloseParen) => break, |
||||||
|
TokenKind::Punctuator(Punctuator::Comma) => { |
||||||
|
if args.is_empty() { |
||||||
|
return Err(ParseError::Unexpected(next_token.clone(), None)); |
||||||
|
} |
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::CloseParen).is_some() { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
_ => { |
||||||
|
if !args.is_empty() { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Punctuator(Punctuator::Comma), |
||||||
|
TokenKind::Punctuator(Punctuator::CloseParen), |
||||||
|
], |
||||||
|
next_token.clone(), |
||||||
|
"argument list", |
||||||
|
)); |
||||||
|
} else { |
||||||
|
cursor.back(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::Spread).is_some() { |
||||||
|
args.push(Node::spread( |
||||||
|
AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
)); |
||||||
|
} else { |
||||||
|
args.push( |
||||||
|
AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(args) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
//! Call expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-CallExpression
|
||||||
|
|
||||||
|
use super::arguments::Arguments; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a call expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
|
||||||
|
#[derive(Debug)] |
||||||
|
pub(super) struct CallExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
first_member_expr: Node, |
||||||
|
} |
||||||
|
|
||||||
|
impl CallExpression { |
||||||
|
/// Creates a new `CallExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A, first_member_expr: Node) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
first_member_expr, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for CallExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut lhs = match cursor.peek(0) { |
||||||
|
Some(tk) if tk.kind == TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
Node::call(self.first_member_expr, args) |
||||||
|
} |
||||||
|
_ => { |
||||||
|
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::Punctuator(Punctuator::OpenParen)], |
||||||
|
next_token.clone(), |
||||||
|
"call expression", |
||||||
|
)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
while let Some(tok) = cursor.peek(0) { |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
lhs = Node::call(lhs, args); |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Dot) => { |
||||||
|
let _ = cursor.next().ok_or(ParseError::AbruptEnd)?; // We move the cursor.
|
||||||
|
match &cursor.next().ok_or(ParseError::AbruptEnd)?.kind { |
||||||
|
TokenKind::Identifier(name) => { |
||||||
|
lhs = Node::get_const_field(lhs, name); |
||||||
|
} |
||||||
|
TokenKind::Keyword(kw) => { |
||||||
|
lhs = Node::get_const_field(lhs, kw.to_string()); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"call expression", |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBracket) => { |
||||||
|
let _ = cursor.next().ok_or(ParseError::AbruptEnd)?; // We move the cursor.
|
||||||
|
let idx = |
||||||
|
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseBracket, "call expression")?; |
||||||
|
lhs = Node::get_field(lhs, idx); |
||||||
|
} |
||||||
|
_ => break, |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
//! Member expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-MemberExpression
|
||||||
|
|
||||||
|
use super::arguments::Arguments; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::{primary::PrimaryExpression, Expression}, |
||||||
|
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a member expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct MemberExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl MemberExpression { |
||||||
|
/// Creates a new `MemberExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for MemberExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut lhs = if cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind |
||||||
|
== TokenKind::Keyword(Keyword::New) |
||||||
|
{ |
||||||
|
let _ = cursor.next().expect("keyword disappeared"); |
||||||
|
let lhs = self.parse(cursor)?; |
||||||
|
let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
let call_node = Node::call(lhs, args); |
||||||
|
|
||||||
|
Node::new(call_node) |
||||||
|
} else { |
||||||
|
PrimaryExpression::new(self.allow_yield, self.allow_await).parse(cursor)? |
||||||
|
}; |
||||||
|
while let Some(tok) = cursor.peek(0) { |
||||||
|
match &tok.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::Dot) => { |
||||||
|
let _ = cursor.next().ok_or(ParseError::AbruptEnd)?; // We move the cursor forward.
|
||||||
|
match &cursor.next().ok_or(ParseError::AbruptEnd)?.kind { |
||||||
|
TokenKind::Identifier(name) => lhs = Node::get_const_field(lhs, name), |
||||||
|
TokenKind::Keyword(kw) => lhs = Node::get_const_field(lhs, kw.to_string()), |
||||||
|
_ => { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"member expression", |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBracket) => { |
||||||
|
let _ = cursor.next().ok_or(ParseError::AbruptEnd)?; // We move the cursor forward.
|
||||||
|
let idx = |
||||||
|
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseBracket, "member expression")?; |
||||||
|
lhs = Node::get_field(lhs, idx); |
||||||
|
} |
||||||
|
_ => break, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
//! Left hand side expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Left-hand-side_expressions
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-left-hand-side-expressions
|
||||||
|
|
||||||
|
mod arguments; |
||||||
|
mod call; |
||||||
|
mod member; |
||||||
|
|
||||||
|
use self::{call::CallExpression, member::MemberExpression}; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a left hand side expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Left-hand-side_expressions
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct LeftHandSideExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl LeftHandSideExpression { |
||||||
|
/// Creates a new `LeftHandSideExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for LeftHandSideExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TODO: Implement NewExpression: new MemberExpression
|
||||||
|
let lhs = MemberExpression::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
match cursor.peek(0) { |
||||||
|
Some(ref tok) if tok.kind == TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
CallExpression::new(self.allow_yield, self.allow_await, lhs).parse(cursor) |
||||||
|
} |
||||||
|
_ => Ok(lhs), // TODO: is this correct?
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,479 @@ |
|||||||
|
//! Expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions
|
||||||
|
|
||||||
|
mod assignment; |
||||||
|
mod left_hand_side; |
||||||
|
mod primary; |
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
mod unary; |
||||||
|
mod update; |
||||||
|
|
||||||
|
use self::assignment::ExponentiationExpression; |
||||||
|
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer}; |
||||||
|
use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser}; |
||||||
|
use crate::syntax::ast::{node::Node, punc::Punctuator, token::TokenKind}; |
||||||
|
|
||||||
|
/// Generates an expression parser.
|
||||||
|
///
|
||||||
|
/// This macro has 2 mandatory identifiers:
|
||||||
|
/// - The `$name` identifier will contain the name of the parsing structure.
|
||||||
|
/// - The `$lower` identifier will contain the parser for lower level expressions.
|
||||||
|
///
|
||||||
|
/// Those exressions are divided by the punctuators passed as the third parameter.
|
||||||
|
macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*] ) => { |
||||||
|
impl TokenParser for $name { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor)?; |
||||||
|
while let Some(tok) = cursor.peek(0) { |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Punctuator(op) if $( op == $op )||* => { |
||||||
|
let _ = cursor.next().expect("token disappeared"); |
||||||
|
lhs = Node::bin_op( |
||||||
|
op.as_binop().expect("could not get binary operation"), |
||||||
|
lhs, |
||||||
|
$lower::new($( self.$low_param ),*).parse(cursor)? |
||||||
|
) |
||||||
|
} |
||||||
|
_ => break
|
||||||
|
} |
||||||
|
} |
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
||||||
|
} } |
||||||
|
|
||||||
|
/// Expression parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Expression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct Expression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl Expression { |
||||||
|
/// Creates a new `Expression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
Expression, |
||||||
|
AssignmentExpression, |
||||||
|
[Punctuator::Comma], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a logical `OR` expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR_2
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct LogicalORExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl LogicalORExpression { |
||||||
|
/// Creates a new `LogicalORExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
LogicalORExpression, |
||||||
|
LogicalANDExpression, |
||||||
|
[Punctuator::BoolOr], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a logical `AND` expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND_2
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct LogicalANDExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl LogicalANDExpression { |
||||||
|
/// Creates a new `LogicalANDExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
LogicalANDExpression, |
||||||
|
BitwiseORExpression, |
||||||
|
[Punctuator::BoolAnd], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a bitwise `OR` expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct BitwiseORExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl BitwiseORExpression { |
||||||
|
/// Creates a new `BitwiseORExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
BitwiseORExpression, |
||||||
|
BitwiseXORExpression, |
||||||
|
[Punctuator::Or], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a bitwise `XOR` expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct BitwiseXORExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl BitwiseXORExpression { |
||||||
|
/// Creates a new `BitwiseXORExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
BitwiseXORExpression, |
||||||
|
BitwiseANDExpression, |
||||||
|
[Punctuator::Xor], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a bitwise `AND` expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct BitwiseANDExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl BitwiseANDExpression { |
||||||
|
/// Creates a new `BitwiseANDExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
BitwiseANDExpression, |
||||||
|
EqualityExpression, |
||||||
|
[Punctuator::And], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses an equality expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality_operators
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-equality-operators
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct EqualityExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl EqualityExpression { |
||||||
|
/// Creates a new `EqualityExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
EqualityExpression, |
||||||
|
RelationalExpression, |
||||||
|
[ |
||||||
|
Punctuator::Eq, |
||||||
|
Punctuator::NotEq, |
||||||
|
Punctuator::StrictEq, |
||||||
|
Punctuator::StrictNotEq |
||||||
|
], |
||||||
|
[allow_in, allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a relational expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Relational_operators
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct RelationalExpression { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl RelationalExpression { |
||||||
|
/// Creates a new `RelationalExpression` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
RelationalExpression, |
||||||
|
ShiftExpression, |
||||||
|
[ |
||||||
|
Punctuator::LessThan, |
||||||
|
Punctuator::GreaterThan, |
||||||
|
Punctuator::LessThanOrEq, |
||||||
|
Punctuator::GreaterThanOrEq |
||||||
|
], |
||||||
|
[allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a bitwise shift expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_shift_operators
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-bitwise-shift-operators
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct ShiftExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ShiftExpression { |
||||||
|
/// Creates a new `ShiftExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
ShiftExpression, |
||||||
|
AdditiveExpression, |
||||||
|
[ |
||||||
|
Punctuator::LeftSh, |
||||||
|
Punctuator::RightSh, |
||||||
|
Punctuator::URightSh |
||||||
|
], |
||||||
|
[allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses an additive expression.
|
||||||
|
///
|
||||||
|
/// This can be either an addition or a subtraction.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-additive-operators
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct AdditiveExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl AdditiveExpression { |
||||||
|
/// Creates a new `AdditiveExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
AdditiveExpression, |
||||||
|
MultiplicativeExpression, |
||||||
|
[Punctuator::Add, Punctuator::Sub], |
||||||
|
[allow_yield, allow_await] |
||||||
|
); |
||||||
|
|
||||||
|
/// Parses a multiplicative expression.
|
||||||
|
///
|
||||||
|
/// This can be either a multiplication, division or a modulo (remainder) expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-multiplicative-operators
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct MultiplicativeExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl MultiplicativeExpression { |
||||||
|
/// Creates a new `MultiplicativeExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
expression!( |
||||||
|
MultiplicativeExpression, |
||||||
|
ExponentiationExpression, |
||||||
|
[Punctuator::Mul, Punctuator::Div, Punctuator::Mod], |
||||||
|
[allow_yield, allow_await] |
||||||
|
); |
@ -0,0 +1,82 @@ |
|||||||
|
//! Array initializer parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-array-initializer
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{constant::Const, node::Node, punc::Punctuator}, |
||||||
|
parser::{ |
||||||
|
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses an array literal.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct ArrayLiteral { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ArrayLiteral { |
||||||
|
/// Creates a new `ArrayLiteral` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ArrayLiteral { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut elements = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
// TODO: Support all features.
|
||||||
|
while cursor.next_if(Punctuator::Comma).is_some() { |
||||||
|
elements.push(Node::Const(Const::Undefined)); |
||||||
|
} |
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::CloseBracket).is_some() { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
let _ = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; // Check that there are more tokens to read.
|
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::Spread).is_some() { |
||||||
|
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
elements.push(Node::spread(node)); |
||||||
|
} else { |
||||||
|
elements.push( |
||||||
|
AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
); |
||||||
|
} |
||||||
|
cursor.next_if(Punctuator::Comma); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Node::ArrayDecl(elements)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
// ! Tests for array initializer parsing.
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{constant::Const, node::Node}, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks an empty array.
|
||||||
|
#[test] |
||||||
|
fn check_empty() { |
||||||
|
check_parser("[]", &[Node::ArrayDecl(Vec::new())]); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an array with empty slot.
|
||||||
|
#[test] |
||||||
|
fn check_empty_slot() { |
||||||
|
check_parser( |
||||||
|
"[,]", |
||||||
|
&[Node::ArrayDecl(vec![Node::Const(Const::Undefined)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks a numeric array.
|
||||||
|
#[test] |
||||||
|
fn check_numeric_array() { |
||||||
|
check_parser( |
||||||
|
"[1, 2, 3]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node(2.0), |
||||||
|
Node::const_node(3.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Checks a numeric array with trailing comma
|
||||||
|
#[test] |
||||||
|
fn check_numeric_array_trailing() { |
||||||
|
check_parser( |
||||||
|
"[1, 2, 3,]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node(2.0), |
||||||
|
Node::const_node(3.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks a numeric array with an elision.
|
||||||
|
#[test] |
||||||
|
fn check_numeric_array_elision() { |
||||||
|
check_parser( |
||||||
|
"[1, 2, , 3]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node(2.0), |
||||||
|
Node::Const(Const::Undefined), |
||||||
|
Node::const_node(3.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks a numeric array with repeated elisions.
|
||||||
|
#[test] |
||||||
|
fn check_numeric_array_repeated_elision() { |
||||||
|
check_parser( |
||||||
|
"[1, 2, ,, 3]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node(2.0), |
||||||
|
Node::Const(Const::Undefined), |
||||||
|
Node::Const(Const::Undefined), |
||||||
|
Node::const_node(3.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks a combined array.
|
||||||
|
#[test] |
||||||
|
fn check_combined() { |
||||||
|
check_parser( |
||||||
|
"[1, \"a\", 2]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node("a"), |
||||||
|
Node::const_node(2.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks a combined array with an empty string
|
||||||
|
#[test] |
||||||
|
fn check_combined_empty_str() { |
||||||
|
check_parser( |
||||||
|
"[1, \"\", 2]", |
||||||
|
&[Node::ArrayDecl(vec![ |
||||||
|
Node::const_node(1.0), |
||||||
|
Node::const_node(""), |
||||||
|
Node::const_node(2.0), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
//! Function expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
function::{FormalParameters, FunctionBody}, |
||||||
|
Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Function expression parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct FunctionExpression; |
||||||
|
|
||||||
|
impl TokenParser for FunctionExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let name = if let TokenKind::Identifier(name) = |
||||||
|
&cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind |
||||||
|
{ |
||||||
|
Some(name) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
if name.is_some() { |
||||||
|
// We move the cursor forward.
|
||||||
|
let _ = cursor.next().expect("nex token disappeared"); |
||||||
|
} |
||||||
|
|
||||||
|
cursor.expect(Punctuator::OpenParen, "function expression")?; |
||||||
|
|
||||||
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "function expression")?; |
||||||
|
cursor.expect(Punctuator::OpenBlock, "function expression")?; |
||||||
|
|
||||||
|
let body = FunctionBody::new(false, false) |
||||||
|
.parse(cursor) |
||||||
|
.map(Node::StatementList)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseBlock, "function expression")?; |
||||||
|
|
||||||
|
Ok(Node::function_decl::<_, &String, _, _>(name, params, body)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
//! Primary expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#Primary_expressions
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression
|
||||||
|
|
||||||
|
mod array_initializer; |
||||||
|
mod function_expression; |
||||||
|
mod object_initializer; |
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use self::{ |
||||||
|
array_initializer::ArrayLiteral, function_expression::FunctionExpression, |
||||||
|
object_initializer::ObjectLiteral, |
||||||
|
}; |
||||||
|
use super::Expression; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{constant::Const, keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
pub(in crate::syntax::parser) use object_initializer::Initializer; |
||||||
|
|
||||||
|
/// Parses a primary expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Primary_expressions
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct PrimaryExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl PrimaryExpression { |
||||||
|
/// Creates a new `PrimaryExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for PrimaryExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match &tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::This) => Ok(Node::This), |
||||||
|
// TokenKind::Keyword(Keyword::Arguments) => Ok(Node::new(NodeBase::Arguments, tok.pos)),
|
||||||
|
TokenKind::Keyword(Keyword::Function) => FunctionExpression.parse(cursor), |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen) => { |
||||||
|
let expr = |
||||||
|
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseParen, "primary expression")?; |
||||||
|
Ok(expr) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBracket) => { |
||||||
|
ArrayLiteral::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||||
|
ObjectLiteral::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::BooleanLiteral(boolean) => Ok(Node::const_node(*boolean)), |
||||||
|
// TODO: ADD TokenKind::UndefinedLiteral
|
||||||
|
TokenKind::Identifier(ref i) if i == "undefined" => Ok(Node::Const(Const::Undefined)), |
||||||
|
TokenKind::NullLiteral => Ok(Node::Const(Const::Null)), |
||||||
|
TokenKind::Identifier(ident) => Ok(Node::local(ident)), |
||||||
|
TokenKind::StringLiteral(s) => Ok(Node::const_node(s)), |
||||||
|
TokenKind::NumericLiteral(num) => Ok(Node::const_node(*num)), |
||||||
|
TokenKind::RegularExpressionLiteral(body, flags) => Ok(Node::new(Node::call( |
||||||
|
Node::local("RegExp"), |
||||||
|
vec![Node::const_node(body), Node::const_node(flags)], |
||||||
|
))), |
||||||
|
_ => Err(ParseError::Unexpected( |
||||||
|
tok.clone(), |
||||||
|
Some("primary expression"), |
||||||
|
)), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,290 @@ |
|||||||
|
//! Object initializer parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-object-initializer
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{ |
||||||
|
node::{self, MethodDefinitionKind, Node}, |
||||||
|
punc::Punctuator, |
||||||
|
token::{Token, TokenKind}, |
||||||
|
}, |
||||||
|
parser::{ |
||||||
|
expression::AssignmentExpression, |
||||||
|
function::{FormalParameters, FunctionBody}, |
||||||
|
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses an object literal.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct ObjectLiteral { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ObjectLiteral { |
||||||
|
/// Creates a new `ObjectLiteral` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ObjectLiteral { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut elements = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
if cursor.next_if(Punctuator::CloseBlock).is_some() { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
elements |
||||||
|
.push(PropertyDefinition::new(self.allow_yield, self.allow_await).parse(cursor)?); |
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::CloseBlock).is_some() { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if cursor.next_if(Punctuator::Comma).is_none() { |
||||||
|
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Punctuator(Punctuator::Comma), |
||||||
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
||||||
|
], |
||||||
|
next_token.clone(), |
||||||
|
"object literal", |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Node::Object(elements)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Parses a property definition.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct PropertyDefinition { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl PropertyDefinition { |
||||||
|
/// Creates a new `PropertyDefinition` parser.
|
||||||
|
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for PropertyDefinition { |
||||||
|
type Output = node::PropertyDefinition; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
if cursor.next_if(Punctuator::Spread).is_some() { |
||||||
|
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
return Ok(node::PropertyDefinition::SpreadObject(node)); |
||||||
|
} |
||||||
|
|
||||||
|
let prop_name = cursor |
||||||
|
.next() |
||||||
|
.map(Token::to_string) |
||||||
|
.ok_or(ParseError::AbruptEnd)?; |
||||||
|
if cursor.next_if(Punctuator::Colon).is_some() { |
||||||
|
let val = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?; |
||||||
|
return Ok(node::PropertyDefinition::Property(prop_name, val)); |
||||||
|
} |
||||||
|
|
||||||
|
if cursor |
||||||
|
.next_if(TokenKind::Punctuator(Punctuator::OpenParen)) |
||||||
|
.is_some() |
||||||
|
|| ["get", "set"].contains(&prop_name.as_str()) |
||||||
|
{ |
||||||
|
return MethodDefinition::new(self.allow_yield, self.allow_await, prop_name) |
||||||
|
.parse(cursor); |
||||||
|
} |
||||||
|
|
||||||
|
let pos = cursor |
||||||
|
.peek(0) |
||||||
|
.map(|tok| tok.pos) |
||||||
|
.ok_or(ParseError::AbruptEnd)?; |
||||||
|
Err(ParseError::General( |
||||||
|
"expected property definition", |
||||||
|
Some(pos), |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Parses a method definition.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
struct MethodDefinition { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
identifier: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl MethodDefinition { |
||||||
|
/// Creates a new `MethodDefinition` parser.
|
||||||
|
fn new<Y, A, I>(allow_yield: Y, allow_await: A, identifier: I) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
I: Into<String>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
identifier: identifier.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for MethodDefinition { |
||||||
|
type Output = node::PropertyDefinition; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let (methodkind, prop_name, params) = match self.identifier.as_str() { |
||||||
|
idn @ "get" | idn @ "set" => { |
||||||
|
let prop_name = cursor |
||||||
|
.next() |
||||||
|
.map(Token::to_string) |
||||||
|
.ok_or(ParseError::AbruptEnd)?; |
||||||
|
cursor.expect( |
||||||
|
TokenKind::Punctuator(Punctuator::OpenParen), |
||||||
|
"property method definition", |
||||||
|
)?; |
||||||
|
let first_param = cursor.peek(0).expect("current token disappeared").clone(); |
||||||
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseParen, "method definition")?; |
||||||
|
if idn == "get" { |
||||||
|
if !params.is_empty() { |
||||||
|
return Err(ParseError::Unexpected( |
||||||
|
first_param, |
||||||
|
Some("getter functions must have no arguments"), |
||||||
|
)); |
||||||
|
} |
||||||
|
(MethodDefinitionKind::Get, prop_name, params) |
||||||
|
} else { |
||||||
|
if params.len() != 1 { |
||||||
|
return Err(ParseError::Unexpected( |
||||||
|
first_param, |
||||||
|
Some("setter functions must have one argument"), |
||||||
|
)); |
||||||
|
} |
||||||
|
(MethodDefinitionKind::Set, prop_name, params) |
||||||
|
} |
||||||
|
} |
||||||
|
prop_name => { |
||||||
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::CloseParen, "method definition")?; |
||||||
|
( |
||||||
|
MethodDefinitionKind::Ordinary, |
||||||
|
prop_name.to_string(), |
||||||
|
params, |
||||||
|
) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
cursor.expect( |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBlock), |
||||||
|
"property method definition", |
||||||
|
)?; |
||||||
|
let body = FunctionBody::new(false, false) |
||||||
|
.parse(cursor) |
||||||
|
.map(Node::StatementList)?; |
||||||
|
cursor.expect( |
||||||
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
||||||
|
"property method definition", |
||||||
|
)?; |
||||||
|
|
||||||
|
Ok(node::PropertyDefinition::MethodDefinition( |
||||||
|
methodkind, |
||||||
|
prop_name, |
||||||
|
Node::FunctionDecl(None, params, Box::new(body)), |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Initializer parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Initializer
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct Initializer { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl Initializer { |
||||||
|
/// Creates a new `Initializer` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<I, Y, A>( |
||||||
|
allow_in: I, |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for Initializer { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(TokenKind::Punctuator(Punctuator::Assign), "initializer")?; |
||||||
|
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::node::{FormalParameter, MethodDefinitionKind, Node, PropertyDefinition}, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks object literal parsing.
|
||||||
|
#[test] |
||||||
|
fn check_object_literal() { |
||||||
|
let object_properties = vec![ |
||||||
|
PropertyDefinition::property("a", Node::const_node(true)), |
||||||
|
PropertyDefinition::property("b", Node::const_node(false)), |
||||||
|
]; |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"const x = { |
||||||
|
a: true, |
||||||
|
b: false, |
||||||
|
}; |
||||||
|
", |
||||||
|
&[Node::const_decl(vec![( |
||||||
|
String::from("x"), |
||||||
|
Node::Object(object_properties), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Tests short function syntax.
|
||||||
|
#[test] |
||||||
|
fn check_object_short_function() { |
||||||
|
let object_properties = vec![ |
||||||
|
PropertyDefinition::property("a", Node::const_node(true)), |
||||||
|
PropertyDefinition::method_definition( |
||||||
|
MethodDefinitionKind::Ordinary, |
||||||
|
"b", |
||||||
|
Node::function_decl::<_, String, _, _>(None, Vec::new(), Node::StatementList(vec![])), |
||||||
|
), |
||||||
|
]; |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"const x = { |
||||||
|
a: true, |
||||||
|
b() {}, |
||||||
|
}; |
||||||
|
", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("x"), |
||||||
|
Node::Object(object_properties), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Testing short function syntax with arguments.
|
||||||
|
#[test] |
||||||
|
fn check_object_short_function_arguments() { |
||||||
|
let object_properties = vec![ |
||||||
|
PropertyDefinition::property("a", Node::const_node(true)), |
||||||
|
PropertyDefinition::method_definition( |
||||||
|
MethodDefinitionKind::Ordinary, |
||||||
|
"b", |
||||||
|
Node::function_decl::<_, String, _, _>( |
||||||
|
None, |
||||||
|
vec![FormalParameter::new("test", None, false)], |
||||||
|
Node::StatementList(Vec::new()), |
||||||
|
), |
||||||
|
), |
||||||
|
]; |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"const x = { |
||||||
|
a: true, |
||||||
|
b(test) {} |
||||||
|
}; |
||||||
|
", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("x"), |
||||||
|
Node::Object(object_properties), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_object_getter() { |
||||||
|
let object_properties = vec![ |
||||||
|
PropertyDefinition::property("a", Node::const_node(true)), |
||||||
|
PropertyDefinition::method_definition( |
||||||
|
MethodDefinitionKind::Get, |
||||||
|
"b", |
||||||
|
Node::FunctionDecl(None, Vec::new(), Box::new(Node::StatementList(Vec::new()))), |
||||||
|
), |
||||||
|
]; |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"const x = { |
||||||
|
a: true, |
||||||
|
get b() {} |
||||||
|
}; |
||||||
|
", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("x"), |
||||||
|
Node::Object(object_properties), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_object_setter() { |
||||||
|
let object_properties = vec![ |
||||||
|
PropertyDefinition::property("a", Node::const_node(true)), |
||||||
|
PropertyDefinition::method_definition( |
||||||
|
MethodDefinitionKind::Set, |
||||||
|
"b", |
||||||
|
Node::FunctionDecl( |
||||||
|
None, |
||||||
|
vec![FormalParameter::new("test", None, false)], |
||||||
|
Box::new(Node::StatementList(Vec::new())), |
||||||
|
), |
||||||
|
), |
||||||
|
]; |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"const x = { |
||||||
|
a: true, |
||||||
|
set b(test) {} |
||||||
|
}; |
||||||
|
", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("x"), |
||||||
|
Node::Object(object_properties), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
use crate::syntax::{ast::node::Node, parser::tests::check_parser}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_string() { |
||||||
|
// Check empty string
|
||||||
|
check_parser("\"\"", &[Node::const_node("")]); |
||||||
|
|
||||||
|
// Check non-empty string
|
||||||
|
check_parser("\"hello\"", &[Node::const_node("hello")]); |
||||||
|
} |
@ -0,0 +1,293 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::node::Node, |
||||||
|
ast::op::{AssignOp, BinOp, BitOp, NumOp}, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks numeric operations
|
||||||
|
#[test] |
||||||
|
fn check_numeric_operations() { |
||||||
|
check_parser( |
||||||
|
"a + b", |
||||||
|
&[Node::bin_op(NumOp::Add, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a+1", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Add, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(1.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a - b", |
||||||
|
&[Node::bin_op(NumOp::Sub, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a-1", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Sub, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(1.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a / b", |
||||||
|
&[Node::bin_op(NumOp::Div, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a/2", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Div, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(2.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a * b", |
||||||
|
&[Node::bin_op(NumOp::Mul, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a*2", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Mul, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(2.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a ** b", |
||||||
|
&[Node::bin_op(NumOp::Exp, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a**2", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Exp, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(2.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a % b", |
||||||
|
&[Node::bin_op(NumOp::Mod, Node::local("a"), Node::local("b"))], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a%2", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Mod, |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(2.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Checks complex numeric operations.
|
||||||
|
#[test] |
||||||
|
fn check_complex_numeric_operations() { |
||||||
|
check_parser( |
||||||
|
"a + d*(b-3)+1", |
||||||
|
&[Node::bin_op( |
||||||
|
NumOp::Add, |
||||||
|
Node::bin_op( |
||||||
|
NumOp::Add, |
||||||
|
Node::local("a"), |
||||||
|
Node::bin_op( |
||||||
|
NumOp::Mul, |
||||||
|
Node::local("d"), |
||||||
|
Node::bin_op(NumOp::Sub, Node::local("b"), Node::const_node(3.0)), |
||||||
|
), |
||||||
|
), |
||||||
|
Node::const_node(1.0), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks bitwise operations.
|
||||||
|
#[test] |
||||||
|
fn check_bitwise_operations() { |
||||||
|
check_parser( |
||||||
|
"a & b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::And), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a&b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::And), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"a | b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Or), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a|b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Or), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"a ^ b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Xor), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a^b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Xor), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"a << b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Shl), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a<<b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Shl), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
|
||||||
|
check_parser( |
||||||
|
"a >> b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Shr), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a>>b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Bit(BitOp::Shr), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks assignment operations.
|
||||||
|
#[test] |
||||||
|
fn check_assign_operations() { |
||||||
|
check_parser( |
||||||
|
"a += b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Add), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a -= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Sub), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a *= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Mul), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a **= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Exp), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a /= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Div), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a %= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Mod), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a &= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::And), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a |= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Or), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a ^= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Xor), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a <<= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Shl), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a >>= b", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Shr), |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
)], |
||||||
|
); |
||||||
|
check_parser( |
||||||
|
"a %= 10 / 2", |
||||||
|
&[Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Mod), |
||||||
|
Node::local("a"), |
||||||
|
Node::bin_op(NumOp::Div, Node::const_node(10.0), Node::const_node(2.0)), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
//! Unary operator parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-unary-operators
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, op::UnaryOp, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::update::UpdateExpression, AllowAwait, AllowYield, Cursor, ParseError, |
||||||
|
ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a unary expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct UnaryExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl UnaryExpression { |
||||||
|
/// Creates a new `UnaryExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for UnaryExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Delete) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Delete, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Void) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Void, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::TypeOf) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::TypeOf, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Add) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Plus, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Sub) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Minus, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Neg) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Tilde, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Not) => { |
||||||
|
Ok(Node::unary_op(UnaryOp::Not, self.parse(cursor)?)) |
||||||
|
} |
||||||
|
_ => { |
||||||
|
cursor.back(); |
||||||
|
UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
//! Update expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-update-expressions
|
||||||
|
|
||||||
|
use super::left_hand_side::LeftHandSideExpression; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{node::Node, op::UnaryOp, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses an update expression.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct UpdateExpression { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl UpdateExpression { |
||||||
|
/// Creates a new `UpdateExpression` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for UpdateExpression { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::Inc) => { |
||||||
|
cursor.next().expect("token disappeared"); |
||||||
|
return Ok(Node::unary_op( |
||||||
|
UnaryOp::IncrementPre, |
||||||
|
LeftHandSideExpression::new(self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
)); |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Dec) => { |
||||||
|
cursor.next().expect("token disappeared"); |
||||||
|
return Ok(Node::unary_op( |
||||||
|
UnaryOp::DecrementPre, |
||||||
|
LeftHandSideExpression::new(self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
)); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
let lhs = LeftHandSideExpression::new(self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
if let Some(tok) = cursor.peek(0) { |
||||||
|
match tok.kind { |
||||||
|
TokenKind::Punctuator(Punctuator::Inc) => { |
||||||
|
cursor.next().expect("token disappeared"); |
||||||
|
return Ok(Node::unary_op(UnaryOp::IncrementPost, lhs)); |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Dec) => { |
||||||
|
cursor.next().expect("token disappeared"); |
||||||
|
return Ok(Node::unary_op(UnaryOp::DecrementPost, lhs)); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(lhs) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,238 @@ |
|||||||
|
//! Function definition parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-function-definitions
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{ |
||||||
|
node::{self, Node}, |
||||||
|
punc::Punctuator, |
||||||
|
token::TokenKind, |
||||||
|
}, |
||||||
|
parser::{statement::StatementList, AllowAwait, AllowYield, Cursor, ParseError, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Formal parameters parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Parameter
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameters
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct FormalParameters { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl FormalParameters { |
||||||
|
/// Creates a new `FormalParameters` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for FormalParameters { |
||||||
|
type Output = Vec<node::FormalParameter>; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let mut params = Vec::new(); |
||||||
|
|
||||||
|
if cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind |
||||||
|
== TokenKind::Punctuator(Punctuator::CloseParen) |
||||||
|
{ |
||||||
|
return Ok(params); |
||||||
|
} |
||||||
|
|
||||||
|
loop { |
||||||
|
let mut rest_param = false; |
||||||
|
|
||||||
|
params.push(if cursor.next_if(Punctuator::Spread).is_some() { |
||||||
|
rest_param = true; |
||||||
|
FunctionRestParameter::new(self.allow_yield, self.allow_await).parse(cursor)? |
||||||
|
} else { |
||||||
|
FormalParameter::new(self.allow_yield, self.allow_await).parse(cursor)? |
||||||
|
}); |
||||||
|
|
||||||
|
if cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind |
||||||
|
== TokenKind::Punctuator(Punctuator::CloseParen) |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if rest_param { |
||||||
|
return Err(ParseError::Unexpected( |
||||||
|
cursor |
||||||
|
.peek_prev() |
||||||
|
.expect("current token disappeared") |
||||||
|
.clone(), |
||||||
|
Some("rest parameter must be the last formal parameter"), |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
cursor.expect(Punctuator::Comma, "parameter list")?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(params) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Rest parameter parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionRestParameter
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct FunctionRestParameter { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl FunctionRestParameter { |
||||||
|
/// Creates a new `FunctionRestParameter` parser.
|
||||||
|
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for FunctionRestParameter { |
||||||
|
type Output = node::FormalParameter; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
Ok(Self::Output::new( |
||||||
|
if let TokenKind::Identifier(name) = &token.kind { |
||||||
|
name |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
token.clone(), |
||||||
|
"rest parameter", |
||||||
|
)); |
||||||
|
}, |
||||||
|
None, |
||||||
|
true, |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Formal parameter parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Parameter
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct FormalParameter { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl FormalParameter { |
||||||
|
/// Creates a new `FormalParameter` parser.
|
||||||
|
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for FormalParameter { |
||||||
|
type Output = node::FormalParameter; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let name = if let TokenKind::Identifier(name) = &token.kind { |
||||||
|
name |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
token.clone(), |
||||||
|
"formal parameter", |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
// TODO: Implement initializer.
|
||||||
|
Ok(Self::Output::new(name, None, false)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A `FunctionBody` is equivalent to a `FunctionStatementList`.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionBody
|
||||||
|
pub(in crate::syntax::parser) type FunctionBody = FunctionStatementList; |
||||||
|
|
||||||
|
/// A function statement list
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionStatementList
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct FunctionStatementList { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl FunctionStatementList { |
||||||
|
/// Creates a new `FunctionStatementList` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for FunctionStatementList { |
||||||
|
type Output = Vec<Node>; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
if let Some(tk) = cursor.peek(0) { |
||||||
|
if tk.kind == Punctuator::CloseBlock.into() { |
||||||
|
return Ok(Vec::new()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StatementList::new(self.allow_yield, self.allow_await, true, true).parse(cursor) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,169 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::node::{FormalParameter, Node}, |
||||||
|
ast::op::NumOp, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks basic function declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_basic() { |
||||||
|
check_parser( |
||||||
|
"function foo(a) { return a; }", |
||||||
|
&[Node::function_decl( |
||||||
|
"foo", |
||||||
|
vec![FormalParameter::new("a", None, false)], |
||||||
|
Node::StatementList(vec![Node::return_node(Node::local("a"))]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks basic function declaration parsing with automatic semicolon insertion.
|
||||||
|
#[test] |
||||||
|
fn check_basic_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"function foo(a) { return a }", |
||||||
|
&[Node::function_decl( |
||||||
|
"foo", |
||||||
|
vec![FormalParameter::new("a", None, false)], |
||||||
|
Node::StatementList(vec![Node::return_node(Node::local("a"))]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks functions with empty returns.
|
||||||
|
#[test] |
||||||
|
fn check_empty_return() { |
||||||
|
check_parser( |
||||||
|
"function foo(a) { return; }", |
||||||
|
&[Node::function_decl( |
||||||
|
"foo", |
||||||
|
vec![FormalParameter::new("a", None, false)], |
||||||
|
Node::StatementList(vec![Node::Return(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks functions with empty returns without semicolon
|
||||||
|
#[test] |
||||||
|
fn check_empty_return_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"function foo(a) { return }", |
||||||
|
&[Node::function_decl( |
||||||
|
"foo", |
||||||
|
vec![FormalParameter::new("a", None, false)], |
||||||
|
Node::StatementList(vec![Node::Return(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks rest operator parsing.
|
||||||
|
#[test] |
||||||
|
fn check_rest_operator() { |
||||||
|
check_parser( |
||||||
|
"function foo(a, ...b) {}", |
||||||
|
&[Node::function_decl( |
||||||
|
"foo", |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, true), |
||||||
|
], |
||||||
|
Node::StatementList(Vec::new()), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an arrow function with only a rest parameter.
|
||||||
|
#[test] |
||||||
|
fn check_arrow_only_rest() { |
||||||
|
check_parser( |
||||||
|
"(...a) => {}", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![FormalParameter::new("a", None, true)], |
||||||
|
Node::StatementList(Vec::new()), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an arrow function with a rest parameter.
|
||||||
|
#[test] |
||||||
|
fn check_arrow_rest() { |
||||||
|
check_parser( |
||||||
|
"(a, b, ...c) => {}", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, false), |
||||||
|
FormalParameter::new("c", None, true), |
||||||
|
], |
||||||
|
Node::StatementList(Vec::new()), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an arrow function with expression return.
|
||||||
|
#[test] |
||||||
|
fn check_arrow() { |
||||||
|
check_parser( |
||||||
|
"(a, b) => { return a + b; }", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, false), |
||||||
|
], |
||||||
|
Node::StatementList(vec![Node::return_node(Node::bin_op( |
||||||
|
NumOp::Add, |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
))]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an arrow function with expression return and automatic semicolon insertion
|
||||||
|
#[test] |
||||||
|
fn check_arrow_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"(a, b) => { return a + b }", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, false), |
||||||
|
], |
||||||
|
Node::StatementList(vec![Node::return_node(Node::bin_op( |
||||||
|
NumOp::Add, |
||||||
|
Node::local("a"), |
||||||
|
Node::local("b"), |
||||||
|
))]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks arrow function with empty return
|
||||||
|
#[test] |
||||||
|
fn check_arrow_epty_return() { |
||||||
|
check_parser( |
||||||
|
"(a, b) => { return; }", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, false), |
||||||
|
], |
||||||
|
Node::StatementList(vec![Node::Return(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks an arrow function with empty return, with automatic semicolon insertion.
|
||||||
|
#[test] |
||||||
|
fn check_arrow_empty_return_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"(a, b) => { return }", |
||||||
|
&[Node::arrow_function_decl( |
||||||
|
vec![ |
||||||
|
FormalParameter::new("a", None, false), |
||||||
|
FormalParameter::new("b", None, false), |
||||||
|
], |
||||||
|
Node::StatementList(vec![Node::Return(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
//! Block statement parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-block
|
||||||
|
|
||||||
|
use super::{declaration::Declaration, Statement}; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// A `BlockStatement` is equivalent to a `Block`.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
|
||||||
|
pub(super) type BlockStatement = Block; |
||||||
|
|
||||||
|
/// Variable declaration list parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Block
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct Block { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl Block { |
||||||
|
/// Creates a new `Block` parser.
|
||||||
|
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for Block { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Punctuator::OpenBlock, "block")?; |
||||||
|
if let Some(tk) = cursor.peek(0) { |
||||||
|
if tk.kind == TokenKind::Punctuator(Punctuator::CloseBlock) { |
||||||
|
cursor.next(); |
||||||
|
return Ok(Node::Block(Vec::new())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let statement_list = |
||||||
|
StatementList::new(self.allow_yield, self.allow_await, self.allow_return, true) |
||||||
|
.parse(cursor) |
||||||
|
.map(Node::Block)?; |
||||||
|
cursor.expect(Punctuator::CloseBlock, "block")?; |
||||||
|
|
||||||
|
Ok(statement_list) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Reads a list of statements.
|
||||||
|
///
|
||||||
|
/// If `break_when_closingbrase` is `true`, it will stop as soon as it finds a `}` character.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-StatementList
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser) struct StatementList { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
break_when_closingbrase: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl StatementList { |
||||||
|
/// Creates a new `StatementList` parser.
|
||||||
|
pub(in crate::syntax::parser) fn new<Y, A, R>( |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
allow_return: R, |
||||||
|
break_when_closingbrase: bool, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
break_when_closingbrase, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for StatementList { |
||||||
|
type Output = Vec<Node>; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let mut items = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
match cursor.peek(0) { |
||||||
|
Some(token) if token.kind == TokenKind::Punctuator(Punctuator::CloseBlock) => { |
||||||
|
if self.break_when_closingbrase { |
||||||
|
break; |
||||||
|
} else { |
||||||
|
return Err(ParseError::Unexpected(token.clone(), None)); |
||||||
|
} |
||||||
|
} |
||||||
|
None => { |
||||||
|
if self.break_when_closingbrase { |
||||||
|
return Err(ParseError::AbruptEnd); |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
let item = |
||||||
|
StatementListItem::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor)?; |
||||||
|
items.push(item); |
||||||
|
|
||||||
|
// move the cursor forward for any consecutive semicolon.
|
||||||
|
while cursor.next_if(Punctuator::Semicolon).is_some() {} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(items) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Statement list item parsing
|
||||||
|
///
|
||||||
|
/// A statement list item can either be an statement or a declaration.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct StatementListItem { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl StatementListItem { |
||||||
|
/// Creates a new `StatementListItem` parser.
|
||||||
|
fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for StatementListItem { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Function) |
||||||
|
| TokenKind::Keyword(Keyword::Const) |
||||||
|
| TokenKind::Keyword(Keyword::Let) => { |
||||||
|
Declaration::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
_ => { |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
//! Break expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-break-statement
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Break statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct BreakStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl BreakStatement { |
||||||
|
/// Creates a new `BreakStatement` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for BreakStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Break, "break statement")?; |
||||||
|
|
||||||
|
if let (true, tok) = cursor.peek_semicolon(false) { |
||||||
|
match tok { |
||||||
|
Some(tok) if tok.kind == TokenKind::Punctuator(Punctuator::Semicolon) => { |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
return Ok(Node::Break(None)); |
||||||
|
} |
||||||
|
|
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let node = if let TokenKind::Identifier(name) = &tok.kind { |
||||||
|
Node::break_node(name) |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"break statement", |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
cursor.expect_semicolon(false, "break statement")?; |
||||||
|
|
||||||
|
Ok(node) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
use crate::syntax::{ast::node::Node, parser::tests::check_parser}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline() { |
||||||
|
check_parser( |
||||||
|
"while (true) break;", |
||||||
|
&[Node::while_loop(Node::const_node(true), Node::Break(None))], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line() { |
||||||
|
check_parser( |
||||||
|
"while (true) |
||||||
|
break;", |
||||||
|
&[Node::while_loop(Node::const_node(true), Node::Break(None))], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline_block_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) {break}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Break(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
break test |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::break_node("test")]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline_block() { |
||||||
|
check_parser( |
||||||
|
"while (true) {break;}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Break(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
break test; |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::break_node("test")]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block_empty() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
break; |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Break(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block_empty_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
break
|
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Break(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
//! Continue expression parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-continue-statement
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// For statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct ContinueStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ContinueStatement { |
||||||
|
/// Creates a new `ContinueStatement` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ContinueStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Continue, "continue statement")?; |
||||||
|
|
||||||
|
if let (true, tok) = cursor.peek_semicolon(false) { |
||||||
|
match tok { |
||||||
|
Some(tok) if tok.kind == TokenKind::Punctuator(Punctuator::Semicolon) => { |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
return Ok(Node::Continue(None)); |
||||||
|
} |
||||||
|
|
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let node = if let TokenKind::Identifier(name) = &tok.kind { |
||||||
|
Node::continue_node(name) |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"continue statement", |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
cursor.expect_semicolon(false, "continue statement")?; |
||||||
|
|
||||||
|
Ok(node) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
use crate::syntax::{ast::node::Node, parser::tests::check_parser}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline() { |
||||||
|
check_parser( |
||||||
|
"while (true) continue;", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Continue(None), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line() { |
||||||
|
check_parser( |
||||||
|
"while (true) |
||||||
|
continue;", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Continue(None), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline_block_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) {continue}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Continue(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
continue test |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::continue_node("test")]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_inline_block() { |
||||||
|
check_parser( |
||||||
|
"while (true) {continue;}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Continue(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
continue test; |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::continue_node("test")]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block_empty() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
continue; |
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Continue(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_new_line_block_empty_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
"while (true) { |
||||||
|
continue
|
||||||
|
}", |
||||||
|
&[Node::while_loop( |
||||||
|
Node::const_node(true), |
||||||
|
Node::Block(vec![Node::Continue(None)]), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
//! Hoistable declaration parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
function::FormalParameters, function::FunctionBody, AllowAwait, AllowDefault, AllowYield, |
||||||
|
Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Hoistable declaration parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct HoistableDeclaration { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_default: AllowDefault, |
||||||
|
} |
||||||
|
|
||||||
|
impl HoistableDeclaration { |
||||||
|
/// Creates a new `HoistableDeclaration` parser.
|
||||||
|
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, allow_default: D) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
D: Into<AllowDefault>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_default: allow_default.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for HoistableDeclaration { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TODO: check for generators and async functions + generators
|
||||||
|
FunctionDeclaration::new(self.allow_yield, self.allow_await, self.allow_default) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Function declaration parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct FunctionDeclaration { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_default: AllowDefault, |
||||||
|
} |
||||||
|
|
||||||
|
impl FunctionDeclaration { |
||||||
|
/// Creates a new `FunctionDeclaration` parser.
|
||||||
|
fn new<Y, A, D>(allow_yield: Y, allow_await: A, allow_default: D) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
D: Into<AllowDefault>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_default: allow_default.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for FunctionDeclaration { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Function, "function declaration")?; |
||||||
|
|
||||||
|
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let name = if let TokenKind::Identifier(name) = &token.kind { |
||||||
|
name.clone() |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("function name")], |
||||||
|
token.clone(), |
||||||
|
"function declaration", |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::OpenParen, "function declaration")?; |
||||||
|
|
||||||
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "function declaration")?; |
||||||
|
cursor.expect(Punctuator::OpenBlock, "function declaration")?; |
||||||
|
|
||||||
|
let body = FunctionBody::new(self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor) |
||||||
|
.map(Node::StatementList)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseBlock, "function declaration")?; |
||||||
|
|
||||||
|
Ok(Node::function_decl(name, params, body)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
//! Lexical declaration parsing.
|
||||||
|
//!
|
||||||
|
//! This parses `let` and `const` declarations.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Initializer, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a lexical declaration.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-LexicalDeclaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct LexicalDeclaration { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl LexicalDeclaration { |
||||||
|
/// Creates a new `LexicalDeclaration` parser.
|
||||||
|
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for LexicalDeclaration { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Const) => { |
||||||
|
BindingList::new(self.allow_in, self.allow_yield, self.allow_await, true) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Let) => { |
||||||
|
BindingList::new(self.allow_in, self.allow_yield, self.allow_await, false) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
_ => unreachable!("unknown token found"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Parses a binding list.
|
||||||
|
///
|
||||||
|
/// It will return an error if a `const` declaration is being parsed and there is no
|
||||||
|
/// initializer.
|
||||||
|
///
|
||||||
|
/// More information: <https://tc39.es/ecma262/#prod-BindingList>.
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct BindingList { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
is_const: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl BindingList { |
||||||
|
/// Creates a new `BindingList` parser.
|
||||||
|
fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A, is_const: bool) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
is_const, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for BindingList { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// Create vectors to store the variable declarations
|
||||||
|
// Const and Let signatures are slightly different, Const needs definitions, Lets don't
|
||||||
|
let mut let_decls = Vec::new(); |
||||||
|
let mut const_decls = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let name = if let TokenKind::Identifier(ref name) = token.kind { |
||||||
|
name.clone() |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
token.clone(), |
||||||
|
if self.is_const { |
||||||
|
"const declaration" |
||||||
|
} else { |
||||||
|
"let declaration" |
||||||
|
}, |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
match cursor.peek(0) { |
||||||
|
Some(token) if token.kind == TokenKind::Punctuator(Punctuator::Assign) => { |
||||||
|
let init = Some( |
||||||
|
Initializer::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
); |
||||||
|
if self.is_const { |
||||||
|
const_decls.push((name, init.unwrap())); |
||||||
|
} else { |
||||||
|
let_decls.push((name, init)); |
||||||
|
}; |
||||||
|
} |
||||||
|
_ => { |
||||||
|
if self.is_const { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::Punctuator(Punctuator::Assign)], |
||||||
|
cursor.next().ok_or(ParseError::AbruptEnd)?.clone(), |
||||||
|
"const declaration", |
||||||
|
)); |
||||||
|
} else { |
||||||
|
let_decls.push((name, None)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
match cursor.peek_semicolon(false) { |
||||||
|
(true, _) => break, |
||||||
|
(false, Some(tk)) if tk.kind == TokenKind::Punctuator(Punctuator::Comma) => { |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Punctuator(Punctuator::Semicolon), |
||||||
|
TokenKind::LineTerminator, |
||||||
|
], |
||||||
|
cursor.next().ok_or(ParseError::AbruptEnd)?.clone(), |
||||||
|
"lexical declaration", |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if self.is_const { |
||||||
|
Ok(Node::ConstDecl(const_decls)) |
||||||
|
} else { |
||||||
|
Ok(Node::LetDecl(let_decls)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
//! Declaration parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#Declarations
|
||||||
|
//! [spec]:https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
|
||||||
|
|
||||||
|
mod hoistable; |
||||||
|
mod lexical; |
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use self::{hoistable::HoistableDeclaration, lexical::LexicalDeclaration}; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Parses a declaration.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Declaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct Declaration { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl Declaration { |
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for Declaration { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Function) => { |
||||||
|
HoistableDeclaration::new(self.allow_yield, self.allow_await, false).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Const) | TokenKind::Keyword(Keyword::Let) => { |
||||||
|
LexicalDeclaration::new(true, self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
_ => unreachable!("unknown token found"), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::node::Node, |
||||||
|
parser::tests::{check_invalid, check_parser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks `var` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_var_declaration() { |
||||||
|
check_parser( |
||||||
|
"var a = 5;", |
||||||
|
&[Node::VarDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Some(Node::const_node(5.0)), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks `var` declaration parsing with no spaces.
|
||||||
|
#[test] |
||||||
|
fn check_var_declaration_no_spaces() { |
||||||
|
check_parser( |
||||||
|
"var a=5;", |
||||||
|
&[Node::VarDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Some(Node::const_node(5.0)), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks empty `var` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_empty_var_declaration() { |
||||||
|
check_parser("var a;", &[Node::VarDecl(vec![(String::from("a"), None)])]); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks multiple `var` declarations.
|
||||||
|
#[test] |
||||||
|
fn check_multiple_var_declaration() { |
||||||
|
check_parser( |
||||||
|
"var a = 5, b, c = 6;", |
||||||
|
&[Node::VarDecl(vec![ |
||||||
|
(String::from("a"), Some(Node::const_node(5.0))), |
||||||
|
(String::from("b"), None), |
||||||
|
(String::from("c"), Some(Node::const_node(6.0))), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks `let` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_let_declaration() { |
||||||
|
check_parser( |
||||||
|
"let a = 5;", |
||||||
|
&[Node::LetDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Some(Node::const_node(5.0)), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks `let` declaration parsing with no spaces.
|
||||||
|
#[test] |
||||||
|
fn check_let_declaration_no_spaces() { |
||||||
|
check_parser( |
||||||
|
"let a=5;", |
||||||
|
&[Node::LetDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Some(Node::const_node(5.0)), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks empty `let` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_empty_let_declaration() { |
||||||
|
check_parser("let a;", &[Node::LetDecl(vec![(String::from("a"), None)])]); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks multiple `let` declarations.
|
||||||
|
#[test] |
||||||
|
fn check_multiple_let_declaration() { |
||||||
|
check_parser( |
||||||
|
"let a = 5, b, c = 6;", |
||||||
|
&[Node::LetDecl(vec![ |
||||||
|
(String::from("a"), Some(Node::const_node(5.0))), |
||||||
|
(String::from("b"), None), |
||||||
|
(String::from("c"), Some(Node::const_node(6.0))), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks `const` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_const_declaration() { |
||||||
|
check_parser( |
||||||
|
"const a = 5;", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Node::const_node(5.0), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks `const` declaration parsing with no spaces.
|
||||||
|
#[test] |
||||||
|
fn check_const_declaration_no_spaces() { |
||||||
|
check_parser( |
||||||
|
"const a=5;", |
||||||
|
&[Node::ConstDecl(vec![( |
||||||
|
String::from("a"), |
||||||
|
Node::const_node(5.0), |
||||||
|
)])], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks empty `const` declaration parsing.
|
||||||
|
#[test] |
||||||
|
fn check_empty_const_declaration() { |
||||||
|
check_invalid("const a;"); |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks multiple `const` declarations.
|
||||||
|
#[test] |
||||||
|
fn check_multiple_const_declaration() { |
||||||
|
check_parser( |
||||||
|
"const a = 5, c = 6;", |
||||||
|
&[Node::ConstDecl(vec![ |
||||||
|
(String::from("a"), Node::const_node(5.0)), |
||||||
|
(String::from("c"), Node::const_node(6.0)), |
||||||
|
])], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use super::Statement; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// If statement parsing.
|
||||||
|
///
|
||||||
|
/// An _If_ statement will have a condition, a block statemet, and an optional _else_ statement.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct IfStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl IfStatement { |
||||||
|
/// Creates a new `IfStatement` parser.
|
||||||
|
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for IfStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::If, "if statement")?; |
||||||
|
cursor.expect(Punctuator::OpenParen, "if statement")?; |
||||||
|
|
||||||
|
let cond = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "if statement")?; |
||||||
|
|
||||||
|
let then_stm = |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
let else_stm = match cursor.next() { |
||||||
|
Some(else_tok) if else_tok.kind == TokenKind::Keyword(Keyword::Else) => Some( |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor)?, |
||||||
|
), |
||||||
|
_ => { |
||||||
|
cursor.back(); |
||||||
|
None |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(Node::if_node::<_, _, Node, _>(cond, then_stm, else_stm)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
//! Do-while statement parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-do-while-statement
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, |
||||||
|
ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Do...while statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::statement) struct DoWhileStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl DoWhileStatement { |
||||||
|
/// Creates a new `DoWhileStatement` parser.
|
||||||
|
pub(in crate::syntax::parser::statement) fn new<Y, A, R>( |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
allow_return: R, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for DoWhileStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Do, "do while statement")?; |
||||||
|
|
||||||
|
let body = |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
if next_token.kind != TokenKind::Keyword(Keyword::While) { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::Keyword(Keyword::While)], |
||||||
|
next_token.clone(), |
||||||
|
"do while statement", |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
cursor.expect(Keyword::While, "do while statement")?; |
||||||
|
cursor.expect(Punctuator::OpenParen, "do while statement")?; |
||||||
|
|
||||||
|
let cond = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "do while statement")?; |
||||||
|
cursor.expect_semicolon(true, "do while statement")?; |
||||||
|
|
||||||
|
Ok(Node::do_while_loop(body, cond)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
//! For statement parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-for-statement
|
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, |
||||||
|
statement::declaration::Declaration, |
||||||
|
statement::{variable::VariableDeclarationList, Statement}, |
||||||
|
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// For statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-for-statement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::statement) struct ForStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl ForStatement { |
||||||
|
/// Creates a new `ForStatement` parser.
|
||||||
|
pub(in crate::syntax::parser::statement) fn new<Y, A, R>( |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
allow_return: R, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ForStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::For, "for statement")?; |
||||||
|
cursor.expect(Punctuator::OpenParen, "for statement")?; |
||||||
|
|
||||||
|
let init = match cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind { |
||||||
|
TokenKind::Keyword(Keyword::Var) => Some( |
||||||
|
VariableDeclarationList::new(false, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
), |
||||||
|
TokenKind::Keyword(Keyword::Let) | TokenKind::Keyword(Keyword::Const) => { |
||||||
|
Some(Declaration::new(self.allow_yield, self.allow_await).parse(cursor)?) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::Semicolon) => None, |
||||||
|
_ => Some(Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?), |
||||||
|
}; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::Semicolon, "for statement")?; |
||||||
|
|
||||||
|
let cond = if cursor.next_if(Punctuator::Semicolon).is_some() { |
||||||
|
Node::const_node(true) |
||||||
|
} else { |
||||||
|
let step = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect(Punctuator::Semicolon, "for statement")?; |
||||||
|
step |
||||||
|
}; |
||||||
|
|
||||||
|
let step = if cursor.next_if(Punctuator::CloseParen).is_some() { |
||||||
|
None |
||||||
|
} else { |
||||||
|
let step = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
cursor.expect( |
||||||
|
TokenKind::Punctuator(Punctuator::CloseParen), |
||||||
|
"for statement", |
||||||
|
)?; |
||||||
|
Some(step) |
||||||
|
}; |
||||||
|
|
||||||
|
let body = |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
let for_node = Node::for_loop::<_, _, _, Node, Node, Node, _>(init, cond, step, body); |
||||||
|
|
||||||
|
Ok(Node::Block(vec![for_node])) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
mod do_while_statement; |
||||||
|
mod for_statement; |
||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
mod while_statement; |
||||||
|
|
||||||
|
pub(super) use self::{ |
||||||
|
do_while_statement::DoWhileStatement, for_statement::ForStatement, |
||||||
|
while_statement::WhileStatement, |
||||||
|
}; |
@ -0,0 +1,50 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::node::Node, |
||||||
|
ast::op::{AssignOp, BinOp, CompOp, UnaryOp}, |
||||||
|
parser::tests::check_parser, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Checks do-while statement parsing.
|
||||||
|
#[test] |
||||||
|
fn check_do_while() { |
||||||
|
check_parser( |
||||||
|
r#"do { |
||||||
|
a += 1; |
||||||
|
} while (true)"#, |
||||||
|
&[Node::do_while_loop( |
||||||
|
Node::Block(vec![Node::bin_op( |
||||||
|
BinOp::Assign(AssignOp::Add), |
||||||
|
Node::local("a"), |
||||||
|
Node::const_node(1.0), |
||||||
|
)]), |
||||||
|
Node::const_node(true), |
||||||
|
)], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Checks automatic semicolon insertion after do-while.
|
||||||
|
#[test] |
||||||
|
fn check_do_while_semicolon_insertion() { |
||||||
|
check_parser( |
||||||
|
r#"var i = 0; |
||||||
|
do {console.log("hello");} while(i++ < 10) console.log("end");"#, |
||||||
|
&[ |
||||||
|
Node::VarDecl(vec![(String::from("i"), Some(Node::const_node(0.0)))]), |
||||||
|
Node::do_while_loop( |
||||||
|
Node::Block(vec![Node::call( |
||||||
|
Node::get_const_field(Node::local("console"), "log"), |
||||||
|
vec![Node::const_node("hello")], |
||||||
|
)]), |
||||||
|
Node::bin_op( |
||||||
|
BinOp::Comp(CompOp::LessThan), |
||||||
|
Node::unary_op(UnaryOp::IncrementPost, Node::local("i")), |
||||||
|
Node::const_node(10.0), |
||||||
|
), |
||||||
|
), |
||||||
|
Node::call( |
||||||
|
Node::get_const_field(Node::local("console"), "log"), |
||||||
|
vec![Node::const_node("end")], |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, |
||||||
|
ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// While statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-while-statement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::statement) struct WhileStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl WhileStatement { |
||||||
|
/// Creates a new `WhileStatement` parser.
|
||||||
|
pub(in crate::syntax::parser::statement) fn new<Y, A, R>( |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
allow_return: R, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for WhileStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::While, "while statement")?; |
||||||
|
cursor.expect(Punctuator::OpenParen, "while statement")?; |
||||||
|
|
||||||
|
let cond = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "while statement")?; |
||||||
|
|
||||||
|
let body = |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
Ok(Node::while_loop(cond, body)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,318 @@ |
|||||||
|
//! Statement and declaration parsing.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [MDN documentation][mdn]
|
||||||
|
//! - [ECMAScript specification][spec]
|
||||||
|
//!
|
||||||
|
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations
|
||||||
|
|
||||||
|
mod block; |
||||||
|
mod break_stm; |
||||||
|
mod continue_stm; |
||||||
|
mod declaration; |
||||||
|
mod if_stm; |
||||||
|
mod iteration; |
||||||
|
mod return_stm; |
||||||
|
mod switch; |
||||||
|
mod throw; |
||||||
|
mod try_stm; |
||||||
|
mod variable; |
||||||
|
|
||||||
|
use self::{ |
||||||
|
block::BlockStatement, |
||||||
|
break_stm::BreakStatement, |
||||||
|
continue_stm::ContinueStatement, |
||||||
|
declaration::Declaration, |
||||||
|
if_stm::IfStatement, |
||||||
|
iteration::{DoWhileStatement, ForStatement, WhileStatement}, |
||||||
|
return_stm::ReturnStatement, |
||||||
|
switch::SwitchStatement, |
||||||
|
throw::ThrowStatement, |
||||||
|
try_stm::TryStatement, |
||||||
|
variable::VariableStatement, |
||||||
|
}; |
||||||
|
use super::{ |
||||||
|
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}; |
||||||
|
use crate::syntax::ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}; |
||||||
|
|
||||||
|
/// Statement parsing.
|
||||||
|
///
|
||||||
|
/// This can be one of the following:
|
||||||
|
///
|
||||||
|
/// - `BlockStatement`
|
||||||
|
/// - `VariableStatement`
|
||||||
|
/// - `EmptyStatement`
|
||||||
|
/// - `ExpressionStatement`
|
||||||
|
/// - `IfStatement`
|
||||||
|
/// - `BreakableStatement`
|
||||||
|
/// - `ContinueStatement`
|
||||||
|
/// - `BreakStatement`
|
||||||
|
/// - `ReturnStatement`
|
||||||
|
/// - `WithStatement`
|
||||||
|
/// - `LabelledStatement`
|
||||||
|
/// - `ThrowStatement`
|
||||||
|
/// - `TryStatement`
|
||||||
|
/// - `DebuggerStatement`
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-Statement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct Statement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl Statement { |
||||||
|
/// Creates a new `Statement` parser.
|
||||||
|
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for Statement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TODO: add BreakableStatement and divide Whiles, fors and so on to another place.
|
||||||
|
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::If) => { |
||||||
|
IfStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Var) => { |
||||||
|
VariableStatement::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::While) => { |
||||||
|
WhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Do) => { |
||||||
|
DoWhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::For) => { |
||||||
|
ForStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Return) => { |
||||||
|
if self.allow_return.0 { |
||||||
|
ReturnStatement::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} else { |
||||||
|
Err(ParseError::Unexpected(tok.clone(), Some("statement"))) |
||||||
|
} |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Break) => { |
||||||
|
BreakStatement::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Continue) => { |
||||||
|
ContinueStatement::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Try) => { |
||||||
|
TryStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Throw) => { |
||||||
|
ThrowStatement::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Keyword(Keyword::Switch) => { |
||||||
|
SwitchStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||||
|
BlockStatement::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor) |
||||||
|
} |
||||||
|
// TODO: https://tc39.es/ecma262/#prod-LabelledStatement
|
||||||
|
// TokenKind::Punctuator(Punctuator::Semicolon) => {
|
||||||
|
// return Ok(Node::new(NodeBase::Nope, tok.pos))
|
||||||
|
// }
|
||||||
|
_ => ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Reads a list of statements.
|
||||||
|
///
|
||||||
|
/// If `break_when_closingbrase` is `true`, it will stop as soon as it finds a `}` character.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-StatementList
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct StatementList { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
break_when_closingbrase: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl StatementList { |
||||||
|
/// Creates a new `StatementList` parser.
|
||||||
|
pub(super) fn new<Y, A, R>( |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
allow_return: R, |
||||||
|
break_when_closingbrase: bool, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
break_when_closingbrase, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for StatementList { |
||||||
|
type Output = Vec<Node>; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Vec<Node>, ParseError> { |
||||||
|
let mut items = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
match cursor.peek(0) { |
||||||
|
Some(token) if token.kind == TokenKind::Punctuator(Punctuator::CloseBlock) => { |
||||||
|
if self.break_when_closingbrase { |
||||||
|
break; |
||||||
|
} else { |
||||||
|
return Err(ParseError::Unexpected(token.clone(), None)); |
||||||
|
} |
||||||
|
} |
||||||
|
None => { |
||||||
|
if self.break_when_closingbrase { |
||||||
|
return Err(ParseError::AbruptEnd); |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
let item = |
||||||
|
StatementListItem::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor)?; |
||||||
|
items.push(item); |
||||||
|
|
||||||
|
// move the cursor forward for any consecutive semicolon.
|
||||||
|
while cursor.next_if(Punctuator::Semicolon).is_some() {} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(items) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Statement list item parsing
|
||||||
|
///
|
||||||
|
/// A statement list item can either be an statement or a declaration.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct StatementListItem { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl StatementListItem { |
||||||
|
/// Creates a new `StatementListItem` parser.
|
||||||
|
fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for StatementListItem { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
match tok.kind { |
||||||
|
TokenKind::Keyword(Keyword::Function) |
||||||
|
| TokenKind::Keyword(Keyword::Const) |
||||||
|
| TokenKind::Keyword(Keyword::Let) => { |
||||||
|
Declaration::new(self.allow_yield, self.allow_await).parse(cursor) |
||||||
|
} |
||||||
|
_ => { |
||||||
|
Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Expression statement parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ExpressionStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct ExpressionStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ExpressionStatement { |
||||||
|
/// Creates a new `ExpressionStatement` parser.
|
||||||
|
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ExpressionStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TODO: lookahead
|
||||||
|
let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect_semicolon(false, "expression statement")?; |
||||||
|
|
||||||
|
Ok(expr) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Return statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct ReturnStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ReturnStatement { |
||||||
|
/// Creates a new `ReturnStatement` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ReturnStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Return, "return statement")?; |
||||||
|
|
||||||
|
if let (true, tok) = cursor.peek_semicolon(false) { |
||||||
|
match tok { |
||||||
|
Some(tok) |
||||||
|
if tok.kind == TokenKind::Punctuator(Punctuator::Semicolon) |
||||||
|
|| tok.kind == TokenKind::LineTerminator => |
||||||
|
{ |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
return Ok(Node::Return(None)); |
||||||
|
} |
||||||
|
|
||||||
|
let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect_semicolon(false, "return statement")?; |
||||||
|
|
||||||
|
Ok(Node::return_node(expr)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator}, |
||||||
|
parser::{ |
||||||
|
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, |
||||||
|
ParseResult, TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Switch statement parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct SwitchStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl SwitchStatement { |
||||||
|
/// Creates a new `SwitchStatement` parser.
|
||||||
|
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for SwitchStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Switch, "switch statement")?; |
||||||
|
cursor.expect(Punctuator::OpenParen, "switch statement")?; |
||||||
|
|
||||||
|
let condition = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect(Punctuator::CloseParen, "switch statement")?; |
||||||
|
|
||||||
|
let (cases, default) = |
||||||
|
CaseBlock::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
Ok(Node::switch::<_, _, _, Node>(condition, cases, default)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Switch case block parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-CaseBlock
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct CaseBlock { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl CaseBlock { |
||||||
|
/// Creates a new `CaseBlock` parser.
|
||||||
|
fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for CaseBlock { |
||||||
|
type Output = (Vec<(Node, Vec<Node>)>, Option<Node>); |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
cursor.expect(Punctuator::OpenBlock, "switch case block")?; |
||||||
|
|
||||||
|
// CaseClauses[?Yield, ?Await, ?Return]opt
|
||||||
|
// CaseClauses[?Yield, ?Await, ?Return]optDefaultClause[?Yield, ?Await, ?Return]CaseClauses[?Yield, ?Await, ?Return]opt
|
||||||
|
|
||||||
|
unimplemented!("switch case block parsing") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// For statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct ThrowStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl ThrowStatement { |
||||||
|
/// Creates a new `ThrowStatement` parser.
|
||||||
|
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for ThrowStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Throw, "throw statement")?; |
||||||
|
|
||||||
|
cursor.peek_expect_no_lineterminator(0, "throw statement")?; |
||||||
|
|
||||||
|
let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
if let Some(tok) = cursor.peek(0) { |
||||||
|
if tok.kind == TokenKind::Punctuator(Punctuator::Semicolon) { |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Node::throw(expr)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
use crate::syntax::{ast::node::Node, parser::tests::check_parser}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn check_throw_parsing() { |
||||||
|
check_parser("throw 'error';", &[Node::throw(Node::const_node("error"))]); |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
#[cfg(test)] |
||||||
|
mod tests; |
||||||
|
|
||||||
|
use super::block::Block; |
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Try...catch statement parsing
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-try-statement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(super) struct TryStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
allow_return: AllowReturn, |
||||||
|
} |
||||||
|
|
||||||
|
impl TryStatement { |
||||||
|
/// Creates a new `TryStatement` parser.
|
||||||
|
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
R: Into<AllowReturn>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
allow_return: allow_return.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for TryStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
// TRY
|
||||||
|
cursor.expect(Keyword::Try, "try statement")?; |
||||||
|
|
||||||
|
let try_clause = |
||||||
|
Block::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?; |
||||||
|
|
||||||
|
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; |
||||||
|
|
||||||
|
if next_token.kind != TokenKind::Keyword(Keyword::Catch) |
||||||
|
&& next_token.kind != TokenKind::Keyword(Keyword::Finally) |
||||||
|
{ |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Keyword(Keyword::Catch), |
||||||
|
TokenKind::Keyword(Keyword::Finally), |
||||||
|
], |
||||||
|
next_token.clone(), |
||||||
|
"try statement", |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
// CATCH
|
||||||
|
let (catch, param) = if next_token.kind == TokenKind::Keyword(Keyword::Catch) { |
||||||
|
// Catch binding
|
||||||
|
cursor.expect(Punctuator::OpenParen, "catch in try statement")?; |
||||||
|
// TODO: should accept BindingPattern
|
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let catch_param = if let TokenKind::Identifier(s) = &tok.kind { |
||||||
|
Node::local(s) |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"catch in try statement", |
||||||
|
)); |
||||||
|
}; |
||||||
|
cursor.expect(Punctuator::CloseParen, "catch in try statement")?; |
||||||
|
|
||||||
|
// Catch block
|
||||||
|
( |
||||||
|
Some( |
||||||
|
Block::new(self.allow_yield, self.allow_await, self.allow_return) |
||||||
|
.parse(cursor)?, |
||||||
|
), |
||||||
|
Some(catch_param), |
||||||
|
) |
||||||
|
} else { |
||||||
|
(None, None) |
||||||
|
}; |
||||||
|
|
||||||
|
// FINALLY
|
||||||
|
let finally_block = if cursor.next_if(Keyword::Finally).is_some() { |
||||||
|
Some(Block::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(Node::try_node::<_, _, _, _, Node, Node, Node>( |
||||||
|
try_clause, |
||||||
|
catch, |
||||||
|
param, |
||||||
|
finally_block, |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
// use super::lexical_declaration_continuation;
|
||||||
|
use crate::syntax::{ |
||||||
|
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, |
||||||
|
parser::{ |
||||||
|
expression::Initializer, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, |
||||||
|
TokenParser, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/// Variable statement parsing.
|
||||||
|
///
|
||||||
|
/// A varible statement contains the `var` keyword.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::statement) struct VariableStatement { |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl VariableStatement { |
||||||
|
/// Creates a new `VariableStatement` parser.
|
||||||
|
pub(in crate::syntax::parser::statement) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for VariableStatement { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
cursor.expect(Keyword::Var, "variable statement")?; |
||||||
|
|
||||||
|
let decl_list = |
||||||
|
VariableDeclarationList::new(true, self.allow_yield, self.allow_await).parse(cursor)?; |
||||||
|
|
||||||
|
cursor.expect_semicolon(false, "variable statement")?; |
||||||
|
|
||||||
|
Ok(decl_list) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Variable declaration list parsing.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [MDN documentation][mdn]
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-VariableDeclarationList
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub(in crate::syntax::parser::statement) struct VariableDeclarationList { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl VariableDeclarationList { |
||||||
|
/// Creates a new `VariableDeclarationList` parser.
|
||||||
|
pub(in crate::syntax::parser::statement) fn new<I, Y, A>( |
||||||
|
allow_in: I, |
||||||
|
allow_yield: Y, |
||||||
|
allow_await: A, |
||||||
|
) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for VariableDeclarationList { |
||||||
|
type Output = Node; |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { |
||||||
|
let mut list = Vec::new(); |
||||||
|
|
||||||
|
loop { |
||||||
|
list.push( |
||||||
|
VariableDeclaration::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
); |
||||||
|
|
||||||
|
match cursor.peek_semicolon(false) { |
||||||
|
(true, _) => break, |
||||||
|
(false, Some(tk)) if tk.kind == TokenKind::Punctuator(Punctuator::Comma) => { |
||||||
|
let _ = cursor.next(); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![ |
||||||
|
TokenKind::Punctuator(Punctuator::Semicolon), |
||||||
|
TokenKind::LineTerminator, |
||||||
|
], |
||||||
|
cursor.next().ok_or(ParseError::AbruptEnd)?.clone(), |
||||||
|
"lexical declaration", |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(Node::VarDecl(list)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Reads an individual variable declaration.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript specification][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#prod-VariableDeclaration
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
struct VariableDeclaration { |
||||||
|
allow_in: AllowIn, |
||||||
|
allow_yield: AllowYield, |
||||||
|
allow_await: AllowAwait, |
||||||
|
} |
||||||
|
|
||||||
|
impl VariableDeclaration { |
||||||
|
/// Creates a new `VariableDeclaration` parser.
|
||||||
|
fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self |
||||||
|
where |
||||||
|
I: Into<AllowIn>, |
||||||
|
Y: Into<AllowYield>, |
||||||
|
A: Into<AllowAwait>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
allow_in: allow_in.into(), |
||||||
|
allow_yield: allow_yield.into(), |
||||||
|
allow_await: allow_await.into(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TokenParser for VariableDeclaration { |
||||||
|
type Output = (String, Option<Node>); |
||||||
|
|
||||||
|
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { |
||||||
|
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; |
||||||
|
let name = if let TokenKind::Identifier(name) = &tok.kind { |
||||||
|
name.clone() |
||||||
|
} else { |
||||||
|
return Err(ParseError::Expected( |
||||||
|
vec![TokenKind::identifier("identifier")], |
||||||
|
tok.clone(), |
||||||
|
"variable declaration", |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
match cursor.peek(0) { |
||||||
|
Some(tk) if tk.kind == TokenKind::Punctuator(Punctuator::Assign) => Ok(( |
||||||
|
name, |
||||||
|
Some( |
||||||
|
Initializer::new(self.allow_in, self.allow_yield, self.allow_await) |
||||||
|
.parse(cursor)?, |
||||||
|
), |
||||||
|
)), |
||||||
|
_ => Ok((name, None)), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue