///| Minimal parser inspired by Serenity TypeScript Parser

///|
fn compute_line_starts(text : String) -> Array[Int] {
  let starts : Array[Int] = [0]
  let mut i = 0
  let n = text.char_length()
  while i < n {
    match text.get_char(i) {
      Some('\n') => {
        i = i + 1
        starts.push(i)
        continue
      }
      Some('\r') => {
        if text.get_char(i + 1) is Some('\n') {
          i = i + 2
        } else {
          i = i + 1
        }
        starts.push(i)
        continue
      }
      _ => ()
    }
    i = i + 1
  }
  starts
}

///|
fn line_col_from_starts(starts : Array[Int], pos : Int) -> (Int, Int) {
  if starts.length() == 0 {
    return (1, pos + 1)
  }
  let mut low = 0
  let mut high = starts.length() - 1
  while low <= high {
    let mid = (low + high) / 2
    let start = starts[mid]
    if start <= pos {
      if mid + 1 < starts.length() && starts[mid + 1] <= pos {
        low = mid + 1
      } else {
        let line = mid + 1
        let column = pos - start + 1
        return (line, column)
      }
    } else {
      if mid == 0 {
        break
      }
      high = mid - 1
    }
  }
  (1, pos + 1)
}

///|
fn span_with_line_info(
  line_starts : Array[Int],
  start : Int,
  end : Int,
) -> Span {
  let (line, column) = line_col_from_starts(line_starts, start)
  let (end_line, end_column) = line_col_from_starts(line_starts, end)
  Span::with_line_info(start, end, line, column, end_line, end_column)
}

///|
fn parse_error_at_with(
  line_starts : Array[Int],
  start : Int,
  end : Int,
  kind : ParseErrorKind,
) -> ParseError {
  ParseError::ParseError(
    span=span_with_line_info(line_starts, start, end),
    kind~,
  )
}

///|
pub enum LexKind {
  Identifier(String)
  PrivateIdentifier(String)
  Number(String)
  String(String)
  Regex(String)
  DocComment(String)
  NoSubTemplate(String, String)
  TemplateHead(String, String)
  TemplateMiddle(String, String)
  TemplateTail(String, String)
  KeywordLet
  KeywordConst
  KeywordVar
  KeywordUsing
  KeywordFunction
  KeywordReturn
  KeywordIf
  KeywordElse
  KeywordWhile
  KeywordFor
  KeywordDo
  KeywordBreak
  KeywordContinue
  KeywordSwitch
  KeywordCase
  KeywordDefault
  KeywordDebugger
  KeywordTry
  KeywordCatch
  KeywordFinally
  KeywordThrow
  KeywordWith
  KeywordNew
  KeywordClass
  KeywordInterface
  KeywordType
  KeywordEnum
  KeywordModule
  KeywordNamespace
  KeywordImport
  KeywordExport
  KeywordAssert
  KeywordGlobal
  KeywordFrom
  KeywordExtends
  KeywordImplements
  KeywordIn
  KeywordInstanceof
  KeywordOf
  KeywordAs
  KeywordSatisfies
  KeywordDelete
  KeywordTypeof
  KeywordVoid
  KeywordAwait
  KeywordYield
  KeywordThis
  KeywordSuper
  KeywordTrue
  KeywordFalse
  KeywordNull
  LParen
  RParen
  LBrace
  RBrace
  LBracket
  RBracket
  Comma
  Dot
  DotDotDot
  Semicolon
  Colon
  Question
  QuestionDot
  Plus
  PlusPlus
  PlusEq
  Minus
  MinusMinus
  MinusEq
  Star
  StarStar
  StarEq
  StarStarEq
  Slash
  SlashEq
  Percent
  PercentEq
  Bang
  Tilde
  Eq
  EqEq
  Arrow
  EqEqEq
  BangEq
  BangEqEq
  Lt
  LtLt
  LtLtEq
  LtSlash
  Gt
  GtGt
  GtGtGt
  GtGtEq
  GtGtGtEq
  LtEq
  GtEq
  Ampersand
  AmpAmp
  AmpersandEq
  AmpAmpEq
  Bar
  BarBar
  BarEq
  BarBarEq
  Caret
  CaretEq
  QuestionQuestion
  QuestionQuestionEq
  At
  Eof
} derive(Eq)

///|
impl Show for LexKind with to_string(self) {
  match self {
    Identifier(name) => "identifier \{name}"
    PrivateIdentifier(name) => "private identifier \{name}"
    Number(text) => "number literal \{text}"
    String(text) => "string literal \"\{text}\""
    Regex(text) => "regex /\{text}/"
    DocComment(_) => "doc comment"
    NoSubTemplate(text, _) => "template `\{text}`"
    TemplateHead(text, _) => "template head `\{text}`"
    TemplateMiddle(text, _) => "template middle `\{text}`"
    TemplateTail(text, _) => "template tail `\{text}`"
    KeywordLet => "let"
    KeywordConst => "const"
    KeywordVar => "var"
    KeywordUsing => "using"
    KeywordFunction => "function"
    KeywordReturn => "return"
    KeywordIf => "if"
    KeywordElse => "else"
    KeywordWhile => "while"
    KeywordFor => "for"
    KeywordDo => "do"
    KeywordBreak => "break"
    KeywordContinue => "continue"
    KeywordSwitch => "switch"
    KeywordCase => "case"
    KeywordDefault => "default"
    KeywordDebugger => "debugger"
    KeywordTry => "try"
    KeywordCatch => "catch"
    KeywordFinally => "finally"
    KeywordThrow => "throw"
    KeywordWith => "with"
    KeywordNew => "new"
    KeywordClass => "class"
    KeywordInterface => "interface"
    KeywordType => "type"
    KeywordEnum => "enum"
    KeywordModule => "module"
    KeywordNamespace => "namespace"
    KeywordImport => "import"
    KeywordExport => "export"
    KeywordAssert => "assert"
    KeywordGlobal => "global"
    KeywordFrom => "from"
    KeywordExtends => "extends"
    KeywordImplements => "implements"
    KeywordIn => "in"
    KeywordInstanceof => "instanceof"
    KeywordOf => "of"
    KeywordAs => "as"
    KeywordSatisfies => "satisfies"
    KeywordDelete => "delete"
    KeywordTypeof => "typeof"
    KeywordVoid => "void"
    KeywordAwait => "await"
    KeywordYield => "yield"
    KeywordThis => "this"
    KeywordSuper => "super"
    KeywordTrue => "true"
    KeywordFalse => "false"
    KeywordNull => "null"
    LParen => "'('"
    RParen => "')'"
    LBrace => "'{'"
    RBrace => "'}'"
    LBracket => "'['"
    RBracket => "']'"
    Comma => "','"
    Dot => "'.'"
    DotDotDot => "'...'"
    Semicolon => "';'"
    Colon => "':'"
    Question => "?'"
    QuestionDot => "'?.'"
    Plus => "'+'"
    PlusPlus => "'++'"
    PlusEq => "'+='"
    Minus => "'-'"
    MinusMinus => "'--'"
    MinusEq => "'-='"
    Star => "'*'"
    StarStar => "'**'"
    StarEq => "'*='"
    StarStarEq => "'**='"
    Slash => "'/'"
    SlashEq => "'/='"
    Percent => "'%'"
    PercentEq => "'%='"
    Bang => "'!'"
    Tilde => "'~'"
    Eq => "'='"
    EqEq => "'=='"
    Arrow => "'=>'"
    EqEqEq => "'==='"
    BangEq => "'!='"
    BangEqEq => "'!=='"
    Lt => "'<'"
    LtLt => "'<<'"
    LtLtEq => "'<<='"
    LtSlash => "' "'>'"
    GtGt => "'>>'"
    GtGtGt => "'>>>'"
    GtGtEq => "'>>='"
    GtGtGtEq => "'>>>='"
    LtEq => "'<='"
    GtEq => "'>='"
    Ampersand => "'&'"
    AmpAmp => "'&&'"
    AmpersandEq => "'&='"
    AmpAmpEq => "'&&='"
    Bar => "'|'"
    BarBar => "'||'"
    BarEq => "'|='"
    BarBarEq => "'||='"
    Caret => "'^'"
    CaretEq => "'^='"
    QuestionQuestion => "'??'"
    QuestionQuestionEq => "'??='"
    At => "'@'"
    Eof => "end of file"
  }
}

///|
impl Show for LexKind with output(self, logger) {
  logger.write_string(self.to_string())
}

///|
pub struct LexToken {
  kind : LexKind
  start : Int
  end : Int
  has_leading_line_break : Bool
} derive(Show)

///|
fn LexToken::new(
  kind : LexKind,
  start : Int,
  end : Int,
  has_leading_line_break : Bool,
) -> LexToken {
  { kind, start, end, has_leading_line_break }
}

///|
fn is_digit(c : Char) -> Bool {
  c >= '0' && c <= '9'
}

