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