#[cfg(test)]
mod tests;
use crate::syntax::lexer::TokenKind;
use crate::{
syntax::{
ast::{
node::{self, FunctionExpr, MethodDefinitionKind, Node, Object},
Punctuator,
},
parser::{
expression::AssignmentExpression,
function::{FormalParameters, FunctionBody},
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
use std::io::Read;
#[derive(Debug, Clone, Copy)]
pub(super) struct ObjectLiteral {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ObjectLiteral {
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<R> TokenParser<R> for ObjectLiteral
where
R: Read,
{
type Output = Object;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("ObjectLiteral", "Parsing");
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,
"object literal",
));
}
}
Ok(Object::from(elements))
}
}
#[derive(Debug, Clone, Copy)]
struct PropertyDefinition {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl PropertyDefinition {
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<R> TokenParser<R> for PropertyDefinition
where
R: Read,
{
type Output = node::PropertyDefinition;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("PropertyDefinition", "Parsing");
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()?.ok_or(ParseError::AbruptEnd)?.to_string();
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)?.ok_or(ParseError::AbruptEnd)?.span().start();
Err(ParseError::general("expected property definition", pos))
}
}
#[derive(Debug, Clone)]
struct MethodDefinition {
allow_yield: AllowYield,
allow_await: AllowAwait,
identifier: String,
}
impl MethodDefinition {
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<R> TokenParser<R> for MethodDefinition
where
R: Read,
{
type Output = node::PropertyDefinition;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("MethodDefinition", "Parsing");
let (methodkind, prop_name, params) = match self.identifier.as_str() {
idn @ "get" | idn @ "set" => {
let prop_name = cursor.next()?.ok_or(ParseError::AbruptEnd)?.to_string();
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,
"getter functions must have no arguments",
));
}
(MethodDefinitionKind::Get, prop_name, params)
} else {
if params.len() != 1 {
return Err(ParseError::unexpected(
first_param,
"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)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"property method definition",
)?;
Ok(node::PropertyDefinition::method_definition(
methodkind,
prop_name,
FunctionExpr::new(None, params, body),
))
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct Initializer {
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl Initializer {
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<R> TokenParser<R> for Initializer
where
R: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Initializer", "Parsing");
cursor.expect(Punctuator::Assign, "initializer")?;
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await).parse(cursor)
}
}