Revision control

Copy as Markdown

Other Tools

use crate::context_stack::{BindingKind, ContextMetadata, ControlInfo, LabelKind};
use crate::early_error_checker::EarlyErrorChecker;
use crate::error::{BoxedParseError, ParseError, Result};
use crate::Token;
use ast::{
arena,
source_atom_set::{CommonSourceAtomSetIndices, SourceAtomSet},
source_location_accessor::SourceLocationAccessor,
source_slice_list::SourceSliceList,
types::*,
SourceLocation,
};
use bumpalo::{vec, Bump};
use std::cell::RefCell;
use std::rc::Rc;
pub struct AstBuilder<'alloc> {
pub allocator: &'alloc Bump,
context_metadata: ContextMetadata,
atoms: Rc<RefCell<SourceAtomSet<'alloc>>>,
slices: Rc<RefCell<SourceSliceList<'alloc>>>,
}
pub trait AstBuilderDelegate<'alloc> {
fn ast_builder_refmut(&mut self) -> &mut AstBuilder<'alloc>;
}
impl<'alloc> AstBuilder<'alloc> {
pub fn new(
allocator: &'alloc Bump,
atoms: Rc<RefCell<SourceAtomSet<'alloc>>>,
slices: Rc<RefCell<SourceSliceList<'alloc>>>,
) -> Self {
Self {
allocator,
context_metadata: ContextMetadata::new(),
atoms,
slices,
}
}
pub fn alloc<T>(&self, value: T) -> arena::Box<'alloc, T> {
arena::alloc(self.allocator, value)
}
pub fn alloc_with<F, T>(&self, gen: F) -> arena::Box<'alloc, T>
where
F: FnOnce() -> T,
{
arena::alloc_with(self.allocator, gen)
}
pub fn alloc_str(&self, s: &str) -> &'alloc str {
arena::alloc_str(self.allocator, s)
}
fn new_vec<T>(&self) -> arena::Vec<'alloc, T> {
arena::Vec::new_in(self.allocator)
}
fn new_vec_single<T>(&self, value: T) -> arena::Vec<'alloc, T> {
vec![in self.allocator; value]
}
fn collect_vec_from_results<T, C>(&self, results: C) -> Result<'alloc, arena::Vec<'alloc, T>>
where
C: IntoIterator<Item = Result<'alloc, T>>,
{
let mut out = self.new_vec();
for result in results {
out.push(result?);
}
Ok(out)
}
fn push<T>(&self, list: &mut arena::Vec<'alloc, T>, value: T) {
list.push(value);
}
fn append<T>(&self, list: &mut arena::Vec<'alloc, T>, elements: &mut arena::Vec<'alloc, T>) {
list.append(elements);
}
// IdentifierReference : Identifier
pub fn identifier_reference(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Identifier>> {
self.on_identifier_reference(&token)?;
Ok(self.alloc_with(|| self.identifier(token)))
}
// BindingIdentifier : Identifier
pub fn binding_identifier(
&mut self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, BindingIdentifier>> {
self.on_binding_identifier(&token)?;
let loc = token.loc;
Ok(self.alloc_with(|| BindingIdentifier {
name: self.identifier(token),
loc,
}))
}
// BindingIdentifier : `yield`
pub fn binding_identifier_yield(
&mut self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, BindingIdentifier>> {
self.on_binding_identifier(&token)?;
let loc = token.loc;
Ok(self.alloc_with(|| BindingIdentifier {
name: Identifier {
value: CommonSourceAtomSetIndices::yield_(),
loc,
},
loc,
}))
}
// BindingIdentifier : `await`
pub fn binding_identifier_await(
&mut self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, BindingIdentifier>> {
self.on_binding_identifier(&token)?;
let loc = token.loc;
Ok(self.alloc_with(|| BindingIdentifier {
name: Identifier {
value: CommonSourceAtomSetIndices::await_(),
loc,
},
loc,
}))
}
// LabelIdentifier : Identifier
pub fn label_identifier(
&mut self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Label>> {
self.on_label_identifier(&token)?;
let loc = token.loc;
Ok(self.alloc_with(|| Label {
value: token.value.as_atom(),
loc,
}))
}
// PrimaryExpression : `this`
pub fn this_expr(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let loc = token.loc;
self.alloc_with(|| Expression::ThisExpression { loc })
}
// PrimaryExpression : IdentifierReference
pub fn identifier_expr(
&self,
name: arena::Box<'alloc, Identifier>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let loc = name.loc;
self.alloc_with(|| {
Expression::IdentifierExpression(IdentifierExpression {
name: name.unbox(),
loc,
})
})
}
// PrimaryExpression : RegularExpressionLiteral
pub fn regexp_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let source = self.slices.borrow().get(token.value.as_slice());
debug_assert!(source.chars().nth(0).unwrap() == '/');
let end = source.rfind('/').unwrap();
let pattern = self.slices.borrow_mut().push(&source[1..end]);
let flags = &source[end + 1..];
let mut global: bool = false;
let mut ignore_case: bool = false;
let mut multi_line: bool = false;
let mut dot_all: bool = false;
let mut unicode: bool = false;
let mut sticky: bool = false;
for c in flags.chars() {
if c == 'g' {
global = true;
}
if c == 'i' {
ignore_case = true;
}
if c == 'm' {
multi_line = true;
}
if c == 's' {
dot_all = true;
}
if c == 'u' {
unicode = true;
}
if c == 'y' {
sticky = true;
}
}
let loc = token.loc;
self.alloc_with(|| Expression::LiteralRegExpExpression {
pattern,
global,
ignore_case,
multi_line,
dot_all,
sticky,
unicode,
loc,
})
}
// PrimaryExpression : TemplateLiteral
pub fn untagged_template_expr(
&self,
template_literal: arena::Box<'alloc, TemplateExpression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.alloc_with(|| Expression::TemplateExpression(template_literal.unbox()))
}
// PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList
pub fn uncover_parenthesized_expression(
&self,
parenthesized: arena::Box<'alloc, CoverParenthesized<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
match parenthesized.unbox() {
CoverParenthesized::Expression { expression, .. } => {
// TODO - does this need to rewalk the expression to look for
// invalid ObjectPattern or ArrayPattern syntax?
Ok(expression)
}
CoverParenthesized::Parameters(_parameters) => Err(ParseError::NotImplemented(
"parenthesized expression with `...` should be a syntax error",
)
.into()),
}
}
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `)`
pub fn cover_parenthesized_expression(
&self,
open_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, CoverParenthesized<'alloc>> {
self.alloc_with(|| CoverParenthesized::Expression {
expression,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
})
}
// CoverParenthesizedExpressionAndArrowParameterList : `(` `)`
pub fn empty_parameter_list(&self) -> arena::Vec<'alloc, Parameter<'alloc>> {
self.new_vec()
}
/// Used when parsing `([a, b=2]=arr) =>` to reinterpret as parameter bindings
/// the snippets `a` and `b=2`, which were previously parsed as assignment targets.
fn assignment_target_maybe_default_to_binding(
&self,
target: AssignmentTargetMaybeDefault<'alloc>,
) -> Result<'alloc, Parameter<'alloc>> {
match target {
AssignmentTargetMaybeDefault::AssignmentTarget(target) => Ok(Parameter::Binding(
self.assignment_target_to_binding(target)?,
)),
AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(
AssignmentTargetWithDefault { binding, init, loc },
) => Ok(Parameter::BindingWithDefault(BindingWithDefault {
binding: self.assignment_target_to_binding(binding)?,
init,
loc,
})),
}
}
fn assignment_target_property_to_binding_property(
&self,
target: AssignmentTargetProperty<'alloc>,
) -> Result<'alloc, BindingProperty<'alloc>> {
Ok(match target {
AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(
AssignmentTargetPropertyIdentifier {
binding: AssignmentTargetIdentifier { name, loc },
init,
loc: loc2,
},
) => BindingProperty::BindingPropertyIdentifier(BindingPropertyIdentifier {
binding: BindingIdentifier { name, loc },
init,
loc: loc2,
}),
AssignmentTargetProperty::AssignmentTargetPropertyProperty(
AssignmentTargetPropertyProperty { name, binding, loc },
) => BindingProperty::BindingPropertyProperty(BindingPropertyProperty {
name,
binding: self.assignment_target_maybe_default_to_binding(binding)?,
loc,
}),
})
}
/// Refine an AssignmentRestProperty into a BindingRestProperty.
fn assignment_rest_property_to_binding_identifier(
&self,
target: AssignmentTarget<'alloc>,
) -> Result<'alloc, arena::Box<'alloc, BindingIdentifier>> {
match target {
// ({...x} = dv) => {}
AssignmentTarget::SimpleAssignmentTarget(
SimpleAssignmentTarget::AssignmentTargetIdentifier(AssignmentTargetIdentifier {
name,
loc,
}),
) => Ok(self.alloc_with(|| BindingIdentifier { name, loc })),
// ({...x.y} = dv) => {}
_ => Err(ParseError::ObjectBindingPatternWithInvalidRest.into()),
}
}
/// Refine the left-hand side of `=` to a parameter binding. The spec says:
///
/// > When the production *ArrowParameters* :
/// > *CoverParenthesizedExpressionAndArrowParameterList* is recognized,
/// > the following grammar is used to refine the interpretation of
/// > *CoverParenthesizedExpressionAndArrowParameterList*:
/// >
/// > *ArrowFormalParameters*\[Yield, Await\] :
/// > `(` *UniqueFormalParameters*\[?Yield, ?Await\] `)`
///
/// Of course, rather than actually reparsing the arrow function parameters,
/// we work by refining the AST we already built.
///
/// When parsing `(a = 1, [b, c] = obj) => {}`, the assignment targets `a`
/// and `[b, c]` are passed to this method.
fn assignment_target_to_binding(
&self,
target: AssignmentTarget<'alloc>,
) -> Result<'alloc, Binding<'alloc>> {
match target {
// (a = dv) => {}
AssignmentTarget::SimpleAssignmentTarget(
SimpleAssignmentTarget::AssignmentTargetIdentifier(AssignmentTargetIdentifier {
name,
loc,
}),
) => Ok(Binding::BindingIdentifier(BindingIdentifier { name, loc })),
// This case is always an early SyntaxError.
// (a.x = dv) => {}
// (a[i] = dv) => {}
AssignmentTarget::SimpleAssignmentTarget(
SimpleAssignmentTarget::MemberAssignmentTarget(_),
) => Err(ParseError::InvalidParameter.into()),
// ([a, b] = dv) => {}
AssignmentTarget::AssignmentTargetPattern(
AssignmentTargetPattern::ArrayAssignmentTarget(ArrayAssignmentTarget {
elements,
rest,
loc,
}),
) => {
let elements: arena::Vec<'alloc, Option<AssignmentTargetMaybeDefault<'alloc>>> =
elements;
let elements: arena::Vec<'alloc, Option<Parameter<'alloc>>> = self
.collect_vec_from_results(elements.into_iter().map(|maybe_target| {
maybe_target
.map(|target| self.assignment_target_maybe_default_to_binding(target))
.transpose()
}))?;
let rest: Option<Result<'alloc, arena::Box<'alloc, Binding<'alloc>>>> = rest.map(
|rest_target| -> Result<'alloc, arena::Box<'alloc, Binding<'alloc>>> {
Ok(self.alloc(self.assignment_target_to_binding(rest_target.unbox())?))
},
);
let rest: Option<arena::Box<'alloc, Binding<'alloc>>> = rest.transpose()?;
Ok(Binding::BindingPattern(BindingPattern::ArrayBinding(
ArrayBinding {
elements,
rest,
loc,
},
)))
}
// ({a, b: c} = dv) => {}
AssignmentTarget::AssignmentTargetPattern(
AssignmentTargetPattern::ObjectAssignmentTarget(ObjectAssignmentTarget {
properties,
rest,
loc,
}),
) => {
let properties =
self.collect_vec_from_results(properties.into_iter().map(|target| {
self.assignment_target_property_to_binding_property(target)
}))?;
let rest = if let Some(rest_target) = rest {
Some(self.assignment_rest_property_to_binding_identifier(rest_target.unbox())?)
} else {
None
};
Ok(Binding::BindingPattern(BindingPattern::ObjectBinding(
ObjectBinding {
properties,
rest,
loc,
},
)))
}
}
}
fn object_property_to_binding_property(
&self,
op: ObjectProperty<'alloc>,
) -> Result<'alloc, BindingProperty<'alloc>> {
match op {
ObjectProperty::NamedObjectProperty(NamedObjectProperty::DataProperty(
DataProperty {
property_name,
expression,
loc,
},
)) => Ok(BindingProperty::BindingPropertyProperty(
BindingPropertyProperty {
name: property_name,
binding: self.expression_to_parameter(expression.unbox())?,
loc,
},
)),
ObjectProperty::NamedObjectProperty(NamedObjectProperty::MethodDefinition(_)) => {
Err(ParseError::ObjectPatternWithMethod.into())
}
ObjectProperty::ShorthandProperty(ShorthandProperty {
name: IdentifierExpression { name, loc },
..
}) => {
// TODO - CoverInitializedName can't be represented in an
// ObjectProperty, but we need it here.
Ok(BindingProperty::BindingPropertyIdentifier(
BindingPropertyIdentifier {
binding: BindingIdentifier { name, loc },
init: None,
loc,
},
))
}
ObjectProperty::SpreadProperty(_expression) => {
Err(ParseError::ObjectPatternWithNonFinalRest.into())
}
}
}
/// Refine an instance of "*PropertyDefinition* : `...`
/// *AssignmentExpression*" into a *BindingRestProperty*.
fn spread_expression_to_rest_binding(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, BindingIdentifier>> {
Ok(match expression.unbox() {
Expression::IdentifierExpression(IdentifierExpression { name, loc }) => {
self.alloc_with(|| BindingIdentifier { name, loc })
}
_ => {
return Err(ParseError::ObjectBindingPatternWithInvalidRest.into());
}
})
}
fn pop_trailing_spread_property(
&self,
properties: &mut arena::Vec<'alloc, arena::Box<'alloc, ObjectProperty<'alloc>>>,
) -> Option<arena::Box<'alloc, Expression<'alloc>>> {
// Check whether we want to pop a PropertyDefinition
match properties.last().map(|boxed| &**boxed) {
Some(ObjectProperty::SpreadProperty(_)) => {}
_ => return None,
}
// We do.
match properties.pop().unwrap().unbox() {
ObjectProperty::SpreadProperty(expression) => Some(expression),
_ => panic!("bug"), // can't happen: we just checked this above
}
}
/// Refine an *ObjectLiteral* into an *ObjectBindingPattern*.
fn object_expression_to_object_binding(
&self,
object: ObjectExpression<'alloc>,
) -> Result<'alloc, ObjectBinding<'alloc>> {
let mut properties = object.properties;
let loc = object.loc;
let rest = self.pop_trailing_spread_property(&mut properties);
Ok(ObjectBinding {
properties: self.collect_vec_from_results(
properties
.into_iter()
.map(|prop| self.object_property_to_binding_property(prop.unbox())),
)?,
rest: rest
.map(|expression| self.spread_expression_to_rest_binding(expression))
.transpose()?,
loc,
})
}
fn array_elements_to_parameters(
&self,
elements: arena::Vec<'alloc, ArrayExpressionElement<'alloc>>,
) -> Result<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>> {
self.collect_vec_from_results(elements.into_iter().map(|element| match element {
ArrayExpressionElement::Expression(expr) =>
Ok(Some(self.expression_to_parameter(expr.unbox())?)),
ArrayExpressionElement::SpreadElement(_expr) =>
// ([...a, b]) => {}
Err(ParseError::ArrayPatternWithNonFinalRest.into()),
ArrayExpressionElement::Elision { .. } => Ok(None),
}))
}
fn pop_trailing_spread_element(
&self,
elements: &mut arena::Vec<'alloc, ArrayExpressionElement<'alloc>>,
) -> Option<arena::Box<'alloc, Expression<'alloc>>> {
// Check whether we want to pop an element.
match elements.last() {
Some(ArrayExpressionElement::SpreadElement(_)) => {}
_ => return None,
}
// We do.
match elements.pop() {
Some(ArrayExpressionElement::SpreadElement(expression)) => Some(expression),
_ => panic!("bug"), // can't happen: we just checked this above
}
}
fn expression_to_binding_no_default(
&self,
expression: Expression<'alloc>,
) -> Result<'alloc, Binding<'alloc>> {
match expression {
Expression::IdentifierExpression(IdentifierExpression { name, loc }) => {
Ok(Binding::BindingIdentifier(BindingIdentifier { name, loc }))
}
Expression::ArrayExpression(ArrayExpression { mut elements, loc }) => {
let rest = self.pop_trailing_spread_element(&mut elements);
let elements = self.array_elements_to_parameters(elements)?;
let rest = rest
.map(|expr| match self.expression_to_parameter(expr.unbox())? {
Parameter::Binding(b) => Ok(self.alloc_with(|| b)),
Parameter::BindingWithDefault(_) => {
let err: BoxedParseError =
ParseError::ArrayBindingPatternWithInvalidRest.into();
Err(err)
}
})
.transpose()?;
Ok(Binding::BindingPattern(BindingPattern::ArrayBinding(
ArrayBinding {
elements,
rest,
loc,
},
)))
}
Expression::ObjectExpression(object) => Ok(Binding::BindingPattern(
BindingPattern::ObjectBinding(self.object_expression_to_object_binding(object)?),
)),
_ => Err(ParseError::InvalidParameter.into()),
}
}
fn expression_to_parameter(
&self,
expression: Expression<'alloc>,
) -> Result<'alloc, Parameter<'alloc>> {
match expression {
Expression::AssignmentExpression {
binding,
expression,
loc,
} => Ok(Parameter::BindingWithDefault(BindingWithDefault {
binding: self.assignment_target_to_binding(binding)?,
init: expression,
loc,
})),
other => Ok(Parameter::Binding(
self.expression_to_binding_no_default(other)?,
)),
}
}
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `,` `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `,` `...` BindingIdentifier `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `,` `...` BindingPattern `)`
pub fn expression_to_parameter_list(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Vec<'alloc, Parameter<'alloc>>> {
// When the production
// *ArrowParameters* `:` *CoverParenthesizedExpressionAndArrowParameterList*
// is recognized the following grammar is used to refine the
// interpretation of
// *CoverParenthesizedExpressionAndArrowParameterList*:
//
// ArrowFormalParameters[Yield, Await]:
// `(` UniqueFormalParameters[?Yield, ?Await] `)`
match expression.unbox() {
Expression::BinaryExpression {
operator: BinaryOperator::Comma { .. },
left,
right,
..
} => {
let mut parameters = self.expression_to_parameter_list(left)?;
self.push(
&mut parameters,
self.expression_to_parameter(right.unbox())?,
);
Ok(parameters)
}
other => Ok(self.new_vec_single(self.expression_to_parameter(other)?)),
}
}
/// Used to convert `async(x, y, ...z)` from a *CallExpression* to async
/// arrow function parameters.
fn arguments_to_parameter_list(
&self,
arguments: Arguments<'alloc>,
) -> Result<'alloc, arena::Box<'alloc, FormalParameters<'alloc>>> {
let loc = arguments.loc;
let mut items = self.new_vec();
let mut rest: Option<Binding<'alloc>> = None;
for arg in arguments.args {
if rest.is_some() {
return Err(ParseError::ArrowParametersWithNonFinalRest.into());
}
match arg {
Argument::Expression(expr) => {
self.push(&mut items, self.expression_to_parameter(expr.unbox())?);
}
Argument::SpreadElement(spread_expr) => {
rest = Some(self.expression_to_binding_no_default(spread_expr.unbox())?);
}
}
}
Ok(self.alloc_with(|| FormalParameters { items, rest, loc }))
}
// CoverParenthesizedExpressionAndArrowParameterList : `(` `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` `...` BindingIdentifier `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` `...` BindingPattern `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `,` `...` BindingIdentifier `)`
// CoverParenthesizedExpressionAndArrowParameterList : `(` Expression `,` `...` BindingPattern `)`
pub fn cover_arrow_parameter_list(
&self,
open_token: arena::Box<'alloc, Token>,
parameters: arena::Vec<'alloc, Parameter<'alloc>>,
rest: Option<arena::Box<'alloc, Binding<'alloc>>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, CoverParenthesized<'alloc>> {
self.alloc_with(|| {
CoverParenthesized::Parameters(self.alloc_with(|| FormalParameters {
items: parameters,
rest: rest.map(|boxed| boxed.unbox()),
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
}))
})
}
// Literal : NullLiteral
pub fn null_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let loc = token.loc;
self.alloc_with(|| Expression::LiteralNullExpression { loc })
}
// Literal : BooleanLiteral
pub fn boolean_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let loc = token.loc;
let s = token.value.as_atom();
assert!(
s == CommonSourceAtomSetIndices::true_() || s == CommonSourceAtomSetIndices::false_()
);
self.alloc_with(|| Expression::LiteralBooleanExpression {
value: s == CommonSourceAtomSetIndices::true_(),
loc,
})
}
fn numeric_literal_value(token: arena::Box<'alloc, Token>) -> f64 {
token.unbox().value.as_number()
}
// Literal : NumericLiteral
pub fn numeric_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let loc = token.loc;
Ok(self.alloc_with(|| {
Expression::LiteralNumericExpression(NumericLiteral {
value: Self::numeric_literal_value(token),
loc,
})
}))
}
// Literal : NumericLiteral
//
// where NumericLiteral is either:
// * DecimalBigIntegerLiteral
// * NonDecimalIntegerLiteralBigIntLiteralSuffix
pub fn bigint_literal(
&self,
_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
Err(ParseError::NotImplemented("BigInt").into())
}
// Literal : StringLiteral
pub fn string_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let loc = token.loc;
// Hack: Prevent emission for scripts with "use strict"
// directive.
let value = token.value.as_atom();
if value == CommonSourceAtomSetIndices::use_strict() {
return Err(ParseError::NotImplemented("use strict directive").into());
}
Ok(self.alloc_with(|| Expression::LiteralStringExpression { value, loc }))
}
// ArrayLiteral : `[` Elision? `]`
pub fn array_literal_empty(
&self,
open_token: arena::Box<'alloc, Token>,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.alloc_with(|| {
Expression::ArrayExpression(match elision {
None => ArrayExpression {
elements: self.new_vec(),
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
},
Some(mut array) => {
array.loc.set_range(open_token.loc, close_token.loc);
array.unbox()
}
})
})
}
// ArrayLiteral : `[` ElementList `]`
pub fn array_literal(
&self,
open_token: arena::Box<'alloc, Token>,
mut array: arena::Box<'alloc, ArrayExpression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
array.loc.set_range(open_token.loc, close_token.loc);
self.alloc_with(|| Expression::ArrayExpression(array.unbox()))
}
// ArrayLiteral : `[` ElementList `,` Elision? `]`
pub fn array_literal_with_trailing_elision(
&self,
open_token: arena::Box<'alloc, Token>,
mut array: arena::Box<'alloc, ArrayExpression<'alloc>>,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
if let Some(mut more) = elision {
self.append(&mut array.elements, &mut more.elements);
}
array.loc.set_range(open_token.loc, close_token.loc);
self.alloc_with(|| Expression::ArrayExpression(array.unbox()))
}
// ElementList : Elision? AssignmentExpression
pub fn element_list_first(
&self,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
element: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
let mut array = elision.unwrap_or_else(|| {
self.alloc_with(|| ArrayExpression {
elements: self.new_vec(),
// This will be overwritten once the enclosing array gets
// parsed.
loc: SourceLocation::default(),
})
});
self.push(
&mut array.elements,
ArrayExpressionElement::Expression(element),
);
array
}
// ElementList : Elision? SpreadElement
pub fn element_list_first_spread(
&self,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
spread_element: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
let mut array = elision.unwrap_or_else(|| {
self.alloc_with(|| ArrayExpression {
elements: self.new_vec(),
// This will be overwritten once the enclosing array gets
// parsed.
loc: SourceLocation::default(),
})
});
self.push(
&mut array.elements,
ArrayExpressionElement::SpreadElement(spread_element),
);
array
}
// ElementList : ElementList `,` Elision? AssignmentExpression
pub fn element_list_append(
&self,
mut array: arena::Box<'alloc, ArrayExpression<'alloc>>,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
element: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
if let Some(mut elision) = elision {
self.append(&mut array.elements, &mut elision.elements);
}
self.push(
&mut array.elements,
ArrayExpressionElement::Expression(element),
);
array
}
// ElementList : ElementList `,` Elision? SpreadElement
pub fn element_list_append_spread(
&self,
mut array: arena::Box<'alloc, ArrayExpression<'alloc>>,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
spread_element: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
if let Some(mut elision) = elision {
self.append(&mut array.elements, &mut elision.elements);
}
self.push(
&mut array.elements,
ArrayExpressionElement::SpreadElement(spread_element),
);
array
}
// Elision : `,`
pub fn elision_single(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
let loc = token.loc;
self.alloc_with(|| ArrayExpression {
elements: self.new_vec_single(ArrayExpressionElement::Elision { loc }),
// This will be overwritten once the enclosing array gets parsed.
loc: SourceLocation::default(),
})
}
// Elision : Elision `,`
pub fn elision_append(
&self,
mut array: arena::Box<'alloc, ArrayExpression<'alloc>>,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, ArrayExpression<'alloc>> {
let loc = token.loc;
self.push(&mut array.elements, ArrayExpressionElement::Elision { loc });
array
}
// SpreadElement : `...` AssignmentExpression
pub fn spread_element(
&self,
expr: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
expr
}
// ObjectLiteral : `{` `}`
pub fn object_literal_empty(
&self,
open_token: arena::Box<'alloc, Token>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.alloc_with(|| {
Expression::ObjectExpression(ObjectExpression {
properties: self.new_vec(),
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
})
})
}
// ObjectLiteral : `{` PropertyDefinitionList `}`
// ObjectLiteral : `{` PropertyDefinitionList `,` `}`
pub fn object_literal(
&self,
open_token: arena::Box<'alloc, Token>,
mut object: arena::Box<'alloc, ObjectExpression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
object.loc.set_range(open_token.loc, close_token.loc);
self.alloc_with(|| Expression::ObjectExpression(object.unbox()))
}
// PropertyDefinitionList : PropertyDefinition
pub fn property_definition_list_single(
&self,
property: arena::Box<'alloc, ObjectProperty<'alloc>>,
) -> arena::Box<'alloc, ObjectExpression<'alloc>> {
self.alloc_with(|| ObjectExpression {
properties: self.new_vec_single(property),
// This will be overwritten once the enclosing object gets parsed.
loc: SourceLocation::default(),
})
}
// PropertyDefinitionList : PropertyDefinitionList `,` PropertyDefinition
pub fn property_definition_list_append(
&self,
mut object: arena::Box<'alloc, ObjectExpression<'alloc>>,
property: arena::Box<'alloc, ObjectProperty<'alloc>>,
) -> arena::Box<'alloc, ObjectExpression<'alloc>> {
self.push(&mut object.properties, property);
object
}
// PropertyDefinition : IdentifierReference
pub fn shorthand_property(
&self,
name: arena::Box<'alloc, Identifier>,
) -> arena::Box<'alloc, ObjectProperty<'alloc>> {
let loc = name.loc;
self.alloc_with(|| {
ObjectProperty::ShorthandProperty(ShorthandProperty {
name: IdentifierExpression {
name: name.unbox(),
loc,
},
loc,
})
})
}
// PropertyDefinition : PropertyName `:` AssignmentExpression
pub fn property_definition(
&self,
name: arena::Box<'alloc, PropertyName<'alloc>>,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ObjectProperty<'alloc>> {
let name_loc = name.get_loc();
let expression_loc = expression.get_loc();
self.alloc_with(|| {
ObjectProperty::NamedObjectProperty(NamedObjectProperty::DataProperty(DataProperty {
property_name: name.unbox(),
expression,
loc: SourceLocation::from_parts(name_loc, expression_loc),
}))
})
}
// PropertyDefinition : MethodDefinition
pub fn property_definition_method(
&self,
method: arena::Box<'alloc, MethodDefinition<'alloc>>,
) -> arena::Box<'alloc, ObjectProperty<'alloc>> {
self.alloc_with(|| {
ObjectProperty::NamedObjectProperty(NamedObjectProperty::MethodDefinition(
method.unbox(),
))
})
}
// PropertyDefinition : `...` AssignmentExpression
pub fn property_definition_spread(
&self,
spread: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ObjectProperty<'alloc>> {
self.alloc_with(|| ObjectProperty::SpreadProperty(spread))
}
// LiteralPropertyName : IdentifierName
pub fn property_name_identifier(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, PropertyName<'alloc>>> {
let value = token.value.as_atom();
if value == CommonSourceAtomSetIndices::__proto__() {
return Err(ParseError::NotImplemented("__proto__ as property name").into());
}
let loc = token.loc;
Ok(self.alloc_with(|| PropertyName::StaticPropertyName(StaticPropertyName { value, loc })))
}
// LiteralPropertyName : StringLiteral
pub fn property_name_string(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, PropertyName<'alloc>>> {
let value = token.value.as_atom();
if value == CommonSourceAtomSetIndices::__proto__() {
return Err(ParseError::NotImplemented("__proto__ as property name").into());
}
let loc = token.loc;
Ok(self.alloc_with(|| PropertyName::StaticPropertyName(StaticPropertyName { value, loc })))
}
// LiteralPropertyName : NumericLiteral
pub fn property_name_numeric(
&self,
token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, PropertyName<'alloc>>> {
let loc = token.loc;
let value = Self::numeric_literal_value(token);
Ok(self
.alloc_with(|| PropertyName::StaticNumericPropertyName(NumericLiteral { value, loc })))
}
// LiteralPropertyName : NumericLiteral
//
// where NumericLiteral is either:
// * DecimalBigIntegerLiteral
// * NonDecimalIntegerLiteralBigIntLiteralSuffix
pub fn property_name_bigint(
&self,
_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, PropertyName<'alloc>>> {
Err(ParseError::NotImplemented("BigInt").into())
}
// ComputedPropertyName : `[` AssignmentExpression `]`
pub fn computed_property_name(
&self,
open_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, PropertyName<'alloc>> {
self.alloc_with(|| {
PropertyName::ComputedPropertyName(ComputedPropertyName {
expression,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
})
})
}
// CoverInitializedName : IdentifierReference Initializer
pub fn cover_initialized_name(
&self,
_name: arena::Box<'alloc, Identifier>,
_initializer: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, ObjectProperty<'alloc>>> {
// Awkward. This needs to be stored somehow until we reach an enclosing
// context where it can be reinterpreted as a default value in an
// object destructuring assignment pattern.
Err(ParseError::NotImplemented("default initializers in object patterns").into())
}
// TemplateLiteral : NoSubstitutionTemplate
pub fn template_literal(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, TemplateExpression<'alloc>> {
let loc = token.loc;
self.alloc_with(|| TemplateExpression {
tag: None,
elements: self.new_vec_single(TemplateExpressionElement::TemplateElement(
TemplateElement {
raw_value: token.value.as_atom(),
loc,
},
)),
loc,
})
}
// SubstitutionTemplate : TemplateHead Expression TemplateSpans
pub fn substitution_template(
&self,
_head: arena::Box<'alloc, Token>,
_expression: arena::Box<'alloc, Expression<'alloc>>,
_spans: arena::Box<'alloc, Void>,
) -> Result<'alloc, arena::Box<'alloc, TemplateExpression<'alloc>>> {
Err(ParseError::NotImplemented("template strings").into())
}
// TemplateSpans : TemplateTail
// TemplateSpans : TemplateMiddleList TemplateTail
pub fn template_spans(
&self,
_middle_list: Option<arena::Box<'alloc, Void>>,
_tail: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("template strings").into())
}
// TemplateMiddleList : TemplateMiddle Expression
pub fn template_middle_list_single(
&self,
_middle: arena::Box<'alloc, Token>,
_expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("template strings").into())
}
// TemplateMiddleList : TemplateMiddleList TemplateMiddle Expression
pub fn template_middle_list_append(
&self,
_middle_list: arena::Box<'alloc, Void>,
_middle: arena::Box<'alloc, Token>,
_expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("template strings").into())
}
// MemberExpression : MemberExpression `[` Expression `]`
// CallExpression : CallExpression `[` Expression `]`
pub fn computed_member_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let object_loc = object.get_loc();
self.alloc_with(|| {
Expression::MemberExpression(MemberExpression::ComputedMemberExpression(
ComputedMemberExpression {
object: ExpressionOrSuper::Expression(object),
expression,
loc: SourceLocation::from_parts(object_loc, close_token.loc),
},
))
})
}
// OptionalExpression : MemberExpression OptionalChain
// OptionalExpression : CallExpression OptionalChain
// OptionalExpression : OptionalExpression OptionalChain
pub fn optional_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
tail: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let object_loc = object.get_loc();
let expression_loc = tail.get_loc();
self.alloc_with(|| Expression::OptionalExpression {
object: ExpressionOrSuper::Expression(object),
tail,
loc: SourceLocation::from_parts(object_loc, expression_loc),
})
}
// OptionalChain : `?.` `[` Expression `]`
pub fn optional_computed_member_expr_tail(
&self,
start_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::ComputedMemberExpressionTail {
expression,
loc: SourceLocation::from_parts(start_token.loc, close_token.loc),
})
})
}
// OptionalChain : `?.` Expression
pub fn optional_static_member_expr_tail(
&self,
start_token: arena::Box<'alloc, Token>,
identifier_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let identifier_token_loc = identifier_token.loc;
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::StaticMemberExpressionTail {
property: self.identifier_name(identifier_token),
loc: SourceLocation::from_parts(start_token.loc, identifier_token_loc),
})
})
}
// OptionalChain : `?.` PrivateIdentifier
pub fn optional_private_field_member_expr_tail(
&self,
start_token: arena::Box<'alloc, Token>,
private_identifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let private_identifier_loc = private_identifier.loc;
let field = self.private_identifier(private_identifier)?;
Ok(self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::PrivateFieldExpressionTail {
field,
loc: SourceLocation::from_parts(start_token.loc, private_identifier_loc),
})
}))
}
// OptionalChain : `?.` Arguments
pub fn optional_call_expr_tail(
&self,
start_token: arena::Box<'alloc, Token>,
arguments: arena::Box<'alloc, Arguments<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let arguments_loc = arguments.loc;
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::CallExpressionTail {
arguments: arguments.unbox(),
loc: SourceLocation::from_parts(start_token.loc, arguments_loc),
})
})
}
// OptionalChain : `?.` TemplateLiteral
pub fn error_optional_chain_with_template(
&self,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
Err(ParseError::IllegalCharacter('`').into())
}
// OptionalChain : OptionalChain `[` Expression `]`
pub fn optional_computed_member_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let object_loc = object.get_loc();
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::ComputedMemberExpression(
ComputedMemberExpression {
object: ExpressionOrSuper::Expression(object),
expression,
loc: SourceLocation::from_parts(object_loc, close_token.loc),
},
))
})
}
// OptionalChain : OptionalChain `.` Expression
pub fn optional_static_member_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
identifier_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let object_loc = object.get_loc();
let identifier_token_loc = identifier_token.loc;
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::StaticMemberExpression(
StaticMemberExpression {
object: ExpressionOrSuper::Expression(object),
property: self.identifier_name(identifier_token),
loc: SourceLocation::from_parts(object_loc, identifier_token_loc),
},
))
})
}
// OptionalChain : OptionalChain `.` PrivateIdentifier
pub fn optional_private_field_member_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
private_identifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let object_loc = object.get_loc();
let private_identifier_loc = private_identifier.loc;
let field = self.private_identifier(private_identifier)?;
Ok(self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::PrivateFieldExpression(
PrivateFieldExpression {
object: ExpressionOrSuper::Expression(object),
field,
loc: SourceLocation::from_parts(object_loc, private_identifier_loc),
},
))
}))
}
// OptionalChain : OptionalChain Arguments
pub fn optional_call_expr(
&self,
callee: arena::Box<'alloc, Expression<'alloc>>,
arguments: arena::Box<'alloc, Arguments<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let callee_loc = callee.get_loc();
let arguments_loc = arguments.loc;
self.alloc_with(|| {
Expression::OptionalChain(OptionalChain::CallExpression(CallExpression {
callee: ExpressionOrSuper::Expression(callee),
arguments: arguments.unbox(),
loc: SourceLocation::from_parts(callee_loc, arguments_loc),
}))
})
}
fn identifier(&self, token: arena::Box<'alloc, Token>) -> Identifier {
Identifier {
value: token.value.as_atom(),
loc: token.loc,
}
}
fn identifier_name(&self, token: arena::Box<'alloc, Token>) -> IdentifierName {
IdentifierName {
value: token.value.as_atom(),
loc: token.loc,
}
}
fn private_identifier(
&self,
_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, PrivateIdentifier> {
Err(ParseError::NotImplemented(
"private fields depends on shell switch, that is not supported",
)
.into())
/*
PrivateIdentifier {
value: token.value.as_atom(),
loc: token.loc,
}
*/
}
// MemberExpression : MemberExpression `.` IdentifierName
// CallExpression : CallExpression `.` IdentifierName
pub fn static_member_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
identifier_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let object_loc = object.get_loc();
let identifier_token_loc = identifier_token.loc;
self.alloc_with(|| {
Expression::MemberExpression(MemberExpression::StaticMemberExpression(
StaticMemberExpression {
object: ExpressionOrSuper::Expression(object),
property: self.identifier_name(identifier_token),
loc: SourceLocation::from_parts(object_loc, identifier_token_loc),
},
))
})
}
// MemberExpression : MemberExpression TemplateLiteral
// CallExpression : CallExpression TemplateLiteral
pub fn tagged_template_expr(
&self,
tag: arena::Box<'alloc, Expression<'alloc>>,
mut template_literal: arena::Box<'alloc, TemplateExpression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
template_literal.tag = Some(tag);
self.alloc_with(|| Expression::TemplateExpression(template_literal.unbox()))
}
// MemberExpression : `new` MemberExpression Arguments
pub fn new_expr_with_arguments(
&self,
new_token: arena::Box<'alloc, Token>,
callee: arena::Box<'alloc, Expression<'alloc>>,
arguments: arena::Box<'alloc, Arguments<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let arguments_loc = arguments.loc;
self.alloc_with(|| Expression::NewExpression {
callee,
arguments: arguments.unbox(),
loc: SourceLocation::from_parts(new_token.loc, arguments_loc),
})
}
// MemberExpression : MemberExpression `.` PrivateIdentifier
pub fn private_field_expr(
&self,
object: arena::Box<'alloc, Expression<'alloc>>,
private_identifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let object_loc = object.get_loc();
let field_loc = private_identifier.loc;
let field = self.private_identifier(private_identifier)?;
Ok(self.alloc_with(|| {
Expression::MemberExpression(MemberExpression::PrivateFieldExpression(
PrivateFieldExpression {
object: ExpressionOrSuper::Expression(object),
field,
loc: SourceLocation::from_parts(object_loc, field_loc),
},
))
}))
}
// SuperProperty : `super` `[` Expression `]`
pub fn super_property_computed(
&self,
super_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
self.check_super()?;
let super_loc = super_token.loc;
Ok(self.alloc_with(|| {
Expression::MemberExpression(MemberExpression::ComputedMemberExpression(
ComputedMemberExpression {
object: ExpressionOrSuper::Super { loc: super_loc },
expression: expression,
loc: SourceLocation::from_parts(super_loc, close_token.loc),
},
))
}))
}
// SuperProperty : `super` `.` IdentifierName
pub fn super_property_static(
&self,
super_token: arena::Box<'alloc, Token>,
identifier_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
self.check_super()?;
let super_loc = super_token.loc;
let identifier_loc = identifier_token.loc;
Ok(self.alloc_with(|| {
Expression::MemberExpression(MemberExpression::StaticMemberExpression(
StaticMemberExpression {
object: ExpressionOrSuper::Super { loc: super_loc },
property: self.identifier_name(identifier_token),
loc: SourceLocation::from_parts(super_loc, identifier_loc),
},
))
}))
}
// NewTarget : `new` `.` `target`
pub fn new_target_expr(
&self,
new_token: arena::Box<'alloc, Token>,
target_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
return self.alloc_with(|| Expression::NewTargetExpression {
loc: SourceLocation::from_parts(new_token.loc, target_token.loc),
});
}
// NewExpression : `new` NewExpression
pub fn new_expr_without_arguments(
&self,
new_token: arena::Box<'alloc, Token>,
callee: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let callee_loc = callee.get_loc();
self.alloc_with(|| Expression::NewExpression {
callee,
arguments: Arguments {
args: self.new_vec(),
loc: SourceLocation::new(callee_loc.end, callee_loc.end),
},
loc: SourceLocation::from_parts(new_token.loc, callee_loc),
})
}
// CallExpression : CallExpression Arguments
// CoverCallExpressionAndAsyncArrowHead : MemberExpression Arguments
// CallMemberExpression : MemberExpression Arguments
pub fn call_expr(
&self,
callee: arena::Box<'alloc, Expression<'alloc>>,
arguments: arena::Box<'alloc, Arguments<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let callee_loc = callee.get_loc();
let arguments_loc = arguments.loc;
self.alloc_with(|| {
Expression::CallExpression(CallExpression {
callee: ExpressionOrSuper::Expression(callee),
arguments: arguments.unbox(),
loc: SourceLocation::from_parts(callee_loc, arguments_loc),
})
})
}
// SuperCall : `super` Arguments
pub fn super_call(
&self,
super_token: arena::Box<'alloc, Token>,
arguments: arena::Box<'alloc, Arguments<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
self.check_super()?;
let super_loc = super_token.loc;
let arguments_loc = arguments.loc;
Ok(self.alloc_with(|| {
Expression::CallExpression(CallExpression {
callee: ExpressionOrSuper::Super { loc: super_loc },
arguments: arguments.unbox(),
loc: SourceLocation::from_parts(super_loc, arguments_loc),
})
}))
}
// ImportCall : `import` `(` AssignmentExpression `)`
pub fn import_call(
&self,
import_token: arena::Box<'alloc, Token>,
argument: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.alloc_with(|| Expression::ImportCallExpression {
argument,
loc: SourceLocation::from_parts(import_token.loc, close_token.loc),
})
}
// Arguments : `(` `)`
pub fn arguments_empty(
&self,
open_token: arena::Box<'alloc, Token>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
self.alloc_with(|| Arguments {
args: self.new_vec(),
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
})
}
pub fn arguments_single(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
self.alloc_with(|| Arguments {
args: self.new_vec_single(Argument::Expression(expression)),
// This will be overwritten once the enclosing arguments gets
// parsed.
loc: SourceLocation::default(),
})
}
pub fn arguments_spread_single(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
self.alloc_with(|| Arguments {
args: self.new_vec_single(Argument::SpreadElement(expression)),
// This will be overwritten once the enclosing arguments gets
// parsed.
loc: SourceLocation::default(),
})
}
pub fn arguments(
&self,
open_token: arena::Box<'alloc, Token>,
mut arguments: arena::Box<'alloc, Arguments<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
arguments.loc.set_range(open_token.loc, close_token.loc);
arguments
}
// ArgumentList : AssignmentExpression
// ArgumentList : ArgumentList `,` AssignmentExpression
pub fn arguments_append(
&self,
mut arguments: arena::Box<'alloc, Arguments<'alloc>>,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
self.push(&mut arguments.args, Argument::Expression(expression));
arguments
}
// ArgumentList : `...` AssignmentExpression
// ArgumentList : ArgumentList `,` `...` AssignmentExpression
pub fn arguments_append_spread(
&self,
mut arguments: arena::Box<'alloc, Arguments<'alloc>>,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Arguments<'alloc>> {
self.push(&mut arguments.args, Argument::SpreadElement(expression));
arguments
}
// UpdateExpression : LeftHandSideExpression `++`
pub fn post_increment_expr(
&self,
operand: arena::Box<'alloc, Expression<'alloc>>,
operator_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let operand = self.expression_to_simple_assignment_target(operand)?;
let operand_loc = operand.get_loc();
Ok(self.alloc_with(|| Expression::UpdateExpression {
is_prefix: false,
operator: UpdateOperator::Increment {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operand_loc, operator_token.loc),
}))
}
// UpdateExpression : LeftHandSideExpression `--`
pub fn post_decrement_expr(
&self,
operand: arena::Box<'alloc, Expression<'alloc>>,
operator_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let operand = self.expression_to_simple_assignment_target(operand)?;
let operand_loc = operand.get_loc();
Ok(self.alloc_with(|| Expression::UpdateExpression {
is_prefix: false,
operator: UpdateOperator::Decrement {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operand_loc, operator_token.loc),
}))
}
// UpdateExpression : `++` UnaryExpression
pub fn pre_increment_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let operand = self.expression_to_simple_assignment_target(operand)?;
let operand_loc = operand.get_loc();
Ok(self.alloc_with(|| Expression::UpdateExpression {
is_prefix: true,
operator: UpdateOperator::Increment {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
}))
}
// UpdateExpression : `--` UnaryExpression
pub fn pre_decrement_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let operand = self.expression_to_simple_assignment_target(operand)?;
let operand_loc = operand.get_loc();
Ok(self.alloc_with(|| Expression::UpdateExpression {
is_prefix: true,
operator: UpdateOperator::Decrement {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
}))
}
// UnaryExpression : `delete` UnaryExpression
pub fn delete_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::Delete {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `void` UnaryExpression
pub fn void_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::Void {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `typeof` UnaryExpression
pub fn typeof_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::Typeof {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `+` UnaryExpression
pub fn unary_plus_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::Plus {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `-` UnaryExpression
pub fn unary_minus_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::Minus {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `~` UnaryExpression
pub fn bitwise_not_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::BitwiseNot {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
// UnaryExpression : `!` UnaryExpression
pub fn logical_not_expr(
&self,
operator_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::UnaryExpression {
operator: UnaryOperator::LogicalNot {
loc: operator_token.loc,
},
operand,
loc: SourceLocation::from_parts(operator_token.loc, operand_loc),
})
}
pub fn equals_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Equals { loc: token.loc }
}
pub fn not_equals_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::NotEquals { loc: token.loc }
}
pub fn strict_equals_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::StrictEquals { loc: token.loc }
}
pub fn strict_not_equals_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::StrictNotEquals { loc: token.loc }
}
pub fn less_than_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::LessThan { loc: token.loc }
}
pub fn less_than_or_equal_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::LessThanOrEqual { loc: token.loc }
}
pub fn greater_than_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::GreaterThan { loc: token.loc }
}
pub fn greater_than_or_equal_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::GreaterThanOrEqual { loc: token.loc }
}
pub fn in_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::In { loc: token.loc }
}
pub fn instanceof_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Instanceof { loc: token.loc }
}
pub fn left_shift_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::LeftShift { loc: token.loc }
}
pub fn right_shift_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::RightShift { loc: token.loc }
}
pub fn right_shift_ext_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::RightShiftExt { loc: token.loc }
}
pub fn add_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Add { loc: token.loc }
}
pub fn sub_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Sub { loc: token.loc }
}
pub fn mul_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Mul { loc: token.loc }
}
pub fn div_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Div { loc: token.loc }
}
pub fn mod_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Mod { loc: token.loc }
}
pub fn pow_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Pow { loc: token.loc }
}
pub fn comma_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Comma { loc: token.loc }
}
pub fn coalesce_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::Coalesce { loc: token.loc }
}
pub fn logical_or_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::LogicalOr { loc: token.loc }
}
pub fn logical_and_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::LogicalAnd { loc: token.loc }
}
pub fn bitwise_or_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::BitwiseOr { loc: token.loc }
}
pub fn bitwise_xor_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::BitwiseXor { loc: token.loc }
}
pub fn bitwise_and_op(&self, token: arena::Box<'alloc, Token>) -> BinaryOperator {
BinaryOperator::BitwiseAnd { loc: token.loc }
}
// Due to limitations of the current parser generator,
// MultiplicativeOperators and CompoundAssignmentOperators currently get
// boxed.
pub fn box_op(&self, op: BinaryOperator) -> arena::Box<'alloc, BinaryOperator> {
self.alloc_with(|| op)
}
// MultiplicativeExpression : MultiplicativeExpression MultiplicativeOperator ExponentiationExpression
pub fn multiplicative_expr(
&self,
left: arena::Box<'alloc, Expression<'alloc>>,
operator: arena::Box<'alloc, BinaryOperator>,
right: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
self.binary_expr(operator.unbox(), left, right)
}
// ExponentiationExpression : UpdateExpression `**` ExponentiationExpression
// AdditiveExpression : AdditiveExpression `+` MultiplicativeExpression
// AdditiveExpression : AdditiveExpression `-` MultiplicativeExpression
// ShiftExpression : ShiftExpression `<<` AdditiveExpression
// ShiftExpression : ShiftExpression `>>` AdditiveExpression
// ShiftExpression : ShiftExpression `>>>` AdditiveExpression
// RelationalExpression : RelationalExpression `<` ShiftExpression
// RelationalExpression : RelationalExpression `>` ShiftExpression
// RelationalExpression : RelationalExpression `<=` ShiftExpression
// RelationalExpression : RelationalExpression `>=` ShiftExpression
// RelationalExpression : RelationalExpression `instanceof` ShiftExpression
// RelationalExpression : [+In] RelationalExpression `in` ShiftExpression
// EqualityExpression : EqualityExpression `==` RelationalExpression
// EqualityExpression : EqualityExpression `!=` RelationalExpression
// EqualityExpression : EqualityExpression `===` RelationalExpression
// EqualityExpression : EqualityExpression `!==` RelationalExpression
// BitwiseANDExpression : BitwiseANDExpression `&` EqualityExpression
// BitwiseXORExpression : BitwiseXORExpression `^` BitwiseANDExpression
// BitwiseORExpression : BitwiseORExpression `|` BitwiseXORExpression
// LogicalANDExpression : LogicalANDExpression `&&` BitwiseORExpression
// LogicalORExpression : LogicalORExpression `||` LogicalANDExpression
pub fn binary_expr(
&self,
operator: BinaryOperator,
left: arena::Box<'alloc, Expression<'alloc>>,
right: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let left_loc = left.get_loc();
let right_loc = right.get_loc();
self.alloc_with(|| Expression::BinaryExpression {
operator,
left,
right,
loc: SourceLocation::from_parts(left_loc, right_loc),
})
}
// ConditionalExpression : LogicalORExpression `?` AssignmentExpression `:` AssignmentExpression
pub fn conditional_expr(
&self,
test: arena::Box<'alloc, Expression<'alloc>>,
consequent: arena::Box<'alloc, Expression<'alloc>>,
alternate: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let test_loc = test.get_loc();
let alternate_loc = alternate.get_loc();
self.alloc_with(|| Expression::ConditionalExpression {
test,
consequent,
alternate,
loc: SourceLocation::from_parts(test_loc, alternate_loc),
})
}
/// Refine an *ArrayLiteral* into an *ArrayAssignmentPattern*.
fn array_expression_to_array_assignment_target(
&self,
mut elements: arena::Vec<'alloc, ArrayExpressionElement<'alloc>>,
loc: SourceLocation,
) -> Result<'alloc, ArrayAssignmentTarget<'alloc>> {
let spread = self.pop_trailing_spread_element(&mut elements);
let elements =
self.collect_vec_from_results(elements.into_iter().map(|element| match element {
ArrayExpressionElement::SpreadElement(_) => {
Err(ParseError::NotImplemented("rest destructuring in array pattern").into())
}
ArrayExpressionElement::Expression(expression) => Ok(Some(
self.expression_to_assignment_target_maybe_default(expression)?,
)),
ArrayExpressionElement::Elision { .. } => Ok(None),
}))?;
let rest: Option<Result<'alloc, arena::Box<'alloc, AssignmentTarget<'alloc>>>> =
spread.map(|expr| Ok(self.alloc(self.expression_to_assignment_target(expr)?)));
let rest = rest.transpose()?;
Ok(ArrayAssignmentTarget {
elements,
rest,
loc,
})
}
fn object_property_to_assignment_target_property(
&self,
property: arena::Box<'alloc, ObjectProperty<'alloc>>,
) -> Result<'alloc, AssignmentTargetProperty<'alloc>> {
Ok(match property.unbox() {
ObjectProperty::NamedObjectProperty(NamedObjectProperty::MethodDefinition(_)) => {
return Err(ParseError::ObjectPatternWithMethod.into())
}
ObjectProperty::NamedObjectProperty(NamedObjectProperty::DataProperty(
DataProperty {
property_name,
expression,
loc,
},
)) => AssignmentTargetProperty::AssignmentTargetPropertyProperty(
AssignmentTargetPropertyProperty {
name: property_name,
binding: self.expression_to_assignment_target_maybe_default(expression)?,
loc,
},
),
ObjectProperty::ShorthandProperty(ShorthandProperty {
name: IdentifierExpression { name, loc },
..
}) => {
// TODO - support CoverInitializedName
AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(
AssignmentTargetPropertyIdentifier {
binding: AssignmentTargetIdentifier { name, loc },
init: None,
loc,
},
)
}
ObjectProperty::SpreadProperty(_expression) => {
return Err(ParseError::ObjectPatternWithNonFinalRest.into())
}
})
}
// Refine an *ObjectLiteral* into an *ObjectAssignmentPattern*.
fn object_expression_to_object_assignment_target(
&self,
mut properties: arena::Vec<'alloc, arena::Box<'alloc, ObjectProperty<'alloc>>>,
loc: SourceLocation,
) -> Result<'alloc, ObjectAssignmentTarget<'alloc>> {
let spread = self.pop_trailing_spread_property(&mut properties);
let properties = self.collect_vec_from_results(
properties
.into_iter()
.map(|p| self.object_property_to_assignment_target_property(p)),
)?;
let rest: Option<Result<'alloc, arena::Box<'alloc, AssignmentTarget<'alloc>>>> =
spread.map(|expr| Ok(self.alloc(self.expression_to_assignment_target(expr)?)));
let rest = rest.transpose()?;
Ok(ObjectAssignmentTarget {
properties,
rest,
loc,
})
}
fn expression_to_assignment_target_maybe_default(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, AssignmentTargetMaybeDefault<'alloc>> {
Ok(match expression.unbox() {
Expression::AssignmentExpression {
binding,
expression,
loc,
} => AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(
AssignmentTargetWithDefault {
binding,
init: expression,
loc,
},
),
other => AssignmentTargetMaybeDefault::AssignmentTarget(
self.expression_to_assignment_target(self.alloc_with(|| other))?,
),
})
}
fn expression_to_assignment_target(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, AssignmentTarget<'alloc>> {
Ok(match expression.unbox() {
Expression::ArrayExpression(ArrayExpression { elements, loc }) => {
AssignmentTarget::AssignmentTargetPattern(
AssignmentTargetPattern::ArrayAssignmentTarget(
self.array_expression_to_array_assignment_target(elements, loc)?,
),
)
}
Expression::ObjectExpression(ObjectExpression { properties, loc }) => {
AssignmentTarget::AssignmentTargetPattern(
AssignmentTargetPattern::ObjectAssignmentTarget(
self.object_expression_to_object_assignment_target(properties, loc)?,
),
)
}
other => AssignmentTarget::SimpleAssignmentTarget(
self.expression_to_simple_assignment_target(self.alloc_with(|| other))?,
),
})
}
fn expression_to_simple_assignment_target(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, SimpleAssignmentTarget<'alloc>> {
Ok(match expression.unbox() {
// Static Semantics: AssignmentTargetType
Expression::IdentifierExpression(IdentifierExpression { name, loc }) => {
// IdentifierReference : Identifier
//
// 1. If this IdentifierReference is contained in strict mode
// code and StringValue of Identifier is "eval" or
// "arguments", return invalid.
if name.value == CommonSourceAtomSetIndices::arguments()
|| name.value == CommonSourceAtomSetIndices::eval()
{
if self.is_strict()? {
return Err(ParseError::InvalidAssignmentTarget.into());
}
}
// 2. Return simple.
//
// IdentifierReference : yield
//
// 1. Return simple.
//
// IdentifierReference : await
//
// 1. Return simple.
SimpleAssignmentTarget::AssignmentTargetIdentifier(AssignmentTargetIdentifier {
name,
loc,
})
}
// Static Semantics: AssignmentTargetType
//
// MemberExpression :
// MemberExpression [ Expression ]
// MemberExpression . IdentifierName
// SuperProperty
//
// 1. Return simple.
Expression::MemberExpression(MemberExpression::StaticMemberExpression(
StaticMemberExpression {
object,
property,
loc,
},
)) => SimpleAssignmentTarget::MemberAssignmentTarget(
MemberAssignmentTarget::StaticMemberAssignmentTarget(
StaticMemberAssignmentTarget {
object,
property,
loc,
},
),
),
Expression::MemberExpression(MemberExpression::ComputedMemberExpression(
ComputedMemberExpression {
object,
expression,
loc,
},
)) => SimpleAssignmentTarget::MemberAssignmentTarget(
MemberAssignmentTarget::ComputedMemberAssignmentTarget(
ComputedMemberAssignmentTarget {
object,
expression,
loc,
},
),
),
Expression::MemberExpression(MemberExpression::PrivateFieldExpression(
PrivateFieldExpression { object, field, loc },
)) => SimpleAssignmentTarget::MemberAssignmentTarget(
MemberAssignmentTarget::PrivateFieldAssignmentTarget(
PrivateFieldAssignmentTarget { object, field, loc },
),
),
// Static Semantics: AssignmentTargetType
//
// CallExpression :
// CallExpression [ Expression ]
// CallExpression . IdentifierName
//
// 1. Return simple.
Expression::CallExpression(CallExpression { .. }) => {
return Err(ParseError::NotImplemented(
"Assignment to CallExpression is allowed for non-strict mode.",
)
.into());
}
_ => {
return Err(ParseError::InvalidAssignmentTarget.into());
}
})
}
// AssignmentExpression : LeftHandSideExpression `=` AssignmentExpression
pub fn assignment_expr(
&self,
left_hand_side: arena::Box<'alloc, Expression<'alloc>>,
value: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let target = self.expression_to_assignment_target(left_hand_side)?;
let target_loc = target.get_loc();
let value_loc = value.get_loc();
Ok(self.alloc_with(|| Expression::AssignmentExpression {
binding: target,
expression: value,
loc: SourceLocation::from_parts(target_loc, value_loc),
}))
}
pub fn add_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Add { loc: token.loc }
}
pub fn sub_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Sub { loc: token.loc }
}
pub fn mul_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Mul { loc: token.loc }
}
pub fn div_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Div { loc: token.loc }
}
pub fn mod_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Mod { loc: token.loc }
}
pub fn pow_assign_op(&self, token: arena::Box<'alloc, Token>) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Pow { loc: token.loc }
}
pub fn left_shift_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::LeftShift { loc: token.loc }
}
pub fn right_shift_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::RightShift { loc: token.loc }
}
pub fn right_shift_ext_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::RightShiftExt { loc: token.loc }
}
pub fn bitwise_or_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Or { loc: token.loc }
}
pub fn bitwise_xor_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Xor { loc: token.loc }
}
pub fn bitwise_and_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::And { loc: token.loc }
}
pub fn logical_or_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::LogicalOr { loc: token.loc }
}
pub fn logical_and_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::LogicalAnd { loc: token.loc }
}
pub fn coalesce_assign_op(
&self,
token: arena::Box<'alloc, Token>,
) -> CompoundAssignmentOperator {
CompoundAssignmentOperator::Coalesce { loc: token.loc }
}
pub fn box_assign_op(
&self,
op: CompoundAssignmentOperator,
) -> arena::Box<'alloc, CompoundAssignmentOperator> {
self.alloc_with(|| op)
}
// AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression
// AssignmentExpression : LeftHandSideExpression LogicalAssignmentOperator AssignmentExpression
pub fn compound_assignment_expr(
&self,
left_hand_side: arena::Box<'alloc, Expression<'alloc>>,
operator: arena::Box<'alloc, CompoundAssignmentOperator>,
value: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let target = self.expression_to_simple_assignment_target(left_hand_side)?;
let target_loc = target.get_loc();
let value_loc = value.get_loc();
Ok(
self.alloc_with(|| Expression::CompoundAssignmentExpression {
operator: operator.unbox(),
binding: target,
expression: value,
loc: SourceLocation::from_parts(target_loc, value_loc),
}),
)
}
// BlockStatement : Block
pub fn block_statement(
&self,
block: arena::Box<'alloc, Block<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let loc = block.loc;
self.alloc_with(|| Statement::BlockStatement {
block: block.unbox(),
loc,
})
}
// Block : `{` StatementList? `}`
pub fn block(
&mut self,
open_token: arena::Box<'alloc, Token>,
statements: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Block<'alloc>>> {
self.check_block_bindings(open_token.loc.start)?;
Ok(self.alloc_with(|| Block {
statements: match statements {
Some(statements) => statements.unbox(),
None => self.new_vec(),
},
declarations: None,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
}))
}
// Block : `{` StatementList? `}`
// for Catch
pub fn catch_block(
&mut self,
open_token: arena::Box<'alloc, Token>,
statements: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Block<'alloc>> {
// Early Error handling is done in Catch.
self.alloc_with(|| Block {
statements: match statements {
Some(statements) => statements.unbox(),
None => self.new_vec(),
},
declarations: None,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
})
}
// StatementList : StatementListItem
pub fn statement_list_single(
&self,
statement: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>> {
self.alloc_with(|| self.new_vec_single(statement.unbox()))
}
// StatementList : StatementList StatementListItem
pub fn statement_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>,
statement: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>> {
self.push(&mut list, statement.unbox());
list
}
// LexicalDeclaration : LetOrConst BindingList `;`
pub fn lexical_declaration(
&mut self,
kind: arena::Box<'alloc, VariableDeclarationKind>,
declarators: arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let binding_kind = match &*kind {
VariableDeclarationKind::Let { .. } => BindingKind::Let,
VariableDeclarationKind::Const { .. } => BindingKind::Const,
_ => panic!("unexpected VariableDeclarationKind"),
};
self.context_metadata
.mark_binding_kind(kind.get_loc().start, None, binding_kind);
// 13.3.1.1 Static Semantics: Early Errors
if let VariableDeclarationKind::Const { .. } = *kind {
for v in declarators.iter() {
if v.init == None {
return Err(ParseError::NotImplemented(
"Missing initializer in a lexical binding.",
)
.into());
}
}
}
let kind_loc = kind.get_loc();
let declarator_loc = declarators
.last()
.expect("There should be at least one declarator")
.get_loc();
Ok(self.alloc_with(|| {
Statement::VariableDeclarationStatement(VariableDeclaration {
kind: kind.unbox(),
declarators: declarators.unbox(),
loc: SourceLocation::from_parts(kind_loc, declarator_loc),
})
}))
}
// ForLexicalDeclaration : LetOrConst BindingList `;`
pub fn for_lexical_declaration(
&mut self,
kind: arena::Box<'alloc, VariableDeclarationKind>,
declarators: arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>>,
) -> Result<'alloc, arena::Box<'alloc, VariableDeclarationOrExpression<'alloc>>> {
let binding_kind = match &*kind {
VariableDeclarationKind::Let { .. } => BindingKind::Let,
VariableDeclarationKind::Const { .. } => BindingKind::Const,
_ => panic!("unexpected VariableDeclarationKind"),
};
self.context_metadata
.mark_binding_kind(kind.get_loc().start, None, binding_kind);
// 13.3.1.1 Static Semantics: Early Errors
if let VariableDeclarationKind::Const { .. } = *kind {
for v in declarators.iter() {
if v.init == None {
return Err(ParseError::NotImplemented(
"Missing initializer in a lexical binding.",
)
.into());
}
}
}
let kind_loc = kind.get_loc();
let declarator_loc = declarators
.last()
.expect("There should be at least one declarator")
.get_loc();
Ok(self.alloc_with(|| {
VariableDeclarationOrExpression::VariableDeclaration(VariableDeclaration {
kind: kind.unbox(),
declarators: declarators.unbox(),
loc: SourceLocation::from_parts(kind_loc, declarator_loc),
})
}))
}
// LetOrConst : `let`
pub fn let_kind(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, VariableDeclarationKind> {
self.alloc_with(|| VariableDeclarationKind::Let { loc: token.loc })
}
// LetOrConst : `const`
pub fn const_kind(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, VariableDeclarationKind> {
self.alloc_with(|| VariableDeclarationKind::Const { loc: token.loc })
}
// VariableStatement : `var` VariableDeclarationList `;`
pub fn variable_statement(
&mut self,
var_token: arena::Box<'alloc, Token>,
declarators: arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let var_loc = var_token.loc;
let declarator_loc = declarators
.last()
.expect("There should be at least one declarator")
.get_loc();
self.context_metadata
.mark_binding_kind(var_loc.start, None, BindingKind::Var);
self.alloc_with(|| {
Statement::VariableDeclarationStatement(VariableDeclaration {
kind: VariableDeclarationKind::Var { loc: var_loc },
declarators: declarators.unbox(),
loc: SourceLocation::from_parts(var_loc, declarator_loc),
})
})
}
// VariableDeclarationList : VariableDeclaration
// BindingList : LexicalBinding
pub fn variable_declaration_list_single(
&self,
decl: arena::Box<'alloc, VariableDeclarator<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>> {
self.alloc_with(|| self.new_vec_single(decl.unbox()))
}
// VariableDeclarationList : VariableDeclarationList `,` VariableDeclaration
// BindingList : BindingList `,` LexicalBinding
pub fn variable_declaration_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>>,
decl: arena::Box<'alloc, VariableDeclarator<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>> {
self.push(&mut list, decl.unbox());
list
}
// VariableDeclaration : BindingIdentifier Initializer?
// VariableDeclaration : BindingPattern Initializer
pub fn variable_declaration(
&self,
binding: arena::Box<'alloc, Binding<'alloc>>,
init: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, VariableDeclarator<'alloc>> {
let binding_loc = binding.get_loc();
let loc = match init {
Some(ref init) => SourceLocation::from_parts(binding_loc, init.get_loc()),
None => binding_loc,
};
self.alloc_with(|| VariableDeclarator {
binding: binding.unbox(),
init,
loc,
})
}
// ObjectBindingPattern : `{` `}`
// ObjectBindingPattern : `{` BindingRestProperty `}`
// ObjectBindingPattern : `{` BindingPropertyList `}`
// ObjectBindingPattern : `{` BindingPropertyList `,` BindingRestProperty? `}`
pub fn object_binding_pattern(
&self,
open_token: arena::Box<'alloc, Token>,
properties: arena::Box<'alloc, arena::Vec<'alloc, BindingProperty<'alloc>>>,
rest: Option<arena::Box<'alloc, BindingIdentifier>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Binding<'alloc>> {
self.alloc_with(|| {
Binding::BindingPattern(BindingPattern::ObjectBinding(ObjectBinding {
properties: properties.unbox(),
rest,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
}))
})
}
pub fn binding_element_list_empty(
&self,
) -> arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>> {
self.alloc_with(|| self.new_vec())
}
// ArrayBindingPattern : `[` Elision? BindingRestElement? `]`
// ArrayBindingPattern : `[` BindingElementList `]`
// ArrayBindingPattern : `[` BindingElementList `,` Elision? BindingRestElement? `]`
pub fn array_binding_pattern(
&self,
open_token: arena::Box<'alloc, Token>,
mut elements: arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>>,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
rest: Option<arena::Box<'alloc, Binding<'alloc>>>,
close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Binding<'alloc>> {
if let Some(elision) = elision {
for _ in 0..elision.elements.len() {
self.push(&mut elements, None);
}
}
self.alloc_with(|| {
Binding::BindingPattern(BindingPattern::ArrayBinding(ArrayBinding {
elements: elements.unbox(),
rest,
loc: SourceLocation::from_parts(open_token.loc, close_token.loc),
}))
})
}
pub fn binding_property_list_empty(
&self,
) -> arena::Box<'alloc, arena::Vec<'alloc, BindingProperty<'alloc>>> {
self.alloc_with(|| self.new_vec())
}
// BindingPropertyList : BindingProperty
pub fn binding_property_list_single(
&self,
property: arena::Box<'alloc, BindingProperty<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, BindingProperty<'alloc>>> {
self.alloc_with(|| self.new_vec_single(property.unbox()))
}
// BindingPropertyList : BindingPropertyList `,` BindingProperty
pub fn binding_property_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, BindingProperty<'alloc>>>,
property: arena::Box<'alloc, BindingProperty<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, BindingProperty<'alloc>>> {
self.push(&mut list, property.unbox());
list
}
// BindingElementList : BindingElementList `,` BindingElisionElement
pub fn binding_element_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>>,
mut element: arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>> {
self.append(&mut list, &mut element);
list
}
// BindingElisionElement : Elision? BindingElement
pub fn binding_elision_element(
&self,
elision: Option<arena::Box<'alloc, ArrayExpression<'alloc>>>,
element: arena::Box<'alloc, Parameter<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Option<Parameter<'alloc>>>> {
let elision_count = elision.map(|v| v.elements.len()).unwrap_or(0);
let mut result = self.alloc_with(|| self.new_vec());
for _ in 0..elision_count {
self.push(&mut result, None);
}
self.push(&mut result, Some(element.unbox()));
result
}
// BindingProperty : SingleNameBinding
pub fn binding_property_shorthand(
&self,
binding: arena::Box<'alloc, Parameter<'alloc>>,
) -> arena::Box<'alloc, BindingProperty<'alloc>> {
// Previous parsing interpreted this as a Parameter. We need to take
// all the pieces out of that box and put them in a new box.
let (binding, init) = match binding.unbox() {
Parameter::Binding(binding) => (binding, None),
Parameter::BindingWithDefault(BindingWithDefault { binding, init, .. }) => {
(binding, Some(init.unbox()))
}
};
let binding = match binding {
Binding::BindingIdentifier(bi) => bi,
_ => {
// The grammar ensures that the parser always passes a valid
// argument to this method.
panic!("invalid argument: binding_property_shorthand requires a Binding::BindingIdentifier");
}
};
let loc = binding.loc;
self.alloc_with(|| {
BindingProperty::BindingPropertyIdentifier(BindingPropertyIdentifier {
binding,
init: init.map(|x| self.alloc_with(|| x)),
loc,
})
})
}
// BindingProperty : PropertyName `:` BindingElement
pub fn binding_property(
&self,
name: arena::Box<'alloc, PropertyName<'alloc>>,
binding: arena::Box<'alloc, Parameter<'alloc>>,
) -> arena::Box<'alloc, BindingProperty<'alloc>> {
let name_loc = name.get_loc();
let binding_loc = binding.get_loc();
self.alloc_with(|| {
BindingProperty::BindingPropertyProperty(BindingPropertyProperty {
name: name.unbox(),
binding: binding.unbox(),
loc: SourceLocation::from_parts(name_loc, binding_loc),
})
})
}
// BindingElement : BindingPattern Initializer?
pub fn binding_element_pattern(
&self,
binding: arena::Box<'alloc, Binding<'alloc>>,
init: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, Parameter<'alloc>> {
self.alloc_with(|| match init {
None => Parameter::Binding(binding.unbox()),
Some(init) => {
let binding_loc = binding.get_loc();
let init_loc = init.get_loc();
Parameter::BindingWithDefault(BindingWithDefault {
binding: binding.unbox(),
init,
loc: SourceLocation::from_parts(binding_loc, init_loc),
})
}
})
}
// SingleNameBinding : BindingIdentifier Initializer?
pub fn single_name_binding(
&self,
name: arena::Box<'alloc, BindingIdentifier>,
init: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, Parameter<'alloc>> {
let binding = Binding::BindingIdentifier(name.unbox());
self.alloc_with(|| match init {
None => Parameter::Binding(binding),
Some(init) => {
let binding_loc = binding.get_loc();
let init_loc = init.get_loc();
Parameter::BindingWithDefault(BindingWithDefault {
binding,
init,
loc: SourceLocation::from_parts(binding_loc, init_loc),
})
}
})
}
// BindingRestElement : `...` BindingIdentifier
pub fn binding_rest_element(
&self,
name: arena::Box<'alloc, BindingIdentifier>,
) -> arena::Box<'alloc, Binding<'alloc>> {
self.alloc_with(|| Binding::BindingIdentifier(name.unbox()))
}
// EmptyStatement : `;`
pub fn empty_statement(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Statement<'alloc>> {
self.alloc_with(|| Statement::EmptyStatement { loc: token.loc })
}
// ExpressionStatement : [lookahead not in {'{', 'function', 'async', 'class', 'let'}] Expression `;`
pub fn expression_statement(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
self.alloc_with(|| Statement::ExpressionStatement(expression))
}
// IfStatement : `if` `(` Expression `)` Statement `else` Statement
// IfStatement : `if` `(` Expression `)` Statement
pub fn if_statement(
&self,
if_token: arena::Box<'alloc, Token>,
test: arena::Box<'alloc, Expression<'alloc>>,
consequent: arena::Box<'alloc, Statement<'alloc>>,
alternate: Option<arena::Box<'alloc, Statement<'alloc>>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(consequent.get_loc().start)?;
if let Some(ref stmt) = alternate {
self.check_single_statement(stmt.get_loc().start)?;
}
let if_loc = if_token.loc;
let loc = match alternate {
Some(ref alternate) => SourceLocation::from_parts(if_loc, alternate.get_loc()),
None => SourceLocation::from_parts(if_loc, consequent.get_loc()),
};
Ok(self.alloc_with(|| {
Statement::IfStatement(IfStatement {
test,
consequent,
alternate,
loc,
})
}))
}
// Create BlockStatement from FunctionDeclaration, for the following:
//
// IfStatement : `if` `(` Expression `)` FunctionDeclaration `else` Statement
// IfStatement : `if` `(` Expression `)` Statement `else` FunctionDeclaration
// IfStatement : `if` `(` Expression `)` FunctionDeclaration `else` FunctionDeclaration
// IfStatement : `if` `(` Expression `)` FunctionDeclaration
pub fn make_block_stmt_from_function_decl(
&mut self,
fun: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let fun_loc = fun.get_loc();
// Annex B. FunctionDeclarations in IfStatement Statement Clauses
//
// This production only applies when parsing non-strict code.
if self.is_strict()? {
return Err(ParseError::FunctionDeclInSingleStatement.into());
}
// Code matching this production is processed as if each matching
// occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the
// sole StatementListItem of a BlockStatement occupying that position
// in the source code. The semantics of such a synthetic BlockStatement
// includes the web legacy compatibility semantics specified in B.3.3.
self.check_block_bindings(fun_loc.start)?;
Ok(self.alloc_with(|| Statement::BlockStatement {
block: Block {
statements: self.new_vec_single(fun.unbox()),
declarations: None,
loc: fun_loc,
},
loc: fun_loc,
}))
}
fn is_strict(&self) -> Result<'alloc, bool> {
Err(ParseError::NotImplemented("strict-mode-only early error is not yet supported").into())
}
// IterationStatement : `do` Statement `while` `(` Expression `)` `;`
pub fn do_while_statement(
&mut self,
do_token: arena::Box<'alloc, Token>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
test: arena::Box<'alloc, Expression<'alloc>>,
close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
self.context_metadata
.pop_unlabelled_breaks_and_continues_from(do_token.loc.start);
Ok(self.alloc_with(|| Statement::DoWhileStatement {
block: stmt,
test,
loc: SourceLocation::from_parts(do_token.loc, close_token.loc),
}))
}
// IterationStatement : `while` `(` Expression `)` Statement
pub fn while_statement(
&mut self,
while_token: arena::Box<'alloc, Token>,
test: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
let stmt_loc = stmt.get_loc();
self.context_metadata
.pop_unlabelled_breaks_and_continues_from(stmt_loc.start);
Ok(self.alloc_with(|| Statement::WhileStatement {
test,
block: stmt,
loc: SourceLocation::from_parts(while_token.loc, stmt_loc),
}))
}
// IterationStatement : `for` `(` [lookahead != 'let'] Expression? `;` Expression? `;` Expression? `)` Statement
// IterationStatement : `for` `(` `var` VariableDeclarationList `;` Expression? `;` Expression? `)` Statement
pub fn for_statement(
&mut self,
for_token: arena::Box<'alloc, Token>,
init: Option<VariableDeclarationOrExpression<'alloc>>,
test: Option<arena::Box<'alloc, Expression<'alloc>>>,
update: Option<arena::Box<'alloc, Expression<'alloc>>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
self.for_statement_common(for_token, init, test, update, stmt)
}
// IterationStatement : `for` `(` ForLexicalDeclaration Expression? `;` Expression? `)` Statement
pub fn for_statement_lexical(
&mut self,
for_token: arena::Box<'alloc, Token>,
init: VariableDeclarationOrExpression<'alloc>,
test: Option<arena::Box<'alloc, Expression<'alloc>>>,
update: Option<arena::Box<'alloc, Expression<'alloc>>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
let init_loc = &init.get_loc();
self.check_lexical_for_bindings(init_loc.start, init_loc.end)?;
self.for_statement_common(for_token, Some(init), test, update, stmt)
}
pub fn for_statement_common(
&mut self,
for_token: arena::Box<'alloc, Token>,
init: Option<VariableDeclarationOrExpression<'alloc>>,
test: Option<arena::Box<'alloc, Expression<'alloc>>>,
update: Option<arena::Box<'alloc, Expression<'alloc>>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let stmt_loc = stmt.get_loc();
self.context_metadata
.pop_unlabelled_breaks_and_continues_from(stmt_loc.start);
Ok(self.alloc_with(|| Statement::ForStatement {
init,
test,
update,
block: stmt,
loc: SourceLocation::from_parts(for_token.loc, stmt_loc),
}))
}
pub fn for_expression(
&self,
expr: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> Option<VariableDeclarationOrExpression<'alloc>> {
expr.map(|expr| VariableDeclarationOrExpression::Expression(expr))
}
pub fn for_var_declaration(
&mut self,
var_token: arena::Box<'alloc, Token>,
declarators: arena::Box<'alloc, arena::Vec<'alloc, VariableDeclarator<'alloc>>>,
) -> VariableDeclarationOrExpression<'alloc> {
let var_loc = var_token.loc;
let declarator_loc = declarators
.last()
.expect("There should be at least one declarator")
.get_loc();
self.context_metadata.mark_binding_kind(
var_loc.start,
Some(declarator_loc.end),
BindingKind::Var,
);
VariableDeclarationOrExpression::VariableDeclaration(VariableDeclaration {
kind: VariableDeclarationKind::Var { loc: var_loc },
declarators: declarators.unbox(),
loc: SourceLocation::from_parts(var_loc, declarator_loc),
})
}
pub fn unbox_for_lexical_declaration(
&self,
declaration: arena::Box<'alloc, VariableDeclarationOrExpression<'alloc>>,
) -> VariableDeclarationOrExpression<'alloc> {
declaration.unbox()
}
// IterationStatement : `for` `(` [lookahead != 'let'] LeftHandSideExpression `in` Expression `)` Statement
// IterationStatement : `for` `(` `var` ForBinding `in` Expression `)` Statement
//
// Annex B: Initializers in ForIn Statement Heads
//
// IterationStatement : `for` `(` `var` BindingIdentifier Initializer `in` Expression `)` Statement
pub fn for_in_statement(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
self.for_in_statement_common(for_token, left, right, stmt)
}
// IterationStatement : `for` `(` ForDeclaration `in` Expression `)` Statement
pub fn for_in_statement_lexical(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
let left_loc = &left.get_loc();
self.check_lexical_for_bindings(left_loc.start, left_loc.end)?;
self.for_in_statement_common(for_token, left, right, stmt)
}
pub fn for_in_statement_common(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let stmt_loc = stmt.get_loc();
self.context_metadata
.pop_unlabelled_breaks_and_continues_from(stmt_loc.start);
Ok(self.alloc_with(|| Statement::ForInStatement {
left,
right,
block: stmt,
loc: SourceLocation::from_parts(for_token.loc, stmt_loc),
}))
}
pub fn for_in_or_of_var_declaration(
&mut self,
var_token: arena::Box<'alloc, Token>,
binding: arena::Box<'alloc, Binding<'alloc>>,
init: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> VariableDeclarationOrAssignmentTarget<'alloc> {
let var_loc = var_token.loc;
let binding_loc = binding.get_loc();
let decl_loc = match init {
Some(ref init) => SourceLocation::from_parts(binding_loc, init.get_loc()),
None => binding_loc,
};
self.context_metadata.mark_binding_kind(
binding_loc.start,
Some(binding_loc.end),
BindingKind::Var,
);
VariableDeclarationOrAssignmentTarget::VariableDeclaration(VariableDeclaration {
kind: VariableDeclarationKind::Var { loc: var_loc },
declarators: self.new_vec_single(VariableDeclarator {
binding: binding.unbox(),
init,
loc: decl_loc,
}),
loc: SourceLocation::from_parts(var_loc, decl_loc),
})
}
pub fn for_assignment_target(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, VariableDeclarationOrAssignmentTarget<'alloc>> {
Ok(VariableDeclarationOrAssignmentTarget::AssignmentTarget(
self.expression_to_assignment_target(expression)?,
))
}
pub fn unbox_for_declaration(
&self,
declaration: arena::Box<'alloc, VariableDeclarationOrAssignmentTarget<'alloc>>,
) -> VariableDeclarationOrAssignmentTarget<'alloc> {
declaration.unbox()
}
// IterationStatement : `for` `(` [lookahead != 'let'] LeftHandSideExpression `of` AssignmentExpression `)` Statement
// IterationStatement : `for` `(` `var` ForBinding `of` AssignmentExpression `)` Statement
pub fn for_of_statement(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
self.for_of_statement_common(for_token, left, right, stmt)
}
// IterationStatement : `for` `(` ForDeclaration `of` AssignmentExpression `)` Statement
pub fn for_of_statement_lexical(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
let left_loc = &left.get_loc();
self.check_lexical_for_bindings(left_loc.start, left_loc.end)?;
self.for_of_statement_common(for_token, left, right, stmt)
}
pub fn for_of_statement_common(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget<'alloc>,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let stmt_loc = stmt.get_loc();
self.context_metadata
.pop_unlabelled_breaks_and_continues_from(stmt_loc.start);
Ok(self.alloc_with(|| Statement::ForOfStatement {
left,
right,
block: stmt,
loc: SourceLocation::from_parts(for_token.loc, stmt_loc),
}))
}
// IterationStatement : `for` `await` `(` [lookahead != 'let'] LeftHandSideExpression `of` AssignmentExpression `)` Statement
// IterationStatement : `for` `await` `(` `var` ForBinding `of` AssignmentExpression `)` Statement
pub fn for_await_of_statement(
&self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
self.for_await_of_statement_common(for_token, left, right, stmt)
}
// IterationStatement : `for` `await` `(` ForDeclaration `of` AssignmentExpression `)` Statement
pub fn for_await_of_statement_lexical(
&mut self,
for_token: arena::Box<'alloc, Token>,
left: VariableDeclarationOrAssignmentTarget,
right: arena::Box<'alloc, Expression<'alloc>>,
stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_single_statement(stmt.get_loc().start)?;
let left_loc = &left.get_loc();
self.check_lexical_for_bindings(left_loc.start, left_loc.end)?;
self.for_await_of_statement_common(for_token, left, right, stmt)
}
pub fn for_await_of_statement_common(
&self,
_for_token: arena::Box<'alloc, Token>,
_left: VariableDeclarationOrAssignmentTarget,
_right: arena::Box<'alloc, Expression<'alloc>>,
_stmt: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
Err(ParseError::NotImplemented("for await statement (missing from AST)").into())
}
// ForDeclaration : LetOrConst ForBinding => ForDeclaration($0, $1)
pub fn for_declaration(
&mut self,
kind: arena::Box<'alloc, VariableDeclarationKind>,
binding: arena::Box<'alloc, Binding<'alloc>>,
) -> arena::Box<'alloc, VariableDeclarationOrAssignmentTarget<'alloc>> {
let binding_kind = match &*kind {
VariableDeclarationKind::Let { .. } => BindingKind::Let,
VariableDeclarationKind::Const { .. } => BindingKind::Const,
_ => panic!("unexpected VariableDeclarationKind"),
};
self.context_metadata
.mark_binding_kind(kind.get_loc().start, None, binding_kind);
let kind_loc = kind.get_loc();
let binding_loc = binding.get_loc();
self.alloc_with(|| {
VariableDeclarationOrAssignmentTarget::VariableDeclaration(VariableDeclaration {
kind: kind.unbox(),
declarators: self.new_vec_single(VariableDeclarator {
binding: binding.unbox(),
init: None,
loc: binding_loc,
}),
loc: SourceLocation::from_parts(kind_loc, binding_loc),
})
})
}
// CatchParameter : BindingIdentifier
// ForBinding : BindingIdentifier
// LexicalBinding : BindingIdentifier Initializer?
// VariableDeclaration : BindingIdentifier Initializer?
pub fn binding_identifier_to_binding(
&self,
identifier: arena::Box<'alloc, BindingIdentifier>,
) -> arena::Box<'alloc, Binding<'alloc>> {
self.alloc_with(|| Binding::BindingIdentifier(identifier.unbox()))
}
// ContinueStatement : `continue` `;`
// ContinueStatement : `continue` LabelIdentifier `;`
pub fn continue_statement(
&mut self,
continue_token: arena::Box<'alloc, Token>,
label: Option<arena::Box<'alloc, Label>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let info = match label {
Some(ref label) => {
// Label is used both for LabelledStatement and for labelled
// ContinueStatements. A label will be noted in the context metadata
// whenever we hit a label, as is the case for BreakStatements. These
// bindings are not necessary, and are at the end of the bindings stack.
// To keep things clean, we will pop the last element (the label we just
// added) off the stack.
let index = self
.context_metadata
.find_first_label(continue_token.loc.start);
self.context_metadata.pop_labels_from(index);
ControlInfo::new_continue(continue_token.loc.start, Some(label.value))
}
None => ControlInfo::new_continue(continue_token.loc.start, None),
};
self.context_metadata.push_break_or_continue(info);
let continue_loc = continue_token.loc;
let loc = match label {
Some(ref label) => SourceLocation::from_parts(continue_loc, label.loc),
None => continue_loc,
};
Ok(self.alloc_with(|| Statement::ContinueStatement {
label: label.map(|boxed| boxed.unbox()),
loc,
}))
}
// BreakStatement : `break` `;`
// BreakStatement : `break` LabelIdentifier `;`
pub fn break_statement(
&mut self,
break_token: arena::Box<'alloc, Token>,
label: Option<arena::Box<'alloc, Label>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let info = match label {
Some(ref label) => {
// Label is used both for LabelledStatement and for labelled
// BreakStatements. A label will be noted in the context metadata
// whenever we hit a label, as is the case for BreakStatements. These
// bindings are not necessary, and are at the end of the bindings stack.
// To keep things clean, we will pop the last element (the label we just
// added) off the stack.
let index = self.context_metadata.find_first_label(label.loc.start);
self.context_metadata.pop_labels_from(index);
ControlInfo::new_break(break_token.loc.start, Some(label.value))
}
None => ControlInfo::new_break(break_token.loc.start, None),
};
self.context_metadata.push_break_or_continue(info);
let break_loc = break_token.loc;
let loc = match label {
Some(ref label) => SourceLocation::from_parts(break_loc, label.loc),
None => break_loc,
};
Ok(self.alloc_with(|| Statement::BreakStatement {
label: label.map(|boxed| boxed.unbox()),
loc,
}))
}
// ReturnStatement : `return` `;`
// ReturnStatement : `return` Expression `;`
pub fn return_statement(
&self,
return_token: arena::Box<'alloc, Token>,
expression: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let return_loc = return_token.loc;
let loc = match expression {
Some(ref expression) => SourceLocation::from_parts(return_loc, expression.get_loc()),
None => return_loc,
};
self.alloc_with(|| Statement::ReturnStatement { expression, loc })
}
// WithStatement : `with` `(` Expression `)` Statement
pub fn with_statement(
&self,
with_token: arena::Box<'alloc, Token>,
object: arena::Box<'alloc, Expression<'alloc>>,
body: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let body_loc = body.get_loc();
self.alloc_with(|| Statement::WithStatement {
object,
body,
loc: SourceLocation::from_parts(with_token.loc, body_loc),
})
}
// SwitchStatement : `switch` `(` Expression `)` CaseBlock
pub fn switch_statement(
&self,
switch_token: arena::Box<'alloc, Token>,
discriminant_expr: arena::Box<'alloc, Expression<'alloc>>,
mut cases: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
match &mut *cases {
Statement::SwitchStatement {
discriminant, loc, ..
} => {
*discriminant = discriminant_expr;
(*loc).start = switch_token.loc.start;
}
Statement::SwitchStatementWithDefault {
discriminant, loc, ..
} => {
*discriminant = discriminant_expr;
(*loc).start = switch_token.loc.start;
}
_ => {
// The grammar ensures that the parser always passes a valid
// argument to this method.
panic!("invalid argument: argument 2 must be a SwitchStatement");
}
}
cases
}
// CaseBlock : `{` CaseClauses? `}`
pub fn case_block(
&mut self,
open_token: arena::Box<'alloc, Token>,
cases: Option<arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>>>,
close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_case_block_binding(open_token.loc.start)?;
Ok(self.alloc_with(|| Statement::SwitchStatement {
// This will be overwritten once the enclosing switch statement
// gets parsed.
discriminant: self.alloc_with(|| Expression::LiteralNullExpression {
loc: SourceLocation::default(),
}),
cases: match cases {
None => self.new_vec(),
Some(boxed) => boxed.unbox(),
},
// `start` of this will be overwritten once the enclosing switch
// statement gets parsed.
loc: close_token.loc,
}))
}
// CaseBlock : `{` CaseClauses DefaultClause CaseClauses `}`
pub fn case_block_with_default(
&mut self,
open_token: arena::Box<'alloc, Token>,
pre_default_cases: Option<arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>>>,
default_case: arena::Box<'alloc, SwitchDefault<'alloc>>,
post_default_cases: Option<arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>>>,
close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
self.check_case_block_binding(open_token.loc.start)?;
Ok(self.alloc_with(|| Statement::SwitchStatementWithDefault {
// This will be overwritten once the enclosing switch statement
// gets parsed.
discriminant: self.alloc_with(|| Expression::LiteralNullExpression {
loc: SourceLocation::default(),
}),
pre_default_cases: match pre_default_cases {
None => self.new_vec(),
Some(boxed) => boxed.unbox(),
},
default_case: default_case.unbox(),
post_default_cases: match post_default_cases {
None => self.new_vec(),
Some(boxed) => boxed.unbox(),
},
// `start` of this will be overwritten once the enclosing switch
// statement gets parsed.
loc: close_token.loc,
}))
}
// CaseClauses : CaseClause
pub fn case_clauses_single(
&self,
case: arena::Box<'alloc, SwitchCase<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>> {
self.alloc_with(|| self.new_vec_single(case.unbox()))
}
// CaseClauses : CaseClauses CaseClause
pub fn case_clauses_append(
&self,
mut cases: arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>>,
case: arena::Box<'alloc, SwitchCase<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, SwitchCase<'alloc>>> {
self.push(&mut cases, case.unbox());
cases
}
// CaseClause : `case` Expression `:` StatementList
pub fn case_clause(
&self,
case_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
colon_token: arena::Box<'alloc, Token>,
statements: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
) -> arena::Box<'alloc, SwitchCase<'alloc>> {
let case_loc = case_token.loc;
if let Some(statements) = statements {
let statement_loc = statements
.last()
.expect("There should be at least one statement")
.get_loc();
self.alloc_with(|| SwitchCase {
test: expression,
consequent: statements.unbox(),
loc: SourceLocation::from_parts(case_loc, statement_loc),
})
} else {
self.alloc_with(|| SwitchCase {
test: expression,
consequent: self.new_vec(),
loc: SourceLocation::from_parts(case_loc, colon_token.loc),
})
}
}
// DefaultClause : `default` `:` StatementList
pub fn default_clause(
&self,
default_token: arena::Box<'alloc, Token>,
colon_token: arena::Box<'alloc, Token>,
statements: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
) -> arena::Box<'alloc, SwitchDefault<'alloc>> {
let default_loc = default_token.loc;
if let Some(statements) = statements {
let statement_loc = statements
.last()
.expect("There should be at least one statement")
.get_loc();
self.alloc_with(|| SwitchDefault {
consequent: statements.unbox(),
loc: SourceLocation::from_parts(default_loc, statement_loc),
})
} else {
self.alloc_with(|| SwitchDefault {
consequent: self.new_vec(),
loc: SourceLocation::from_parts(default_loc, colon_token.loc),
})
}
}
// LabelledStatement : LabelIdentifier `:` LabelledItem
pub fn labelled_statement(
&mut self,
label: arena::Box<'alloc, Label>,
body: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Statement<'alloc>>> {
let label_loc = label.loc;
let body_loc = body.get_loc();
self.mark_labelled_statement(&label, &body);
self.check_labelled_statement(label.value, label_loc.start, body_loc.start)?;
Ok(self.alloc_with(|| Statement::LabelledStatement {
label: label.unbox(),
body,
loc: SourceLocation::from_parts(label_loc, body_loc),
}))
}
// ThrowStatement : `throw` Expression `;`
pub fn throw_statement(
&self,
throw_token: arena::Box<'alloc, Token>,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let expression_loc = expression.get_loc();
self.alloc_with(|| Statement::ThrowStatement {
expression,
loc: SourceLocation::from_parts(throw_token.loc, expression_loc),
})
}
// TryStatement : `try` Block Catch
// TryStatement : `try` Block Finally
// TryStatement : `try` Block Catch Finally
pub fn try_statement(
&self,
try_token: arena::Box<'alloc, Token>,
body: arena::Box<'alloc, Block<'alloc>>,
catch_clause: Option<arena::Box<'alloc, CatchClause<'alloc>>>,
finally_block: Option<arena::Box<'alloc, Block<'alloc>>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let try_loc = try_token.loc;
match (catch_clause, finally_block) {
(Some(catch_clause), None) => {
let catch_clause_loc = catch_clause.loc;
self.alloc_with(|| Statement::TryCatchStatement {
body: body.unbox(),
catch_clause: catch_clause.unbox(),
loc: SourceLocation::from_parts(try_loc, catch_clause_loc),
})
}
(catch_clause, Some(finally_block)) => {
let finally_block_loc = finally_block.loc;
self.alloc_with(|| Statement::TryFinallyStatement {
body: body.unbox(),
catch_clause: catch_clause.map(|boxed| boxed.unbox()),
finalizer: finally_block.unbox(),
loc: SourceLocation::from_parts(try_loc, finally_block_loc),
})
}
_ => {
// The grammar won't accept a bare try-block, so the parser always
// a catch clause, a finally block, or both.
panic!("invalid argument: try_statement requires a catch or finally block");
}
}
}
// Catch : `catch` `(` CatchParameter `)` Block
pub fn catch(
&mut self,
catch_token: arena::Box<'alloc, Token>,
binding: arena::Box<'alloc, Binding<'alloc>>,
body: arena::Box<'alloc, Block<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, CatchClause<'alloc>>> {
let catch_loc = catch_token.loc;
let body_loc = body.loc;
let is_simple = match &*binding {
Binding::BindingIdentifier(_) => true,
_ => false,
};
let bindings_loc = &binding.get_loc();
self.check_catch_bindings(is_simple, bindings_loc.start, bindings_loc.end)?;
Ok(self.alloc_with(|| CatchClause {
binding: Some(binding),
body: body.unbox(),
loc: SourceLocation::from_parts(catch_loc, body_loc),
}))
}
// Catch : `catch` `(` CatchParameter `)` Block
pub fn catch_no_param(
&mut self,
catch_token: arena::Box<'alloc, Token>,
body: arena::Box<'alloc, Block<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, CatchClause<'alloc>>> {
let catch_loc = catch_token.loc;
let body_loc = body.loc;
self.check_catch_no_param_bindings(catch_loc.start)?;
Ok(self.alloc_with(|| CatchClause {
binding: None,
body: body.unbox(),
loc: SourceLocation::from_parts(catch_loc, body_loc),
}))
}
// DebuggerStatement : `debugger` `;`
pub fn debugger_statement(
&self,
token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, Statement<'alloc>> {
self.alloc_with(|| Statement::DebuggerStatement { loc: token.loc })
}
pub fn function_decl(&mut self, f: Function<'alloc>) -> arena::Box<'alloc, Statement<'alloc>> {
self.context_metadata
.mark_binding_kind(f.loc.start, None, BindingKind::Function);
self.alloc_with(|| Statement::FunctionDeclaration(f))
}
pub fn async_or_generator_decl(
&mut self,
f: Function<'alloc>,
) -> arena::Box<'alloc, Statement<'alloc>> {
self.context_metadata
.mark_binding_kind(f.loc.start, None, BindingKind::AsyncOrGenerator);
self.alloc_with(|| Statement::FunctionDeclaration(f))
}
pub fn function_expr(&mut self, f: Function<'alloc>) -> arena::Box<'alloc, Expression<'alloc>> {
let index = self.context_metadata.find_first_binding(f.loc.start);
self.context_metadata.pop_bindings_from(index);
let label_index = self.context_metadata.find_first_label(f.loc.start);
self.context_metadata.pop_labels_from(label_index);
self.alloc_with(|| Expression::FunctionExpression(f))
}
// FunctionDeclaration : `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}`
// FunctionDeclaration : [+Default] `function` `(` FormalParameters `)` `{` FunctionBody `}`
// FunctionExpression : `function` BindingIdentifier? `(` FormalParameters `)` `{` FunctionBody `}`
pub fn function(
&mut self,
function_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, Function<'alloc>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
let is_simple = Self::is_params_simple(&params);
self.check_function_bindings(is_simple, param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(Function {
name: name.map(|b| b.unbox()),
is_async: false,
is_generator: false,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(function_token.loc, body_close_loc),
})
}
// AsyncFunctionDeclaration : `async` `function` BindingIdentifier `(` FormalParameters `)` `{` AsyncFunctionBody `}`
// AsyncFunctionDeclaration : [+Default] `async` `function` `(` FormalParameters `)` `{` AsyncFunctionBody `}`
// AsyncFunctionExpression : `async` `function` `(` FormalParameters `)` `{` AsyncFunctionBody `}`
pub fn async_function(
&mut self,
async_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, Function<'alloc>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
let is_simple = Self::is_params_simple(&params);
self.check_function_bindings(is_simple, param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(Function {
name: name.map(|b| b.unbox()),
is_async: true,
is_generator: false,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(async_token.loc, body_close_loc),
})
}
// GeneratorDeclaration : `function` `*` BindingIdentifier `(` FormalParameters `)` `{` GeneratorBody `}`
// GeneratorDeclaration : [+Default] `function` `*` `(` FormalParameters `)` `{` GeneratorBody `}`
// GeneratorExpression : `function` `*` BindingIdentifier? `(` FormalParameters `)` `{` GeneratorBody `}`
pub fn generator(
&mut self,
function_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, Function<'alloc>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
let is_simple = Self::is_params_simple(&params);
self.check_function_bindings(is_simple, param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(Function {
name: name.map(|b| b.unbox()),
is_async: false,
is_generator: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(function_token.loc, body_close_loc),
})
}
// AsyncGeneratorDeclaration : `async` `function` `*` BindingIdentifier `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
// AsyncGeneratorDeclaration : [+Default] `async` `function` `*` `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
// AsyncGeneratorExpression : `async` `function` `*` BindingIdentifier? `(` FormalParameters `)` `{` AsyncGeneratorBody `}`
pub fn async_generator(
&mut self,
async_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, Function<'alloc>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
let is_simple = Self::is_params_simple(&params);
self.check_function_bindings(is_simple, param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(Function {
name: name.map(|b| b.unbox()),
is_async: true,
is_generator: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(async_token.loc, body_close_loc),
})
}
// UniqueFormalParameters : FormalParameters
pub fn unique_formal_parameters(
&self,
parameters: arena::Box<'alloc, FormalParameters<'alloc>>,
) -> arena::Box<'alloc, FormalParameters<'alloc>> {
parameters
}
// FormalParameters : [empty]
pub fn empty_formal_parameters(&self) -> arena::Box<'alloc, FormalParameters<'alloc>> {
self.alloc_with(|| FormalParameters {
items: self.new_vec(),
rest: None,
// This will be overwritten once the enclosing function gets parsed.
loc: SourceLocation::default(),
})
}
// FormalParameters : FunctionRestParameter
// FormalParameters : FormalParameterList `,` FunctionRestParameter
pub fn with_rest_parameter(
&self,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
rest: arena::Box<'alloc, Binding<'alloc>>,
) -> arena::Box<'alloc, FormalParameters<'alloc>> {
params.rest = Some(rest.unbox());
params
}
// FormalParameterList : FormalParameter
pub fn formal_parameter_list_single(
&self,
parameter: arena::Box<'alloc, Parameter<'alloc>>,
) -> arena::Box<'alloc, FormalParameters<'alloc>> {
self.alloc_with(|| FormalParameters {
items: self.new_vec_single(parameter.unbox()),
rest: None,
// This will be overwritten once the enclosing function gets parsed.
loc: SourceLocation::default(),
})
}
// FormalParameterList : FormalParameterList "," FormalParameter
pub fn formal_parameter_list_append(
&self,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
next_param: arena::Box<'alloc, Parameter<'alloc>>,
) -> arena::Box<'alloc, FormalParameters<'alloc>> {
self.push(&mut params.items, next_param.unbox());
params
}
// FunctionBody : FunctionStatementList
pub fn function_body(
&self,
statements: arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>,
) -> arena::Box<'alloc, FunctionBody<'alloc>> {
// TODO: Directives
self.alloc_with(|| FunctionBody {
directives: self.new_vec(),
statements: statements.unbox(),
// This will be overwritten once the enclosing function gets parsed.
loc: SourceLocation::default(),
})
}
// FunctionStatementList : StatementList?
pub fn function_statement_list(
&self,
statements: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>> {
match statements {
Some(statements) => statements,
None => self.alloc_with(|| self.new_vec()),
}
}
// ArrowFunction : ArrowParameters `=>` ConciseBody
pub fn arrow_function(
&mut self,
params: arena::Box<'alloc, FormalParameters<'alloc>>,
body: arena::Box<'alloc, ArrowExpressionBody<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
self.check_unique_function_bindings(params.loc.start, params.loc.end)?;
let params_loc = params.loc;
let body_loc = body.get_loc();
Ok(self.alloc_with(|| Expression::ArrowExpression {
is_async: false,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(params_loc, body_loc),
}))
}
// ArrowParameters : BindingIdentifier
pub fn arrow_parameters_bare(
&mut self,
identifier: arena::Box<'alloc, BindingIdentifier>,
) -> arena::Box<'alloc, FormalParameters<'alloc>> {
let loc = identifier.loc;
self.alloc_with(|| FormalParameters {
items: self.new_vec_single(Parameter::Binding(Binding::BindingIdentifier(
identifier.unbox(),
))),
rest: None,
loc,
})
}
// ArrowParameters : CoverParenthesizedExpressionAndArrowParameterList
pub fn uncover_arrow_parameters(
&self,
covered: arena::Box<'alloc, CoverParenthesized<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, FormalParameters<'alloc>>> {
Ok(match covered.unbox() {
CoverParenthesized::Expression { expression, loc } => self.alloc(FormalParameters {
items: self.expression_to_parameter_list(expression)?,
rest: None,
loc,
}),
CoverParenthesized::Parameters(parameters) => parameters,
})
}
// ConciseBody : [lookahead != `{`] AssignmentExpression
pub fn concise_body_expression(
&self,
expression: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, ArrowExpressionBody<'alloc>> {
self.alloc_with(|| ArrowExpressionBody::Expression(expression))
}
// ConciseBody : `{` FunctionBody `}`
pub fn concise_body_block(
&self,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, ArrowExpressionBody<'alloc>> {
body.loc
.set_range(body_open_token.loc, body_close_token.loc);
self.alloc_with(|| ArrowExpressionBody::FunctionBody(body.unbox()))
}
// MethodDefinition : ClassElementName `(` UniqueFormalParameters `)` `{` FunctionBody `}`
pub fn method_definition(
&mut self,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, MethodDefinition<'alloc>>> {
let name_loc = name.get_loc();
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
self.check_unique_function_bindings(param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(self.alloc_with(|| {
MethodDefinition::Method(Method {
name: name.unbox(),
is_async: false,
is_generator: false,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(name_loc, body_close_loc),
})
}))
}
// MethodDefinition : `get` ClassElementName `(` `)` `{` FunctionBody `}`
pub fn getter(
&self,
get_token: arena::Box<'alloc, Token>,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, MethodDefinition<'alloc>> {
let body_close_loc = body_close_token.loc;
body.loc.set_range(body_open_token.loc, body_close_loc);
self.alloc_with(|| {
MethodDefinition::Getter(Getter {
property_name: name.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(get_token.loc, body_close_loc),
})
})
}
// MethodDefinition : `set` ClassElementName `(` PropertySetParameterList `)` `{` FunctionBody `}`
pub fn setter(
&mut self,
set_token: arena::Box<'alloc, Token>,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
param_open_token: arena::Box<'alloc, Token>,
mut parameter: arena::Box<'alloc, Parameter<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, MethodDefinition<'alloc>>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
// A setter only has one parameter, but it can be a destructuring
// pattern, so it is still possible to flunk this check.
self.check_unique_function_bindings(param_open_loc.start, param_close_loc.end)?;
parameter.set_loc(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(self.alloc_with(|| {
MethodDefinition::Setter(Setter {
property_name: name.unbox(),
param: parameter.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(set_token.loc, body_close_loc),
})
}))
}
// GeneratorMethod : `*` ClassElementName `(` UniqueFormalParameters `)` `{` GeneratorBody `}`
pub fn generator_method(
&mut self,
generator_token: arena::Box<'alloc, Token>,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, MethodDefinition<'alloc>>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
self.check_unique_function_bindings(param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(self.alloc_with(|| {
MethodDefinition::Method(Method {
name: name.unbox(),
is_async: false,
is_generator: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(generator_token.loc, body_close_loc),
})
}))
}
// YieldExpression : `yield`
// YieldExpression : `yield` AssignmentExpression
pub fn yield_expr(
&self,
yield_token: arena::Box<'alloc, Token>,
operand: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let yield_loc = yield_token.loc;
let loc = match operand {
Some(ref operand) => SourceLocation::from_parts(yield_loc, operand.get_loc()),
None => yield_loc,
};
self.alloc_with(|| Expression::YieldExpression {
expression: operand,
loc,
})
}
// YieldExpression : `yield` `*` AssignmentExpression
pub fn yield_star_expr(
&self,
yield_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let yield_loc = yield_token.loc;
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::YieldGeneratorExpression {
expression: operand,
loc: SourceLocation::from_parts(yield_loc, operand_loc),
})
}
// AsyncGeneratorMethod ::= "async" "*" ClassElementName "(" UniqueFormalParameters ")" "{" AsyncGeneratorBody "}"
pub fn async_generator_method(
&mut self,
async_token: arena::Box<'alloc, Token>,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, MethodDefinition<'alloc>>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
self.check_unique_function_bindings(param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(self.alloc_with(|| {
MethodDefinition::Method(Method {
name: name.unbox(),
is_async: true,
is_generator: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(async_token.loc, body_close_loc),
})
}))
}
// ClassDeclaration : `class` BindingIdentifier ClassTail
// ClassDeclaration : `class` ClassTail
pub fn class_declaration(
&mut self,
class_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
tail: arena::Box<'alloc, ClassExpression<'alloc>>,
) -> arena::Box<'alloc, Statement<'alloc>> {
let class_loc = class_token.loc;
self.context_metadata
.mark_binding_kind(class_loc.start, None, BindingKind::Class);
let tail = tail.unbox();
let tail_loc = tail.loc;
self.alloc_with(|| {
Statement::ClassDeclaration(ClassDeclaration {
name: match name {
None => {
let loc = SourceLocation::new(class_loc.end, class_loc.end);
BindingIdentifier {
name: Identifier {
value: CommonSourceAtomSetIndices::default(),
loc,
},
loc,
}
}
Some(bi) => bi.unbox(),
},
super_: tail.super_,
elements: tail.elements,
loc: SourceLocation::from_parts(class_loc, tail_loc),
})
})
}
// ClassExpression : `class` BindingIdentifier? ClassTail
pub fn class_expression(
&mut self,
class_token: arena::Box<'alloc, Token>,
name: Option<arena::Box<'alloc, BindingIdentifier>>,
mut tail: arena::Box<'alloc, ClassExpression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let offset = class_token.loc.start;
let index = self.context_metadata.find_first_binding(offset);
self.context_metadata.pop_bindings_from(index);
tail.name = name.map(|boxed| boxed.unbox());
tail.loc.start = class_token.loc.start;
self.alloc_with(|| Expression::ClassExpression(tail.unbox()))
}
// ClassTail : ClassHeritage? `{` ClassBody? `}`
pub fn class_tail(
&self,
heritage: Option<arena::Box<'alloc, Expression<'alloc>>>,
body: Option<
arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>>,
>,
body_close_token: arena::Box<'alloc, Token>,
) -> arena::Box<'alloc, ClassExpression<'alloc>> {
self.alloc_with(|| ClassExpression {
name: None,
super_: heritage,
elements: match body {
None => self.new_vec(),
Some(boxed) => boxed.unbox(),
},
// `start` of this will be overwritten once the enclosing class
// gets parsed.
loc: body_close_token.loc,
})
}
// ClassElementList : ClassElementList ClassElement
pub fn class_element_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>>,
mut element: arena::Box<
'alloc,
arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>,
>,
) -> arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>> {
self.append(&mut list, &mut element);
list
}
// FieldDefinition : ClassElementName Initializer?
pub fn class_field_definition(
&self,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
init: Option<arena::Box<'alloc, Expression<'alloc>>>,
) -> arena::Box<'alloc, ClassElement<'alloc>> {
let name_loc = name.get_loc();
let loc = match &init {
None => name_loc,
Some(expr) => SourceLocation::from_parts(name_loc, expr.get_loc()),
};
self.alloc_with(|| ClassElement::FieldDefinition {
name: name.unbox(),
init,
loc,
})
}
// ClassElementName : PropertyName
pub fn property_name_to_class_element_name(
&self,
name: arena::Box<'alloc, PropertyName<'alloc>>,
) -> arena::Box<'alloc, ClassElementName<'alloc>> {
self.alloc_with(|| match name.unbox() {
PropertyName::ComputedPropertyName(cpn) => ClassElementName::ComputedPropertyName(cpn),
PropertyName::StaticPropertyName(spn) => ClassElementName::StaticPropertyName(spn),
PropertyName::StaticNumericPropertyName(snpn) => {
ClassElementName::StaticNumericPropertyName(snpn)
}
})
}
// ClassElementName : PrivateIdentifier
pub fn class_element_name_private(
&self,
private_identifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, ClassElementName<'alloc>>> {
let name = self.private_identifier(private_identifier)?;
Ok(self.alloc_with(|| ClassElementName::PrivateFieldName(name)))
}
// ClassElement : MethodDefinition
pub fn class_element(
&self,
method: arena::Box<'alloc, MethodDefinition<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>> {
let loc = method.get_loc();
self.class_element_to_vec(self.alloc_with(|| ClassElement::MethodDefinition {
is_static: false,
method: method.unbox(),
loc,
}))
}
// ClassElement : FieldDefinition `;`
pub fn class_element_to_vec(
&self,
element: arena::Box<'alloc, ClassElement<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>> {
self.alloc_with(|| self.new_vec_single(element))
}
// ClassElement : `static` MethodDefinition
pub fn class_element_static(
&self,
static_token: arena::Box<'alloc, Token>,
method: arena::Box<'alloc, MethodDefinition<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>> {
let method_loc = method.get_loc();
self.alloc_with(|| {
self.new_vec_single(self.alloc_with(|| ClassElement::MethodDefinition {
is_static: true,
method: method.unbox(),
loc: SourceLocation::from_parts(static_token.loc, method_loc),
}))
})
}
// ClassElement : `static` MethodDefinition
pub fn class_element_static_field(
&self,
_static_token: arena::Box<'alloc, Token>,
_field: arena::Box<'alloc, ClassElement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("class static field").into())
}
// ClassElement : `;`
pub fn class_element_empty(
&self,
) -> arena::Box<'alloc, arena::Vec<'alloc, arena::Box<'alloc, ClassElement<'alloc>>>> {
self.alloc_with(|| self.new_vec())
}
// AsyncMethod : `async` ClassElementName `(` UniqueFormalParameters `)` `{` AsyncFunctionBody `}`
pub fn async_method(
&mut self,
async_token: arena::Box<'alloc, Token>,
name: arena::Box<'alloc, ClassElementName<'alloc>>,
param_open_token: arena::Box<'alloc, Token>,
mut params: arena::Box<'alloc, FormalParameters<'alloc>>,
param_close_token: arena::Box<'alloc, Token>,
body_open_token: arena::Box<'alloc, Token>,
mut body: arena::Box<'alloc, FunctionBody<'alloc>>,
body_close_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, MethodDefinition<'alloc>>> {
let param_open_loc = param_open_token.loc;
let param_close_loc = param_close_token.loc;
let body_close_loc = body_close_token.loc;
self.check_unique_function_bindings(param_open_loc.start, param_close_loc.end)?;
params.loc.set_range(param_open_loc, param_close_loc);
body.loc.set_range(body_open_token.loc, body_close_loc);
Ok(self.alloc_with(|| {
MethodDefinition::Method(Method {
name: name.unbox(),
is_async: true,
is_generator: false,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(async_token.loc, body_close_loc),
})
}))
}
// AwaitExpression : `await` UnaryExpression
pub fn await_expr(
&self,
await_token: arena::Box<'alloc, Token>,
operand: arena::Box<'alloc, Expression<'alloc>>,
) -> arena::Box<'alloc, Expression<'alloc>> {
let operand_loc = operand.get_loc();
self.alloc_with(|| Expression::AwaitExpression {
expression: operand,
loc: SourceLocation::from_parts(await_token.loc, operand_loc),
})
}
// AsyncArrowFunction : `async` AsyncArrowBindingIdentifier `=>` AsyncConciseBody
// AsyncArrowFunction : CoverCallExpressionAndAsyncArrowHead `=>` AsyncConciseBody
pub fn async_arrow_function_bare(
&mut self,
async_token: arena::Box<'alloc, Token>,
identifier: arena::Box<'alloc, BindingIdentifier>,
body: arena::Box<'alloc, ArrowExpressionBody<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let params = self.arrow_parameters_bare(identifier);
self.check_unique_function_bindings(params.loc.start, params.loc.end)?;
let body_loc = body.get_loc();
Ok(self.alloc_with(|| Expression::ArrowExpression {
is_async: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(async_token.loc, body_loc),
}))
}
pub fn async_arrow_function(
&mut self,
params: arena::Box<'alloc, Expression<'alloc>>,
body: arena::Box<'alloc, ArrowExpressionBody<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Expression<'alloc>>> {
let (params, call_loc) = self.async_arrow_parameters(params)?;
self.check_unique_function_bindings(params.loc.start, params.loc.end)?;
let body_loc = body.get_loc();
Ok(self.alloc_with(|| Expression::ArrowExpression {
is_async: true,
params: params.unbox(),
body: body.unbox(),
loc: SourceLocation::from_parts(call_loc, body_loc),
}))
}
// AsyncArrowFunction : CoverCallExpressionAndAsyncArrowHead `=>` AsyncConciseBody
//
// This is used to convert the Expression that is produced by parsing a CoverCallExpressionAndAsyncArrowHead
fn async_arrow_parameters(
&self,
call_expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, (arena::Box<'alloc, FormalParameters<'alloc>>, SourceLocation)> {
match call_expression.unbox() {
Expression::CallExpression(CallExpression {
callee: ce,
arguments,
loc,
}) => {
// Check that `callee` is `async`.
match ce {
ExpressionOrSuper::Expression(callee) => match callee.unbox() {
Expression::IdentifierExpression(IdentifierExpression { name, .. }) => {
if name.value != CommonSourceAtomSetIndices::async_() {
// `foo(a, b) => {}`
return Err(ParseError::ArrowHeadInvalid.into());
}
}
_ => {
// `obj.async() => {}`
return Err(ParseError::ArrowHeadInvalid.into());
}
},
ExpressionOrSuper::Super { .. } => {
// Can't happen: `super()` doesn't match
// CoverCallExpressionAndAsyncArrowHead.
return Err(ParseError::ArrowHeadInvalid.into());
}
}
Ok((self.arguments_to_parameter_list(arguments)?, loc))
}
_ => {
// The grammar ensures that the parser always passes
// a valid CallExpression to this function.
panic!("invalid argument");
}
}
}
// Script : ScriptBody?
pub fn script(
&mut self,
script: Option<arena::Box<'alloc, Script<'alloc>>>,
) -> Result<'alloc, arena::Box<'alloc, Script<'alloc>>> {
self.check_script_bindings()?;
Ok(match script {
Some(script) => script,
None => self.alloc_with(|| Script {
directives: self.new_vec(),
statements: self.new_vec(),
loc: SourceLocation::default(),
}),
})
}
// ScriptBody : StatementList
pub fn script_body(
&self,
statements: arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>,
) -> arena::Box<'alloc, Script<'alloc>> {
let loc = if statements.is_empty() {
SourceLocation::default()
} else {
SourceLocation::from_parts(
statements.first().unwrap().get_loc(),
statements.last().unwrap().get_loc(),
)
};
// TODO: directives
self.alloc_with(|| Script {
directives: self.new_vec(),
statements: statements.unbox(),
loc,
})
}
// Module : ModuleBody?
pub fn module(
&mut self,
body: Option<arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>>,
) -> Result<'alloc, arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>> {
self.check_module_bindings()?;
Ok(body.unwrap_or_else(|| self.alloc_with(|| self.new_vec())))
}
// ModuleItemList : ModuleItem
pub fn module_item_list_single(
&self,
item: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>> {
self.alloc_with(|| self.new_vec_single(item.unbox()))
}
// ModuleItemList : ModuleItemList ModuleItem
pub fn module_item_list_append(
&self,
mut list: arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>>,
item: arena::Box<'alloc, Statement<'alloc>>,
) -> arena::Box<'alloc, arena::Vec<'alloc, Statement<'alloc>>> {
self.push(&mut list, item.unbox());
list
}
// ImportDeclaration : `import` ImportClause FromClause `;`
// ImportDeclaration : `import` ModuleSpecifier `;`
pub fn import_declaration(
&self,
_import_clause: Option<arena::Box<'alloc, Void>>,
_module_specifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// ImportClause : ImportedDefaultBinding
// ImportClause : NameSpaceImport
// ImportClause : NamedImports
// ImportClause : ImportedDefaultBinding `,` NameSpaceImport
// ImportClause : ImportedDefaultBinding `,` NamedImports
pub fn import_clause(
&self,
_default_binding: Option<arena::Box<'alloc, BindingIdentifier>>,
_name_space_import: Option<arena::Box<'alloc, Void>>,
_named_imports: Option<arena::Box<'alloc, Void>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// NameSpaceImport : `*` `as` ImportedBinding
pub fn name_space_import(
&self,
_name: arena::Box<'alloc, BindingIdentifier>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// NamedImports : `{` `}`
pub fn imports_list_empty(&self) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// ImportsList : ImportSpecifier
// ImportsList : ImportsList `,` ImportSpecifier
pub fn imports_list_append(
&self,
_list: arena::Box<'alloc, Void>,
_item: arena::Box<'alloc, Void>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// ImportSpecifier : ImportedBinding
pub fn import_specifier(
&self,
_name: arena::Box<'alloc, BindingIdentifier>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// ImportSpecifier : IdentifierName `as` ImportedBinding
pub fn import_specifier_renaming(
&self,
_original_name: arena::Box<'alloc, Token>,
_local_name: arena::Box<'alloc, BindingIdentifier>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("import").into())
}
// ModuleSpecifier : StringLiteral
pub fn module_specifier(
&self,
_token: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Token>> {
Err(ParseError::NotImplemented("import").into())
}
// ExportDeclaration : `export` `*` FromClause `;`
pub fn export_all_from(
&self,
_module_specifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` ExportClause FromClause `;`
pub fn export_set_from(
&self,
_export_clause: arena::Box<'alloc, Void>,
_module_specifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` ExportClause `;`
pub fn export_set(
&self,
_export_clause: arena::Box<'alloc, Void>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` VariableStatement
pub fn export_vars(
&self,
_statement: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` Declaration
pub fn export_declaration(
&self,
_declaration: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` `default` HoistableDeclaration
pub fn export_default_hoistable(
&self,
_declaration: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` `default` ClassDeclaration
pub fn export_default_class(
&self,
_class_declaration: arena::Box<'alloc, Statement<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportDeclaration : `export` `default` [lookahead <! {`function`, `async`, `class`}] AssignmentExpression `;`
pub fn export_default_value(
&self,
_expression: arena::Box<'alloc, Expression<'alloc>>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportClause : `{` `}`
pub fn exports_list_empty(&self) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportsList : ExportSpecifier
// ExportsList : ExportsList `,` ExportSpecifier
pub fn exports_list_append(
&self,
_list: arena::Box<'alloc, Void>,
_export_specifier: arena::Box<'alloc, Void>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportSpecifier : IdentifierName
pub fn export_specifier(
&self,
_identifier: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// ExportSpecifier : IdentifierName `as` IdentifierName
pub fn export_specifier_renaming(
&self,
_local_name: arena::Box<'alloc, Token>,
_exported_name: arena::Box<'alloc, Token>,
) -> Result<'alloc, arena::Box<'alloc, Void>> {
Err(ParseError::NotImplemented("export").into())
}
// Returns IsSimpleParameterList of `params`.
//
// NOTE: For Syntax-only parsing (NYI), the stack value for FormalParameters
// should contain this information.
fn is_params_simple(params: &FormalParameters<'alloc>) -> bool {
for param in params.items.iter() {
match param {
Parameter::Binding(Binding::BindingIdentifier(_)) => {}
_ => {
return false;
}
}
}
if params.rest.is_some() {
return false;
}
true
}
fn mark_labelled_statement(
&mut self,
label: &arena::Box<'alloc, Label>,
body: &Statement<'alloc>,
) {
let start_label_offset = label.loc.start;
let kind = match body {
Statement::ForStatement { .. }
| Statement::ForOfStatement { .. }
| Statement::ForInStatement { .. }
| Statement::WhileStatement { .. }
| Statement::DoWhileStatement { .. } => LabelKind::Loop,
Statement::LabelledStatement { .. } => LabelKind::LabelledLabel,
Statement::FunctionDeclaration { .. } => LabelKind::Function,
_ => LabelKind::Other,
};
self.context_metadata
.mark_label_kind_at_offset(start_label_offset, kind);
}
}
impl<'alloc> EarlyErrorChecker<'alloc> for AstBuilder<'alloc> {
fn context_metadata_mut(&mut self) -> &mut ContextMetadata {
&mut self.context_metadata
}
fn context_metadata(&self) -> &ContextMetadata {
&self.context_metadata
}
fn atoms(&self) -> &Rc<RefCell<SourceAtomSet<'alloc>>> {
&self.atoms
}
}