///|
fn is_alpha(c : Char) -> Bool {
  (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$'
}

///|
fn is_ident_start(c : Char) -> Bool {
  is_alpha(c)
}

///|
fn is_ident_part(c : Char) -> Bool {
  is_alpha(c) || is_digit(c)
}

///|
fn is_hex_digit(c : Char) -> Bool {
  (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
}

///|
fn is_bin_digit(c : Char) -> Bool {
  c == '0' || c == '1'
}

///|
fn is_oct_digit(c : Char) -> Bool {
  c >= '0' && c <= '7'
}

///|
fn is_dec_digit_or_sep(c : Char) -> Bool {
  is_digit(c) || c == '_'
}

///|
fn is_hex_digit_or_sep(c : Char) -> Bool {
  is_hex_digit(c) || c == '_'
}

///|
fn is_bin_digit_or_sep(c : Char) -> Bool {
  is_bin_digit(c) || c == '_'
}

///|
fn is_oct_digit_or_sep(c : Char) -> Bool {
  is_oct_digit(c) || c == '_'
}

///|
fn read_digits(text : String, start : Int, pred : (Char) -> Bool) -> Int {
  let mut i = start
  while text.get_char(i) is Some(c) && pred(c) {
    i = i + 1
  }
  i
}

///|
fn trim_leading_whitespace(text : String) -> String {
  text.trim_start(chars=" \t").to_string()
}

///|
fn trim_ending_whitespace(text : String) -> String {
  text.trim_end(chars=" \t").to_string()
}

///|
fn trim_doc_comment_content(raw : String) -> String {
  let lines : Array[String] = []
  let n = raw.char_length()
  let mut line_start = 0
  let mut i = 0
  while i < n {
    match raw.get_char(i) {
      Some('\n') => {
        lines.push(try! raw[line_start:i].to_string())
        line_start = i + 1
      }
      Some('\r') => {
        lines.push(try! raw[line_start:i].to_string())
        if i + 1 < n && raw[i + 1] == '\n' {
          i = i + 1
        }
        line_start = i + 1
      }
      _ => ()
    }
    i = i + 1
  }
  if line_start <= n {
    lines.push(try! raw[line_start:n].to_string())
  }
  let processed : Array[String] = []
  let last_index = if lines.length() == 0 { 0 } else { lines.length() - 1 }
  for idx = 0; idx < lines.length(); idx = idx + 1 {
    let line = lines[idx]
    let trimmed = if idx == 0 || idx == last_index {
      trim_leading_whitespace(line)
    } else {
      let mut temp = trim_leading_whitespace(line)
      if temp.char_length() > 0 && temp.get_char(0) is Some('*') {
        temp = try! temp[1:temp.char_length()].to_string()
        temp = trim_leading_whitespace(temp)
      }
      temp
    }
    let trimmed = trim_ending_whitespace(trimmed)
    processed.push(trimmed)
  }
  let mut start_line = 0
  while start_line < processed.length() && processed[start_line] == "" {
    start_line = start_line + 1
  }
  let mut end_line = processed.length()
  while end_line > start_line && processed[end_line - 1] == "" {
    end_line = end_line - 1
  }
  let mut result = ""
  let mut j = start_line
  while j < end_line {
    if j > start_line {
      result = result + "\n"
    }
    result = result + processed[j]
    j = j + 1
  }
  result
}

///|
fn trailing_numeric_sep_pos(text : String, start : Int, end : Int) -> Int? {
  if end > start {
    match text.get_char(end - 1) {
      Some('_') => Some(end - 1)
      _ => None
    }
  } else {
    None
  }
}

///|
fn read_exponent(text : String, start : Int) -> (Int, Int?) {
  let mut i = start
  let mut trailing_sep : Int? = None
  if text.get_char(i) is Some(c) && (c == 'e' || c == 'E') {
    let mut j = i + 1
    if text.get_char(j) is Some(sign) && (sign == '+' || sign == '-') {
      j = j + 1
    }
    let after = read_digits(text, j, is_dec_digit_or_sep)
    if after > j {
      trailing_sep = trailing_numeric_sep_pos(text, j, after)
      i = after
    }
  }
  (i, trailing_sep)
}

///|
fn can_end_expr(kind : LexKind) -> Bool {
  match kind {
    Identifier(_) => true
    KeywordType => true
    KeywordAs => true
    KeywordFrom => true
    KeywordOf => true
    KeywordUsing => true
    Number(_) => true
    String(_) => true
    Regex(_) => true
    NoSubTemplate(_, _) => true
    KeywordThis => true
    KeywordSuper => true
    KeywordTrue => true
    KeywordFalse => true
    KeywordNull => true
    RParen => true
    RBracket => true
    RBrace => true
    PlusPlus => true
    MinusMinus => true
    _ => false
  }
}

///|
fn modifier_kind_from_name(name : String) -> ModifierKind? {
  match name {
    "abstract" => Some(ModifierKind::Abstract)
    "accessor" => Some(ModifierKind::Accessor)
    "async" => Some(ModifierKind::Async)
    "const" => Some(ModifierKind::Const)
    "declare" => Some(ModifierKind::Declare)
    "default" => Some(ModifierKind::Default)
    "export" => Some(ModifierKind::Export)
    "in" => Some(ModifierKind::In)
    "public" => Some(ModifierKind::Public)
    "private" => Some(ModifierKind::Private)
    "protected" => Some(ModifierKind::Protected)
    "readonly" => Some(ModifierKind::Readonly)
    "override" => Some(ModifierKind::Override)
    "out" => Some(ModifierKind::Out)
    "static" => Some(ModifierKind::Static)
    _ => None
  }
}

///|
fn is_param_modifier(kind : ModifierKind) -> Bool {
  match kind {
    ModifierKind::Public => true
    ModifierKind::Private => true
    ModifierKind::Protected => true
    ModifierKind::Readonly => true
    ModifierKind::Override => true
    _ => false
  }
}

///|
fn Parser::parse_decorators(
  self : Parser,
  fallback_start : Int,
) -> NodeArray[Expr]? raise ParseError {
  let items : Array[Expr] = []
  let mut start_start : Int? = None
  let mut end_start : Int? = None
  while self.at(LexKind::At) {
    let at_tok = self.advance()
    let expr = self.parse_starttfix_expr()
    if start_start is None {
      start_start = Some(at_tok.start)
    }
    end_start = Some(expr.span.end)
    items.push(expr)
    self.skip_doc_comments_in_expr()
  }
  if items.length() == 0 {
    None
  } else {
    Some(
      self.node_array(
        items,
        start_start.unwrap_or(fallback_start),
        end_start.unwrap_or(fallback_start),
      ),
    )
  }
}

///|
fn Parser::parse_modifiers(
  self : Parser,
  fallback_start : Int,
) -> NodeArray[ModifierKind]? {
  let items : Array[ModifierKind] = []
  let mut start_start : Int? = None
  let mut end_start : Int? = None
  for {
    self.skip_doc_comments_in_expr()
    match self.current().kind {
      KeywordExport => {
        self.has_export_syntax = true
        let tok = self.advance()
        if start_start is None {
          start_start = Some(tok.start)
        }
        end_start = Some(tok.end)
        items.push(ModifierKind::Export)
        continue
      }
      KeywordDefault => {
        let tok = self.advance()
        if start_start is None {
          start_start = Some(tok.start)
        }
        end_start = Some(tok.end)
        items.push(ModifierKind::Default)
        continue
      }
      Identifier(text) =>
        match modifier_kind_from_name(text) {
          Some(kind) => {
            let tok = self.advance()
            if start_start is None {
              start_start = Some(tok.start)
            }
            end_start = Some(tok.end)
            items.push(kind)
            continue
          }
          None => break
        }
      _ => break
    }
  }
  if items.length() == 0 {
    None
  } else {
    Some(
      self.node_array(
        items,
        start_start.unwrap_or(fallback_start),
        end_start.unwrap_or(fallback_start),
      ),
    )
  }
}

///|
fn Parser::parse_parameter_modifiers(
  self : Parser,
  fallback_start : Int,
) -> NodeArray[ModifierKind]? {
  let items : Array[ModifierKind] = []
  let mut start_start : Int? = None
  let mut end_start : Int? = None
  for {
    self.skip_doc_comments_in_expr()
    match self.current().kind {
      Identifier(text) =>
        match modifier_kind_from_name(text) {
          Some(kind) if is_param_modifier(kind) => {
            let tok = self.advance()
            if start_start is None {
              start_start = Some(tok.start)
            }
            end_start = Some(tok.end)
            items.push(kind)
            continue
          }
          _ => break
        }
      _ => break
    }
  }
  if items.length() == 0 {
    None
  } else {
    Some(
      self.node_array(
        items,
        start_start.unwrap_or(fallback_start),
        end_start.unwrap_or(fallback_start),
      ),
    )
  }
}

///|

///|
fn lex(
  text : String,
  line_starts : Array[Int],
) -> Array[LexToken] raise ParseError {
  fn slice(text : String, start : Int, end : Int) -> String raise ParseError {
    if start <= end && end <= text.char_length() {
      text[start:end].to_string() catch {
        _ =>
          raise parse_error_at_with(
            line_starts,
            start,
            end,
            ParseErrorKind::InvalidSlice(end~),
          )
      }
    } else {
      raise parse_error_at_with(
        line_starts,
        start,
        end,
        ParseErrorKind::InvalidSlice(end~),
      )
    }
  }

  let tokens : Array[LexToken] = []
  let mut i = 0
  let n = text.char_length()
  let mut prev_can_end = false
  let mut template_expr_depth = 0
  let template_stack : Array[Int] = []
  let mut has_line_break = false
  while i < n {
    let c = match text.get_char(i) {
      Some(ch) => ch
      None =>
        raise parse_error_at_with(
          line_starts,
          i,
          i,
          ParseErrorKind::InvalidChar('\u0000'),
        )
    }
    if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
      if c == '\n' || c == '\r' {
        has_line_break = true
      }
      i = i + 1
      continue
    }
    if c == '#' && text.get_char(i + 1) is Some(next) && is_ident_start(next) {
      let start = i
      i = i + 1
      let name_start = i
      i = i + 1
      while text.get_char(i) is Some(c) && is_ident_part(c) {
        i = i + 1
      }
      let value = slice(text, name_start, i)
      let kind = LexKind::PrivateIdentifier(value.to_string())
      tokens.push(LexToken::new(kind, start, i, has_line_break))
      has_line_break = false
      prev_can_end = false
      continue
    }
    if template_expr_depth > 0 && c == '}' {
      template_expr_depth = template_expr_depth - 1
      if template_expr_depth > 0 {
        let start = i
        i = i + 1
        let kind = LexKind::RBrace
        tokens.push(LexToken::new(kind, start, i, has_line_break))
        has_line_break = false
        prev_can_end = can_end_expr(kind)
        continue
      }
      i = i + 1
      let chunk_start = i
      let mut terminated = false
      while i < n {
        if text[i] == '\\' && i + 1 < n {
          i = i + 2
          continue
        }
        if text[i] == '`' {
          let raw = slice(text, chunk_start, i)
          i = i + 1
          let kind = LexKind::TemplateTail(raw.to_string(), raw.to_string())
          tokens.push(LexToken::new(kind, chunk_start, i, has_line_break))
          has_line_break = false
          prev_can_end = true
          template_expr_depth = match template_stack.pop() {
            Some(d) => d
            None => 0
          }
          terminated = true
          break
        }
        if text[i] == '$' && i + 1 < n && text[i + 1] == '{' {
          let raw = slice(text, chunk_start, i)
          i = i + 2
          let kind = LexKind::TemplateMiddle(raw.to_string(), raw.to_string())
          tokens.push(LexToken::new(kind, chunk_start, i, has_line_break))
          has_line_break = false
          prev_can_end = false
          template_expr_depth = 1
          terminated = true
          break
        }
        i = i + 1
      }
      if !terminated {
        raise parse_error_at_with(
          line_starts,
          chunk_start,
          i,
          ParseErrorKind::UnterminatedString,
        )
      }
      continue
    }
    if c == '`' {
      let start = i
      i = i + 1
      let chunk_start = i
      let mut terminated = false
      while i < n {
        if text[i] == '\\' && i + 1 < n {
          i = i + 2
          continue
        }
        if text[i] == '`' {
          let raw = slice(text, chunk_start, i)
          i = i + 1
          let kind = LexKind::NoSubTemplate(raw.to_string(), raw.to_string())
          tokens.push(LexToken::new(kind, start, i, has_line_break))
          has_line_break = false
          prev_can_end = true
          terminated = true
          break
        }
        if text[i] == '$' && i + 1 < n && text[i + 1] == '{' {
          let raw = slice(text, chunk_start, i)
          i = i + 2
          let kind = LexKind::TemplateHead(raw.to_string(), raw.to_string())
          tokens.push(LexToken::new(kind, start, i, has_line_break))
          has_line_break = false
          prev_can_end = false
          template_stack.push(template_expr_depth)
          template_expr_depth = 1
          terminated = true
          break
        }
        i = i + 1
      }
      if !terminated {
        raise parse_error_at_with(
          line_starts,
          start,
          i,
          ParseErrorKind::UnterminatedString,
        )
      }
      continue
    }
    if c == '/' && text.get_char(i + 1) is Some(next) {
      if next == '/' {
        i = i + 2
        while i < n && text[i] != '\n' {
          i = i + 1
        }
        continue
      }
      if next == '*' {
        if i + 2 < n && text[i + 2] == '*' {
          let start = i
          i = i + 3
          let content_start = i
          let mut comment_has_line_break = false
          let mut terminated = false
          while i + 1 < n {
            if text[i] == '\n' || text[i] == '\r' {
              comment_has_line_break = true
            }
            if text[i] == '*' && text[i + 1] == '/' {
              let raw = slice(text, content_start, i)
              let trimmed = trim_doc_comment_content(raw)
              i = i + 2
              let kind = LexKind::DocComment(trimmed)
              tokens.push(LexToken::new(kind, start, i, has_line_break))
              has_line_break = has_line_break || comment_has_line_break
              terminated = true
              break
            }
            i = i + 1
          }
          if !terminated {
            i = n
            has_line_break = has_line_break || comment_has_line_break
          }
          continue
        }
        i = i + 2
        while i + 1 < n {
          if text[i] == '\n' || text[i] == '\r' {
            has_line_break = true
          }
          if text[i] == '*' && text[i + 1] == '/' {
            i = i + 2
            break
          }
          i = i + 1
        }
        continue
      }
      if !prev_can_end {
        let start = i
        i = i + 1
        while i < n {
          if text[i] == '\\' && i + 1 < n {
            i = i + 2
            continue
          }
          if text[i] == '/' {
            i = i + 1
            break
          }
          i = i + 1
        }
        while i < n {
          match text.get_char(i) {
            Some(ch) if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') =>
              i = i + 1
            _ => break
          }
        }
        let value = slice(text, start, i)
        let kind = LexKind::Regex(value.to_string())
        tokens.push(LexToken::new(kind, start, i, has_line_break))
        has_line_break = false
        prev_can_end = true
        continue
      }
    }
    if is_ident_start(c) {
      let start = i
      i = i + 1
      while text.get_char(i) is Some(c) && is_ident_part(c) {
        i = i + 1
      }
      let value = slice(text, start, i)
      let kind = match value {
        "let" => LexKind::KeywordLet
        "const" => LexKind::KeywordConst
        "var" => LexKind::KeywordVar
        "using" => LexKind::KeywordUsing
        "function" => LexKind::KeywordFunction
        "return" => LexKind::KeywordReturn
        "if" => LexKind::KeywordIf
        "else" => LexKind::KeywordElse
        "while" => LexKind::KeywordWhile
        "for" => LexKind::KeywordFor
        "do" => LexKind::KeywordDo
        "break" => LexKind::KeywordBreak
        "continue" => LexKind::KeywordContinue
        "switch" => LexKind::KeywordSwitch
        "case" => LexKind::KeywordCase
        "default" => LexKind::KeywordDefault
        "debugger" => LexKind::KeywordDebugger
        "try" => LexKind::KeywordTry
        "catch" => LexKind::KeywordCatch
        "finally" => LexKind::KeywordFinally
        "throw" => LexKind::KeywordThrow
        "with" => LexKind::KeywordWith
        "new" => LexKind::KeywordNew
        "class" => LexKind::KeywordClass
        "interface" => LexKind::KeywordInterface
        "type" => LexKind::KeywordType
        "enum" => LexKind::KeywordEnum
        "module" => LexKind::KeywordModule
        "namespace" => LexKind::KeywordNamespace
        "import" => LexKind::KeywordImport
        "export" => LexKind::KeywordExport
        "assert" => LexKind::KeywordAssert
        "global" => LexKind::KeywordGlobal
        "from" => LexKind::KeywordFrom
        "extends" => LexKind::KeywordExtends
        "implements" => LexKind::KeywordImplements
        "in" => LexKind::KeywordIn
        "instanceof" => LexKind::KeywordInstanceof
        "of" => LexKind::KeywordOf
        "as" => LexKind::KeywordAs
        "satisfies" => LexKind::KeywordSatisfies
        "delete" => LexKind::KeywordDelete
        "typeof" => LexKind::KeywordTypeof
        "void" => LexKind::KeywordVoid
        "await" => LexKind::KeywordAwait
        "yield" => LexKind::KeywordYield
        "this" => LexKind::KeywordThis
        "super" => LexKind::KeywordSuper
        "true" => LexKind::KeywordTrue
        "false" => LexKind::KeywordFalse
        "null" => LexKind::KeywordNull
        _ => LexKind::Identifier(value.to_string())
      }
      tokens.push(LexToken::new(kind, start, i, has_line_break))
      has_line_break = false
      prev_can_end = can_end_expr(kind)
      continue
    }
    if is_digit(c) {
      let start = i
      if c == '0' && text.get_char(i + 1) is Some(next) {
        match next {
          'x' | 'X' => {
            i = i + 2
            let after = read_digits(text, i, is_hex_digit_or_sep)
            if after == i {
              raise parse_error_at_with(
                line_starts,
                i,
                i + 1,
                ParseErrorKind::InvalidChar(next),
              )
            }
            if trailing_numeric_sep_pos(text, i, after) is Some(pos) {
              raise parse_error_at_with(
                line_starts,
                pos,
                pos + 1,
                ParseErrorKind::NumericSeparatorAtEnd,
              )
            }
            i = after
            if text.get_char(i) is Some('n') {
              i = i + 1
            }
            let value = slice(text, start, i)
            tokens.push(
              LexToken::new(
                LexKind::Number(value.to_string()),
                start,
                i,
                has_line_break,
              ),
            )
            has_line_break = false
            continue
          }
          'b' | 'B' => {
            i = i + 2
            let after = read_digits(text, i, is_bin_digit_or_sep)
            if after == i {
              raise parse_error_at_with(
                line_starts,
                i,
                i + 1,
                ParseErrorKind::InvalidChar(next),
              )
            }
            if trailing_numeric_sep_pos(text, i, after) is Some(pos) {
              raise parse_error_at_with(
                line_starts,
                pos,
                pos + 1,
                ParseErrorKind::NumericSeparatorAtEnd,
              )
            }
            i = after
            if text.get_char(i) is Some('n') {
              i = i + 1
            }
            let value = slice(text, start, i)
            tokens.push(
              LexToken::new(
                LexKind::Number(value.to_string()),
                start,
                i,
                has_line_break,
              ),
            )
            has_line_break = false
            continue
          }
          'o' | 'O' => {
            i = i + 2
            let after = read_digits(text, i, is_oct_digit_or_sep)
            if after == i {
              raise parse_error_at_with(
                line_starts,
                i,
                i + 1,
                ParseErrorKind::InvalidChar(next),
              )
            }
            if trailing_numeric_sep_pos(text, i, after) is Some(pos) {
              raise parse_error_at_with(
                line_starts,
                pos,
                pos + 1,
                ParseErrorKind::NumericSeparatorAtEnd,
              )
            }
            i = after
            if text.get_char(i) is Some('n') {
              i = i + 1
            }
            let value = slice(text, start, i)
            tokens.push(
              LexToken::new(
                LexKind::Number(value.to_string()),
                start,
                i,
                has_line_break,
              ),
            )
            has_line_break = false
            continue
          }
          _ => ()
        }
      }
      i = read_digits(text, i + 1, is_dec_digit_or_sep)
      if trailing_numeric_sep_pos(text, start + 1, i) is Some(pos) {
        raise parse_error_at_with(
          line_starts,
          pos,
          pos + 1,
          ParseErrorKind::NumericSeparatorAtEnd,
        )
      }
      if text.get_char(i) is Some('.') && !(text.get_char(i + 1) is Some('.')) {
        let fraction_start = i + 1
        i = read_digits(text, fraction_start, is_dec_digit_or_sep)
        if trailing_numeric_sep_pos(text, fraction_start, i) is Some(pos) {
          raise parse_error_at_with(
            line_starts,
            pos,
            pos + 1,
            ParseErrorKind::NumericSeparatorAtEnd,
          )
        }
      }
      let (after_exp, trailing_exp_sep) = read_exponent(text, i)
      if trailing_exp_sep is Some(pos) {
        raise parse_error_at_with(
          line_starts,
          pos,
          pos + 1,
          ParseErrorKind::NumericSeparatorAtEnd,
        )
      }
      i = after_exp
      if text.get_char(i) is Some('n') {
        i = i + 1
      }
      if text.get_char(i) is Some(next) && is_ident_start(next) {
        raise parse_error_at_with(
          line_starts,
          i,
          i + 1,
          ParseErrorKind::IdentifierAfterNumericLiteral,
        )
      }
      let value = slice(text, start, i)
      tokens.push(
        LexToken::new(
          LexKind::Number(value.to_string()),
          start,
          i,
          has_line_break,
        ),
      )
      has_line_break = false
      prev_can_end = true
      continue
    }
    if c == '\'' || c == '"' {
      let quote = c
      let start = i
      i = i + 1
      let value_start = i
      while text.get_char(i) is Some(c) && c != quote {
        if c == '\\' && i + 1 < n {
          i = i + 2
          continue
        }
        i = i + 1
      }
      let value = slice(text, value_start, i)
      if text.get_char(i) is Some(c) && c == quote {
        i = i + 1
      } else {
        raise parse_error_at_with(
          line_starts,
          start,
          i,
          ParseErrorKind::UnterminatedString,
        )
      }
      tokens.push(
        LexToken::new(
          LexKind::String(value.to_string()),
          start,
          i,
          has_line_break,
        ),
      )
      has_line_break = false
      prev_can_end = true
      continue
    }
    let start = i
    let kind = match c {
      '(' => LexKind::LParen
      ')' => LexKind::RParen
      '{' => LexKind::LBrace
      '}' => LexKind::RBrace
      '[' => LexKind::LBracket
      ']' => LexKind::RBracket
      ',' => LexKind::Comma
      '.' => {
        if i + 2 < n && text[i + 1] == '.' && text[i + 2] == '.' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::DotDotDot, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if text.get_char(i + 1) is Some(next) && is_digit(next) {
          let number_start = i
          i = read_digits(text, i + 1, is_dec_digit_or_sep)
          if trailing_numeric_sep_pos(text, number_start + 1, i) is Some(pos) {
            raise parse_error_at_with(
              line_starts,
              pos,
              pos + 1,
              ParseErrorKind::NumericSeparatorAtEnd,
            )
          }
          let (after_exp, trailing_exp_sep) = read_exponent(text, i)
          if trailing_exp_sep is Some(pos) {
            raise parse_error_at_with(
              line_starts,
              pos,
              pos + 1,
              ParseErrorKind::NumericSeparatorAtEnd,
            )
          }
          i = after_exp
          if text.get_char(i) is Some('n') {
            i = i + 1
          }
          if text.get_char(i) is Some(next) && is_ident_start(next) {
            raise parse_error_at_with(
              line_starts,
              i,
              i + 1,
              ParseErrorKind::IdentifierAfterNumericLiteral,
            )
          }
          let value = slice(text, number_start, i)
          tokens.push(
            LexToken::new(
              LexKind::Number(value.to_string()),
              number_start,
              i,
              has_line_break,
            ),
          )
          has_line_break = false
          continue
        }
        LexKind::Dot
      }
      ';' => LexKind::Semicolon
      ':' => LexKind::Colon
      '?' => {
        if i + 1 < n && text[i + 1] == '.' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::QuestionDot, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 2 < n && text[i + 1] == '?' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::QuestionQuestionEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '?' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::QuestionQuestion, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        LexKind::Question
      }
      '+' => {
        if i + 1 < n && text[i + 1] == '+' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::PlusPlus, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::PlusEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Plus
      }
      '-' => {
        if i + 1 < n && text[i + 1] == '-' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::MinusMinus, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::MinusEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Minus
      }
      '*' => {
        if i + 2 < n && text[i + 1] == '*' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::StarStarEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '*' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::StarStar, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::StarEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Star
      }
      '/' => {
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::SlashEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Slash
      }
      '%' => {
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::PercentEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        LexKind::Percent
      }
      '!' => {
        if i + 2 < n && text[i + 1] == '=' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::BangEqEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::BangEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Bang
      }
      '~' => LexKind::Tilde
      '=' => {
        if i + 1 < n && text[i + 1] == '>' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::Arrow, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 2 < n && text[i + 1] == '=' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(LexToken::new(LexKind::EqEqEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::EqEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Eq
      }
      '<' => {
        if i + 1 < n && text[i + 1] == '<' {
          if i + 2 < n && text[i + 2] == '=' {
            i = i + 3
            tokens.push(
              LexToken::new(LexKind::LtLtEq, start, i, has_line_break),
            )
            has_line_break = false
            continue
          }
          i = i + 2
          tokens.push(LexToken::new(LexKind::LtLt, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '/' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::LtSlash, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::LtEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Lt
      }
      '>' => {
        if i + 2 < n && text[i + 1] == '>' && text[i + 2] == '>' {
          if i + 3 < n && text[i + 3] == '=' {
            i = i + 4
            tokens.push(
              LexToken::new(LexKind::GtGtGtEq, start, i, has_line_break),
            )
            has_line_break = false
            continue
          }
          i = i + 3
          tokens.push(LexToken::new(LexKind::GtGtGt, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '>' {
          if i + 2 < n && text[i + 2] == '=' {
            i = i + 3
            tokens.push(
              LexToken::new(LexKind::GtGtEq, start, i, has_line_break),
            )
            has_line_break = false
            continue
          }
          i = i + 2
          tokens.push(LexToken::new(LexKind::GtGt, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::GtEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Gt
      }
      '&' => {
        if i + 2 < n && text[i + 1] == '&' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::AmpAmpEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '&' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::AmpAmp, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(
            LexToken::new(LexKind::AmpersandEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        LexKind::Ampersand
      }
      '|' => {
        if i + 2 < n && text[i + 1] == '|' && text[i + 2] == '=' {
          i = i + 3
          tokens.push(
            LexToken::new(LexKind::BarBarEq, start, i, has_line_break),
          )
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '|' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::BarBar, start, i, has_line_break))
          has_line_break = false
          continue
        }
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::BarEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Bar
      }
      '^' => {
        if i + 1 < n && text[i + 1] == '=' {
          i = i + 2
          tokens.push(LexToken::new(LexKind::CaretEq, start, i, has_line_break))
          has_line_break = false
          continue
        }
        LexKind::Caret
      }
      '@' => LexKind::At
      _ =>
        raise parse_error_at_with(
          line_starts,
          start,
          start + 1,
          ParseErrorKind::InvalidChar(c),
        )
    }
    i = i + 1
    tokens.push(LexToken::new(kind, start, i, has_line_break))
    has_line_break = false
    if template_expr_depth > 0 && kind == LexKind::LBrace {
      template_expr_depth = template_expr_depth + 1
    }
    prev_can_end = can_end_expr(kind)
  }
  tokens.push(LexToken::new(LexKind::Eof, n, n, has_line_break))
  tokens
}

///|
pub struct Parser {
  tokens : Array[LexToken]
  mut index : Int
  line_starts : Array[Int]
  is_module : Bool
  script_kind : ScriptKind
  mut in_generator : Int
  mut in_function : Int
  mut in_async_function : Int
  mut in_conditional_extends : Int
  mut has_import_syntax : Bool
  mut has_export_syntax : Bool
  mut has_non_namespace_export : Bool
  mut has_export_assignment : Bool
  mut export_namespace_range : (Int, Int)?
  mut top_level_await_range : (Int, Int)?
  mut for_await_range : (Int, Int)?
  mut await_using_range : (Int, Int)?
  mut pending_doc_comment : String?
  mut allow_doc_comments : Bool
} derive(Show)

///|
pub fn Parser::new(
  text : String,
  script_kind? : ScriptKind = TS,
  is_module? : Bool = false,
) -> Parser raise ParseError {
  let line_starts = compute_line_starts(text)
  {
    tokens: lex(text, line_starts),
    index: 0,
    line_starts,
    in_generator: 0,
    in_function: 0,
    in_async_function: 0,
    in_conditional_extends: 0,
    has_import_syntax: false,
    has_export_syntax: false,
    has_non_namespace_export: false,
    has_export_assignment: false,
    export_namespace_range: None,
    top_level_await_range: None,
    for_await_range: None,
    await_using_range: None,
    pending_doc_comment: None,
    allow_doc_comments: true,
    script_kind,
    is_module,
  }
}

///|
fn Parser::parse_error_at(
  self : Parser,
  start : Int,
  end : Int,
  kind : ParseErrorKind,
) -> ParseError {
  parse_error_at_with(self.line_starts, start, end, kind)
}

///|
fn Parser::skip_doc_comments(self : Parser) -> Unit {
  let mut last : String? = None
  while self.current().kind is LexKind::DocComment(text) {
    self.advance() |> ignore
    if self.allow_doc_comments {
      last = Some(text)
    }
  }
  if self.allow_doc_comments {
    if last is Some(value) {
      self.pending_doc_comment = Some(value)
    }
  } else if last is Some(_) {
    self.pending_doc_comment = None
  }
}

///|
fn Parser::take_doc_comment(self : Parser) -> String? {
  let value = self.pending_doc_comment
  self.pending_doc_comment = None
  value
}

///|
fn Parser::clear_doc_comment(self : Parser) -> Unit {
  self.pending_doc_comment = None
}

///|
fn Parser::skip_doc_comments_in_expr(self : Parser) -> Unit {
  while self.current().kind is LexKind::DocComment(_) {
    self.advance() |> ignore
  }
}

///|
fn Parser::mark_non_namespace_export(
  self : Parser,
  start : Int,
  end : Int,
) -> Unit raise ParseError {
  if self.has_export_assignment {
    raise self.parse_error_at(
      start,
      end,
      ParseErrorKind::ExportAssignmentCannotBeUsedWithOtherExports,
    )
  }
  self.has_non_namespace_export = true
}

///|
fn Parser::record_top_level_await(self : Parser, span : Span) -> Unit {
  if self.in_function == 0 {
    if self.top_level_await_range is None {
      self.top_level_await_range = Some((span.start, span.end))
    }
  }
}

///|
fn Parser::record_top_level_await_using(self : Parser, span : Span) -> Unit {
  if self.in_function == 0 {
    if self.await_using_range is None {
      self.await_using_range = Some((span.start, span.end))
    }
  }
}

///|
fn Parser::current(self : Parser) -> LexToken {
  self.tokens[self.index]
}

///|
fn Parser::at(self : Parser, kind : LexKind) -> Bool {
  self.current().kind == kind
}

///|
fn Parser::advance(self : Parser) -> LexToken {
  let tok = self.current()
  self.index = self.index + 1
  tok
}

///|
fn Parser::peek(self : Parser, offset : Int) -> LexToken {
  let idx = self.index + offset
  if idx < self.tokens.length() {
    self.tokens[idx]
  } else {
    self.tokens[self.tokens.length() - 1]
  }
}

///|
fn Parser::has_preceding_line_break(self : Parser) -> Bool {
  self.current().has_leading_line_break
}

///|
fn Parser::can_parse_semicolon(self : Parser) -> Bool {
  match self.current().kind {
    Semicolon => true
    RBrace => true
    Eof => true
    _ => self.has_preceding_line_break()
  }
}

///|
fn Parser::try_parse_semicolon(self : Parser) -> Bool {
  if !self.can_parse_semicolon() {
    return false
  }
  if self.at(LexKind::Semicolon) {
    self.advance() |> ignore
  }
  true
}

///|
fn Parser::parse_semicolon(self : Parser) -> Unit raise ParseError {
  if self.try_parse_semicolon() {
    return ()
  }
  let tok = self.current()
  raise self.parse_error_at(
    tok.start,
    tok.end,
    ParseErrorKind::ExpectedToken(expected=LexKind::Semicolon, actual=tok.kind),
  )
}

///|
fn Parser::expect(self : Parser, kind : LexKind) -> LexToken raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  if tok.kind == kind {
    self.index = self.index + 1
    tok
  } else {
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::ExpectedToken(expected=kind, actual=tok.kind),
    )
  }
}

///|
/// Special handling for expecting '>' token with token splitting for >> ambiguity
fn Parser::expect_gt(self : Parser) -> LexToken raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Gt => {
      self.index = self.index + 1
      tok
    }
    GtGt => {
      // Split >> into two > tokens
      // Return first >, replace current token with second > (keep index at second >)
      let first_gt = LexToken::new(
        LexKind::Gt,
        tok.start,
        tok.start + 1,
        tok.has_leading_line_break,
      )
      let second_gt = LexToken::new(LexKind::Gt, tok.start + 1, tok.end, false)
      self.tokens[self.index] = second_gt
      // Don't increment index - next call will consume the second >
      first_gt
    }
    GtGtGt => {
      // Split >>> into > and >>
      let first_gt = LexToken::new(
        LexKind::Gt,
        tok.start,
        tok.start + 1,
        tok.has_leading_line_break,
      )
      let rest_gtgt = LexToken::new(
        LexKind::GtGt,
        tok.start + 1,
        tok.end,
        false,
      )
      self.tokens[self.index] = rest_gtgt
      first_gt
    }
    GtGtEq => {
      // Split >>= into > and >=
      let first_gt = LexToken::new(
        LexKind::Gt,
        tok.start,
        tok.start + 1,
        tok.has_leading_line_break,
      )
      let rest_gteq = LexToken::new(
        LexKind::GtEq,
        tok.start + 1,
        tok.end,
        false,
      )
      self.tokens[self.index] = rest_gteq
      first_gt
    }
    GtGtGtEq => {
      // Split >>>= into > and >>=
      let first_gt = LexToken::new(
        LexKind::Gt,
        tok.start,
        tok.start + 1,
        tok.has_leading_line_break,
      )
      let rest_gtgteq = LexToken::new(
        LexKind::GtGtEq,
        tok.start + 1,
        tok.end,
        false,
      )
      self.tokens[self.index] = rest_gtgteq
      first_gt
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::ExpectedToken(expected=LexKind::Gt, actual=tok.kind),
      )
  }
}

///|
fn Parser::span_from(self : Parser, start : Int, end : Int) -> Span {
  span_with_line_info(self.line_starts, start, end)
}

///|
fn[T] Parser::node_array(
  self : Parser,
  elements : Array[T],
  start : Int,
  end : Int,
) -> NodeArray[T] {
  {
    elements,
    start: self.span_from(start, start),
    end: self.span_from(end, end),
    has_trailing_comma: false,
    is_missing_list: false,
  }
}

///|
fn type_keyword_from_name(name : String) -> TypeKeyword? {
  match name {
    "any" => Some(TypeKeyword::Any)
    "unknown" => Some(TypeKeyword::Unknown)
    "number" => Some(TypeKeyword::Number)
    "bigint" => Some(TypeKeyword::BigInt)
    "object" => Some(TypeKeyword::Object)
    "boolean" => Some(TypeKeyword::Boolean)
    "string" => Some(TypeKeyword::String)
    "symbol" => Some(TypeKeyword::Symbol)
    "void" => Some(TypeKeyword::Void)
    "undefined" => Some(TypeKeyword::Undefined)
    "null" => Some(TypeKeyword::Null)
    "never" => Some(TypeKeyword::Never)
    _ => None
  }
}

///|
fn Parser::parse_constructor_type(
  self : Parser,
  start : Int,
  is_abstract : Bool,
) -> TypeNode raise ParseError {
  if is_abstract {
    self.advance() |> ignore
  }
  self.expect(LexKind::KeywordNew) |> ignore
  let type_params = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  let params = self.parse_parameter_list(true)
  self.expect(LexKind::Arrow) |> ignore
  let return_type = if !self.at(LexKind::Semicolon) && !self.at(LexKind::RBrace) {
    Some(self.parse_type())
  } else {
    None
  }
  let end = if return_type is Some(t) {
    t.span.end
  } else {
    self.current().start
  }
  let ctor = ConstructorTypeNode::new(
    is_abstract, type_params, params, return_type,
  )
  TypeNode::new(TypeNodeKind::ConstructorType(ctor), self.span_from(start, end))
}

///|
fn Parser::parse_type(self : Parser) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  self.parse_conditional_type()
}

///|
fn Parser::parse_conditional_type(self : Parser) -> TypeNode raise ParseError {
  let check_type = self.parse_union_type()
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::KeywordExtends) {
    let start = check_type.span.start
    self.advance() |> ignore
    self.in_conditional_extends = self.in_conditional_extends + 1
    let extends_type = self.parse_type()
    self.in_conditional_extends = self.in_conditional_extends - 1
    self.expect(LexKind::Question) |> ignore
    let true_type = self.parse_type()
    self.expect(LexKind::Colon) |> ignore
    let false_type = self.parse_type()
    let end = false_type.span.end
    TypeNode::new(
      TypeNodeKind::ConditionalType(
        ConditionalTypeNode::new(
          check_type, extends_type, true_type, false_type,
        ),
      ),
      self.span_from(start, end),
    )
  } else {
    check_type
  }
}

///|
fn Parser::parse_union_type(self : Parser) -> TypeNode raise ParseError {
  let types : Array[TypeNode] = []
  types.push(self.parse_intersection_type())
  while {
          self.skip_doc_comments_in_expr()
          self.at(LexKind::Bar)
        } {
    self.advance() |> ignore
    types.push(self.parse_intersection_type())
  }
  if types.length() == 1 {
    types[0]
  } else {
    let start = types[0].span.start
    let end = types[types.length() - 1].span.end
    TypeNode::new(
      TypeNodeKind::UnionType(self.node_array(types, start, end)),
      self.span_from(start, end),
    )
  }
}

///|
fn Parser::parse_intersection_type(self : Parser) -> TypeNode raise ParseError {
  let types : Array[TypeNode] = []
  types.push(self.parse_type_operator())
  while {
          self.skip_doc_comments_in_expr()
          self.at(LexKind::Ampersand)
        } {
    self.advance() |> ignore
    types.push(self.parse_type_operator())
  }
  if types.length() == 1 {
    types[0]
  } else {
    let start = types[0].span.start
    let end = types[types.length() - 1].span.end
    TypeNode::new(
      TypeNodeKind::IntersectionType(self.node_array(types, start, end)),
      self.span_from(start, end),
    )
  }
}

///|
fn Parser::parse_type_operator(self : Parser) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier("keyof") => {
      self.advance() |> ignore
      let operand = self.parse_type_operator()
      TypeNode::new(
        TypeNodeKind::TypeOperator(
          TypeOperatorNode::new(TypeOperatorKind::KeyOf, operand),
        ),
        self.span_from(tok.start, operand.span.end),
      )
    }
    Identifier("unique") => {
      self.advance() |> ignore
      let operand = self.parse_type_operator()
      TypeNode::new(
        TypeNodeKind::TypeOperator(
          TypeOperatorNode::new(TypeOperatorKind::Unique, operand),
        ),
        self.span_from(tok.start, operand.span.end),
      )
    }
    Identifier("readonly") => {
      self.advance() |> ignore
      let operand = self.parse_type_operator()
      TypeNode::new(
        TypeNodeKind::TypeOperator(
          TypeOperatorNode::new(TypeOperatorKind::Readonly, operand),
        ),
        self.span_from(tok.start, operand.span.end),
      )
    }
    _ => self.parse_type_primary()
  }
}

///|
fn Parser::parse_type_primary(self : Parser) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  let mut base = match tok.kind {
    KeywordVoid => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::KeywordType(TypeKeyword::Void),
        self.span_from(tok.start, tok.end),
      )
    }
    KeywordNull => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::KeywordType(TypeKeyword::Null),
        self.span_from(tok.start, tok.end),
      )
    }
    KeywordTypeof => {
      let start = self.advance().start
      if self.at(LexKind::KeywordImport) {
        let import_type = self.parse_import_type()
        let end = import_type.span.end
        TypeNode::new(
          TypeNodeKind::TypeQuery(
            TypeQueryNode::new(
              TypeQueryExprName::ImportType(
                match import_type.kind {
                  TypeNodeKind::ImportType(node) => node
                  _ => abort("unreachable")
                },
              ),
              None,
            ),
          ),
          self.span_from(start, end),
        )
      } else {
        let first = self.parse_identifier()
        let (entity, end_start) = self.parse_entity_name(first)
        let type_args = if self.at(LexKind::Lt) {
          Some(self.parse_type_arguments())
        } else {
          None
        }
        let end = match type_args {
          Some(args) => args.end.end
          None => end_start
        }
        TypeNode::new(
          TypeNodeKind::TypeQuery(
            TypeQueryNode::new(TypeQueryExprName::EntityName(entity), type_args),
          ),
          self.span_from(start, end),
        )
      }
    }
    KeywordThis => {
      self.advance() |> ignore
      TypeNode::new(TypeNodeKind::ThisType, self.span_from(tok.start, tok.end))
    }
    KeywordTrue => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::LiteralType(
          Expr::new(ExprKind::TrueLiteral, self.span_from(tok.start, tok.end)),
        ),
        self.span_from(tok.start, tok.end),
      )
    }
    KeywordFalse => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::LiteralType(
          Expr::new(ExprKind::FalseLiteral, self.span_from(tok.start, tok.end)),
        ),
        self.span_from(tok.start, tok.end),
      )
    }
    Number(text) => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::LiteralType(
          Expr::new(
            ExprKind::NumericLiteral(
              NumericLiteral::new(text, self.span_from(tok.start, tok.end)),
            ),
            self.span_from(tok.start, tok.end),
          ),
        ),
        self.span_from(tok.start, tok.end),
      )
    }
    String(text) => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::LiteralType(
          Expr::new(
            ExprKind::StringLiteral(
              StringLiteral::new(text, self.span_from(tok.start, tok.end)),
            ),
            self.span_from(tok.start, tok.end),
          ),
        ),
        self.span_from(tok.start, tok.end),
      )
    }
    NoSubTemplate(text, _raw) => {
      self.advance() |> ignore
      TypeNode::new(
        TypeNodeKind::LiteralType(
          Expr::new(
            ExprKind::StringLiteral(
              StringLiteral::new(text, self.span_from(tok.start, tok.end)),
            ),
            self.span_from(tok.start, tok.end),
          ),
        ),
        self.span_from(tok.start, tok.end),
      )
    }
    TemplateHead(text, raw) =>
      self.parse_template_literal_type(text, raw, tok.start)
    Minus => {
      let start = self.advance().start
      let num_tok = self.current()
      match num_tok.kind {
        Number(text) => {
          self.advance() |> ignore
          let num_span = self.span_from(num_tok.start, num_tok.end)
          let num_expr = Expr::new(
            ExprKind::NumericLiteral(NumericLiteral::new(text, num_span)),
            num_span,
          )
          let full_span = self.span_from(start, num_tok.end)
          TypeNode::new(
            TypeNodeKind::LiteralType(
              Expr::new(
                ExprKind::PrefixUnaryExpr(
                  PrefixUnaryExpr::new(UnaryOperator::Minus, num_expr),
                ),
                full_span,
              ),
            ),
            full_span,
          )
        }
        _ =>
          raise self.parse_error_at(
            num_tok.start,
            num_tok.end,
            ParseErrorKind::ExpectedNumericLiteralAfterMinus(num_tok.kind),
          )
      }
    }
    KeywordImport => self.parse_import_type()
    KeywordNew =>
      if self.peek(1).kind == LexKind::LParen ||
        self.peek(1).kind == LexKind::Lt {
        self.parse_constructor_type(tok.start, false)
      } else {
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::UnexpectedToken(tok.kind),
        )
      }
    Identifier(text) =>
      if text == "abstract" &&
        self.peek(1).kind == LexKind::KeywordNew &&
        (
          self.peek(2).kind == LexKind::LParen ||
          self.peek(2).kind == LexKind::Lt
        ) {
        self.parse_constructor_type(tok.start, true)
      } else if text == "infer" {
        let start = self.advance().start
        if self.in_conditional_extends == 0 {
          raise self.parse_error_at(
            start,
            self.current().start,
            ParseErrorKind::InferTypeOnlyInConditionalExtends,
          )
        }
        let name = self.parse_identifier()
        let constraint = if self.at(LexKind::KeywordExtends) {
          self.advance() |> ignore
          Some(self.parse_type())
        } else {
          None
        }
        let end = match constraint {
          Some(c) => c.span.end
          None => name.span.end
        }
        TypeNode::new(
          TypeNodeKind::InferType(
            TypeParameterDecl::new(
              name,
              TypeParameterModifiers::default(),
              constraint,
              None,
            ),
          ),
          self.span_from(start, end),
        )
      } else {
        let id = self.parse_identifier()
        match type_keyword_from_name(id.text) {
          Some(keyword) =>
            TypeNode::new(
              TypeNodeKind::KeywordType(keyword),
              self.span_from(tok.start, tok.end),
            )
          None => {
            let (entity, end_start) = self.parse_entity_name(id)
            self.parse_type_reference(tok.start, entity, end_start)
          }
        }
      }
    Lt =>
      if self.looks_like_type_arguments() {
        let start = self.current().start
        let type_params = self.parse_type_parameters()
        let params = self.parse_parameter_list(false)
        self.expect(LexKind::Arrow) |> ignore
        let return_type = if !self.at(LexKind::Semicolon) &&
          !self.at(LexKind::RBrace) {
          Some(self.parse_type())
        } else {
          None
        }
        let end = if return_type is Some(t) {
          t.span.end
        } else {
          self.current().start
        }
        let func = FunctionTypeNode::new(Some(type_params), params, return_type)
        TypeNode::new(
          TypeNodeKind::FunctionType(func),
          self.span_from(start, end),
        )
      } else {
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::UnexpectedToken(tok.kind),
        )
      }
    LParen =>
      if self.looks_like_function_type() {
        let start = self.current().start
        let params = self.parse_parameter_list(false)
        self.expect(LexKind::Arrow) |> ignore
        let return_type = if !self.at(LexKind::Semicolon) &&
          !self.at(LexKind::RBrace) {
          Some(self.parse_type())
        } else {
          None
        }
        let end = if return_type is Some(t) {
          t.span.end
        } else {
          self.current().start
        }
        let func = FunctionTypeNode::new(None, params, return_type)
        TypeNode::new(
          TypeNodeKind::FunctionType(func),
          self.span_from(start, end),
        )
      } else {
        let start = self.advance().start
        let inner = self.parse_type()
        let end_tok = self.expect(LexKind::RParen)
        TypeNode::new(
          TypeNodeKind::ParenthesizedType(inner),
          self.span_from(start, end_tok.end),
        )
      }
    LBracket => {
      let start = self.advance().start
      let elements : Array[TupleElement] = []
      if !self.at(LexKind::RBracket) {
        for {
          self.skip_doc_comments_in_expr()
          let start = self.current().start
          let mut is_rest = false
          if self.at(LexKind::DotDotDot) {
            self.advance() |> ignore
            is_rest = true
          }
          let mut label : Identifier? = None
          let mut is_optional = false
          let mut type_node : TypeNode? = None
          match self.current().kind {
            Identifier(_) => {
              let next = self.peek(1)
              let is_label = next.kind == LexKind::Colon ||
                (
                  next.kind == LexKind::Question &&
                  self.peek(2).kind == LexKind::Colon
                )
              if is_label {
                label = Some(self.parse_identifier())
                if self.at(LexKind::Question) {
                  self.advance() |> ignore
                  is_optional = true
                }
                self.expect(LexKind::Colon) |> ignore
                type_node = Some(self.parse_type())
              }
            }
            _ => ()
          }
          if type_node is None {
            type_node = Some(self.parse_type())
            if !is_rest && !is_optional && self.at(LexKind::Question) {
              self.advance() |> ignore
              is_optional = true
            }
          }
          let type_node = type_node.unwrap()
          let end = if is_optional && label is None {
            // Unnamed optional type ending with ?
            // The ? was consumed, current startition is after ?
            self.tokens[self.index - 1].end
          } else {
            type_node.span.end
          }
          elements.push(
            TupleElement::new(
              label,
              is_optional,
              is_rest,
              type_node,
              self.span_from(start, end),
            ),
          )
          self.skip_doc_comments_in_expr()
          if self.at(LexKind::Comma) {
            self.advance() |> ignore
            if self.at(LexKind::RBracket) {
              break
            }
            continue
          }
          break
        }
      }
      let end_tok = self.expect(LexKind::RBracket)
      TypeNode::new(
        TypeNodeKind::TupleType(self.node_array(elements, start, end_tok.end)),
        self.span_from(start, end_tok.end),
      )
    }
    LBrace => {
      let start = self.advance().start
      if self.looks_like_mapped_type() {
        self.parse_mapped_type(start)
      } else {
        let members = self.parse_type_literal_members()
        let end_tok = self.expect(LexKind::RBrace)
        TypeNode::new(
          TypeNodeKind::TypeLiteral(
            self.node_array(members, start, end_tok.end),
          ),
          self.span_from(start, end_tok.end),
        )
      }
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedToken(tok.kind),
      )
  }
  for {
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::LBracket) {
      if self.peek(1).kind == LexKind::RBracket {
        let start = base.span.start
        self.advance() |> ignore
        let end_tok = self.expect(LexKind::RBracket)
        base = TypeNode::new(
          TypeNodeKind::ArrayType(base),
          self.span_from(start, end_tok.end),
        )
        continue
      } else {
        let start = base.span.start
        self.advance() |> ignore
        let index_type = self.parse_type()
        let end_tok = self.expect(LexKind::RBracket)
        base = TypeNode::new(
          TypeNodeKind::IndexedAccessType(
            IndexedAccessTypeNode::new(base, index_type),
          ),
          self.span_from(start, end_tok.end),
        )
        continue
      }
    }
    break
  }
  base
}

