use super::class_or_object::ParseClassBodyResult;
use super::expr::Asi;
use super::pattern::is_valid_pattern_identifier;
use super::pattern::ParsePatternRules;
use super::ParseCtx;
use super::Parser;
use crate::ast::Node;
use crate::ast::Syntax;
use crate::ast::VarDeclMode;
use crate::ast::VariableDeclarator;
use crate::error::SyntaxErrorType;
use crate::error::SyntaxResult;
use crate::token::TokenType;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum VarDeclParseMode {
Asi,
Leftmost,
}
impl<'a> Parser<'a> {
pub fn parse_decl_var(
&mut self,
ctx: ParseCtx,
parse_mode: VarDeclParseMode,
export: bool,
) -> SyntaxResult<Node> {
let t = self.next()?;
let mode = match t.typ {
TokenType::KeywordLet => VarDeclMode::Let,
TokenType::KeywordConst => VarDeclMode::Const,
TokenType::KeywordVar => VarDeclMode::Var,
_ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("variable declaration"))),
};
let mut declarators = Vec::new();
let mut loc = t.loc;
loop {
let pattern = self.parse_pattern(ctx)?;
loc.extend(pattern.loc);
let mut asi = match parse_mode {
VarDeclParseMode::Asi => Asi::can(),
VarDeclParseMode::Leftmost => Asi::no(),
};
let initializer = if self.consume_if(TokenType::Equals)?.is_match() {
let expr = self.parse_expr_until_either_with_asi(
ctx,
TokenType::Semicolon,
TokenType::Comma,
&mut asi,
)?;
loc.extend(expr.loc);
Some(expr)
} else {
None
};
declarators.push(VariableDeclarator {
pattern,
initializer,
});
match parse_mode {
VarDeclParseMode::Asi => {
if self.consume_if(TokenType::Semicolon)?.is_match() || asi.did_end_with_asi {
break;
}
let t = self.peek()?;
if t.preceded_by_line_terminator && t.typ != TokenType::Comma {
break;
};
self.require(TokenType::Comma)?;
}
VarDeclParseMode::Leftmost => {
if !self.consume_if(TokenType::Comma)?.is_match() {
break;
}
}
}
}
Ok(Node::new(loc, Syntax::VarDecl {
export,
mode,
declarators,
}))
}
pub fn parse_decl_function(
&mut self,
ctx: ParseCtx,
export: bool,
export_default: bool,
) -> SyntaxResult<Node> {
let is_async = self.consume_if(TokenType::KeywordAsync)?.is_match();
let start = self.require(TokenType::KeywordFunction)?.loc;
let generator = self.consume_if(TokenType::Asterisk)?.is_match();
let name = match self
.consume_if_pred(|t| is_valid_pattern_identifier(t.typ, ctx.rules))?
.match_loc()
{
Some(name) => Some(Node::new(name, Syntax::ClassOrFunctionName {
name: self.string(name),
})),
_ => {
if !export_default {
return Err(start.error(SyntaxErrorType::ExpectedSyntax("function name"), None));
};
None
}
};
let parameters = self.parse_function_parameters(ctx)?;
let body = self.parse_function_body(ctx.with_rules(ParsePatternRules {
await_allowed: !is_async && ctx.rules.await_allowed,
yield_allowed: !generator && ctx.rules.yield_allowed,
}))?;
Ok(Node::new(start + body.loc, Syntax::FunctionDecl {
export,
export_default,
name,
function: Node::new(start + body.loc, Syntax::Function {
arrow: false,
async_: is_async,
generator,
parameters,
body,
}),
}))
}
pub fn parse_decl_class(
&mut self,
ctx: ParseCtx,
export: bool,
export_default: bool,
) -> SyntaxResult<Node> {
let start = self.require(TokenType::KeywordClass)?.loc;
let name = match self
.consume_if_pred(|t| is_valid_pattern_identifier(t.typ, ctx.rules))?
.match_loc()
{
Some(name) => {
let name_node = Node::new(name, Syntax::ClassOrFunctionName {
name: self.string(name),
});
Some(name_node)
}
None => {
if !export_default {
return Err(start.error(SyntaxErrorType::ExpectedSyntax("class name"), None));
};
None
}
};
let extends = if self.consume_if(TokenType::KeywordExtends)?.is_match() {
Some(self.parse_expr(ctx, TokenType::BraceOpen)?)
} else {
None
};
let ParseClassBodyResult { end, members } = self.parse_class_body(ctx)?;
Ok(Node::new(start + end, Syntax::ClassDecl {
export,
export_default,
name,
extends,
members,
}))
}
}