use crate::common::Span;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
pub newline_before: bool,
}
impl Token {
#[inline]
#[must_use]
pub fn text<'s>(&self, source: &'s str) -> &'s str {
self.span.slice(source)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TokenKind {
Eof,
Identifier,
Keyword(Keyword),
PrivateName,
Number,
BigInt,
String,
Regex,
NoSubstitutionTemplate,
TemplateHead,
TemplateMiddle,
TemplateTail,
LBrace,
RBrace,
LParen,
RParen,
LBracket,
RBracket,
Semicolon,
Comma,
Dot,
DotDotDot,
Colon,
Question,
QuestionDot,
QuestionQuestion,
Arrow,
Lt,
Gt,
LtEq,
GtEq,
EqEq,
BangEq,
EqEqEq,
BangEqEq,
Plus,
Minus,
Star,
Slash,
Percent,
StarStar,
PlusPlus,
MinusMinus,
Shl,
Shr,
Ushr,
Amp,
Pipe,
Caret,
Bang,
Tilde,
AmpAmp,
PipePipe,
Eq,
PlusEq,
MinusEq,
StarEq,
SlashEq,
PercentEq,
StarStarEq,
ShlEq,
ShrEq,
UshrEq,
AmpEq,
PipeEq,
CaretEq,
AmpAmpEq,
PipePipeEq,
QuestionQuestionEq,
}
impl TokenKind {
#[must_use]
pub fn is_template_open(self) -> bool {
matches!(self, TokenKind::TemplateHead | TokenKind::TemplateMiddle)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[allow(missing_docs)] pub enum Keyword {
Await,
Break,
Case,
Catch,
Class,
Const,
Continue,
Debugger,
Default,
Delete,
Do,
Else,
Enum,
Export,
Extends,
False,
Finally,
For,
Function,
If,
Import,
In,
Instanceof,
New,
Null,
Return,
Super,
Switch,
This,
Throw,
True,
Try,
Typeof,
Var,
Void,
While,
With,
Implements,
Interface,
Let,
Package,
Private,
Protected,
Public,
Static,
Yield,
As,
Async,
From,
Get,
Of,
Set,
Target,
Accessor,
}
impl Keyword {
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Option<Keyword> {
use Keyword::*;
Some(match s {
"await" => Await,
"break" => Break,
"case" => Case,
"catch" => Catch,
"class" => Class,
"const" => Const,
"continue" => Continue,
"debugger" => Debugger,
"default" => Default,
"delete" => Delete,
"do" => Do,
"else" => Else,
"enum" => Enum,
"export" => Export,
"extends" => Extends,
"false" => False,
"finally" => Finally,
"for" => For,
"function" => Function,
"if" => If,
"import" => Import,
"in" => In,
"instanceof" => Instanceof,
"new" => New,
"null" => Null,
"return" => Return,
"super" => Super,
"switch" => Switch,
"this" => This,
"throw" => Throw,
"true" => True,
"try" => Try,
"typeof" => Typeof,
"var" => Var,
"void" => Void,
"while" => While,
"with" => With,
"implements" => Implements,
"interface" => Interface,
"let" => Let,
"package" => Package,
"private" => Private,
"protected" => Protected,
"public" => Public,
"static" => Static,
"yield" => Yield,
"as" => As,
"async" => Async,
"from" => From,
"get" => Get,
"of" => Of,
"set" => Set,
"target" => Target,
"accessor" => Accessor,
_ => return None,
})
}
#[must_use]
pub fn as_str(self) -> &'static str {
use Keyword::*;
match self {
Await => "await",
Break => "break",
Case => "case",
Catch => "catch",
Class => "class",
Const => "const",
Continue => "continue",
Debugger => "debugger",
Default => "default",
Delete => "delete",
Do => "do",
Else => "else",
Enum => "enum",
Export => "export",
Extends => "extends",
False => "false",
Finally => "finally",
For => "for",
Function => "function",
If => "if",
Import => "import",
In => "in",
Instanceof => "instanceof",
New => "new",
Null => "null",
Return => "return",
Super => "super",
Switch => "switch",
This => "this",
Throw => "throw",
True => "true",
Try => "try",
Typeof => "typeof",
Var => "var",
Void => "void",
While => "while",
With => "with",
Implements => "implements",
Interface => "interface",
Let => "let",
Package => "package",
Private => "private",
Protected => "protected",
Public => "public",
Static => "static",
Yield => "yield",
As => "as",
Async => "async",
From => "from",
Get => "get",
Of => "of",
Set => "set",
Target => "target",
Accessor => "accessor",
}
}
#[must_use]
pub fn is_contextual(self) -> bool {
use Keyword::*;
matches!(self, As | Async | From | Get | Of | Set | Target | Accessor)
}
#[must_use]
pub fn before_expression(self) -> bool {
use Keyword::*;
!matches!(self, This | Super | True | False | Null)
}
}
#[cfg(test)]
mod tests {
use super::{Keyword, TokenKind};
#[test]
fn keyword_roundtrip() {
for kw in [
Keyword::Await,
Keyword::Function,
Keyword::Yield,
Keyword::Of,
] {
assert_eq!(Keyword::from_str(kw.as_str()), Some(kw));
}
assert_eq!(Keyword::from_str("notakeyword"), None);
assert_eq!(Keyword::from_str("Function"), None); }
#[test]
fn template_open_classification() {
assert!(TokenKind::TemplateHead.is_template_open());
assert!(TokenKind::TemplateMiddle.is_template_open());
assert!(!TokenKind::TemplateTail.is_template_open());
assert!(!TokenKind::NoSubstitutionTemplate.is_template_open());
}
#[test]
fn regex_after_keyword() {
assert!(Keyword::Return.before_expression());
assert!(Keyword::Typeof.before_expression());
assert!(!Keyword::This.before_expression());
assert!(!Keyword::True.before_expression());
}
}