///|
fn Parser::looks_like_mapped_type(self : Parser) -> Bool {
  let mut i = self.index
  // Skip optional 'readonly'
  while i < self.tokens.length() &&
        self.tokens[i].kind is LexKind::DocComment(_) {
    i = i + 1
  }
  match self.tokens[i].kind {
    Identifier("readonly") => i = i + 1
    _ => ()
  }
  while i < self.tokens.length() &&
        self.tokens[i].kind is LexKind::DocComment(_) {
    i = i + 1
  }
  // Expect [
  if self.tokens[i].kind != LexKind::LBracket {
    return false
  }
  i = i + 1
  while i < self.tokens.length() &&
        self.tokens[i].kind is LexKind::DocComment(_) {
    i = i + 1
  }
  // Expect identifier
  match self.tokens[i].kind {
    Identifier(_) => i = i + 1
    _ => return false
  }
  while i < self.tokens.length() &&
        self.tokens[i].kind is LexKind::DocComment(_) {
    i = i + 1
  }
  // Expect 'in'
  self.tokens[i].kind == LexKind::KeywordIn
}

///|
fn Parser::parse_mapped_type(
  self : Parser,
  start : Int,
) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  let is_readonly = match self.current().kind {
    Identifier("readonly") => {
      self.advance() |> ignore
      true
    }
    _ => false
  }
  self.expect(LexKind::LBracket) |> ignore
  let name = self.parse_identifier()
  self.expect(LexKind::KeywordIn) |> ignore
  let constraint = self.parse_type()
  let name_type = if self.at(LexKind::KeywordAs) {
    self.advance() |> ignore
    Some(self.parse_type())
  } else {
    None
  }
  self.expect(LexKind::RBracket) |> ignore
  let is_optional = if self.at(LexKind::Question) {
    self.advance() |> ignore
    true
  } else {
    false
  }
  let type_node = if self.at(LexKind::Colon) {
    self.advance() |> ignore
    Some(self.parse_type())
  } else {
    None
  }
  if self.at(LexKind::Semicolon) {
    self.advance() |> ignore
  }
  let end_tok = self.expect(LexKind::RBrace)
  let type_param = TypeParameterDecl::new(
    name,
    TypeParameterModifiers::new(),
    Some(constraint),
    None,
  )
  TypeNode::new(
    TypeNodeKind::MappedType(
      MappedTypeNode::new(
        is_readonly, type_param, name_type, is_optional, type_node,
      ),
    ),
    self.span_from(start, end_tok.end),
  )
}

///|
fn Parser::parse_import_type(self : Parser) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  let start = self.expect(LexKind::KeywordImport).start
  self.expect(LexKind::LParen) |> ignore
  let arg_tok = self.current()
  let argument = match arg_tok.kind {
    String(text) => {
      self.advance() |> ignore
      Expr::new(
        ExprKind::StringLiteral(
          StringLiteral::new(text, self.span_from(arg_tok.start, arg_tok.end)),
        ),
        self.span_from(arg_tok.start, arg_tok.end),
      )
    }
    _ =>
      raise self.parse_error_at(
        arg_tok.start,
        arg_tok.end,
        ParseErrorKind::ExpectedStringLiteralInImportType(arg_tok.kind),
      )
  }
  let rparen_end = self.expect(LexKind::RParen).end
  let (qualifier, end_after_qual) = if self.at(LexKind::Dot) {
    self.advance() |> ignore
    let first = self.parse_identifier()
    let (entity, end_start) = self.parse_entity_name(first)
    (Some(entity), end_start)
  } else {
    (None, rparen_end)
  }
  let type_args = if self.at(LexKind::Lt) {
    Some(self.parse_type_arguments())
  } else {
    None
  }
  let end = match type_args {
    Some(args) => args.end.end
    None => end_after_qual
  }
  TypeNode::new(
    TypeNodeKind::ImportType(
      ImportTypeNode::new(argument, qualifier, type_args),
    ),
    self.span_from(start, end),
  )
}

///|
fn Parser::parse_template_literal_type(
  self : Parser,
  head_text : String,
  head_raw : String,
  start : Int,
) -> TypeNode raise ParseError {
  self.advance() |> ignore
  let head = TemplateHead::new(head_text, head_raw)
  let spans : Array[TemplateLiteralTypeSpan] = []
  let mut end_start = start
  for {
    self.skip_doc_comments_in_expr()
    let type_node = self.parse_type()
    self.skip_doc_comments_in_expr()
    let tok = self.current()
    match tok.kind {
      TemplateMiddle(text, raw) => {
        self.advance() |> ignore
        let middle = TemplateMiddle::new(text, raw)
        spans.push(
          TemplateLiteralTypeSpan::new(
            type_node,
            TemplateMiddleOrTemplateTail::TemplateMiddle(middle),
          ),
        )
        continue
      }
      TemplateTail(text, raw) => {
        end_start = tok.end
        self.advance() |> ignore
        let tail = TemplateTail::new(text, raw)
        spans.push(
          TemplateLiteralTypeSpan::new(
            type_node,
            TemplateMiddleOrTemplateTail::TemplateTail(tail),
          ),
        )
        break
      }
      _ =>
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::ExpectedTemplateLiteralTypeSpan(tok.kind),
        )
    }
  }
  TypeNode::new(
    TypeNodeKind::TemplateLiteralType(
      TemplateLiteralTypeNode::new(
        head,
        self.node_array(spans, start, end_start),
      ),
    ),
    self.span_from(start, end_start),
  )
}

///|
fn Parser::parse_type_reference(
  self : Parser,
  start : Int,
  entity : EntityName,
  entity_end : Int,
) -> TypeNode raise ParseError {
  self.skip_doc_comments_in_expr()
  let type_args = if self.at(LexKind::Lt) {
    Some(self.parse_type_arguments())
  } else {
    None
  }
  let ref_node = TypeReferenceNode::new(entity, type_args)
  let end = match type_args {
    Some(args) => args.end.end
    None => entity_end
  }
  TypeNode::new(
    TypeNodeKind::TypeReference(ref_node),
    self.span_from(start, end),
  )
}

///|
fn Parser::parse_type_arguments(
  self : Parser,
) -> NodeArray[TypeNode] raise ParseError {
  let start = self.expect(LexKind::Lt).start
  let args : Array[TypeNode] = []
  if !self.at(LexKind::Gt) {
    for {
      self.skip_doc_comments_in_expr()
      args.push(self.parse_type())
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        continue
      }
      break
    }
  }
  let end_tok = self.expect_gt()
  self.node_array(args, start, end_tok.end)
}

///|
fn Parser::parse_type_parameters(
  self : Parser,
) -> NodeArray[TypeParameterDecl] raise ParseError {
  let start = self.expect(LexKind::Lt).start
  let params : Array[TypeParameterDecl] = []
  if !self.at(LexKind::Gt) {
    for {
      self.skip_doc_comments_in_expr()
      let variance_items : Array[ModifierKind] = []
      let mut variance_start : Int? = None
      let mut variance_end : Int? = None
      let mut seen_in = false
      let mut seen_out = false
      for {
        self.skip_doc_comments_in_expr()
        let tok = self.current()
        match tok.kind {
          KeywordIn | Identifier("in") => {
            if seen_in {
              raise self.parse_error_at(
                tok.start,
                tok.end,
                ParseErrorKind::DuplicateModifier(ModifierKind::In),
              )
            }
            if seen_out {
              raise self.parse_error_at(
                tok.start,
                tok.end,
                ParseErrorKind::InvalidVarianceOrder(
                  previous=ModifierKind::Out,
                  current=ModifierKind::In,
                ),
              )
            }
            self.advance() |> ignore
            variance_items.push(ModifierKind::In)
            seen_in = true
          }
          Identifier("out") => {
            if seen_out {
              raise self.parse_error_at(
                tok.start,
                tok.end,
                ParseErrorKind::DuplicateModifier(ModifierKind::Out),
              )
            }
            self.advance() |> ignore
            variance_items.push(ModifierKind::Out)
            seen_out = true
          }
          _ => break
        }
        if variance_start is None {
          variance_start = Some(tok.start)
        }
        variance_end = Some(tok.end)
      }
      let variance = if variance_items.length() > 0 {
        Some(
          self.node_array(
            variance_items,
            variance_start.unwrap_or(start),
            variance_end.unwrap_or(start),
          ),
        )
      } else {
        None
      }
      let name = self.parse_identifier()
      let constraint = if self.at(LexKind::KeywordExtends) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      let default_type = if self.at(LexKind::Eq) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      let span = match variance {
        Some(v) => v.start
        None => name.span
      }
      let type_param_mods = parse_type_parameter_modifiers(variance, span)
      params.push(
        TypeParameterDecl::new(name, type_param_mods, constraint, default_type),
      )
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        continue
      }
      break
    }
  }
  let end_tok = self.expect_gt()
  self.node_array(params, start, end_tok.end)
}

///|
fn Parser::parse_identifier(self : Parser) -> Identifier raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(name) => {
      self.advance() |> ignore
      Identifier::new(name, self.span_from(tok.start, tok.end))
    }
    _ =>
      if is_contextual_keyword_kind(tok.kind) {
        match keyword_text(tok.kind) {
          Some(text) => {
            self.advance() |> ignore
            Identifier::new(text, self.span_from(tok.start, tok.end))
          }
          None =>
            raise self.parse_error_at(
              tok.start,
              tok.end,
              ParseErrorKind::UnexpectedToken(tok.kind),
            )
        }
      } else {
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::UnexpectedToken(tok.kind),
        )
      }
  }
}

///|
fn is_contextual_keyword_kind(kind : LexKind) -> Bool {
  kind
  is (KeywordType
  | KeywordAs
  | KeywordFrom
  | KeywordOf
  | KeywordUsing
  | KeywordGlobal)
}

///|
fn is_identifier_or_contextual(kind : LexKind) -> Bool {
  match kind {
    Identifier(_) => true
    _ => is_contextual_keyword_kind(kind)
  }
}

///|
fn keyword_text(kind : LexKind) -> String? {
  match kind {
    KeywordLet => Some("let")
    KeywordConst => Some("const")
    KeywordVar => Some("var")
    KeywordUsing => Some("using")
    KeywordFunction => Some("function")
    KeywordReturn => Some("return")
    KeywordIf => Some("if")
    KeywordElse => Some("else")
    KeywordWhile => Some("while")
    KeywordFor => Some("for")
    KeywordDo => Some("do")
    KeywordBreak => Some("break")
    KeywordContinue => Some("continue")
    KeywordSwitch => Some("switch")
    KeywordCase => Some("case")
    KeywordDefault => Some("default")
    KeywordDebugger => Some("debugger")
    KeywordTry => Some("try")
    KeywordCatch => Some("catch")
    KeywordFinally => Some("finally")
    KeywordThrow => Some("throw")
    KeywordWith => Some("with")
    KeywordNew => Some("new")
    KeywordClass => Some("class")
    KeywordInterface => Some("interface")
    KeywordType => Some("type")
    KeywordEnum => Some("enum")
    KeywordModule => Some("module")
    KeywordNamespace => Some("namespace")
    KeywordImport => Some("import")
    KeywordExport => Some("export")
    KeywordAssert => Some("assert")
    KeywordGlobal => Some("global")
    KeywordFrom => Some("from")
    KeywordExtends => Some("extends")
    KeywordImplements => Some("implements")
    KeywordIn => Some("in")
    KeywordInstanceof => Some("instanceof")
    KeywordOf => Some("of")
    KeywordAs => Some("as")
    KeywordSatisfies => Some("satisfies")
    KeywordDelete => Some("delete")
    KeywordTypeof => Some("typeof")
    KeywordVoid => Some("void")
    KeywordAwait => Some("await")
    KeywordYield => Some("yield")
    KeywordThis => Some("this")
    KeywordSuper => Some("super")
    KeywordTrue => Some("true")
    KeywordFalse => Some("false")
    KeywordNull => Some("null")
    _ => None
  }
}

