boa_parser 0.21.1

ECMAScript parser for the Boa JavaScript engine.
Documentation
use crate::parser::tests::check_script_parser;
use boa_ast::{
    Declaration, Expression, Span, Statement, StatementList, StatementListItem,
    declaration::{LexicalDeclaration, Variable, VariableList},
    expression::{
        Call, Identifier, NewTarget,
        access::{PropertyAccess, SimplePropertyAccess},
        literal::Literal,
    },
    function::{
        ClassDeclaration, ClassElement, ClassFieldDefinition, ClassMethodDefinition,
        FormalParameterList, FunctionBody, FunctionExpression,
    },
    property::MethodDefinitionKind,
};
use boa_interner::Interner;
use boa_macros::utf16;
use indoc::indoc;

#[test]
fn check_async_ordinary_method() {
    let interner = &mut Interner::default();

    let elements = vec![ClassElement::MethodDefinition(ClassMethodDefinition::new(
        boa_ast::function::ClassElementName::PropertyName(
            Identifier::new(
                interner.get_or_intern_static("async", utf16!("async")),
                Span::new((2, 5), (2, 10)),
            )
            .into(),
        ),
        FormalParameterList::default(),
        FunctionBody::new(StatementList::default(), Span::new((2, 13), (2, 16))),
        MethodDefinitionKind::Ordinary,
        false,
        boa_ast::LinearPosition::default(),
    ))];

    check_script_parser(
        indoc! {r#"
            class A {
                async() { }
            }
        "#},
        [Declaration::ClassDeclaration(
            ClassDeclaration::new(
                Identifier::new(
                    interner.get_or_intern_static("A", utf16!("A")),
                    Span::new((1, 7), (1, 8)),
                ),
                None,
                None,
                elements.into(),
            )
            .into(),
        )
        .into()],
        interner,
    );
}

#[test]
fn check_async_field_initialization() {
    let interner = &mut Interner::default();

    let elements = vec![ClassElement::FieldDefinition(ClassFieldDefinition::new(
        Identifier::new(
            interner.get_or_intern_static("async", utf16!("async")),
            Span::new((2, 5), (2, 10)),
        )
        .into(),
        Some(Literal::new(1, Span::new((3, 7), (3, 8))).into()),
    ))];

    check_script_parser(
        indoc! {"
            class A {
                async
                = 1
            }
        "},
        [
            Declaration::ClassDeclaration(Box::new(ClassDeclaration::new(
                Identifier::new(
                    interner.get_or_intern_static("A", utf16!("A")),
                    Span::new((1, 7), (1, 8)),
                ),
                None,
                None,
                elements.into(),
            )))
            .into(),
        ],
        interner,
    );
}

#[test]
fn check_async_field() {
    let interner = &mut Interner::default();

    let elements = vec![ClassElement::FieldDefinition(ClassFieldDefinition::new(
        Identifier::new(
            interner.get_or_intern_static("async", utf16!("async")),
            Span::new((2, 5), (2, 10)),
        )
        .into(),
        None,
    ))];

    check_script_parser(
        indoc! {r#"
            class A {
                async
            }
        "#},
        [
            Declaration::ClassDeclaration(Box::new(ClassDeclaration::new(
                Identifier::new(
                    interner.get_or_intern_static("A", utf16!("A")),
                    Span::new((1, 7), (1, 8)),
                ),
                None,
                None,
                elements.into(),
            )))
            .into(),
        ],
        interner,
    );
}

#[test]
fn check_new_target_with_property_access() {
    let interner = &mut Interner::default();

    let new_target = Expression::PropertyAccess(
        SimplePropertyAccess::new(
            NewTarget::new(Span::new((3, 21), (3, 31))).into(),
            Identifier::new(
                interner.get_or_intern_static("name", utf16!("name")),
                Span::new((3, 32), (3, 36)),
            ),
        )
        .into(),
    );

    let console = Call::new(
        PropertyAccess::Simple(SimplePropertyAccess::new(
            Identifier::new(
                interner.get_or_intern_static("console", utf16!("console")),
                Span::new((3, 9), (3, 16)),
            )
            .into(),
            Identifier::new(
                interner.get_or_intern_static("log", utf16!("log")),
                Span::new((3, 17), (3, 20)),
            ),
        ))
        .into(),
        [new_target].into(),
        Span::new((3, 20), (3, 37)),
    )
    .into();

    let constructor = FunctionExpression::new(
        Some(Identifier::new(
            interner.get_or_intern_static("A", utf16!("A")),
            Span::new((1, 7), (1, 8)),
        )),
        FormalParameterList::default(),
        FunctionBody::new(
            StatementList::new(
                [Statement::Expression(console).into()],
                boa_ast::LinearPosition::new(0),
                false,
            ),
            Span::new((2, 19), (4, 6)),
        ),
        None,
        false,
        Span::new((2, 5), (4, 6)),
    );

    let class = ClassDeclaration::new(
        Identifier::new(interner.get("A").unwrap(), Span::new((1, 7), (1, 8))),
        None,
        Some(constructor),
        Box::default(),
    );

    let instantiation = Expression::New(
        Call::new(
            Identifier::new(interner.get("A").unwrap(), Span::new((6, 15), (6, 16))).into(),
            Box::default(),
            Span::new((6, 11), (6, 18)),
        )
        .into(),
    );

    let const_decl = LexicalDeclaration::Const(
        VariableList::new(
            [Variable::from_identifier(
                Identifier::new(
                    interner.get_or_intern_static("a", utf16!("a")),
                    Span::new((6, 7), (6, 8)),
                ),
                Some(instantiation),
            )]
            .into(),
        )
        .unwrap(),
    );

    let script = [
        StatementListItem::Declaration(Declaration::ClassDeclaration(class.into()).into()),
        StatementListItem::Declaration(Declaration::Lexical(const_decl).into()),
    ];

    check_script_parser(
        indoc! {r#"
            class A {
                constructor() {
                    console.log(new.target.name);
                }
            }
            const a = new A();
        "#},
        script,
        interner,
    );
}

#[test]
fn check_non_reserved_keyword_as_identifier() {
    let interner = &mut Interner::default();

    check_script_parser(
        indoc! {r#"
            class of {}
        "#},
        [Declaration::ClassDeclaration(
            ClassDeclaration::new(
                Identifier::new(
                    interner.get_or_intern_static("of", utf16!("of")),
                    Span::new((1, 7), (1, 9)),
                ),
                None,
                None,
                vec![].into(),
            )
            .into(),
        )
        .into()],
        interner,
    );
}