use crate::error::Error;
use crate::expression::parse_assignment_expression;
use crate::statement::{parse_binding_pattern, parse_block_body};
use crate::stream::{Peek, expect_identifier, expect_kind, is_kind, peek, span_at};
use ecma_lex_cat::token::{Token, TokenKind};
use ecma_syntax_cat::class::{Class, ClassMember, MethodKind};
use ecma_syntax_cat::expression::PropertyKey;
use ecma_syntax_cat::function::Function;
use ecma_syntax_cat::pattern::{Pattern, PatternKind};
use ecma_syntax_cat::span::Span;
pub fn parse_function_declaration(
tokens: &[Token],
pos: usize,
) -> Result<(Function, usize), Error> {
let after_kw = expect_kind(tokens, pos, &TokenKind::KwFunction, "keyword `function`")?;
let (is_generator, after_star) = if is_kind(tokens, after_kw, &TokenKind::Star) {
(true, after_kw + 1)
} else {
(false, after_kw)
};
let (id, after_id) = expect_identifier(tokens, after_star)?;
let (params, after_params) = parse_formal_parameters(tokens, after_id)?;
let (body, after_body) = parse_block_body(tokens, after_params)?;
Ok((
Function::new(Some(id), params, body, false, is_generator),
after_body,
))
}
pub fn parse_function_declaration_optional_name(
tokens: &[Token],
pos: usize,
) -> Result<(Function, usize), Error> {
let after_kw = expect_kind(tokens, pos, &TokenKind::KwFunction, "keyword `function`")?;
let (is_generator, after_star) = if is_kind(tokens, after_kw, &TokenKind::Star) {
(true, after_kw + 1)
} else {
(false, after_kw)
};
let (id, after_id) = if matches!(
peek(tokens, after_star),
Peek::Token(t) if matches!(t.value(), TokenKind::Identifier(_))
) {
let (name, after_name) = expect_identifier(tokens, after_star)?;
(Some(name), after_name)
} else {
(None, after_star)
};
let (params, after_params) = parse_formal_parameters(tokens, after_id)?;
let (body, after_body) = parse_block_body(tokens, after_params)?;
Ok((
Function::new(id, params, body, false, is_generator),
after_body,
))
}
pub fn parse_formal_parameters(
tokens: &[Token],
pos: usize,
) -> Result<(Vec<Pattern>, usize), Error> {
let after_open = expect_kind(tokens, pos, &TokenKind::LParen, "`(`")?;
if is_kind(tokens, after_open, &TokenKind::RParen) {
Ok((Vec::new(), after_open + 1))
} else {
collect_formal_params(tokens, after_open, Vec::new())
}
}
fn collect_formal_params(
tokens: &[Token],
pos: usize,
acc: Vec<Pattern>,
) -> Result<(Vec<Pattern>, usize), Error> {
let (param, after_param) = parse_formal_parameter(tokens, pos)?;
let extended: Vec<Pattern> = acc.into_iter().chain(std::iter::once(param)).collect();
if is_kind(tokens, after_param, &TokenKind::Comma) {
let next = after_param + 1;
if is_kind(tokens, next, &TokenKind::RParen) {
Ok((extended, next + 1))
} else {
collect_formal_params(tokens, next, extended)
}
} else {
let after_close = expect_kind(tokens, after_param, &TokenKind::RParen, "`)` or `,`")?;
Ok((extended, after_close))
}
}
fn parse_formal_parameter(tokens: &[Token], pos: usize) -> Result<(Pattern, usize), Error> {
if is_kind(tokens, pos, &TokenKind::Spread) {
let start_span = span_at(tokens, pos);
let (inner, after_inner) = parse_binding_pattern(tokens, pos + 1)?;
let span = Span::new(start_span.start(), inner.span().end());
Ok((
Pattern::new(
PatternKind::Rest {
argument: Box::new(inner),
},
span,
),
after_inner,
))
} else {
parse_binding_pattern(tokens, pos)
}
}
pub fn parse_class(tokens: &[Token], pos: usize) -> Result<(Class, usize), Error> {
let after_kw = expect_kind(tokens, pos, &TokenKind::KwClass, "keyword `class`")?;
let (id, after_id) = if matches!(
peek(tokens, after_kw),
Peek::Token(t) if matches!(t.value(), TokenKind::Identifier(_))
) {
let (name, after_name) = expect_identifier(tokens, after_kw)?;
(Some(name), after_name)
} else {
(None, after_kw)
};
let (super_class, after_super) = if is_kind(tokens, after_id, &TokenKind::KwExtends) {
let (expr, after_expr) = parse_assignment_expression(tokens, after_id + 1)?;
(Some(expr), after_expr)
} else {
(None, after_id)
};
let after_lbrace = expect_kind(tokens, after_super, &TokenKind::LBrace, "`{`")?;
let (members, after_rbrace) = collect_class_members(tokens, after_lbrace, Vec::new())?;
Ok((Class::new(id, super_class, members), after_rbrace))
}
fn collect_class_members(
tokens: &[Token],
pos: usize,
acc: Vec<ClassMember>,
) -> Result<(Vec<ClassMember>, usize), Error> {
if is_kind(tokens, pos, &TokenKind::RBrace) {
Ok((acc, pos + 1))
} else if is_kind(tokens, pos, &TokenKind::Semicolon) {
collect_class_members(tokens, pos + 1, acc)
} else {
let (member, after_member) = parse_class_member(tokens, pos)?;
let extended: Vec<ClassMember> = acc.into_iter().chain(std::iter::once(member)).collect();
collect_class_members(tokens, after_member, extended)
}
}
fn parse_class_member(tokens: &[Token], pos: usize) -> Result<(ClassMember, usize), Error> {
let (is_static, after_static) = if is_static_keyword_at(tokens, pos) {
(true, pos + 1)
} else {
(false, pos)
};
if is_kind(tokens, after_static, &TokenKind::LBrace) && is_static {
let (body, after_body) = parse_block_body(tokens, after_static)?;
Ok((ClassMember::StaticBlock { body }, after_body))
} else {
parse_class_method_or_field(tokens, after_static, is_static)
}
}
fn is_static_keyword_at(tokens: &[Token], pos: usize) -> bool {
matches!(
peek(tokens, pos),
Peek::Token(t) if matches!(t.value(), TokenKind::KwStatic)
) && !matches!(
peek(tokens, pos + 1),
Peek::Token(t) if matches!(t.value(), TokenKind::Eq | TokenKind::Semicolon | TokenKind::LParen)
)
}
fn parse_class_method_or_field(
tokens: &[Token],
pos: usize,
is_static: bool,
) -> Result<(ClassMember, usize), Error> {
let (method_kind, after_kind) = detect_method_kind(tokens, pos);
let (key, computed, after_key) = parse_class_property_key(tokens, after_kind)?;
if is_kind(tokens, after_key, &TokenKind::LParen) {
let (params, after_params) = parse_formal_parameters(tokens, after_key)?;
let (body, after_body) = parse_block_body(tokens, after_params)?;
let function = Function::new(None, params, body, false, false);
Ok((
ClassMember::Method {
key,
value: function,
kind: method_kind,
is_static,
computed,
},
after_body,
))
} else if is_kind(tokens, after_key, &TokenKind::Eq) {
let (value, after_value) = parse_assignment_expression(tokens, after_key + 1)?;
let after_semi = if is_kind(tokens, after_value, &TokenKind::Semicolon) {
after_value + 1
} else {
after_value
};
Ok((
ClassMember::Property {
key,
value: Some(value),
is_static,
computed,
},
after_semi,
))
} else {
let after_semi = if is_kind(tokens, after_key, &TokenKind::Semicolon) {
after_key + 1
} else {
after_key
};
Ok((
ClassMember::Property {
key,
value: None,
is_static,
computed,
},
after_semi,
))
}
}
fn detect_method_kind(tokens: &[Token], pos: usize) -> (MethodKind, usize) {
match peek(tokens, pos) {
Peek::Eof => (MethodKind::Method, pos),
Peek::Token(tok) => match tok.value() {
TokenKind::Identifier(name) if name == "constructor" => (MethodKind::Constructor, pos),
TokenKind::Identifier(name) if name == "get" && peek_after_modifier(tokens, pos) => {
(MethodKind::Get, pos + 1)
}
TokenKind::Identifier(name) if name == "set" && peek_after_modifier(tokens, pos) => {
(MethodKind::Set, pos + 1)
}
_other => (MethodKind::Method, pos),
},
}
}
fn peek_after_modifier(tokens: &[Token], pos: usize) -> bool {
matches!(
peek(tokens, pos + 1),
Peek::Token(t) if matches!(
t.value(),
TokenKind::Identifier(_)
| TokenKind::PrivateIdentifier(_)
| TokenKind::String(_)
| TokenKind::Number(_)
| TokenKind::LBracket
)
)
}
fn parse_class_property_key(
tokens: &[Token],
pos: usize,
) -> Result<(PropertyKey, bool, usize), Error> {
match peek(tokens, pos) {
Peek::Eof => Err(Error::UnexpectedEof {
expected: "class member name",
}),
Peek::Token(tok) => match tok.value() {
TokenKind::LBracket => {
let (inner, after_inner) = parse_assignment_expression(tokens, pos + 1)?;
let after_close = expect_kind(tokens, after_inner, &TokenKind::RBracket, "`]`")?;
Ok((PropertyKey::Computed(Box::new(inner)), true, after_close))
}
TokenKind::String(s) => Ok((PropertyKey::String(s.clone()), false, pos + 1)),
TokenKind::Number(n) => Ok((PropertyKey::Number(*n), false, pos + 1)),
TokenKind::PrivateIdentifier(_) => {
let (id, after) = crate::stream::expect_private_identifier(tokens, pos)?;
Ok((PropertyKey::Private(id), false, after))
}
_other => {
let (id, after) = crate::stream::expect_identifier_or_keyword(tokens, pos)?;
Ok((PropertyKey::Identifier(id), false, after))
}
},
}
}