///|
fn Parser::parse_identifier_name(self : Parser) -> Identifier raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(name) => {
      self.advance() |> ignore
      Identifier::new(name, self.span_from(tok.start, tok.end))
    }
    _ =>
      match keyword_text(tok.kind) {
        Some(text) => {
          self.advance() |> ignore
          Identifier::new(text, self.span_from(tok.start, tok.end))
        }
        None =>
          raise self.parse_error_at(
            tok.start,
            tok.end,
            ParseErrorKind::UnexpectedToken(tok.kind),
          )
      }
  }
}

///|
fn Parser::is_module_export_name_start(self : Parser) -> Bool {
  match self.current().kind {
    Identifier(_) => true
    String(_) => true
    _ => keyword_text(self.current().kind) is Some(_)
  }
}

///|
fn Parser::parse_module_export_name(
  self : Parser,
) -> Identifier raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    String(value) => {
      self.advance() |> ignore
      Identifier::new(value, self.span_from(tok.start, tok.end))
    }
    _ => self.parse_identifier_name()
  }
}

///|
fn Parser::parse_parameter_list(
  self : Parser,
  allow_modifiers : Bool,
) -> NodeArray[ParameterDecl] raise ParseError {
  let open = self.expect(LexKind::LParen)
  let params : Array[ParameterDecl] = []
  if !self.at(LexKind::RParen) {
    for {
      self.skip_doc_comments_in_expr()
      let modifiers = if allow_modifiers {
        self.parse_parameter_modifiers(self.current().start)
      } else {
        None
      }
      let mut is_rest = false
      if self.at(LexKind::DotDotDot) {
        self.advance() |> ignore
        is_rest = true
      }
      let (binding, binding_span) = self.parse_binding_name()
      let mut is_optional = false
      if self.at(LexKind::Question) {
        self.advance() |> ignore
        is_optional = true
      }
      let type_node = if self.at(LexKind::Colon) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      let initializer = if self.at(LexKind::Eq) {
        self.advance() |> ignore
        Some(self.parse_assignment_expr())
      } else {
        None
      }
      let param_start = if modifiers is Some(list) {
        list.start.start
      } else {
        binding_span.start
      }
      let end_start = if initializer is Some(init) {
        init.span.end
      } else if type_node is Some(type_value) {
        type_value.span.end
      } else {
        binding_span.end
      }
      let span = match modifiers {
        Some(m) => m.start
        None => binding_span
      }
      let param_mods = parse_parameter_modifiers(modifiers, span)
      let param = ParameterDecl::new(
        binding,
        param_mods,
        is_rest,
        is_optional,
        type_node,
        initializer,
        self.span_from(param_start, end_start),
      )
      params.push(param)
      if is_rest && self.at(LexKind::Comma) {
        let tok = self.current()
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::RestParameterMustBeLastInParameterList,
        )
      }
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        continue
      }
      break
    }
  }
  let end_tok = self.expect(LexKind::RParen)
  self.node_array(params, open.start, end_tok.end)
}

///|
fn Parser::looks_like_arrow_from_paren(self : Parser) -> Bool {
  self.looks_like_arrow_from_paren_at(self.index)
}

///|
fn Parser::looks_like_arrow_from_paren_at(
  self : Parser,
  start_index : Int,
) -> Bool {
  let mut depth = 0
  let mut i = start_index
  let len = self.tokens.length()
  while i < len {
    match self.tokens[i].kind {
      LexKind::DocComment(_) => {
        i = i + 1
        continue
      }
      LexKind::LParen => depth = depth + 1
      LexKind::RParen => {
        depth = depth - 1
        if depth == 0 {
          if i + 1 < len {
            let mut j = i + 1
            while j < len && self.tokens[j].kind is LexKind::DocComment(_) {
              j = j + 1
            }
            return j < len && self.tokens[j].kind == LexKind::Arrow
          }
          return false
        }
      }
      LexKind::Eof => return false
      _ => ()
    }
    i = i + 1
  }
  false
}

///|
fn Parser::looks_like_type_arguments(self : Parser) -> Bool {
  if !self.at(LexKind::Lt) {
    return false
  }
  let mut depth = 0
  let mut i = self.index
  let len = self.tokens.length()
  while i < len {
    match self.tokens[i].kind {
      LexKind::DocComment(_) => {
        i = i + 1
        continue
      }
      LexKind::Lt => depth = depth + 1
      LexKind::Gt => {
        depth = depth - 1
        if depth == 0 {
          if i + 1 < len {
            let mut j = i + 1
            while j < len && self.tokens[j].kind is LexKind::DocComment(_) {
              j = j + 1
            }
            return j < len && self.tokens[j].kind == LexKind::LParen
          }
          return false
        }
      }
      LexKind::Eof => return false
      _ => ()
    }
    i = i + 1
  }
  false
}

///|
fn Parser::looks_like_function_type(self : Parser) -> Bool {
  if !self.at(LexKind::LParen) {
    return false
  }
  let mut depth = 0
  let mut i = self.index
  let len = self.tokens.length()
  while i < len {
    match self.tokens[i].kind {
      LexKind::DocComment(_) => {
        i = i + 1
        continue
      }
      LexKind::LParen => depth = depth + 1
      LexKind::RParen => {
        depth = depth - 1
        if depth == 0 {
          if i + 1 < len {
            let mut j = i + 1
            while j < len && self.tokens[j].kind is LexKind::DocComment(_) {
              j = j + 1
            }
            return j < len && self.tokens[j].kind == LexKind::Arrow
          }
          return false
        }
      }
      LexKind::Eof => return false
      _ => ()
    }
    i = i + 1
  }
  false
}

///|
fn Parser::parse_entity_name(
  self : Parser,
  first : Identifier,
) -> (EntityName, Int) raise ParseError {
  let mut current = EntityName::Identifier(first)
  let mut end_start = first.span.end
  while {
          self.skip_doc_comments_in_expr()
          self.at(LexKind::Dot)
        } {
    self.advance() |> ignore
    let right = self.parse_identifier()
    end_start = right.span.end
    current = EntityName::QualifiedName(QualifiedName::new(current, right))
  }
  (current, end_start)
}

///|
fn Parser::parse_type_parameter_list(
  self : Parser,
) -> NodeArray[TypeParameterDecl]? raise ParseError {
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
}

///|
fn Parser::parse_binding_name(
  self : Parser,
) -> (BindingName, Span) raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    LBrace => {
      let start = self.advance().start
      let elements = self.parse_object_binding_elements()
      let end_tok = self.expect(LexKind::RBrace)
      let span = self.span_from(start, end_tok.end)
      (
        BindingName::ObjectBindingPattern(
          self.node_array(elements, start, end_tok.end),
        ),
        span,
      )
    }
    LBracket => {
      let start = self.advance().start
      let elements = self.parse_array_binding_elements()
      let end_tok = self.expect(LexKind::RBracket)
      let span = self.span_from(start, end_tok.end)
      (
        BindingName::ArrayBindingPattern(
          self.node_array(elements, start, end_tok.end),
        ),
        span,
      )
    }
    _ => {
      let id = self.parse_identifier()
      (BindingName::Identifier(id), id.span)
    }
  }
}

///|
fn Parser::parse_object_binding_elements(
  self : Parser,
) -> Array[BindingElement] raise ParseError {
  let elements : Array[BindingElement] = []
  if !self.at(LexKind::RBrace) {
    for {
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::DotDotDot) {
        self.advance() |> ignore
        let (name, _) = self.parse_binding_name()
        elements.push(BindingElement::new(name, None, true, None))
        if self.at(LexKind::Comma) && self.peek(1).kind != LexKind::RBrace {
          let tok = self.current()
          raise self.parse_error_at(
            tok.start,
            tok.end,
            ParseErrorKind::RestBindingMustBeLastInPattern,
          )
        }
      } else {
        let name_tok = self.current()
        let prop_name = self.parse_property_name()
        if self.at(LexKind::Colon) {
          self.advance() |> ignore
          let (name, _) = self.parse_binding_name()
          let initializer = if self.at(LexKind::Eq) {
            self.advance() |> ignore
            Some(self.parse_assignment_expr())
          } else {
            None
          }
          elements.push(
            BindingElement::new(name, Some(prop_name), false, initializer),
          )
        } else {
          match prop_name {
            PropertyName::Identifier(id) => {
              let initializer = if self.at(LexKind::Eq) {
                self.advance() |> ignore
                Some(self.parse_assignment_expr())
              } else {
                None
              }
              elements.push(
                BindingElement::new(
                  BindingName::Identifier(id),
                  None,
                  false,
                  initializer,
                ),
              )
            }
            _ =>
              raise self.parse_error_at(
                name_tok.start,
                name_tok.end,
                ParseErrorKind::UnexpectedToken(name_tok.kind),
              )
          }
        }
      }
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        if self.at(LexKind::RBrace) {
          break
        }
        continue
      }
      break
    }
  }
  elements
}

///|
fn Parser::parse_array_binding_elements(
  self : Parser,
) -> Array[ArrayBindingElement] raise ParseError {
  let elements : Array[ArrayBindingElement] = []
  if !self.at(LexKind::RBracket) {
    for {
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        elements.push(ArrayBindingElement::OmittedExpr)
        if self.at(LexKind::RBracket) {
          break
        }
        continue
      }
      if self.at(LexKind::DotDotDot) {
        self.advance() |> ignore
        let (name, _) = self.parse_binding_name()
        let element = BindingElement::new(name, None, true, None)
        elements.push(ArrayBindingElement::BindingElement(element))
        if self.at(LexKind::Comma) && self.peek(1).kind != LexKind::RBracket {
          let tok = self.current()
          raise self.parse_error_at(
            tok.start,
            tok.end,
            ParseErrorKind::RestBindingMustBeLastInPattern,
          )
        }
      } else {
        let (name, _) = self.parse_binding_name()
        let initializer = if self.at(LexKind::Eq) {
          self.advance() |> ignore
          Some(self.parse_assignment_expr())
        } else {
          None
        }
        let element = BindingElement::new(name, None, false, initializer)
        elements.push(ArrayBindingElement::BindingElement(element))
      }
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        if self.at(LexKind::RBracket) {
          break
        }
        continue
      }
      break
    }
  }
  elements
}

///|
fn Parser::parse_type_literal_members(
  self : Parser,
) -> Array[TypeElement] raise ParseError {
  let members : Array[TypeElement] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments()
    if self.at(LexKind::RBrace) || self.at(LexKind::Eof) {
      break
    }
    if self.at(LexKind::Semicolon) || self.at(LexKind::Comma) {
      self.advance() |> ignore
      self.clear_doc_comment()
      continue
    }
    if self.at(LexKind::LParen) {
      self.clear_doc_comment()
      let params = self.parse_parameter_list(false)
      let type_node = if self.at(LexKind::Colon) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      members.push(
        TypeElement::CallSignature(
          CallSignatureDecl::new(None, params, type_node),
        ),
      )
      continue
    }
    if self.at(LexKind::KeywordNew) {
      self.clear_doc_comment()
      self.advance() |> ignore
      let params = self.parse_parameter_list(false)
      let type_node = if self.at(LexKind::Colon) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      members.push(
        TypeElement::ConstructSignature(
          ConstructSignatureDecl::new(None, params, type_node),
        ),
      )
      continue
    }
    if self.at(LexKind::LBracket) {
      self.clear_doc_comment()
      self.advance() |> ignore
      let param_name = self.parse_identifier()
      self.expect(LexKind::Colon) |> ignore
      let key_type = self.parse_type()
      self.expect(LexKind::RBracket) |> ignore
      self.expect(LexKind::Colon) |> ignore
      let value_type = self.parse_type()
      let param_span = self.span_from(param_name.span.start, key_type.span.end)
      let param = ParameterDecl::new(
        BindingName::Identifier(param_name),
        ParameterModifiers::default(),
        false,
        false,
        Some(key_type),
        None,
        param_span,
      )
      let param_start = param.span.start
      let param_end = param.span.end
      members.push(
        TypeElement::IndexSignature(
          IndexSignatureDecl::new(
            false,
            self.node_array([param], param_start, param_end),
            value_type,
          ),
        ),
      )
      continue
    }
    let member_doc = self.take_doc_comment()
    let name = self.parse_property_name()
    let mut is_optional = false
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Question) {
      self.advance() |> ignore
      is_optional = true
    }
    match name {
      PropertyName::Identifier(id) =>
        if id.text == "get" &&
          {
            self.skip_doc_comments_in_expr()
            !self.at(LexKind::Colon)
          } {
          let accessor_name = self.parse_property_name()
          let params = self.parse_parameter_list(false)
          let type_node = if self.at(LexKind::Colon) {
            self.advance() |> ignore
            Some(self.parse_type())
          } else {
            None
          }
          members.push(
            TypeElement::GetAccessor(
              GetAccessorDecl::new(
                member_doc,
                None,
                ClassMemberModifiers::default(),
                accessor_name,
                params,
                type_node,
                None,
              ),
            ),
          )
          continue
        } else if id.text == "set" &&
          {
            self.skip_doc_comments_in_expr()
            !self.at(LexKind::Colon)
          } {
          let accessor_name = self.parse_property_name()
          let params = self.parse_parameter_list(false)
          members.push(
            TypeElement::SetAccessor(
              SetAccessorDecl::new(
                member_doc,
                None,
                ClassMemberModifiers::default(),
                accessor_name,
                params,
                None,
              ),
            ),
          )
          continue
        }
      _ => ()
    }
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::LParen) {
      let type_params = self.parse_type_parameter_list()
      let params = self.parse_parameter_list(false)
      let type_node = if self.at(LexKind::Colon) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      members.push(
        TypeElement::MethodSignature(
          MethodSignature::new(
            member_doc, name, is_optional, type_params, params, type_node,
          ),
        ),
      )
      continue
    }
    let type_node = if self.at(LexKind::Colon) {
      self.advance() |> ignore
      Some(self.parse_type())
    } else {
      None
    }
    members.push(
      TypeElement::PropertySignature(
        PropertySignature::new(member_doc, false, name, is_optional, type_node),
      ),
    )
  }
  members
}

///|
fn Parser::parse_property_name(self : Parser) -> PropertyName raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(_) => {
      let id = self.parse_identifier_name()
      PropertyName::Identifier(id)
    }
    _ if keyword_text(tok.kind) is Some(_) => {
      let id = self.parse_identifier_name()
      PropertyName::Identifier(id)
    }
    PrivateIdentifier(name) => {
      self.advance() |> ignore
      PropertyName::PrivateIdentifier(name)
    }
    String(value) => {
      self.advance() |> ignore
      let lit = StringLiteral::new(value, self.span_from(tok.start, tok.end))
      PropertyName::StringLiteral(lit)
    }
    Number(value) => {
      self.advance() |> ignore
      let lit = NumericLiteral::new(value, self.span_from(tok.start, tok.end))
      PropertyName::NumericLiteral(lit)
    }
    LBracket => {
      self.advance() |> ignore
      let expr = self.parse_assignment_expr()
      self.expect(LexKind::RBracket) |> ignore
      PropertyName::ComputedPropertyName(expr)
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedPrimary(tok.kind),
      )
  }
}

///|
fn Parser::parse_array_literal(
  self : Parser,
  start : Int,
) -> Expr raise ParseError {
  let elements : Array[Expr] = []
  if !self.at(LexKind::RBracket) {
    for {
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        let comma = self.advance()
        elements.push(
          Expr::new(
            ExprKind::OmittedExpr,
            self.span_from(comma.start, comma.end),
          ),
        )
        continue
      }
      if self.at(LexKind::DotDotDot) {
        let spread_tok = self.advance()
        let expr = self.parse_assignment_expr()
        let span = self.span_from(spread_tok.start, expr.span.end)
        elements.push(Expr::new(ExprKind::SpreadElement(expr), span))
      } else {
        elements.push(self.parse_assignment_expr())
      }
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        if self.at(LexKind::RBracket) {
          break
        }
        continue
      }
      break
    }
  }
  let end_tok = self.expect(LexKind::RBracket)
  let span = self.span_from(start, end_tok.end)
  Expr::new(
    ExprKind::ArrayLiteralExpr(self.node_array(elements, start, end_tok.end)),
    span,
  )
}

///|
fn Parser::parse_object_literal(
  self : Parser,
  start : Int,
) -> Expr raise ParseError {
  let elements : Array[ObjectLiteralElementLike] = []
  if !self.at(LexKind::RBrace) {
    for {
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::DotDotDot) {
        self.advance() |> ignore
        let expr = self.parse_assignment_expr()
        elements.push(ObjectLiteralElementLike::SpreadAssignment(expr))
      } else {
        let mut is_generator = false
        if self.at(LexKind::Star) {
          self.advance() |> ignore
          is_generator = true
        }
        let name_tok = self.current()
        let prop_name = self.parse_property_name()
        let mut is_optional = false
        self.skip_doc_comments_in_expr()
        if self.at(LexKind::Question) {
          self.advance() |> ignore
          is_optional = true
        }
        if !is_generator {
          match prop_name {
            PropertyName::Identifier(id) =>
              if id.text == "get" &&
                {
                  self.skip_doc_comments_in_expr()
                  !self.at(LexKind::Colon)
                } {
                let accessor_name = self.parse_property_name()
                let params = self.parse_parameter_list(false)
                let type_node = if self.at(LexKind::Colon) {
                  self.advance() |> ignore
                  Some(self.parse_type())
                } else {
                  None
                }
                let (body, _) = self.parse_block_with_function_context(
                  false, false,
                )
                elements.push(
                  ObjectLiteralElementLike::GetAccessor(
                    GetAccessorDecl::new(
                      None,
                      None,
                      ClassMemberModifiers::default(),
                      accessor_name,
                      params,
                      type_node,
                      Some(body),
                    ),
                  ),
                )
                if self.at(LexKind::Comma) {
                  self.advance() |> ignore
                }
                if self.at(LexKind::RBrace) {
                  break
                }
                continue
              } else if id.text == "set" &&
                {
                  self.skip_doc_comments_in_expr()
                  !self.at(LexKind::Colon)
                } {
                let accessor_name = self.parse_property_name()
                let params = self.parse_parameter_list(false)
                let (body, _) = self.parse_block_with_function_context(
                  false, false,
                )
                elements.push(
                  ObjectLiteralElementLike::SetAccessor(
                    SetAccessorDecl::new(
                      None,
                      None,
                      ClassMemberModifiers::default(),
                      accessor_name,
                      params,
                      Some(body),
                    ),
                  ),
                )
                if self.at(LexKind::Comma) {
                  self.advance() |> ignore
                }
                if self.at(LexKind::RBrace) {
                  break
                }
                continue
              }
            _ => ()
          }
        }
        self.skip_doc_comments_in_expr()
        if self.at(LexKind::Colon) {
          self.advance() |> ignore
          let value = self.parse_assignment_expr()
          elements.push(
            ObjectLiteralElementLike::PropertyAssignment(
              PropertyAssignment::new(prop_name, value),
            ),
          )
        } else if {
            self.skip_doc_comments_in_expr()
            self.at(LexKind::LParen)
          } {
          let params = self.parse_parameter_list(false)
          let type_node = if self.at(LexKind::Colon) {
            self.advance() |> ignore
            Some(self.parse_type())
          } else {
            None
          }
          let (body, _) = self.parse_block_with_function_context(
            is_generator, false,
          )
          elements.push(
            ObjectLiteralElementLike::MethodDecl(
              MethodDecl::new(
                None,
                None,
                ClassMemberModifiers::default(),
                is_generator,
                prop_name,
                is_optional,
                None,
                params,
                type_node,
                Some(body),
              ),
            ),
          )
        } else {
          match prop_name {
            PropertyName::Identifier(id) =>
              elements.push(
                ObjectLiteralElementLike::ShorthandPropertyAssignment(
                  ShorthandPropertyAssignment::new(id),
                ),
              )
            _ =>
              raise self.parse_error_at(
                name_tok.start,
                name_tok.end,
                ParseErrorKind::UnexpectedToken(name_tok.kind),
              )
          }
        }
      }
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        if self.at(LexKind::RBrace) {
          break
        }
        continue
      }
      break
    }
  }
  let end_tok = self.expect(LexKind::RBrace)
  let span = self.span_from(start, end_tok.end)
  Expr::new(
    ExprKind::ObjectLiteralExpr(self.node_array(elements, start, end_tok.end)),
    span,
  )
}

///|
fn Parser::parse_new_expr(self : Parser, start : Int) -> Expr raise ParseError {
  let callee = self.parse_starttfix_expr()
  self.skip_doc_comments_in_expr()
  let type_args = if self.looks_like_type_arguments() {
    Some(self.parse_type_arguments())
  } else {
    None
  }
  self.skip_doc_comments_in_expr()
  let args = if self.at(LexKind::LParen) {
    let open = self.advance()
    let list : Array[Expr] = []
    if !self.at(LexKind::RParen) {
      for {
        self.skip_doc_comments_in_expr()
        list.push(self.parse_assignment_expr())
        self.skip_doc_comments_in_expr()
        if self.at(LexKind::Comma) {
          self.advance() |> ignore
          continue
        }
        break
      }
    }
    let end_tok = self.expect(LexKind::RParen)
    Some(self.node_array(list, open.start, end_tok.end))
  } else {
    None
  }
  let end = match args {
    Some(list) => list.end.end
    None => callee.span.end
  }
  let new_expr = NewExpr::new(callee, type_args, args)
  Expr::new(ExprKind::NewExpr(new_expr), self.span_from(start, end))
}

///|
fn Parser::parse_function_expr(
  self : Parser,
  start : Int,
) -> Expr raise ParseError {
  let mut is_generator = false
  if self.at(LexKind::Star) {
    self.advance() |> ignore
    is_generator = true
  }
  let name = if self.current().kind is Identifier(_) {
    Some(self.parse_identifier())
  } else {
    None
  }
  let params = self.parse_parameter_list(false)
  let return_type = if self.at(LexKind::Colon) {
    self.advance() |> ignore
    Some(self.parse_type())
  } else {
    None
  }
  let (body_stmts, body_span) = self.parse_block_with_function_context(
    is_generator, false,
  )
  let func = FunctionExpr::new(
    false,
    is_generator,
    name,
    None,
    params,
    return_type,
    Some(body_stmts),
  )
  let span = self.span_from(start, body_span.end)
  Expr::new(ExprKind::FunctionExpr(func), span)
}

///|
fn Parser::parse_template_expr(
  self : Parser,
  head_text : String,
  head_raw : String,
  start : Int,
) -> Expr raise ParseError {
  let head = TemplateHead::new(head_text, head_raw)
  let spans : Array[TemplateSpan] = []
  let mut end_start = start
  for {
    self.skip_doc_comments_in_expr()
    let expr = self.parse_expr()
    self.skip_doc_comments_in_expr()
    let tok = self.current()
    match tok.kind {
      TemplateMiddle(text, raw) => {
        self.advance() |> ignore
        let middle = TemplateMiddle::new(text, raw)
        spans.push(
          TemplateSpan::new(
            expr,
            TemplateMiddleOrTemplateTail::TemplateMiddle(middle),
          ),
        )
        continue
      }
      TemplateTail(text, raw) => {
        self.advance() |> ignore
        let tail = TemplateTail::new(text, raw)
        spans.push(
          TemplateSpan::new(
            expr,
            TemplateMiddleOrTemplateTail::TemplateTail(tail),
          ),
        )
        end_start = tok.end
        break
      }
      _ =>
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::ExpectedTemplateSpan(tok.kind),
        )
    }
  }
  let span = self.span_from(start, end_start)
  let template = TemplateExpr::new(
    head,
    self.node_array(spans, start, end_start),
  )
  Expr::new(ExprKind::TemplateExpr(template), span)
}

///|
fn modifiers_has(
  modifiers : NodeArray[ModifierKind]?,
  kind : ModifierKind,
) -> Bool {
  match modifiers {
    Some(list) => {
      for item in list.elements.iter() {
        if item == kind {
          return true
        }
      }
      false
    }
    None => false
  }
}

///|
fn Parser::parse_expr_with_type_arguments(
  self : Parser,
) -> ExprWithTypeArguments raise ParseError {
  let expr = self.parse_starttfix_expr()
  self.skip_doc_comments_in_expr()
  let type_args = if self.at(LexKind::Lt) && self.looks_like_type_arguments() {
    Some(self.parse_type_arguments())
  } else {
    None
  }
  ExprWithTypeArguments::new(expr, type_args)
}

///|
fn Parser::parse_heritage_clause(
  self : Parser,
  kind : HeritageKind,
  start : Int,
) -> HeritageClause raise ParseError {
  let types : Array[ExprWithTypeArguments] = []
  for {
    types.push(self.parse_expr_with_type_arguments())
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  HeritageClause::new(kind, self.node_array(types, start, self.current().start))
}

///|
fn Parser::parse_class_elements(
  self : Parser,
  start : Int,
) -> NodeArray[ClassElement] raise ParseError {
  let members : Array[ClassElement] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments()
    if self.at(LexKind::RBrace) || self.at(LexKind::Eof) {
      break
    }
    if self.at(LexKind::Semicolon) {
      self.advance() |> ignore
      self.clear_doc_comment()
      members.push(ClassElement::SemicolonClassElement)
      continue
    }
    let member_start = self.current().start
    let decorators = self.parse_decorators(member_start)
    let modifiers = self.parse_modifiers(member_start)
    if self.at(LexKind::Semicolon) {
      if decorators is Some(list) {
        raise self.parse_error_at(
          list.start.start,
          list.end.end,
          ParseErrorKind::DecoratorsNotAllowedHere,
        )
      }
      self.advance() |> ignore
      members.push(ClassElement::SemicolonClassElement)
      continue
    }
    if self.at(LexKind::LBrace) &&
      modifiers_has(modifiers, ModifierKind::Static) {
      if decorators is Some(list) {
        raise self.parse_error_at(
          list.start.start,
          list.end.end,
          ParseErrorKind::DecoratorsNotAllowedHere,
        )
      }
      self.clear_doc_comment()
      let (body, _) = self.parse_block()
      let static_block = ClassStaticBlockDecl::new(body)
      members.push(ClassElement::ClassStaticBlockDecl(static_block))
      continue
    }
    if self.at(LexKind::LBracket) {
      if decorators is Some(list) {
        raise self.parse_error_at(
          list.start.start,
          list.end.end,
          ParseErrorKind::DecoratorsNotAllowedHere,
        )
      }
      self.clear_doc_comment()
      self.advance() |> ignore
      let param_name = self.parse_identifier()
      self.expect(LexKind::Colon) |> ignore
      let key_type = self.parse_type()
      self.expect(LexKind::RBracket) |> ignore
      self.expect(LexKind::Colon) |> ignore
      let value_type = self.parse_type()
      let param_span = self.span_from(param_name.span.start, key_type.span.end)
      let param = ParameterDecl::new(
        BindingName::Identifier(param_name),
        ParameterModifiers::default(),
        false,
        false,
        Some(key_type),
        None,
        param_span,
      )
      let param_start = param.span.start
      let param_end = param.span.end
      let span = match modifiers {
        Some(m) => m.start
        None => param_span
      }
      let mod_array = match modifiers {
        Some(m) => Some(m.elements)
        None => None
      }
      let class_mods = parse_class_member_modifiers(mod_array, span)
      let is_readonly = class_mods.is_readonly
      let index_sig = IndexSignatureDecl::new(
        is_readonly,
        self.node_array([param], param_start, param_end),
        value_type,
      )
      self.try_parse_semicolon() |> ignore
      members.push(ClassElement::IndexSignature(index_sig))
      continue
    }
    let mut is_generator = false
    let is_async = modifiers_has(modifiers, ModifierKind::Async)
    if self.at(LexKind::Star) {
      self.advance() |> ignore
      is_generator = true
    }
    let member_doc = self.take_doc_comment()
    let prop_name = self.parse_property_name()
    let mut is_optional = false
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Question) {
      self.advance() |> ignore
      is_optional = true
    }
    let mut is_definite = false
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Bang) {
      self.advance() |> ignore
      is_definite = true
    }
    if !is_generator {
      match prop_name {
        PropertyName::Identifier(id) =>
          if id.text == "get" &&
            {
              self.skip_doc_comments_in_expr()
              !self.at(LexKind::Colon)
            } {
            let accessor_name = self.parse_property_name()
            let params = self.parse_parameter_list(false)
            let type_node = if self.at(LexKind::Colon) {
              self.advance() |> ignore
              Some(self.parse_type())
            } else {
              None
            }
            let body = if self.at(LexKind::LBrace) {
              let (block, _) = self.parse_block_with_function_context(
                false, is_async,
              )
              Some(block)
            } else {
              self.try_parse_semicolon() |> ignore
              None
            }
            let span = match modifiers {
              Some(m) => m.start
              None => params.start
            }
            let mod_array = modifiers.map(m => m.elements)
            let class_mods = parse_class_member_modifiers(mod_array, span)
            let get_decl = GetAccessorDecl::new(
              member_doc, decorators, class_mods, accessor_name, params, type_node,
              body,
            )
            members.push(ClassElement::GetAccessor(get_decl))
            continue
          } else if id.text == "set" &&
            {
              self.skip_doc_comments_in_expr()
              !self.at(LexKind::Colon)
            } {
            let accessor_name = self.parse_property_name()
            let params = self.parse_parameter_list(false)
            let body = if self.at(LexKind::LBrace) {
              let (block, _) = self.parse_block_with_function_context(
                false, is_async,
              )
              Some(block)
            } else {
              self.try_parse_semicolon() |> ignore
              None
            }
            let span = match modifiers {
              Some(m) => m.start
              None => params.start
            }
            let mod_array = modifiers.map(m => m.elements)
            let class_mods = parse_class_member_modifiers(mod_array, span)
            let set_decl = SetAccessorDecl::new(
              member_doc, decorators, class_mods, accessor_name, params, body,
            )
            members.push(ClassElement::SetAccessor(set_decl))
            continue
          }
        _ => ()
      }
    }
    self.skip_doc_comments_in_expr()
    let type_params = if self.at(LexKind::Lt) &&
      self.looks_like_type_arguments() {
      Some(self.parse_type_parameters())
    } else {
      None
    }
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::LParen) {
      if !is_generator {
        match prop_name {
          PropertyName::Identifier(id) if id.text == "constructor" => {
            if decorators is Some(list) {
              raise self.parse_error_at(
                list.start.start,
                list.end.end,
                ParseErrorKind::DecoratorsNotAllowedHere,
              )
            }
            let params = self.parse_parameter_list(true)
            let body = if self.at(LexKind::LBrace) {
              let (block, _) = self.parse_block_with_function_context(
                false, false,
              )
              Some(block)
            } else {
              self.try_parse_semicolon() |> ignore
              None
            }
            let span = match modifiers {
              Some(m) => m.start
              None => params.start
            }
            let mod_array = modifiers.map(m => m.elements)
            let class_mods = parse_class_member_modifiers(mod_array, span)
            let ctor = ConstructorDecl::new(
              member_doc, class_mods, params, body,
            )
            members.push(ClassElement::Constructor(ctor))
            continue
          }
          _ => ()
        }
      }
      let params = self.parse_parameter_list(false)
      let type_node = if self.at(LexKind::Colon) {
        self.advance() |> ignore
        Some(self.parse_type())
      } else {
        None
      }
      let body = if self.at(LexKind::LBrace) {
        let (block, _) = self.parse_block_with_function_context(
          is_generator, is_async,
        )
        Some(block)
      } else {
        self.try_parse_semicolon() |> ignore
        None
      }
      let span = match modifiers {
        Some(m) => m.start
        None => params.start
      }
      let mod_array = modifiers.map(m => m.elements)
      let class_mods = parse_class_member_modifiers(mod_array, span)
      let mth = MethodDecl::new(
        member_doc, decorators, class_mods, is_generator, prop_name, is_optional,
        type_params, params, type_node, body,
      )
      members.push(ClassElement::MethodDecl(mth))
      continue
    }
    let type_node = if self.at(LexKind::Colon) {
      self.advance() |> ignore
      Some(self.parse_type())
    } else {
      None
    }
    let initializer = if self.at(LexKind::Eq) {
      self.advance() |> ignore
      Some(self.parse_expr())
    } else {
      None
    }
    self.try_parse_semicolon() |> ignore
    let span = match modifiers {
      Some(m) => m.start
      None => self.span_from(self.current().start, self.current().start)
    }
    let mod_array = match modifiers {
      Some(m) => Some(m.elements)
      None => None
    }
    let class_mods = parse_class_member_modifiers(mod_array, span)
    let prop = PropertyDecl::new(
      member_doc, decorators, class_mods, prop_name, is_optional, is_definite, type_node,
      initializer,
    )
    members.push(ClassElement::PropertyDecl(prop))
  }
  self.node_array(members, start, self.current().start)
}

///|
fn Parser::parse_class_expr(
  self : Parser,
  start : Int,
) -> Expr raise ParseError {
  let name = if self.current().kind is Identifier(_) {
    Some(self.parse_identifier())
  } else {
    None
  }
  let type_parameters = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  let heritage_items : Array[HeritageClause] = []
  if self.at(LexKind::KeywordExtends) {
    let extends_tok = self.advance()
    let clause = self.parse_heritage_clause(
      HeritageKind::Extends,
      extends_tok.start,
    )
    heritage_items.push(clause)
  }
  if self.at(LexKind::KeywordImplements) {
    let implements_tok = self.advance()
    let clause = self.parse_heritage_clause(
      HeritageKind::Implements,
      implements_tok.start,
    )
    heritage_items.push(clause)
  }
  let heritage = if heritage_items.length() > 0 {
    Some(self.node_array(heritage_items, start, self.current().start))
  } else {
    None
  }
  let body_start = self.expect(LexKind::LBrace).start
  let members = self.parse_class_elements(body_start)
  let end_tok = self.expect(LexKind::RBrace)
  let class_expr = ClassExpr::new(name, type_parameters, heritage, members)
  Expr::new(ExprKind::ClassExpr(class_expr), self.span_from(start, end_tok.end))
}

///|
fn Parser::parse_primary_expr(self : Parser) -> Expr raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(name) => {
      if name == "async" {
        if self.peek(1).kind == LexKind::LParen &&
          self.looks_like_arrow_from_paren_at(self.index + 1) {
          let start = tok.start
          self.advance() |> ignore
          let params = self.parse_parameter_list(false)
          self.expect(LexKind::Arrow) |> ignore
          let (body, end_start) = if self.at(LexKind::LBrace) {
            let (stmts, span) = self.parse_block_with_function_context(
              false, true,
            )
            (ArrowFunctionBody::Block(stmts), span.end)
          } else {
            let expr = self.parse_arrow_expr_body(true)
            (ArrowFunctionBody::Expr(expr), expr.span.end)
          }
          let arrow = ArrowFunction::new(true, None, params, None, body)
          return Expr::new(
            ExprKind::ArrowFunction(arrow),
            self.span_from(start, end_start),
          )
        }
        if self.peek(1).kind is Identifier(_) &&
          self.peek(2).kind == LexKind::Arrow {
          let start = tok.start
          self.advance() |> ignore
          let param_id = self.parse_identifier()
          let param_span = param_id.span
          let param = ParameterDecl::new(
            BindingName::Identifier(param_id),
            ParameterModifiers::default(),
            false,
            false,
            None,
            None,
            param_span,
          )
          let params = self.node_array(
            [param],
            param_span.start,
            param_span.end,
          )
          self.expect(LexKind::Arrow) |> ignore
          let (body, end_start) = if self.at(LexKind::LBrace) {
            let (stmts, span) = self.parse_block_with_function_context(
              false, true,
            )
            (ArrowFunctionBody::Block(stmts), span.end)
          } else {
            let expr = self.parse_arrow_expr_body(true)
            (ArrowFunctionBody::Expr(expr), expr.span.end)
          }
          let arrow = ArrowFunction::new(true, None, params, None, body)
          return Expr::new(
            ExprKind::ArrowFunction(arrow),
            self.span_from(start, end_start),
          )
        }
      }
      if self.peek(1).kind == LexKind::Arrow {
        let param = ParameterDecl::new(
          BindingName::Identifier(
            Identifier::new(name, self.span_from(tok.start, tok.end)),
          ),
          ParameterModifiers::default(),
          false,
          false,
          None,
          None,
          self.span_from(tok.start, tok.end),
        )
        let params = self.node_array([param], tok.start, tok.end)
        self.advance() |> ignore
        self.expect(LexKind::Arrow) |> ignore
        let (body, end_start) = if self.at(LexKind::LBrace) {
          let (stmts, span) = self.parse_block_with_function_context(
            false, false,
          )
          (ArrowFunctionBody::Block(stmts), span.end)
        } else {
          let expr = self.parse_arrow_expr_body(false)
          (ArrowFunctionBody::Expr(expr), expr.span.end)
        }
        let arrow = ArrowFunction::new(false, None, params, None, body)
        return Expr::new(
          ExprKind::ArrowFunction(arrow),
          self.span_from(tok.start, end_start),
        )
      }
      self.advance() |> ignore
      let id = Identifier::new(name, self.span_from(tok.start, tok.end))
      Expr::new(ExprKind::Identifier(id), self.span_from(tok.start, tok.end))
    }
    KeywordType | KeywordAs | KeywordFrom | KeywordOf | KeywordUsing => {
      let id = self.parse_identifier()
      Expr::new(ExprKind::Identifier(id), id.span)
    }
    Number(value) => {
      self.advance() |> ignore
      let lit = NumericLiteral::new(value, self.span_from(tok.start, tok.end))
      Expr::new(
        ExprKind::NumericLiteral(lit),
        self.span_from(tok.start, tok.end),
      )
    }
    Regex(value) => {
      self.advance() |> ignore
      let lit = RegularExprLiteral::new(
        value,
        self.span_from(tok.start, tok.end),
      )
      Expr::new(
        ExprKind::RegularExprLiteral(lit),
        self.span_from(tok.start, tok.end),
      )
    }
    NoSubTemplate(text, raw) => {
      self.advance() |> ignore
      let lit = NoSubstitutionTemplateLiteral::new(text, raw)
      Expr::new(
        ExprKind::NoSubstitutionTemplateLiteral(lit),
        self.span_from(tok.start, tok.end),
      )
    }
    TemplateHead(text, raw) => {
      self.advance() |> ignore
      self.parse_template_expr(text, raw, tok.start)
    }
    String(value) => {
      self.advance() |> ignore
      let lit = StringLiteral::new(value, self.span_from(tok.start, tok.end))
      Expr::new(
        ExprKind::StringLiteral(lit),
        self.span_from(tok.start, tok.end),
      )
    }
    LParen =>
      if self.looks_like_arrow_from_paren() {
        let start = self.current().start
        let params = self.parse_parameter_list(false)
        self.expect(LexKind::Arrow) |> ignore
        let (body, end_start) = if self.at(LexKind::LBrace) {
          let (stmts, span) = self.parse_block_with_function_context(
            false, false,
          )
          (ArrowFunctionBody::Block(stmts), span.end)
        } else {
          let expr = self.parse_arrow_expr_body(false)
          (ArrowFunctionBody::Expr(expr), expr.span.end)
        }
        let arrow = ArrowFunction::new(false, None, params, None, body)
        Expr::new(
          ExprKind::ArrowFunction(arrow),
          self.span_from(start, end_start),
        )
      } else {
        let start = self.advance().start
        let inner = self.parse_expr()
        let end_tok = self.expect(LexKind::RParen)
        let span = self.span_from(start, end_tok.end)
        Expr::new(ExprKind::ParenthesizedExpr(inner), span)
      }
    LBracket => {
      let start = self.advance().start
      self.parse_array_literal(start)
    }
    LBrace => {
      let start = self.advance().start
      self.parse_object_literal(start)
    }
    KeywordFunction => {
      let start = self.advance().start
      self.parse_function_expr(start)
    }
    KeywordClass => {
      let start = self.advance().start
      self.parse_class_expr(start)
    }
    KeywordNew => {
      if self.peek(1).kind == LexKind::Dot &&
        self.peek(2).kind is Identifier("target") {
        let start = tok.start
        self.advance() |> ignore
        self.advance() |> ignore
        let target_tok = self.advance()
        return Expr::new(
          ExprKind::MetaProperty(MetaPropertyKind::NewTarget),
          self.span_from(start, target_tok.end),
        )
      }
      let start = self.advance().start
      self.parse_new_expr(start)
    }
    KeywordThis => {
      self.advance() |> ignore
      Expr::new(ExprKind::ThisExpr, self.span_from(tok.start, tok.end))
    }
    KeywordSuper => {
      self.advance() |> ignore
      Expr::new(ExprKind::SuperExpr, self.span_from(tok.start, tok.end))
    }
    KeywordTrue => {
      self.advance() |> ignore
      Expr::new(ExprKind::TrueLiteral, self.span_from(tok.start, tok.end))
    }
    KeywordFalse => {
      self.advance() |> ignore
      Expr::new(ExprKind::FalseLiteral, self.span_from(tok.start, tok.end))
    }
    KeywordNull => {
      self.advance() |> ignore
      Expr::new(ExprKind::NullLiteral, self.span_from(tok.start, tok.end))
    }
    KeywordImport => {
      if self.peek(1).kind == LexKind::Dot &&
        self.peek(2).kind is Identifier("meta") {
        let start = tok.start
        self.advance() |> ignore
        self.advance() |> ignore
        let meta_tok = self.advance()
        return Expr::new(
          ExprKind::MetaProperty(MetaPropertyKind::ImportMeta),
          self.span_from(start, meta_tok.end),
        )
      }
      if self.peek(1).kind == LexKind::LParen {
        let start = tok.start
        self.advance() |> ignore
        let open = self.expect(LexKind::LParen)
        let args : Array[Expr] = []
        if !self.at(LexKind::RParen) {
          for {
            self.skip_doc_comments_in_expr()
            args.push(self.parse_assignment_expr())
            self.skip_doc_comments_in_expr()
            if self.at(LexKind::Comma) {
              self.advance() |> ignore
              continue
            }
            break
          }
        }
        let end_tok = self.expect(LexKind::RParen)
        return Expr::new(
          ExprKind::ImportExpr(self.node_array(args, open.start, end_tok.end)),
          self.span_from(start, end_tok.end),
        )
      }
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedPrimary(tok.kind),
      )
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedPrimary(tok.kind),
      )
  }
}

///|
fn Parser::parse_starttfix_expr(self : Parser) -> Expr raise ParseError {
  let mut expr = self.parse_primary_expr()
  for {
    self.skip_doc_comments_in_expr()
    let tok = self.current()
    match tok.kind {
      Lt => {
        if self.looks_like_type_arguments() {
          let call_type_args = Some(self.parse_type_arguments())
          if self.at(LexKind::LParen) {
            let start = self.current().start
            self.advance() |> ignore
            let args : Array[Expr] = []
            if !self.at(LexKind::RParen) {
              for {
                self.skip_doc_comments_in_expr()
                if self.at(LexKind::DotDotDot) {
                  let spread_tok = self.advance()
                  let arg = self.parse_assignment_expr()
                  let span = self.span_from(spread_tok.start, arg.span.end)
                  args.push(Expr::new(ExprKind::SpreadElement(arg), span))
                } else {
                  args.push(self.parse_assignment_expr())
                }
                self.skip_doc_comments_in_expr()
                if self.at(LexKind::Comma) {
                  self.advance() |> ignore
                  continue
                }
                break
              }
            }
            let end_tok = self.expect(LexKind::RParen)
            let call = CallExpr::new(
              expr,
              call_type_args,
              self.node_array(args, start, end_tok.end),
              false,
            )
            let span = self.span_from(expr.span.start, end_tok.end)
            expr = Expr::new(ExprKind::CallExpr(call), span)
            continue
          }
          break
        }
        break
      }
      QuestionDot => {
        self.advance() |> ignore
        let call_type_args = if self.at(LexKind::Lt) &&
          self.looks_like_type_arguments() {
          Some(self.parse_type_arguments())
        } else {
          None
        }
        if self.at(LexKind::LParen) {
          let call_start = self.current().start
          self.advance() |> ignore
          let args : Array[Expr] = []
          if !self.at(LexKind::RParen) {
            for {
              self.skip_doc_comments_in_expr()
              if self.at(LexKind::DotDotDot) {
                let spread_tok = self.advance()
                let arg = self.parse_assignment_expr()
                let span = self.span_from(spread_tok.start, arg.span.end)
                args.push(Expr::new(ExprKind::SpreadElement(arg), span))
              } else {
                args.push(self.parse_assignment_expr())
              }
              self.skip_doc_comments_in_expr()
              if self.at(LexKind::Comma) {
                self.advance() |> ignore
                continue
              }
              break
            }
          }
          let end_tok = self.expect(LexKind::RParen)
          let call = CallExpr::new(
            expr,
            call_type_args,
            self.node_array(args, call_start, end_tok.end),
            true,
          )
          let span = self.span_from(expr.span.start, end_tok.end)
          expr = Expr::new(ExprKind::CallExpr(call), span)
          continue
        }
        if self.at(LexKind::LBracket) {
          self.advance() |> ignore
          let arg = self.parse_expr()
          let end_tok = self.expect(LexKind::RBracket)
          let access = ElementAccessExpr::new(expr, arg, true)
          let span = self.span_from(expr.span.start, end_tok.end)
          expr = Expr::new(ExprKind::ElementAccessExpr(access), span)
          continue
        }
        let name = self.parse_identifier_name()
        let span = self.span_from(expr.span.start, name.span.end)
        let access = PropertyAccessExpr::new(expr, name, true)
        expr = Expr::new(ExprKind::PropertyAccessExpr(access), span)
      }
      Dot => {
        self.advance() |> ignore
        let name = self.parse_identifier_name()
        let span = self.span_from(expr.span.start, name.span.end)
        let access = PropertyAccessExpr::new(expr, name, false)
        expr = Expr::new(ExprKind::PropertyAccessExpr(access), span)
      }
      LParen => {
        let start = tok.start
        self.advance() |> ignore
        let args : Array[Expr] = []
        if !self.at(LexKind::RParen) {
          for {
            self.skip_doc_comments_in_expr()
            if self.at(LexKind::DotDotDot) {
              let spread_tok = self.advance()
              let arg = self.parse_assignment_expr()
              let span = self.span_from(spread_tok.start, arg.span.end)
              args.push(Expr::new(ExprKind::SpreadElement(arg), span))
            } else {
              args.push(self.parse_assignment_expr())
            }
            self.skip_doc_comments_in_expr()
            if self.at(LexKind::Comma) {
              self.advance() |> ignore
              continue
            }
            break
          }
        }
        let end_tok = self.expect(LexKind::RParen)
        let call = CallExpr::new(
          expr,
          None,
          self.node_array(args, start, end_tok.end),
          false,
        )
        let span = self.span_from(expr.span.start, end_tok.end)
        expr = Expr::new(ExprKind::CallExpr(call), span)
      }
      LBracket => {
        self.advance() |> ignore
        let arg = self.parse_expr()
        let end_tok = self.expect(LexKind::RBracket)
        let access = ElementAccessExpr::new(expr, arg, false)
        let span = self.span_from(expr.span.start, end_tok.end)
        expr = Expr::new(ExprKind::ElementAccessExpr(access), span)
      }
      NoSubTemplate(text, raw) => {
        self.advance() |> ignore
        let lit = NoSubstitutionTemplateLiteral::new(text, raw)
        let template_span = self.span_from(tok.start, tok.end)
        let template = Expr::new(
          ExprKind::NoSubstitutionTemplateLiteral(lit),
          template_span,
        )
        let span = self.span_from(expr.span.start, tok.end)
        let tagged = TaggedTemplateExpr::new(expr, None, template)
        expr = Expr::new(ExprKind::TaggedTemplateExpr(tagged), span)
      }
      TemplateHead(text, raw) => {
        self.advance() |> ignore
        let template = self.parse_template_expr(text, raw, tok.start)
        let span = self.span_from(expr.span.start, template.span.end)
        let tagged = TaggedTemplateExpr::new(expr, None, template)
        expr = Expr::new(ExprKind::TaggedTemplateExpr(tagged), span)
      }
      PlusPlus => {
        let op = self.advance()
        let span = self.span_from(expr.span.start, op.end)
        let postfix = PostfixUnaryExpr::new(expr, UnaryOperator::PlusPlus)
        expr = Expr::new(ExprKind::PostfixUnaryExpr(postfix), span)
      }
      MinusMinus => {
        let op = self.advance()
        let span = self.span_from(expr.span.start, op.end)
        let postfix = PostfixUnaryExpr::new(expr, UnaryOperator::MinusMinus)
        expr = Expr::new(ExprKind::PostfixUnaryExpr(postfix), span)
      }
      Bang => {
        let op = self.advance()
        let span = self.span_from(expr.span.start, op.end)
        expr = Expr::new(ExprKind::NonNullExpr(expr), span)
      }
      _ => break
    }
  }
  expr
}

///|
fn Parser::parse_unary_expr(self : Parser) -> Expr raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Lt => {
      let start = self.advance().start
      let type_node = self.parse_type()
      self.expect_gt() |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let assertion = TypeAssertionExpr::new(type_node, operand)
      Expr::new(ExprKind::TypeAssertionExpr(assertion), span)
    }
    KeywordDelete => {
      let start = tok.start
      self.advance() |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      Expr::new(ExprKind::DeleteExpr(operand), span)
    }
    KeywordTypeof => {
      let start = tok.start
      self.advance() |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      Expr::new(ExprKind::TypeOfExpr(operand), span)
    }
    KeywordVoid => {
      let start = tok.start
      self.advance() |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      Expr::new(ExprKind::VoidExpr(operand), span)
    }
    KeywordAwait => {
      let start = tok.start
      if self.in_function > 0 && self.in_async_function == 0 {
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::AwaitOnlyInAsyncFunction,
        )
      }
      self.advance() |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      self.record_top_level_await(span)
      Expr::new(ExprKind::AwaitExpr(operand), span)
    }
    PlusPlus => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::PlusPlus, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    MinusMinus => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::MinusMinus, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    Plus => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::Plus, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    Minus => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::Minus, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    Bang => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::Exclamation, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    Tilde => {
      let start = tok.start
      self.advance() |> ignore |> ignore
      let operand = self.parse_unary_expr()
      let span = self.span_from(start, operand.span.end)
      let unary = PrefixUnaryExpr::new(UnaryOperator::Tilde, operand)
      Expr::new(ExprKind::PrefixUnaryExpr(unary), span)
    }
    _ => self.parse_starttfix_expr()
  }
}

///|
fn binary_precedence(kind : LexKind) -> Int {
  match kind {
    StarStar => 11
    Star => 10
    Slash => 10
    Percent => 10
    Plus => 9
    Minus => 9
    LtLt => 8
    GtGt => 8
    GtGtGt => 8
    Lt => 7
    LtEq => 7
    Gt => 7
    GtEq => 7
    KeywordIn => 7
    KeywordInstanceof => 7
    EqEq => 6
    EqEqEq => 6
    BangEq => 6
    BangEqEq => 6
    Ampersand => 5
    Caret => 4
    Bar => 3
    AmpAmp => 2
    BarBar => 1
    QuestionQuestion => 1
    _ => 0
  }
}

///|
fn binary_precedence_no_in(kind : LexKind) -> Int {
  if kind == LexKind::KeywordIn {
    return 0
  }
  binary_precedence(kind)
}

///|
fn binary_operator(kind : LexKind) -> BinaryOperator? {
  match kind {
    StarStar => Some(BinaryOperator::AsteriskAsterisk)
    Star => Some(BinaryOperator::Asterisk)
    Slash => Some(BinaryOperator::Slash)
    Percent => Some(BinaryOperator::Percent)
    Plus => Some(BinaryOperator::Plus)
    Minus => Some(BinaryOperator::Minus)
    LtLt => Some(BinaryOperator::ShiftLeft)
    GtGt => Some(BinaryOperator::ShiftRight)
    GtGtGt => Some(BinaryOperator::ShiftRightUnsigned)
    Lt => Some(BinaryOperator::LessThan)
    LtEq => Some(BinaryOperator::LessThanEquals)
    Gt => Some(BinaryOperator::GreaterThan)
    GtEq => Some(BinaryOperator::GreaterThanEquals)
    KeywordIn => Some(BinaryOperator::In)
    KeywordInstanceof => Some(BinaryOperator::InstanceOf)
    EqEq => Some(BinaryOperator::EqualsEquals)
    EqEqEq => Some(BinaryOperator::EqualsEqualsEquals)
    BangEq => Some(BinaryOperator::ExclamationEquals)
    BangEqEq => Some(BinaryOperator::ExclamationEqualsEquals)
    Ampersand => Some(BinaryOperator::BitwiseAnd)
    Caret => Some(BinaryOperator::BitwiseXor)
    Bar => Some(BinaryOperator::BitwiseOr)
    AmpAmp => Some(BinaryOperator::AmpersandAmpersand)
    BarBar => Some(BinaryOperator::BarBar)
    QuestionQuestion => Some(BinaryOperator::QuestionQuestion)
    _ => None
  }
}

///|
fn assignment_operator(kind : LexKind) -> BinaryOperator? {
  match kind {
    Eq => Some(BinaryOperator::Equals)
    PlusEq => Some(BinaryOperator::PlusEquals)
    MinusEq => Some(BinaryOperator::MinusEquals)
    StarEq => Some(BinaryOperator::AsteriskEquals)
    StarStarEq => Some(BinaryOperator::AsteriskAsteriskEquals)
    SlashEq => Some(BinaryOperator::SlashEquals)
    PercentEq => Some(BinaryOperator::PercentEquals)
    AmpersandEq => Some(BinaryOperator::AmpersandEquals)
    BarEq => Some(BinaryOperator::BarEquals)
    CaretEq => Some(BinaryOperator::CaretEquals)
    LtLtEq => Some(BinaryOperator::ShiftLeftEquals)
    GtGtEq => Some(BinaryOperator::ShiftRightEquals)
    GtGtGtEq => Some(BinaryOperator::ShiftRightUnsignedEquals)
    AmpAmpEq => Some(BinaryOperator::AmpersandAmpersandEquals)
    BarBarEq => Some(BinaryOperator::BarBarEquals)
    QuestionQuestionEq => Some(BinaryOperator::QuestionQuestionEquals)
    _ => None
  }
}

///|
fn Parser::parse_binary_expr(
  self : Parser,
  min_prec : Int,
) -> Expr raise ParseError {
  let mut left = self.parse_unary_expr()
  for {
    self.skip_doc_comments_in_expr()
    let tok = self.current()
    let prec = binary_precedence(tok.kind)
    if prec < min_prec || prec == 0 {
      break
    }
    self.advance() |> ignore
    let next_prec = if tok.kind == LexKind::StarStar { prec } else { prec + 1 }
    let right = self.parse_binary_expr(next_prec)
    let span = self.span_from(left.span.start, right.span.end)
    let op = match binary_operator(tok.kind) {
      Some(op) => op
      None =>
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::InvalidBinaryOperator(tok.kind),
        )
    }
    let bin = BinaryExpr::new(left, op, right)
    left = Expr::new(ExprKind::BinaryExpr(bin), span)
  }
  left
}

///|
fn Parser::parse_binary_expr_no_in(
  self : Parser,
  min_prec : Int,
) -> Expr raise ParseError {
  let mut left = self.parse_unary_expr()
  for {
    self.skip_doc_comments_in_expr()
    let tok = self.current()
    let prec = binary_precedence_no_in(tok.kind)
    if prec < min_prec || prec == 0 {
      break
    }
    self.advance() |> ignore
    let next_prec = if tok.kind == LexKind::StarStar { prec } else { prec + 1 }
    let right = self.parse_binary_expr_no_in(next_prec)
    let span = self.span_from(left.span.start, right.span.end)
    let op = match binary_operator(tok.kind) {
      Some(op) => op
      None =>
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::InvalidBinaryOperator(tok.kind),
        )
    }
    let bin = BinaryExpr::new(left, op, right)
    left = Expr::new(ExprKind::BinaryExpr(bin), span)
  }
  left
}

///|
fn Parser::parse_conditional_expr(self : Parser) -> Expr raise ParseError {
  let condition = self.parse_binary_expr(1)
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Question) {
    let start = condition.span.start
    self.advance() |> ignore
    let when_true = self.parse_expr()
    self.expect(LexKind::Colon) |> ignore
    let when_false = self.parse_expr()
    let span = self.span_from(start, when_false.span.end)
    let cond = ConditionalExpr::new(condition, when_true, when_false)
    return Expr::new(ExprKind::ConditionalExpr(cond), span)
  }
  condition
}

///|
fn Parser::parse_conditional_expr_no_in(self : Parser) -> Expr raise ParseError {
  let condition = self.parse_binary_expr_no_in(1)
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Question) {
    let start = condition.span.start
    self.advance() |> ignore
    let when_true = self.parse_expr_no_in()
    self.expect(LexKind::Colon) |> ignore
    let when_false = self.parse_expr_no_in()
    let span = self.span_from(start, when_false.span.end)
    let cond = ConditionalExpr::new(condition, when_true, when_false)
    return Expr::new(ExprKind::ConditionalExpr(cond), span)
  }
  condition
}

///|
fn Parser::parse_as_expr(self : Parser) -> Expr raise ParseError {
  let mut expr = self.parse_conditional_expr()
  for {
    self.skip_doc_comments_in_expr()
    if !(self.at(LexKind::KeywordAs) || self.at(LexKind::KeywordSatisfies)) {
      break
    }
    let start = expr.span.start
    let is_satisfies = self.at(LexKind::KeywordSatisfies)
    self.advance() |> ignore
    let type_node = self.parse_type()
    let span = self.span_from(start, type_node.span.end)
    if is_satisfies {
      expr = Expr::new(
        ExprKind::SatisfiesExpr(SatisfiesExpr::new(expr, type_node)),
        span,
      )
    } else {
      expr = Expr::new(ExprKind::AsExpr(AsExpr::new(expr, type_node)), span)
    }
  }
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Question) {
    let start = expr.span.start
    self.advance() |> ignore
    let when_true = self.parse_expr()
    self.expect(LexKind::Colon) |> ignore
    let when_false = self.parse_expr()
    let span = self.span_from(start, when_false.span.end)
    let cond = ConditionalExpr::new(expr, when_true, when_false)
    return Expr::new(ExprKind::ConditionalExpr(cond), span)
  }
  expr
}

///|
fn Parser::parse_as_expr_no_in(self : Parser) -> Expr raise ParseError {
  let mut expr = self.parse_conditional_expr_no_in()
  for {
    self.skip_doc_comments_in_expr()
    if !(self.at(LexKind::KeywordAs) || self.at(LexKind::KeywordSatisfies)) {
      break
    }
    let start = expr.span.start
    let is_satisfies = self.at(LexKind::KeywordSatisfies)
    self.advance() |> ignore
    let type_node = self.parse_type()
    let span = self.span_from(start, type_node.span.end)
    if is_satisfies {
      expr = Expr::new(
        ExprKind::SatisfiesExpr(SatisfiesExpr::new(expr, type_node)),
        span,
      )
    } else {
      expr = Expr::new(ExprKind::AsExpr(AsExpr::new(expr, type_node)), span)
    }
  }
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Question) {
    let start = expr.span.start
    self.advance() |> ignore
    let when_true = self.parse_expr_no_in()
    self.expect(LexKind::Colon) |> ignore
    let when_false = self.parse_expr_no_in()
    let span = self.span_from(start, when_false.span.end)
    let cond = ConditionalExpr::new(expr, when_true, when_false)
    return Expr::new(ExprKind::ConditionalExpr(cond), span)
  }
  expr
}

///|
fn Parser::parse_assignment_expr(self : Parser) -> Expr raise ParseError {
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::KeywordYield) {
    if self.in_generator == 0 {
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::YieldOnlyInGenerator,
      )
    }
    let start = self.advance().start
    let is_delegate = if self.at(LexKind::Star) {
      self.advance() |> ignore
      true
    } else {
      false
    }
    let expr = if !self.can_parse_semicolon() {
      Some(self.parse_assignment_expr())
    } else {
      None
    }
    let end = if expr is Some(value) {
      value.span.end
    } else {
      self.current().start
    }
    let yield_expr = YieldExpr::new(is_delegate, expr)
    return Expr::new(
      ExprKind::YieldExpr(yield_expr),
      self.span_from(start, end),
    )
  }
  let left = self.parse_as_expr()
  match assignment_operator(self.current().kind) {
    Some(op) => {
      let start = left.span.start
      self.advance() |> ignore
      let right = self.parse_assignment_expr()
      let span = self.span_from(start, right.span.end)
      let bin = BinaryExpr::new(left, op, right)
      Expr::new(ExprKind::BinaryExpr(bin), span)
    }
    None => left
  }
}

///|
fn Parser::parse_assignment_expr_no_in(self : Parser) -> Expr raise ParseError {
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::KeywordYield) {
    if self.in_generator == 0 {
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::YieldOnlyInGenerator,
      )
    }
    let start = self.advance().start
    let is_delegate = if self.at(LexKind::Star) {
      self.advance() |> ignore
      true
    } else {
      false
    }
    let expr = if !self.can_parse_semicolon() {
      Some(self.parse_assignment_expr_no_in())
    } else {
      None
    }
    let end = if expr is Some(value) {
      value.span.end
    } else {
      self.current().start
    }
    let yield_expr = YieldExpr::new(is_delegate, expr)
    return Expr::new(
      ExprKind::YieldExpr(yield_expr),
      self.span_from(start, end),
    )
  }
  let left = self.parse_as_expr_no_in()
  match assignment_operator(self.current().kind) {
    Some(op) => {
      let start = left.span.start
      self.advance() |> ignore
      let right = self.parse_assignment_expr_no_in()
      let span = self.span_from(start, right.span.end)
      let bin = BinaryExpr::new(left, op, right)
      Expr::new(ExprKind::BinaryExpr(bin), span)
    }
    None => left
  }
}

///|
fn Parser::parse_expr(self : Parser) -> Expr raise ParseError {
  let first = self.parse_assignment_expr()
  self.skip_doc_comments_in_expr()
  if !self.at(LexKind::Comma) {
    return first
  }
  let expressions : Array[Expr] = [first]
  let mut end_start = first.span.end
  while self.at(LexKind::Comma) {
    self.advance() |> ignore
    self.skip_doc_comments_in_expr()
    let expr = self.parse_assignment_expr()
    end_start = expr.span.end
    expressions.push(expr)
  }
  let span = self.span_from(first.span.start, end_start)
  Expr::new(
    ExprKind::CommaListExpr(
      self.node_array(expressions, first.span.start, end_start),
    ),
    span,
  )
}

///|
fn Parser::parse_expr_no_in(self : Parser) -> Expr raise ParseError {
  let first = self.parse_assignment_expr_no_in()
  self.skip_doc_comments_in_expr()
  if !self.at(LexKind::Comma) {
    return first
  }
  let expressions : Array[Expr] = [first]
  let mut end_start = first.span.end
  while self.at(LexKind::Comma) {
    self.advance() |> ignore
    self.skip_doc_comments_in_expr()
    let expr = self.parse_assignment_expr_no_in()
    end_start = expr.span.end
    expressions.push(expr)
  }
  let span = self.span_from(first.span.start, end_start)
  Expr::new(
    ExprKind::CommaListExpr(
      self.node_array(expressions, first.span.start, end_start),
    ),
    span,
  )
}

///|
fn Parser::parse_variable_decl_list(
  self : Parser,
  start : Int,
  kind : VariableDeclKind,
) -> NodeArray[VariableDecl] raise ParseError {
  let decls : Array[VariableDecl] = []
  for {
    self.skip_doc_comments_in_expr()
    let (binding, _) = self.parse_binding_name()
    self.skip_doc_comments_in_expr()
    let type_node = if self.at(LexKind::Colon) {
      self.advance() |> ignore
      Some(self.parse_type())
    } else {
      None
    }
    self.skip_doc_comments_in_expr()
    let initializer = if self.at(LexKind::Eq) {
      self.advance() |> ignore
      Some(self.parse_assignment_expr())
    } else {
      None
    }
    let decl = VariableDecl::new(kind, binding, type_node, initializer)
    decls.push(decl)
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  self.node_array(decls, start, self.current().start)
}

///|
fn variable_decl_kind_from_token(kind : LexKind) -> VariableDeclKind {
  match kind {
    KeywordLet => VariableDeclKind::Let
    KeywordConst => VariableDeclKind::Const
    _ => VariableDeclKind::Var
  }
}

///|
fn Parser::parse_using_decl_list(
  self : Parser,
  start : Int,
) -> NodeArray[UsingDecl] raise ParseError {
  let decls : Array[UsingDecl] = []
  for {
    self.skip_doc_comments_in_expr()
    let name = self.parse_identifier()
    self.skip_doc_comments_in_expr()
    let type_node = if self.at(LexKind::Colon) {
      self.advance() |> ignore
      Some(self.parse_type())
    } else {
      None
    }
    self.skip_doc_comments_in_expr()
    self.expect(LexKind::Eq) |> ignore
    let initializer = self.parse_assignment_expr()
    let decl = UsingDecl::new(name, type_node, Some(initializer))
    decls.push(decl)
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  self.node_array(decls, start, self.current().start)
}

///|
fn Parser::parse_variable_stmt_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  let decl_kind = variable_decl_kind_from_token(self.current().kind)
  self.advance() |> ignore
  let list = self.parse_variable_decl_list(start, decl_kind)
  self.parse_semicolon()
  let stmt = VariableStmt::new(doc_comment, modifiers, list)
  let span = self.span_from(start, self.current().start)
  Stmt::new(StmtKind::VariableStmt(stmt), span)
}

///|
fn Parser::parse_using_stmt_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
  is_await : Bool,
) -> Stmt raise ParseError {
  self.clear_doc_comment()
  if is_await {
    self.advance() |> ignore
    self.expect(LexKind::KeywordUsing) |> ignore
    if self.in_function > 0 && self.in_async_function == 0 {
      raise self.parse_error_at(
        start,
        self.current().end,
        ParseErrorKind::AwaitUsingOnlyInAsyncFunction,
      )
    }
  } else {
    self.advance() |> ignore
  }
  let list = self.parse_using_decl_list(start)
  self.parse_semicolon()
  let stmt = UsingStmt::new(modifiers, list, is_await)
  let span = self.span_from(start, self.current().start)
  if is_await {
    self.record_top_level_await_using(span)
  }
  Stmt::new(StmtKind::UsingStmt(stmt), span)
}

///|
fn Parser::parse_variable_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_variable_stmt_with_modifiers(start, None)
}

///|
fn Parser::parse_block(self : Parser) -> (Array[Stmt], Span) raise ParseError {
  self.parse_block_with_doc_comments(false)
}

///|
fn Parser::parse_block_with_doc_comments(
  self : Parser,
  allow_doc_comments : Bool,
) -> (Array[Stmt], Span) raise ParseError {
  let prev_allow = self.allow_doc_comments
  self.allow_doc_comments = allow_doc_comments
  let start = self.expect(LexKind::LBrace).start
  let stmts : Array[Stmt] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments()
    if self.at(LexKind::RBrace) || self.at(LexKind::Eof) {
      break
    }
    stmts.push(self.parse_stmt())
  }
  let end_tok = self.expect(LexKind::RBrace)
  self.allow_doc_comments = prev_allow
  (stmts, self.span_from(start, end_tok.end))
}

///|

///|
fn Parser::parse_block_with_function_context(
  self : Parser,
  is_generator : Bool,
  is_async : Bool,
) -> (Array[Stmt], Span) raise ParseError {
  let prev_generator = self.in_generator
  let prev_function = self.in_function
  let prev_async_function = self.in_async_function
  self.in_generator = if is_generator { 1 } else { 0 }
  self.in_function = prev_function + 1
  self.in_async_function = if is_async { prev_async_function + 1 } else { 0 }
  let result = self.parse_block()
  self.in_generator = prev_generator
  self.in_function = prev_function
  self.in_async_function = prev_async_function
  result
}

///|
fn Parser::parse_arrow_expr_body(
  self : Parser,
  is_async : Bool,
) -> Expr raise ParseError {
  let prev_generator = self.in_generator
  let prev_function = self.in_function
  let prev_async_function = self.in_async_function
  self.in_generator = 0
  self.in_function = prev_function + 1
  self.in_async_function = if is_async { prev_async_function + 1 } else { 0 }
  let expr = self.parse_assignment_expr()
  self.in_generator = prev_generator
  self.in_function = prev_function
  self.in_async_function = prev_async_function
  expr
}

///|
fn Parser::parse_if_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  self.expect(LexKind::LParen) |> ignore
  let condition = self.parse_expr()
  self.expect(LexKind::RParen) |> ignore
  let then_stmt = self.parse_stmt()
  let else_stmt = if self.at(LexKind::KeywordElse) {
    self.advance() |> ignore
    Some(self.parse_stmt())
  } else {
    None
  }
  let end = match else_stmt {
    Some(stmt) => stmt.span.end
    None => then_stmt.span.end
  }
  let stmt = IfStmt::new(condition, then_stmt, else_stmt)
  Stmt::new(StmtKind::IfStmt(stmt), self.span_from(start, end))
}

///|
fn Parser::parse_return_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let expr = if self.can_parse_semicolon() {
    None
  } else {
    Some(self.parse_expr())
  }
  self.parse_semicolon()
  let end = if expr is Some(e) { e.span.end } else { self.current().start }
  Stmt::new(StmtKind::ReturnStmt(expr), self.span_from(start, end))
}

///|
fn Parser::parse_break_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let label = if self.can_parse_semicolon() {
    None
  } else {
    Some(self.parse_identifier())
  }
  self.parse_semicolon()
  Stmt::new(
    StmtKind::BreakStmt(label),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_continue_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let label = if self.can_parse_semicolon() {
    None
  } else {
    Some(self.parse_identifier())
  }
  self.parse_semicolon()
  Stmt::new(
    StmtKind::ContinueStmt(label),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_throw_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  if self.has_preceding_line_break() {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::LineBreakAfterThrow,
    )
  }
  let expr = self.parse_expr()
  self.parse_semicolon()
  Stmt::new(StmtKind::ThrowStmt(expr), self.span_from(start, expr.span.end))
}

///|
fn Parser::parse_while_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  self.expect(LexKind::LParen) |> ignore
  let condition = self.parse_expr()
  self.expect(LexKind::RParen) |> ignore
  let body = self.parse_stmt()
  let stmt = WhileStmt::new(condition, body)
  Stmt::new(StmtKind::WhileStmt(stmt), self.span_from(start, body.span.end))
}

///|
fn Parser::parse_do_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let body = self.parse_stmt()
  self.expect(LexKind::KeywordWhile) |> ignore
  self.expect(LexKind::LParen) |> ignore
  let condition = self.parse_expr()
  self.expect(LexKind::RParen) |> ignore
  self.parse_semicolon()
  let stmt = DoStmt::new(body, condition)
  Stmt::new(StmtKind::DoStmt(stmt), self.span_from(start, condition.span.end))
}

///|
fn Parser::parse_for_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let is_await = if self.at(LexKind::KeywordAwait) {
    self.advance() |> ignore
    true
  } else {
    false
  }
  if is_await && self.in_function > 0 && self.in_async_function == 0 {
    raise self.parse_error_at(
      start,
      self.current().end,
      ParseErrorKind::ForAwaitOnlyInAsyncFunction,
    )
  }
  if is_await && self.for_await_range is None {
    self.for_await_range = Some((start, self.current().end))
  }
  self.expect(LexKind::LParen) |> ignore
  if self.at(LexKind::Semicolon) {
    self.advance() |> ignore
    let condition = if !self.at(LexKind::Semicolon) {
      Some(self.parse_expr())
    } else {
      None
    }
    self.expect(LexKind::Semicolon) |> ignore
    let incrementor = if !self.at(LexKind::RParen) {
      Some(self.parse_expr())
    } else {
      None
    }
    self.expect(LexKind::RParen) |> ignore
    let body = self.parse_stmt()
    let stmt = ForStmt::new(None, condition, incrementor, body)
    return Stmt::new(
      StmtKind::ForStmt(stmt),
      self.span_from(start, body.span.end),
    )
  }
  let init : ForInitializer = if self.at(LexKind::KeywordLet) ||
    self.at(LexKind::KeywordConst) ||
    self.at(LexKind::KeywordVar) ||
    self.is_using_stmt_start() ||
    self.is_await_using_stmt_start() {
    if self.is_await_using_stmt_start() {
      let await_start = self.current().start
      self.advance() |> ignore
      self.expect(LexKind::KeywordUsing) |> ignore
      if self.in_function > 0 && self.in_async_function == 0 {
        raise self.parse_error_at(
          await_start,
          self.current().end,
          ParseErrorKind::AwaitUsingOnlyInAsyncFunction,
        )
      }
      let list = self.parse_using_decl_list(start)
      let span = self.span_from(await_start, self.current().start)
      self.record_top_level_await_using(span)
      ForInitializer::UsingDeclList(list, true)
    } else if self.is_using_stmt_start() {
      self.advance() |> ignore
      let list = self.parse_using_decl_list(start)
      ForInitializer::UsingDeclList(list, false)
    } else {
      let decl_kind = variable_decl_kind_from_token(self.current().kind)
      self.advance() |> ignore
      let list = self.parse_variable_decl_list(start, decl_kind)
      ForInitializer::VariableDeclList(list)
    }
  } else {
    ForInitializer::Expr(self.parse_expr_no_in())
  }
  if self.at(LexKind::KeywordIn) || self.at(LexKind::KeywordOf) {
    let is_for_of = self.at(LexKind::KeywordOf)
    if is_await && !is_for_of {
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::ForAwaitMustUseOf(tok.kind),
      )
    }
    self.advance() |> ignore
    let expr = self.parse_expr()
    self.expect(LexKind::RParen) |> ignore
    let body = self.parse_stmt()
    if is_for_of {
      let stmt = ForOfStmt::new(init, expr, body, is_await)
      return Stmt::new(
        StmtKind::ForOfStmt(stmt),
        self.span_from(start, body.span.end),
      )
    }
    let stmt = ForInStmt::new(init, expr, body)
    return Stmt::new(
      StmtKind::ForInStmt(stmt),
      self.span_from(start, body.span.end),
    )
  }
  if is_await {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::ForAwaitMustUseOf(tok.kind),
    )
  }
  self.expect(LexKind::Semicolon) |> ignore
  let condition = if !self.at(LexKind::Semicolon) {
    Some(self.parse_expr())
  } else {
    None
  }
  self.expect(LexKind::Semicolon) |> ignore
  let incrementor = if !self.at(LexKind::RParen) {
    Some(self.parse_expr())
  } else {
    None
  }
  self.expect(LexKind::RParen) |> ignore
  let body = self.parse_stmt()
  let stmt = ForStmt::new(Some(init), condition, incrementor, body)
  Stmt::new(StmtKind::ForStmt(stmt), self.span_from(start, body.span.end))
}

///|
fn Parser::parse_with_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  self.expect(LexKind::LParen) |> ignore
  let expr = self.parse_expr()
  self.expect(LexKind::RParen) |> ignore
  let body = self.parse_stmt()
  let stmt = WithStmt::new(expr, body)
  Stmt::new(StmtKind::WithStmt(stmt), self.span_from(start, body.span.end))
}

///|
fn Parser::parse_case_block(self : Parser) -> CaseBlock raise ParseError {
  let clauses : Array[CaseOrDefaultClause] = []
  self.expect(LexKind::LBrace) |> ignore
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    if self.at(LexKind::KeywordCase) {
      self.advance() |> ignore
      let expr = self.parse_expr()
      self.expect(LexKind::Colon) |> ignore
      let stmts : Array[Stmt] = []
      while !self.at(LexKind::KeywordCase) &&
            !self.at(LexKind::KeywordDefault) &&
            !self.at(LexKind::RBrace) &&
            !self.at(LexKind::Eof) {
        stmts.push(self.parse_stmt())
      }
      let stmt_arr = self.node_array(
        stmts,
        expr.span.start,
        self.current().start,
      )
      clauses.push(
        CaseOrDefaultClause::CaseClause(CaseClause::new(expr, stmt_arr)),
      )
      continue
    }
    if self.at(LexKind::KeywordDefault) {
      self.advance() |> ignore
      self.expect(LexKind::Colon) |> ignore
      let stmts : Array[Stmt] = []
      while !self.at(LexKind::KeywordCase) &&
            !self.at(LexKind::KeywordDefault) &&
            !self.at(LexKind::RBrace) &&
            !self.at(LexKind::Eof) {
        stmts.push(self.parse_stmt())
      }
      clauses.push(
        CaseOrDefaultClause::DefaultClause(
          self.node_array(stmts, 0, self.current().start),
        ),
      )
      continue
    }
    break
  }
  self.expect(LexKind::RBrace) |> ignore
  CaseBlock::new(self.node_array(clauses, 0, self.current().start))
}

///|
fn Parser::parse_switch_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  self.expect(LexKind::LParen) |> ignore
  let expr = self.parse_expr()
  self.expect(LexKind::RParen) |> ignore
  let block = self.parse_case_block()
  let stmt = SwitchStmt::new(expr, block)
  Stmt::new(
    StmtKind::SwitchStmt(stmt),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_try_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  let (try_block, _) = self.parse_block()
  let mut catch_clause : CatchClause? = None
  if self.at(LexKind::KeywordCatch) {
    self.advance() |> ignore
    let mut binding : VariableDecl? = None
    if self.at(LexKind::LParen) {
      self.advance() |> ignore
      let (name, _) = self.parse_binding_name()
      self.expect(LexKind::RParen) |> ignore
      binding = Some(VariableDecl::new(VariableDeclKind::Let, name, None, None))
    }
    let (catch_block, _) = self.parse_block()
    catch_clause = Some(CatchClause::new(binding, catch_block))
  }
  let finally_block = if self.at(LexKind::KeywordFinally) {
    self.advance() |> ignore
    let (block, _) = self.parse_block()
    Some(block)
  } else {
    None
  }
  let stmt = TryStmt::new(try_block, catch_clause, finally_block)
  Stmt::new(
    StmtKind::TryStmt(stmt),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_labeled_stmt(self : Parser) -> Stmt raise ParseError {
  let label = self.parse_identifier()
  self.expect(LexKind::Colon) |> ignore
  let statement = self.parse_stmt()
  let stmt = LabeledStmt::new(label, statement)
  Stmt::new(
    StmtKind::LabeledStmt(stmt),
    self.span_from(label.span.start, statement.span.end),
  )
}

///|
fn Parser::parse_debugger_stmt(self : Parser) -> Stmt raise ParseError {
  let start = self.advance().start
  self.parse_semicolon()
  Stmt::new(StmtKind::DebuggerStmt, self.span_from(start, self.current().start))
}

///|
fn Parser::parse_function_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  self.expect(LexKind::KeywordFunction) |> ignore
  let mut is_generator = false
  if self.at(LexKind::Star) {
    self.advance() |> ignore
    is_generator = true
  }
  let name = self.parse_identifier()
  let type_parameters = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  let params = self.parse_parameter_list(false)
  let return_type = if self.at(LexKind::Colon) {
    self.advance() |> ignore
    Some(self.parse_type())
  } else {
    None
  }
  let is_async = modifiers_has(modifiers, ModifierKind::Async)
  let (body, body_span) = if self.at(LexKind::LBrace) {
    let (stmts, span) = self.parse_block_with_function_context(
      is_generator, is_async,
    )
    (Some(stmts), Some(span))
  } else {
    if !self.try_parse_semicolon() {
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::MissingFunctionBodyOrSemicolon(tok.kind),
      )
    }
    (None, None)
  }
  let top_mods = parse_top_level_modifiers(
    modifiers,
    false,
    false,
    self.span_from(start, self.current().start),
  )
  let func = FunctionDecl::new(
    doc_comment,
    top_mods,
    is_async,
    is_generator,
    Some(name),
    type_parameters,
    params,
    return_type,
    body,
  )
  Stmt::new(
    StmtKind::FunctionDecl(func),
    self.span_from(
      start,
      match body_span {
        Some(span) => span.end
        None => self.current().start
      },
    ),
  )
}

///|
fn Parser::parse_function_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_function_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_class_decl_with_modifiers(
  self : Parser,
  start : Int,
  decorators : NodeArray[Expr]?,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  self.expect(LexKind::KeywordClass) |> ignore
  let name = self.parse_identifier()
  let type_parameters = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  let heritage_items : Array[HeritageClause] = []
  if self.at(LexKind::KeywordExtends) {
    let extends_tok = self.advance()
    let clause = self.parse_heritage_clause(
      HeritageKind::Extends,
      extends_tok.start,
    )
    heritage_items.push(clause)
  }
  if self.at(LexKind::KeywordImplements) {
    let implements_tok = self.advance()
    let clause = self.parse_heritage_clause(
      HeritageKind::Implements,
      implements_tok.start,
    )
    heritage_items.push(clause)
  }
  let heritage = if heritage_items.length() > 0 {
    Some(self.node_array(heritage_items, start, self.current().start))
  } else {
    None
  }
  let body_start = self.expect(LexKind::LBrace).start
  let members = self.parse_class_elements(body_start)
  let end_tok = self.expect(LexKind::RBrace)
  let top_mods = parse_top_level_modifiers(
    modifiers,
    true,
    false,
    self.span_from(start, end_tok.end),
  )
  let decl = ClassDecl::new(
    doc_comment,
    decorators,
    top_mods,
    Some(name),
    type_parameters,
    heritage,
    members,
  )
  Stmt::new(StmtKind::ClassDecl(decl), self.span_from(start, end_tok.end))
}

///|
fn Parser::parse_class_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_class_decl_with_modifiers(start, None, None)
}

///|
fn Parser::parse_interface_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  self.expect(LexKind::KeywordInterface) |> ignore
  let name = self.parse_identifier()
  let type_parameters = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  let heritage_items : Array[HeritageClause] = []
  if self.at(LexKind::KeywordExtends) {
    let extends_tok = self.advance()
    let clause = self.parse_heritage_clause(
      HeritageKind::Extends,
      extends_tok.start,
    )
    heritage_items.push(clause)
  }
  let heritage = if heritage_items.length() > 0 {
    Some(self.node_array(heritage_items, start, self.current().start))
  } else {
    None
  }
  let body_start = self.expect(LexKind::LBrace).start
  let members = self.parse_type_literal_members()
  let end_tok = self.expect(LexKind::RBrace)
  let top_mods = parse_top_level_modifiers(
    modifiers,
    false,
    false,
    self.span_from(start, end_tok.end),
  )
  let decl = InterfaceDecl::new(
    doc_comment,
    top_mods,
    name,
    type_parameters,
    heritage,
    self.node_array(members, body_start, end_tok.end),
  )
  Stmt::new(StmtKind::InterfaceDecl(decl), self.span_from(start, end_tok.end))
}

///|
fn Parser::parse_interface_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_interface_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_type_alias_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  self.expect(LexKind::KeywordType) |> ignore
  if self.has_preceding_line_break() {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::LineBreakAfterTypeKeyword,
    )
  }
  let name = self.parse_identifier()
  let type_parameters = if self.at(LexKind::Lt) {
    Some(self.parse_type_parameters())
  } else {
    None
  }
  self.expect(LexKind::Eq) |> ignore
  let type_node = self.parse_type()
  self.parse_semicolon()
  let alias_end = self.current().start
  let top_mods = parse_top_level_modifiers(
    modifiers,
    false,
    false,
    self.span_from(start, alias_end),
  )
  let decl = TypeAliasDecl::new(
    doc_comment, top_mods, name, type_parameters, type_node,
  )
  Stmt::new(
    StmtKind::TypeAliasDecl(decl),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_type_alias_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_type_alias_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_enum_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  self.expect(LexKind::KeywordEnum) |> ignore
  let name = self.parse_identifier()
  let body_start = self.expect(LexKind::LBrace).start
  let members : Array[EnumMember] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments()
    if self.at(LexKind::RBrace) || self.at(LexKind::Eof) {
      break
    }
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      self.clear_doc_comment()
      continue
    }
    let member_doc = self.take_doc_comment()
    let member_name = self.parse_property_name()
    let initializer = if self.at(LexKind::Eq) {
      self.advance() |> ignore
      Some(self.parse_assignment_expr())
    } else {
      None
    }
    members.push(EnumMember::new(member_doc, member_name, initializer))
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  let end_tok = self.expect(LexKind::RBrace)
  let top_mods = parse_top_level_modifiers(
    modifiers,
    false,
    true,
    self.span_from(start, end_tok.end),
  )
  let decl = EnumDecl::new(
    doc_comment,
    top_mods,
    name,
    self.node_array(members, body_start, end_tok.end),
  )
  Stmt::new(StmtKind::EnumDecl(decl), self.span_from(start, end_tok.end))
}

///|
fn Parser::parse_enum_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_enum_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_module_name(self : Parser) -> ModuleName raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(_) => ModuleName::Identifier(self.parse_identifier())
    KeywordGlobal => {
      self.advance() |> ignore
      let id = Identifier::new("global", self.span_from(tok.start, tok.end))
      ModuleName::Identifier(id)
    }
    String(value) => {
      self.advance() |> ignore
      let lit = StringLiteral::new(value, self.span_from(tok.start, tok.end))
      ModuleName::StringLiteral(lit)
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedToken(tok.kind),
      )
  }
}

///|
fn Parser::parse_module_block(self : Parser) -> ModuleBody raise ParseError {
  let (stmts, span) = self.parse_block_with_doc_comments(true)
  ModuleBody::ModuleBlock(self.node_array(stmts, span.start, span.end))
}

///|
fn Parser::parse_module_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  let has_declare = modifiers_has(modifiers, ModifierKind::Declare)
  let keyword = self.current().kind
  self.advance() |> ignore
  let end = self.current().start
  if keyword == LexKind::KeywordNamespace &&
    self.current().kind is LexKind::String(_) {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::InvalidNamespaceNameStringLiteral(tok.kind),
    )
  }
  let first_name = self.parse_module_name()
  match first_name {
    ModuleName::StringLiteral(s) => {
      if !has_declare {
        raise self.parse_error_at(
          start,
          self.current().start,
          ParseErrorKind::StringLiteralModuleNameOnlyInDeclare,
        )
      }
      let body = if self.at(LexKind::LBrace) {
        Some(self.parse_module_block())
      } else {
        self.parse_semicolon()
        None
      }
      let span = match modifiers {
        Some(m) => m.start
        None => s.span
      }
      let top_mods = parse_top_level_modifiers(modifiers, false, false, span)
      let decl = ModuleDecl::new(doc_comment, top_mods, first_name, body)
      return Stmt::new(
        StmtKind::ModuleDecl(decl),
        self.span_from(start, self.current().start),
      )
    }
    ModuleName::Identifier(first_id) => {
      let names : Array[Identifier] = [first_id]
      while self.at(LexKind::Dot) {
        self.advance() |> ignore
        names.push(self.parse_identifier())
      }
      let leaf_body = if self.at(LexKind::LBrace) {
        Some(self.parse_module_block())
      } else if has_declare {
        self.parse_semicolon()
        None
      } else {
        raise self.parse_error_at(
          start,
          self.current().start,
          ParseErrorKind::ModuleBodyRequiredWithoutDeclare,
        )
      }
      let mut current_body = leaf_body
      let mut i = names.length()
      while i > 0 {
        i = i - 1
        let name = ModuleName::Identifier(names[i])
        let m = parse_top_level_modifiers(
          modifiers,
          false,
          false,
          self.span_from(start, end),
        )
        let current_doc = if i == 0 { doc_comment } else { None }
        let decl = ModuleDecl::new(current_doc, m, name, current_body)
        current_body = Some(ModuleBody::ModuleDecl(decl))
      }
      let body_decl = match current_body {
        Some(ModuleBody::ModuleDecl(decl)) => decl
        _ =>
          raise self.parse_error_at(
            start,
            self.current().start,
            ParseErrorKind::InvalidModuleBody,
          )
      }
      Stmt::new(
        StmtKind::ModuleDecl(body_decl),
        self.span_from(start, self.current().start),
      )
    }
  }
}

///|
fn Parser::parse_module_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_module_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_global_module_decl_with_modifiers(
  self : Parser,
  start : Int,
  modifiers : NodeArray[ModifierKind]?,
) -> Stmt raise ParseError {
  let doc_comment = self.take_doc_comment()
  let tok = self.advance()
  let name = Identifier::new("global", self.span_from(tok.start, tok.end))
  let body = if self.at(LexKind::LBrace) {
    Some(self.parse_module_block())
  } else {
    self.parse_semicolon()
    None
  }
  let span = match modifiers {
    Some(m) => m.start
    None => name.span
  }
  let top_mods = parse_top_level_modifiers(modifiers, false, false, span)
  let decl = ModuleDecl::new(
    doc_comment,
    top_mods,
    ModuleName::Identifier(name),
    body,
  )
  Stmt::new(
    StmtKind::ModuleDecl(decl),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_global_module_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.current().start
  self.parse_global_module_decl_with_modifiers(start, None)
}

///|
fn Parser::parse_import_attribute_name(
  self : Parser,
) -> ImportAttributeName raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    Identifier(_) => ImportAttributeName::Identifier(self.parse_identifier())
    String(value) => {
      self.advance() |> ignore
      let lit = StringLiteral::new(value, self.span_from(tok.start, tok.end))
      ImportAttributeName::StringLiteral(lit)
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::UnexpectedToken(tok.kind),
      )
  }
}

///|
fn Parser::parse_import_attributes(
  self : Parser,
) -> ImportAttributes? raise ParseError {
  if self.has_preceding_line_break() {
    return None
  }
  self.skip_doc_comments_in_expr()
  if !self.at(LexKind::KeywordWith) && !self.at(LexKind::KeywordAssert) {
    return None
  }
  let start = self.advance().start
  self.expect(LexKind::LBrace) |> ignore
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Comma) {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::LeadingCommaInImportAttributes,
    )
  }
  let items : Array[ImportAttribute] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    let name = self.parse_import_attribute_name()
    self.expect(LexKind::Colon) |> ignore
    let value = self.parse_assignment_expr()
    items.push(ImportAttribute::new(name, value))
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  let end_tok = self.expect(LexKind::RBrace)
  Some(ImportAttributes::new(self.node_array(items, start, end_tok.end)))
}

///|
fn Parser::parse_module_specifier(self : Parser) -> Expr raise ParseError {
  self.skip_doc_comments_in_expr()
  let tok = self.current()
  match tok.kind {
    String(value) => {
      self.advance() |> ignore
      let lit = StringLiteral::new(value, self.span_from(tok.start, tok.end))
      Expr::new(
        ExprKind::StringLiteral(lit),
        self.span_from(tok.start, tok.end),
      )
    }
    _ =>
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::ExpectedStringLiteralInModuleSpecifier(tok.kind),
      )
  }
}

///|
fn Parser::parse_import_specifier(
  self : Parser,
) -> ImportSpecifier raise ParseError {
  self.skip_doc_comments_in_expr()
  let mut is_type_only = false
  let mut property_name : Identifier? = None
  let mut can_parse_as_keyword = true
  let mut name : Identifier = if self.at(LexKind::KeywordType) {
    let mut parsed_name = self.parse_identifier_name()
    if self.at(LexKind::KeywordAs) {
      let first_as = self.parse_identifier_name()
      if self.at(LexKind::KeywordAs) {
        let second_as = self.parse_identifier_name()
        if self.is_module_export_name_start() {
          is_type_only = true
          property_name = Some(first_as)
          parsed_name = self.parse_module_export_name()
          can_parse_as_keyword = false
        } else {
          property_name = Some(parsed_name)
          parsed_name = second_as
          can_parse_as_keyword = false
        }
      } else if self.is_module_export_name_start() {
        property_name = Some(parsed_name)
        parsed_name = self.parse_module_export_name()
        can_parse_as_keyword = false
      } else {
        is_type_only = true
        parsed_name = first_as
      }
    } else if self.is_module_export_name_start() {
      is_type_only = true
      parsed_name = self.parse_module_export_name()
    }
    parsed_name
  } else {
    self.parse_module_export_name()
  }
  if can_parse_as_keyword && self.at(LexKind::KeywordAs) {
    self.advance() |> ignore
    property_name = Some(name)
    name = self.parse_module_export_name()
  }
  ImportSpecifier::new(is_type_only, property_name, name)
}

///|
fn Parser::parse_named_import_bindings(
  self : Parser,
) -> NamedImportBindings raise ParseError {
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Star) {
    self.advance() |> ignore
    self.expect(LexKind::KeywordAs) |> ignore
    let name = self.parse_identifier()
    return NamedImportBindings::NamespaceImport(name)
  }
  let open = self.expect(LexKind::LBrace)
  if self.at(LexKind::Comma) {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::LeadingCommaInNamedImports,
    )
  }
  let specifiers : Array[ImportSpecifier] = []
  while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    specifiers.push(self.parse_import_specifier())
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      continue
    }
    break
  }
  let end_tok = self.expect(LexKind::RBrace)
  NamedImportBindings::NamedImports(
    self.node_array(specifiers, open.start, end_tok.end),
  )
}

///|
fn Parser::parse_import_equals_module_ref(
  self : Parser,
) -> ImportEqualsModuleRef raise ParseError {
  self.skip_doc_comments_in_expr()
  match self.current().kind {
    Identifier(name) if name == "require" &&
      self.peek(1).kind == LexKind::LParen => {
      self.advance() |> ignore
      self.expect(LexKind::LParen) |> ignore
      let module_specifier = self.parse_module_specifier()
      self.expect(LexKind::RParen) |> ignore
      ImportEqualsModuleRef::ExternalModuleRef(module_specifier)
    }
    Identifier(_) => {
      let first = self.parse_identifier()
      let (entity, _) = self.parse_entity_name(first)
      ImportEqualsModuleRef::EntityName(entity)
    }
    _ =>
      raise self.parse_error_at(
        self.current().start,
        self.current().end,
        ParseErrorKind::UnexpectedToken(self.current().kind),
      )
  }
}

///|
fn Parser::parse_import_equals_decl(
  self : Parser,
  start : Int,
  is_type_only : Bool,
  name : Identifier,
) -> Stmt raise ParseError {
  self.expect(LexKind::Eq) |> ignore
  let module_ref = self.parse_import_equals_module_ref()
  self.parse_semicolon()
  let decl = ImportEqualsDecl::new(is_type_only, name, module_ref)
  Stmt::new(
    StmtKind::ImportEqualsDecl(decl),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_import_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.expect(LexKind::KeywordImport).start
  self.has_import_syntax = true
  let mut phase = ImportPhase::Normal
  let mut is_type_only = false
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::KeywordType) {
    let mut is_phase = true
    if self.peek(1).kind == LexKind::KeywordFrom {
      if self.peek(2).kind is String(_) {
        is_phase = false
      }
    }
    if is_phase {
      self.advance() |> ignore
      phase = ImportPhase::Type
      is_type_only = true
    }
  } else if self.current().kind is Identifier(text) && text == "defer" {
    let next_kind = self.peek(1).kind
    let mut is_phase = true
    if next_kind == LexKind::KeywordFrom {
      if self.peek(2).kind is String(_) {
        is_phase = false
      }
    } else if next_kind == LexKind::Comma || next_kind == LexKind::Eq {
      is_phase = false
    }
    if is_phase {
      self.advance() |> ignore
      phase = ImportPhase::Defer
    }
  }
  if self.current().kind is String(_) {
    let module_specifier = self.parse_module_specifier()
    let attributes = self.parse_import_attributes()
    self.parse_semicolon()
    let span = self.span_from(start, module_specifier.span.end)
    let decl = ImportDecl::new(None, module_specifier, attributes, phase)
    return Stmt::new(StmtKind::ImportDecl(decl), span)
  }
  let mut name : Identifier? = None
  let mut named_bindings : NamedImportBindings? = None
  self.skip_doc_comments_in_expr()
  if self.current().kind is Identifier(_) {
    let id = self.parse_identifier()
    if self.at(LexKind::Eq) {
      if phase == ImportPhase::Defer {
        let tok = self.current()
        raise self.parse_error_at(
          tok.start,
          tok.end,
          ParseErrorKind::UnexpectedToken(tok.kind),
        )
      }
      return self.parse_import_equals_decl(start, is_type_only, id)
    }
    name = Some(id)
    self.skip_doc_comments_in_expr()
    if self.at(LexKind::Comma) {
      self.advance() |> ignore
      named_bindings = Some(self.parse_named_import_bindings())
    }
  } else {
    named_bindings = Some(self.parse_named_import_bindings())
  }
  self.expect(LexKind::KeywordFrom) |> ignore
  let module_specifier = self.parse_module_specifier()
  let attributes = self.parse_import_attributes()
  self.parse_semicolon()
  let import_clause = ImportClause::new(is_type_only, name, named_bindings)
  let decl = ImportDecl::new(
    Some(import_clause),
    module_specifier,
    attributes,
    phase,
  )
  Stmt::new(
    StmtKind::ImportDecl(decl),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_export_specifier(
  self : Parser,
) -> ExportSpecifier raise ParseError {
  self.skip_doc_comments_in_expr()
  let mut is_type_only = false
  let mut property_name : Identifier? = None
  let mut can_parse_as_keyword = true
  let mut name : Identifier = if self.at(LexKind::KeywordType) {
    let mut parsed_name = self.parse_identifier_name()
    if self.at(LexKind::KeywordAs) {
      let first_as = self.parse_identifier_name()
      if self.at(LexKind::KeywordAs) {
        let second_as = self.parse_identifier_name()
        if self.is_module_export_name_start() {
          is_type_only = true
          property_name = Some(first_as)
          parsed_name = self.parse_module_export_name()
          can_parse_as_keyword = false
        } else {
          property_name = Some(parsed_name)
          parsed_name = second_as
          can_parse_as_keyword = false
        }
      } else if self.is_module_export_name_start() {
        property_name = Some(parsed_name)
        parsed_name = self.parse_module_export_name()
        can_parse_as_keyword = false
      } else {
        is_type_only = true
        parsed_name = first_as
      }
    } else if self.is_module_export_name_start() {
      is_type_only = true
      parsed_name = self.parse_module_export_name()
    }
    parsed_name
  } else {
    self.parse_module_export_name()
  }
  if can_parse_as_keyword && self.at(LexKind::KeywordAs) {
    self.advance() |> ignore
    property_name = Some(name)
    name = self.parse_module_export_name()
  }
  ExportSpecifier::new(is_type_only, property_name, name)
}

///|
fn Parser::parse_namespace_export_decl(
  self : Parser,
  start : Int,
) -> Stmt raise ParseError {
  self.expect(LexKind::KeywordAs) |> ignore
  self.expect(LexKind::KeywordNamespace) |> ignore
  let name = self.parse_identifier()
  self.parse_semicolon()
  let decl = NamespaceExportDecl::new(name)
  Stmt::new(
    StmtKind::NamespaceExportDecl(decl),
    self.span_from(start, self.current().start),
  )
}

///|
fn Parser::parse_export_decl(self : Parser) -> Stmt raise ParseError {
  let start = self.expect(LexKind::KeywordExport).start
  self.has_export_syntax = true
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::Eq) {
    if !(self.script_kind is TSDeclarations) {
      raise self.parse_error_at(
        start,
        self.current().end,
        ParseErrorKind::ExportAssignmentOnlyInDeclarationFile,
      )
    }
    if self.has_non_namespace_export {
      raise self.parse_error_at(
        start,
        self.current().end,
        ParseErrorKind::ExportAssignmentCannotBeUsedWithOtherExports,
      )
    }
    self.advance() |> ignore
    let expr = self.parse_expr()
    self.parse_semicolon()
    let mods = self.node_array([ModifierKind::Export], start, start)
    let assign = ExportAssignment::new(Some(mods), true, expr)
    self.has_export_assignment = true
    self.clear_doc_comment()
    return Stmt::new(
      StmtKind::ExportAssignment(assign),
      self.span_from(start, self.current().start),
    )
  }
  if self.at(LexKind::KeywordDefault) {
    let default_tok = self.advance()
    let mods = self.node_array(
      [ModifierKind::Export, ModifierKind::Default],
      start,
      default_tok.end,
    )
    if self.at(LexKind::KeywordClass) {
      self.mark_non_namespace_export(start, self.current().end)
      return self.parse_class_decl_with_modifiers(start, None, Some(mods))
    }
    if self.at(LexKind::KeywordFunction) {
      self.mark_non_namespace_export(start, self.current().end)
      return self.parse_function_decl_with_modifiers(start, Some(mods))
    }
    let expr = self.parse_expr()
    self.try_parse_semicolon() |> ignore
    let assign = ExportAssignment::new(Some(mods), false, expr)
    self.mark_non_namespace_export(start, self.current().end)
    self.clear_doc_comment()
    return Stmt::new(
      StmtKind::ExportAssignment(assign),
      self.span_from(start, self.current().start),
    )
  }
  if self.at(LexKind::KeywordAs) {
    if !(self.script_kind is TSDeclarations) {
      raise self.parse_error_at(
        start,
        self.current().end,
        ParseErrorKind::ExportNamespaceOnlyInDeclarationFile,
      )
    }
    if self.export_namespace_range is None {
      self.export_namespace_range = Some((start, self.current().end))
    }
    self.clear_doc_comment()
    return self.parse_namespace_export_decl(start)
  }
  let mut is_type_only = false
  if self.at(LexKind::KeywordType) {
    let next_kind = self.peek(1).kind
    if next_kind == LexKind::LBrace || next_kind == LexKind::Star {
      self.advance() |> ignore
      is_type_only = true
    }
  }
  let items : Array[ModifierKind] = [ModifierKind::Export]
  let mut end_start = self.current().start
  if self.current().kind is Identifier(text) {
    match modifier_kind_from_name(text) {
      Some(_) => {
        let extra = self.parse_modifiers(start)
        if extra is Some(list) {
          for item in list.elements.iter() {
            items.push(item)
          }
          end_start = list.end.end
        }
      }
      None => ()
    }
  }
  let mods = self.node_array(items, start, end_start)
  self.skip_doc_comments_in_expr()
  if self.at(LexKind::KeywordConst) && self.peek(1).kind == LexKind::KeywordEnum {
    self.mark_non_namespace_export(start, self.current().end)
    let const_tok = self.advance()
    let enum_items = mods.elements + [ModifierKind::Const]
    let enum_mods = self.node_array(enum_items, start, const_tok.end)
    return self.parse_enum_decl_with_modifiers(start, Some(enum_mods))
  }
  if self.at(LexKind::KeywordClass) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_class_decl_with_modifiers(start, None, Some(mods))
  }
  if self.at(LexKind::KeywordFunction) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_function_decl_with_modifiers(start, Some(mods))
  }
  if self.at(LexKind::KeywordInterface) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_interface_decl_with_modifiers(start, Some(mods))
  }
  if self.at(LexKind::KeywordType) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_type_alias_decl_with_modifiers(start, Some(mods))
  }
  if self.at(LexKind::KeywordEnum) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_enum_decl_with_modifiers(start, Some(mods))
  }
  if self.at(LexKind::KeywordModule) || self.at(LexKind::KeywordNamespace) {
    self.mark_non_namespace_export(start, self.current().end)
    return self.parse_module_decl_with_modifiers(start, Some(mods))
  }
  let is_var_decl = self.at(LexKind::KeywordLet) ||
    self.at(LexKind::KeywordConst) ||
    self.at(LexKind::KeywordVar) ||
    self.is_using_stmt_start() ||
    self.is_await_using_stmt_start()
  if is_var_decl {
    self.mark_non_namespace_export(start, self.current().end)
    if self.is_using_stmt_start() {
      return self.parse_using_stmt_with_modifiers(start, Some(mods), false)
    }
    if self.is_await_using_stmt_start() {
      return self.parse_using_stmt_with_modifiers(start, Some(mods), true)
    }
    return self.parse_variable_stmt_with_modifiers(start, Some(mods))
  }
  if self.at(LexKind::Star) {
    self.mark_non_namespace_export(start, self.current().end)
    self.advance() |> ignore
    let mut export_clause : NamedExportBindings? = None
    if self.at(LexKind::KeywordAs) {
      self.advance() |> ignore
      let name = self.parse_identifier()
      export_clause = Some(NamedExportBindings::NamespaceExport(name))
    }
    self.expect(LexKind::KeywordFrom) |> ignore
    let module_specifier = self.parse_module_specifier()
    let attributes = self.parse_import_attributes()
    self.parse_semicolon()
    let decl = ExportDecl::new(
      Some(mods),
      is_type_only,
      export_clause,
      Some(module_specifier),
      attributes,
    )
    self.clear_doc_comment()
    return Stmt::new(
      StmtKind::ExportDecl(decl),
      self.span_from(start, self.current().start),
    )
  }
  if self.at(LexKind::LBrace) {
    self.mark_non_namespace_export(start, self.current().end)
    let open = self.advance()
    if self.at(LexKind::Comma) {
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::LeadingCommaInNamedExports,
      )
    }
    let specifiers : Array[ExportSpecifier] = []
    while !self.at(LexKind::RBrace) && !self.at(LexKind::Eof) {
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        continue
      }
      specifiers.push(self.parse_export_specifier())
      self.skip_doc_comments_in_expr()
      if self.at(LexKind::Comma) {
        self.advance() |> ignore
        continue
      }
      break
    }
    let end_tok = self.expect(LexKind::RBrace)
    let export_clause = Some(
      NamedExportBindings::NamedExports(
        self.node_array(specifiers, open.start, end_tok.end),
      ),
    )
    let mut module_specifier : Expr? = None
    if self.at(LexKind::KeywordFrom) {
      self.advance() |> ignore
      module_specifier = Some(self.parse_module_specifier())
    }
    let attributes = self.parse_import_attributes()
    self.parse_semicolon()
    let decl = ExportDecl::new(
      Some(mods),
      is_type_only,
      export_clause,
      module_specifier,
      attributes,
    )
    self.clear_doc_comment()
    return Stmt::new(
      StmtKind::ExportDecl(decl),
      self.span_from(start, self.current().start),
    )
  }
  let tok = self.current()
  raise self.parse_error_at(
    tok.start,
    tok.end,
    ParseErrorKind::UnexpectedToken(tok.kind),
  )
}

///|
fn Parser::parse_expr_stmt(self : Parser) -> Stmt raise ParseError {
  self.clear_doc_comment()
  if self.script_kind is TSDeclarations {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::DeclarationFileStatementNotAllowed,
    )
  }
  let start = self.current().start
  let expr = self.parse_expr()
  self.parse_semicolon()
  let span = self.span_from(start, expr.span.end)
  Stmt::new(StmtKind::ExprStmt(expr), span)
}

///|
fn Parser::is_using_stmt_start(self : Parser) -> Bool {
  self.at(LexKind::KeywordUsing) &&
  is_identifier_or_contextual(self.peek(1).kind)
}

///|
fn Parser::is_await_using_stmt_start(self : Parser) -> Bool {
  self.at(LexKind::KeywordAwait) &&
  self.peek(1).kind == LexKind::KeywordUsing &&
  is_identifier_or_contextual(self.peek(2).kind)
}

///|
fn Parser::parse_stmt(self : Parser) -> Stmt raise ParseError {
  self.skip_doc_comments()
  if !self.is_doc_comment_attachable_start() {
    self.clear_doc_comment()
  }
  let is_declare_only = self.script_kind is TSDeclarations &&
    (
      self.at(LexKind::KeywordExport) ||
      self.current().kind is Identifier("declare")
    )
  if self.script_kind is TSDeclarations && !is_declare_only {
    let tok = self.current()
    raise self.parse_error_at(
      tok.start,
      tok.end,
      ParseErrorKind::DeclarationFileStatementNotAllowed,
    )
  }
  match self.current().kind {
    At => {
      let start = self.current().start
      let decorators = self.parse_decorators(start)
      let modifiers = self.parse_modifiers(start)
      if self.at(LexKind::KeywordClass) {
        return self.parse_class_decl_with_modifiers(
          start, decorators, modifiers,
        )
      }
      if decorators is Some(list) {
        raise self.parse_error_at(
          list.start.start,
          list.end.end,
          ParseErrorKind::DecoratorsNotAllowedHere,
        )
      }
      let tok = self.current()
      raise self.parse_error_at(
        tok.start,
        tok.end,
        ParseErrorKind::MissingDeclarationAfterModifiers(tok.kind),
      )
    }
    Identifier(text) => {
      if self.peek(1).kind == LexKind::Colon {
        return self.parse_labeled_stmt()
      }
      match modifier_kind_from_name(text) {
        Some(_) => {
          let next_kind = self.peek(1).kind
          let is_decl = next_kind == LexKind::KeywordClass ||
            next_kind == LexKind::KeywordFunction ||
            next_kind == LexKind::KeywordInterface ||
            next_kind == LexKind::KeywordType ||
            next_kind == LexKind::KeywordEnum ||
            next_kind == LexKind::KeywordModule ||
            next_kind == LexKind::KeywordNamespace ||
            next_kind == LexKind::KeywordGlobal ||
            next_kind == LexKind::KeywordLet ||
            next_kind == LexKind::KeywordConst ||
            next_kind == LexKind::KeywordVar ||
            (
              next_kind == LexKind::KeywordUsing &&
              is_identifier_or_contextual(self.peek(2).kind)
            ) ||
            (
              next_kind == LexKind::KeywordAwait &&
              self.peek(2).kind == LexKind::KeywordUsing &&
              is_identifier_or_contextual(self.peek(3).kind)
            )
          if is_decl {
            let start = self.current().start
            let modifiers = self.parse_modifiers(start)
            if self.at(LexKind::KeywordConst) &&
              self.peek(1).kind == LexKind::KeywordEnum {
              let const_tok = self.advance()
              let items : Array[ModifierKind] = []
              let start_start = match modifiers {
                Some(list) => {
                  for item in list.elements.iter() {
                    items.push(item)
                  }
                  list.start.start
                }
                None => start
              }
              items.push(ModifierKind::Const)
              let combined = self.node_array(items, start_start, const_tok.end)
              return self.parse_enum_decl_with_modifiers(start, Some(combined))
            }
            if self.at(LexKind::KeywordClass) {
              return self.parse_class_decl_with_modifiers(
                start,
                None,
                modifiers,
              )
            }
            if self.at(LexKind::KeywordFunction) {
              return self.parse_function_decl_with_modifiers(start, modifiers)
            }
            if self.at(LexKind::KeywordInterface) {
              return self.parse_interface_decl_with_modifiers(start, modifiers)
            }
            if self.at(LexKind::KeywordType) {
              return self.parse_type_alias_decl_with_modifiers(start, modifiers)
            }
            if self.at(LexKind::KeywordEnum) {
              return self.parse_enum_decl_with_modifiers(start, modifiers)
            }
            if self.at(LexKind::KeywordModule) ||
              self.at(LexKind::KeywordNamespace) {
              return self.parse_module_decl_with_modifiers(start, modifiers)
            }
            if self.at(LexKind::KeywordGlobal) {
              return self.parse_global_module_decl_with_modifiers(
                start, modifiers,
              )
            }
            if self.is_using_stmt_start() {
              return self.parse_using_stmt_with_modifiers(
                start, modifiers, false,
              )
            }
            if self.is_await_using_stmt_start() {
              return self.parse_using_stmt_with_modifiers(
                start, modifiers, true,
              )
            }
            return self.parse_variable_stmt_with_modifiers(start, modifiers)
          }
          self.parse_expr_stmt()
        }
        None => self.parse_expr_stmt()
      }
    }
    KeywordLet => self.parse_variable_stmt()
    KeywordConst =>
      if self.peek(1).kind == LexKind::KeywordEnum {
        let start = self.advance().start
        let modifiers = self.node_array(
          [ModifierKind::Const],
          start,
          self.current().start,
        )
        self.parse_enum_decl_with_modifiers(start, Some(modifiers))
      } else {
        self.parse_variable_stmt()
      }
    KeywordVar => self.parse_variable_stmt()
    KeywordUsing =>
      if self.is_using_stmt_start() {
        self.parse_using_stmt_with_modifiers(self.current().start, None, false)
      } else {
        self.parse_expr_stmt()
      }
    KeywordAwait =>
      if self.is_await_using_stmt_start() {
        self.parse_using_stmt_with_modifiers(self.current().start, None, true)
      } else {
        self.parse_expr_stmt()
      }
    KeywordImport =>
      if self.peek(1).kind == LexKind::LParen ||
        self.peek(1).kind == LexKind::Dot {
        self.parse_expr_stmt()
      } else {
        self.parse_import_decl()
      }
    KeywordExport => self.parse_export_decl()
    KeywordInterface => self.parse_interface_decl()
    KeywordType => self.parse_type_alias_decl()
    KeywordEnum => self.parse_enum_decl()
    KeywordModule => self.parse_module_decl()
    KeywordNamespace => self.parse_module_decl()
    KeywordGlobal => self.parse_global_module_decl()
    KeywordDo => self.parse_do_stmt()
    KeywordWhile => self.parse_while_stmt()
    KeywordFor => self.parse_for_stmt()
    KeywordBreak => self.parse_break_stmt()
    KeywordContinue => self.parse_continue_stmt()
    KeywordThrow => self.parse_throw_stmt()
    KeywordTry => self.parse_try_stmt()
    KeywordSwitch => self.parse_switch_stmt()
    KeywordWith => self.parse_with_stmt()
    KeywordDebugger => self.parse_debugger_stmt()
    KeywordIf => self.parse_if_stmt()
    KeywordReturn => self.parse_return_stmt()
    KeywordFunction => self.parse_function_decl()
    KeywordClass => self.parse_class_decl()
    Semicolon => {
      let start = self.advance().start
      Stmt::new(
        StmtKind::EmptyStmt,
        self.span_from(start, self.current().start),
      )
    }
    LBrace => {
      let (stmts, span) = self.parse_block()
      Stmt::new(StmtKind::Block(stmts), span)
    }
    _ => self.parse_expr_stmt()
  }
}

///|
fn Parser::is_doc_comment_attachable_start(self : Parser) -> Bool {
  match self.current().kind {
    At => true
    KeywordExport => true
    KeywordClass
    | KeywordFunction
    | KeywordInterface
    | KeywordType
    | KeywordEnum
    | KeywordModule
    | KeywordNamespace
    | KeywordGlobal
    | KeywordConst
    | KeywordLet
    | KeywordVar => true
    Identifier(text) =>
      if self.peek(1).kind == LexKind::Colon {
        false
      } else {
        modifier_kind_from_name(text) is Some(_)
      }
    _ => false
  }
}

///|
pub fn Parser::parse_source_file(
  self : Parser,
  file_name : String,
  text : String,
) -> SourceFile raise ParseError {
  let stmts : Array[Stmt] = []
  while !self.at(LexKind::Eof) {
    self.skip_doc_comments()
    if self.at(LexKind::Eof) {
      break
    }
    stmts.push(self.parse_stmt())
  }
  let is_module = self.is_module ||
    self.has_import_syntax ||
    self.has_export_syntax
  if self.top_level_await_range is Some((start, end)) && !is_module {
    raise self.parse_error_at(
      start,
      end,
      ParseErrorKind::TopLevelAwaitOnlyInModule,
    )
  }
  if self.export_namespace_range is Some((start, end)) &&
    !(self.has_import_syntax ||
    self.has_non_namespace_export ||
    self.has_export_assignment) {
    raise self.parse_error_at(
      start,
      end,
      ParseErrorKind::ExportNamespaceOnlyInModule,
    )
  }
  if self.for_await_range is Some((start, end)) && !is_module {
    raise self.parse_error_at(start, end, ParseErrorKind::ForAwaitOnlyInModule)
  }
  if self.await_using_range is Some((start, end)) && !is_module {
    raise self.parse_error_at(
      start,
      end,
      ParseErrorKind::AwaitUsingOnlyInModule,
    )
  }
  let stmts_arr = self.node_array(stmts, 0, text.length())
  SourceFile::new(stmts_arr, file_name, text, is_module, self.script_kind)
}