123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- // Package js is an ECMAScript5.1 lexer following the specifications at http://www.ecma-international.org/ecma-262/5.1/.
- package js
- import (
- "unicode"
- "unicode/utf8"
- "github.com/tdewolff/parse/v2"
- )
- var identifierStart = []*unicode.RangeTable{unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Other_ID_Start}
- var identifierContinue = []*unicode.RangeTable{unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Mn, unicode.Mc, unicode.Nd, unicode.Pc, unicode.Other_ID_Continue}
- // IsIdentifierStart returns true if the byte-slice start is the start of an identifier
- func IsIdentifierStart(b []byte) bool {
- r, _ := utf8.DecodeRune(b)
- return r == '$' || r == '\\' || r == '_' || unicode.IsOneOf(identifierStart, r)
- }
- // IsIdentifierContinue returns true if the byte-slice start is a continuation of an identifier
- func IsIdentifierContinue(b []byte) bool {
- r, _ := utf8.DecodeRune(b)
- return r == '$' || r == '\\' || r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r)
- }
- // IsIdentifierEnd returns true if the byte-slice end is a start or continuation of an identifier
- func IsIdentifierEnd(b []byte) bool {
- r, _ := utf8.DecodeLastRune(b)
- return r == '$' || r == '\\' || r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r)
- }
- ////////////////////////////////////////////////////////////////
- // Lexer is the state for the lexer.
- type Lexer struct {
- r *parse.Input
- err error
- prevLineTerminator bool
- prevNumericLiteral bool
- level int
- templateLevels []int
- }
- // NewLexer returns a new Lexer for a given io.Reader.
- func NewLexer(r *parse.Input) *Lexer {
- return &Lexer{
- r: r,
- prevLineTerminator: true,
- level: 0,
- templateLevels: []int{},
- }
- }
- // Err returns the error encountered during lexing, this is often io.EOF but also other errors can be returned.
- func (l *Lexer) Err() error {
- if l.err != nil {
- return l.err
- }
- return l.r.Err()
- }
- // RegExp reparses the input stream for a regular expression. It is assumed that we just received DivToken or DivEqToken with Next(). This function will go back and read that as a regular expression.
- func (l *Lexer) RegExp() (TokenType, []byte) {
- if 0 < l.r.Offset() && l.r.Peek(-1) == '/' {
- l.r.Move(-1)
- } else if 1 < l.r.Offset() && l.r.Peek(-1) == '=' && l.r.Peek(-2) == '/' {
- l.r.Move(-2)
- } else {
- l.err = parse.NewErrorLexer(l.r, "expected / or /=")
- return ErrorToken, nil
- }
- l.r.Skip() // trick to set start = pos
- if l.consumeRegExpToken() {
- return RegExpToken, l.r.Shift()
- }
- l.err = parse.NewErrorLexer(l.r, "unexpected EOF or newline")
- return ErrorToken, nil
- }
- // Next returns the next Token. It returns ErrorToken when an error was encountered. Using Err() one can retrieve the error message.
- func (l *Lexer) Next() (TokenType, []byte) {
- prevLineTerminator := l.prevLineTerminator
- l.prevLineTerminator = false
- prevNumericLiteral := l.prevNumericLiteral
- l.prevNumericLiteral = false
- // study on 50x jQuery shows:
- // spaces: 20k
- // alpha: 16k
- // newlines: 14.4k
- // operators: 4k
- // numbers and dot: 3.6k
- // (): 3.4k
- // {}: 1.8k
- // []: 0.9k
- // "': 1k
- // semicolon: 2.4k
- // colon: 0.8k
- // comma: 2.4k
- // slash: 1.4k
- // `~: almost 0
- c := l.r.Peek(0)
- switch c {
- case ' ', '\t', '\v', '\f':
- l.r.Move(1)
- for l.consumeWhitespace() {
- }
- l.prevLineTerminator = prevLineTerminator
- return WhitespaceToken, l.r.Shift()
- case '\n', '\r':
- l.r.Move(1)
- for l.consumeLineTerminator() {
- }
- l.prevLineTerminator = true
- return LineTerminatorToken, l.r.Shift()
- case '>', '=', '!', '+', '*', '%', '&', '|', '^', '~', '?':
- if tt := l.consumeOperatorToken(); tt != ErrorToken {
- return tt, l.r.Shift()
- }
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
- if tt := l.consumeNumericToken(); tt != ErrorToken || l.r.Pos() != 0 {
- l.prevNumericLiteral = true
- return tt, l.r.Shift()
- } else if c == '.' {
- l.r.Move(1)
- if l.r.Peek(0) == '.' && l.r.Peek(1) == '.' {
- l.r.Move(2)
- return EllipsisToken, l.r.Shift()
- }
- return DotToken, l.r.Shift()
- }
- case ',':
- l.r.Move(1)
- return CommaToken, l.r.Shift()
- case ';':
- l.r.Move(1)
- return SemicolonToken, l.r.Shift()
- case '(':
- l.level++
- l.r.Move(1)
- return OpenParenToken, l.r.Shift()
- case ')':
- l.level--
- l.r.Move(1)
- return CloseParenToken, l.r.Shift()
- case '/':
- if tt := l.consumeCommentToken(); tt != ErrorToken || l.err != nil {
- if l.err != nil {
- return ErrorToken, nil
- }
- return tt, l.r.Shift()
- } else if tt := l.consumeOperatorToken(); tt != ErrorToken {
- return tt, l.r.Shift()
- }
- case '{':
- l.level++
- l.r.Move(1)
- return OpenBraceToken, l.r.Shift()
- case '}':
- l.level--
- if len(l.templateLevels) != 0 && l.level == l.templateLevels[len(l.templateLevels)-1] {
- return l.consumeTemplateToken(), l.r.Shift()
- }
- l.r.Move(1)
- return CloseBraceToken, l.r.Shift()
- case ':':
- l.r.Move(1)
- return ColonToken, l.r.Shift()
- case '\'', '"':
- return l.consumeStringToken(), l.r.Shift()
- case ']':
- l.r.Move(1)
- return CloseBracketToken, l.r.Shift()
- case '[':
- l.r.Move(1)
- return OpenBracketToken, l.r.Shift()
- case '<', '-':
- if l.consumeHTMLLikeCommentToken(prevLineTerminator) {
- return CommentToken, l.r.Shift()
- } else if tt := l.consumeOperatorToken(); tt != ErrorToken {
- return tt, l.r.Shift()
- }
- case '`':
- l.templateLevels = append(l.templateLevels, l.level)
- return l.consumeTemplateToken(), l.r.Shift()
- case '#':
- l.r.Move(1)
- if l.consumeIdentifierToken() {
- return PrivateIdentifierToken, l.r.Shift()
- }
- default:
- if l.consumeIdentifierToken() {
- if prevNumericLiteral {
- l.err = parse.NewErrorLexer(l.r, "unexpected identifier after number")
- return ErrorToken, nil
- } else if keyword, ok := Keywords[string(l.r.Lexeme())]; ok {
- return keyword, l.r.Shift()
- }
- return IdentifierToken, l.r.Shift()
- }
- if 0xC0 <= c {
- if l.consumeWhitespace() {
- for l.consumeWhitespace() {
- }
- l.prevLineTerminator = prevLineTerminator
- return WhitespaceToken, l.r.Shift()
- } else if l.consumeLineTerminator() {
- for l.consumeLineTerminator() {
- }
- l.prevLineTerminator = true
- return LineTerminatorToken, l.r.Shift()
- }
- } else if c == 0 && l.r.Err() != nil {
- return ErrorToken, nil
- }
- }
- r, _ := l.r.PeekRune(0)
- l.err = parse.NewErrorLexer(l.r, "unexpected %s", parse.Printable(r))
- return ErrorToken, l.r.Shift()
- }
- ////////////////////////////////////////////////////////////////
- /*
- The following functions follow the specifications at http://www.ecma-international.org/ecma-262/5.1/
- */
- func (l *Lexer) consumeWhitespace() bool {
- c := l.r.Peek(0)
- if c == ' ' || c == '\t' || c == '\v' || c == '\f' {
- l.r.Move(1)
- return true
- } else if 0xC0 <= c {
- if r, n := l.r.PeekRune(0); r == '\u00A0' || r == '\uFEFF' || unicode.Is(unicode.Zs, r) {
- l.r.Move(n)
- return true
- }
- }
- return false
- }
- func (l *Lexer) isLineTerminator() bool {
- c := l.r.Peek(0)
- if c == '\n' || c == '\r' {
- return true
- } else if c == 0xE2 && l.r.Peek(1) == 0x80 && (l.r.Peek(2) == 0xA8 || l.r.Peek(2) == 0xA9) {
- return true
- }
- return false
- }
- func (l *Lexer) consumeLineTerminator() bool {
- c := l.r.Peek(0)
- if c == '\n' {
- l.r.Move(1)
- return true
- } else if c == '\r' {
- if l.r.Peek(1) == '\n' {
- l.r.Move(2)
- } else {
- l.r.Move(1)
- }
- return true
- } else if c == 0xE2 && l.r.Peek(1) == 0x80 && (l.r.Peek(2) == 0xA8 || l.r.Peek(2) == 0xA9) {
- l.r.Move(3)
- return true
- }
- return false
- }
- func (l *Lexer) consumeDigit() bool {
- if c := l.r.Peek(0); c >= '0' && c <= '9' {
- l.r.Move(1)
- return true
- }
- return false
- }
- func (l *Lexer) consumeHexDigit() bool {
- if c := l.r.Peek(0); (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') {
- l.r.Move(1)
- return true
- }
- return false
- }
- func (l *Lexer) consumeBinaryDigit() bool {
- if c := l.r.Peek(0); c == '0' || c == '1' {
- l.r.Move(1)
- return true
- }
- return false
- }
- func (l *Lexer) consumeOctalDigit() bool {
- if c := l.r.Peek(0); c >= '0' && c <= '7' {
- l.r.Move(1)
- return true
- }
- return false
- }
- func (l *Lexer) consumeUnicodeEscape() bool {
- if l.r.Peek(0) != '\\' || l.r.Peek(1) != 'u' {
- return false
- }
- mark := l.r.Pos()
- l.r.Move(2)
- if c := l.r.Peek(0); c == '{' {
- l.r.Move(1)
- if l.consumeHexDigit() {
- for l.consumeHexDigit() {
- }
- if c := l.r.Peek(0); c == '}' {
- l.r.Move(1)
- return true
- }
- }
- l.r.Rewind(mark)
- return false
- } else if !l.consumeHexDigit() || !l.consumeHexDigit() || !l.consumeHexDigit() || !l.consumeHexDigit() {
- l.r.Rewind(mark)
- return false
- }
- return true
- }
- func (l *Lexer) consumeSingleLineComment() {
- for {
- c := l.r.Peek(0)
- if c == '\r' || c == '\n' || c == 0 && l.r.Err() != nil {
- break
- } else if 0xC0 <= c {
- if r, _ := l.r.PeekRune(0); r == '\u2028' || r == '\u2029' {
- break
- }
- }
- l.r.Move(1)
- }
- }
- ////////////////////////////////////////////////////////////////
- func (l *Lexer) consumeHTMLLikeCommentToken(prevLineTerminator bool) bool {
- c := l.r.Peek(0)
- if c == '<' && l.r.Peek(1) == '!' && l.r.Peek(2) == '-' && l.r.Peek(3) == '-' {
- // opening HTML-style single line comment
- l.r.Move(4)
- l.consumeSingleLineComment()
- return true
- } else if prevLineTerminator && c == '-' && l.r.Peek(1) == '-' && l.r.Peek(2) == '>' {
- // closing HTML-style single line comment
- // (only if current line didn't contain any meaningful tokens)
- l.r.Move(3)
- l.consumeSingleLineComment()
- return true
- }
- return false
- }
- func (l *Lexer) consumeCommentToken() TokenType {
- c := l.r.Peek(1)
- if c == '/' {
- // single line comment
- l.r.Move(2)
- l.consumeSingleLineComment()
- return CommentToken
- } else if c == '*' {
- l.r.Move(2)
- tt := CommentToken
- for {
- c := l.r.Peek(0)
- if c == '*' && l.r.Peek(1) == '/' {
- l.r.Move(2)
- break
- } else if c == 0 && l.r.Err() != nil {
- l.err = parse.NewErrorLexer(l.r, "unexpected EOF in comment")
- return ErrorToken
- } else if l.consumeLineTerminator() {
- l.prevLineTerminator = true
- tt = CommentLineTerminatorToken
- } else {
- l.r.Move(1)
- }
- }
- return tt
- }
- return ErrorToken
- }
- var opTokens = map[byte]TokenType{
- '=': EqToken,
- '!': NotToken,
- '<': LtToken,
- '>': GtToken,
- '+': AddToken,
- '-': SubToken,
- '*': MulToken,
- '/': DivToken,
- '%': ModToken,
- '&': BitAndToken,
- '|': BitOrToken,
- '^': BitXorToken,
- '~': BitNotToken,
- '?': QuestionToken,
- }
- var opEqTokens = map[byte]TokenType{
- '=': EqEqToken,
- '!': NotEqToken,
- '<': LtEqToken,
- '>': GtEqToken,
- '+': AddEqToken,
- '-': SubEqToken,
- '*': MulEqToken,
- '/': DivEqToken,
- '%': ModEqToken,
- '&': BitAndEqToken,
- '|': BitOrEqToken,
- '^': BitXorEqToken,
- }
- var opOpTokens = map[byte]TokenType{
- '<': LtLtToken,
- '+': IncrToken,
- '-': DecrToken,
- '*': ExpToken,
- '&': AndToken,
- '|': OrToken,
- '?': NullishToken,
- }
- var opOpEqTokens = map[byte]TokenType{
- '<': LtLtEqToken,
- '*': ExpEqToken,
- '&': AndEqToken,
- '|': OrEqToken,
- '?': NullishEqToken,
- }
- func (l *Lexer) consumeOperatorToken() TokenType {
- c := l.r.Peek(0)
- l.r.Move(1)
- if l.r.Peek(0) == '=' {
- l.r.Move(1)
- if l.r.Peek(0) == '=' && (c == '!' || c == '=') {
- l.r.Move(1)
- if c == '!' {
- return NotEqEqToken
- }
- return EqEqEqToken
- }
- return opEqTokens[c]
- } else if l.r.Peek(0) == c && (c == '+' || c == '-' || c == '*' || c == '&' || c == '|' || c == '?' || c == '<') {
- l.r.Move(1)
- if l.r.Peek(0) == '=' && c != '+' && c != '-' {
- l.r.Move(1)
- return opOpEqTokens[c]
- }
- return opOpTokens[c]
- } else if c == '?' && l.r.Peek(0) == '.' && (l.r.Peek(1) < '0' || l.r.Peek(1) > '9') {
- l.r.Move(1)
- return OptChainToken
- } else if c == '=' && l.r.Peek(0) == '>' {
- l.r.Move(1)
- return ArrowToken
- } else if c == '>' && l.r.Peek(0) == '>' {
- l.r.Move(1)
- if l.r.Peek(0) == '>' {
- l.r.Move(1)
- if l.r.Peek(0) == '=' {
- l.r.Move(1)
- return GtGtGtEqToken
- }
- return GtGtGtToken
- } else if l.r.Peek(0) == '=' {
- l.r.Move(1)
- return GtGtEqToken
- }
- return GtGtToken
- }
- return opTokens[c]
- }
- func (l *Lexer) consumeIdentifierToken() bool {
- c := l.r.Peek(0)
- if identifierStartTable[c] {
- l.r.Move(1)
- } else if 0xC0 <= c {
- if r, n := l.r.PeekRune(0); unicode.IsOneOf(identifierStart, r) {
- l.r.Move(n)
- } else {
- return false
- }
- } else if !l.consumeUnicodeEscape() {
- return false
- }
- for {
- c := l.r.Peek(0)
- if identifierTable[c] {
- l.r.Move(1)
- } else if 0xC0 <= c {
- if r, n := l.r.PeekRune(0); r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) {
- l.r.Move(n)
- } else {
- break
- }
- } else if !l.consumeUnicodeEscape() {
- break
- }
- }
- return true
- }
- func (l *Lexer) consumeNumericSeparator(f func() bool) bool {
- if l.r.Peek(0) != '_' {
- return false
- }
- l.r.Move(1)
- if !f() {
- l.r.Move(-1)
- return false
- }
- return true
- }
- func (l *Lexer) consumeNumericToken() TokenType {
- // assume to be on 0 1 2 3 4 5 6 7 8 9 .
- first := l.r.Peek(0)
- if first == '0' {
- l.r.Move(1)
- if l.r.Peek(0) == 'x' || l.r.Peek(0) == 'X' {
- l.r.Move(1)
- if l.consumeHexDigit() {
- for l.consumeHexDigit() || l.consumeNumericSeparator(l.consumeHexDigit) {
- }
- return HexadecimalToken
- }
- l.err = parse.NewErrorLexer(l.r, "invalid hexadecimal number")
- return ErrorToken
- } else if l.r.Peek(0) == 'b' || l.r.Peek(0) == 'B' {
- l.r.Move(1)
- if l.consumeBinaryDigit() {
- for l.consumeBinaryDigit() || l.consumeNumericSeparator(l.consumeBinaryDigit) {
- }
- return BinaryToken
- }
- l.err = parse.NewErrorLexer(l.r, "invalid binary number")
- return ErrorToken
- } else if l.r.Peek(0) == 'o' || l.r.Peek(0) == 'O' {
- l.r.Move(1)
- if l.consumeOctalDigit() {
- for l.consumeOctalDigit() || l.consumeNumericSeparator(l.consumeOctalDigit) {
- }
- return OctalToken
- }
- l.err = parse.NewErrorLexer(l.r, "invalid octal number")
- return ErrorToken
- } else if l.r.Peek(0) == 'n' {
- l.r.Move(1)
- return BigIntToken
- } else if '0' <= l.r.Peek(0) && l.r.Peek(0) <= '9' {
- l.err = parse.NewErrorLexer(l.r, "legacy octal numbers are not supported")
- return ErrorToken
- }
- } else if first != '.' {
- for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) {
- }
- }
- // we have parsed a 0 or an integer number
- c := l.r.Peek(0)
- if c == '.' {
- l.r.Move(1)
- if l.consumeDigit() {
- for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) {
- }
- c = l.r.Peek(0)
- } else if first == '.' {
- // number starts with a dot and must be followed by digits
- l.r.Move(-1)
- return ErrorToken // may be dot or ellipsis
- } else {
- c = l.r.Peek(0)
- }
- } else if c == 'n' {
- l.r.Move(1)
- return BigIntToken
- }
- if c == 'e' || c == 'E' {
- l.r.Move(1)
- c = l.r.Peek(0)
- if c == '+' || c == '-' {
- l.r.Move(1)
- }
- if !l.consumeDigit() {
- l.err = parse.NewErrorLexer(l.r, "invalid number")
- return ErrorToken
- }
- for l.consumeDigit() || l.consumeNumericSeparator(l.consumeDigit) {
- }
- }
- return DecimalToken
- }
- func (l *Lexer) consumeStringToken() TokenType {
- // assume to be on ' or "
- delim := l.r.Peek(0)
- l.r.Move(1)
- for {
- c := l.r.Peek(0)
- if c == delim {
- l.r.Move(1)
- break
- } else if c == '\\' {
- l.r.Move(1)
- if !l.consumeLineTerminator() {
- if c := l.r.Peek(0); c == delim || c == '\\' {
- l.r.Move(1)
- }
- }
- continue
- } else if c == '\n' || c == '\r' || c == 0 && l.r.Err() != nil {
- l.err = parse.NewErrorLexer(l.r, "unterminated string literal")
- return ErrorToken
- }
- l.r.Move(1)
- }
- return StringToken
- }
- func (l *Lexer) consumeRegExpToken() bool {
- // assume to be on /
- l.r.Move(1)
- inClass := false
- for {
- c := l.r.Peek(0)
- if !inClass && c == '/' {
- l.r.Move(1)
- break
- } else if c == '[' {
- inClass = true
- } else if c == ']' {
- inClass = false
- } else if c == '\\' {
- l.r.Move(1)
- if l.isLineTerminator() || l.r.Peek(0) == 0 && l.r.Err() != nil {
- return false
- }
- } else if l.isLineTerminator() || c == 0 && l.r.Err() != nil {
- return false
- }
- l.r.Move(1)
- }
- // flags
- for {
- c := l.r.Peek(0)
- if identifierTable[c] {
- l.r.Move(1)
- } else if 0xC0 <= c {
- if r, n := l.r.PeekRune(0); r == '\u200C' || r == '\u200D' || unicode.IsOneOf(identifierContinue, r) {
- l.r.Move(n)
- } else {
- break
- }
- } else {
- break
- }
- }
- return true
- }
- func (l *Lexer) consumeTemplateToken() TokenType {
- // assume to be on ` or } when already within template
- continuation := l.r.Peek(0) == '}'
- l.r.Move(1)
- for {
- c := l.r.Peek(0)
- if c == '`' {
- l.templateLevels = l.templateLevels[:len(l.templateLevels)-1]
- l.r.Move(1)
- if continuation {
- return TemplateEndToken
- }
- return TemplateToken
- } else if c == '$' && l.r.Peek(1) == '{' {
- l.level++
- l.r.Move(2)
- if continuation {
- return TemplateMiddleToken
- }
- return TemplateStartToken
- } else if c == '\\' {
- l.r.Move(1)
- if c := l.r.Peek(0); c != 0 {
- l.r.Move(1)
- }
- continue
- } else if c == 0 && l.r.Err() != nil {
- l.err = parse.NewErrorLexer(l.r, "unterminated template literal")
- return ErrorToken
- }
- l.r.Move(1)
- }
- }
- var identifierStartTable = [256]bool{
- // ASCII
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, true, false, false, false, // $
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, true, true, true, true, true, true, true, // A, B, C, D, E, F, G
- true, true, true, true, true, true, true, true, // H, I, J, K, L, M, N, O
- true, true, true, true, true, true, true, true, // P, Q, R, S, T, U, V, W
- true, true, true, false, false, false, false, true, // X, Y, Z, _
- false, true, true, true, true, true, true, true, // a, b, c, d, e, f, g
- true, true, true, true, true, true, true, true, // h, i, j, k, l, m, n, o
- true, true, true, true, true, true, true, true, // p, q, r, s, t, u, v, w
- true, true, true, false, false, false, false, false, // x, y, z
- // non-ASCII
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- }
- var identifierTable = [256]bool{
- // ASCII
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, true, false, false, false, // $
- false, false, false, false, false, false, false, false,
- true, true, true, true, true, true, true, true, // 0, 1, 2, 3, 4, 5, 6, 7
- true, true, false, false, false, false, false, false, // 8, 9
- false, true, true, true, true, true, true, true, // A, B, C, D, E, F, G
- true, true, true, true, true, true, true, true, // H, I, J, K, L, M, N, O
- true, true, true, true, true, true, true, true, // P, Q, R, S, T, U, V, W
- true, true, true, false, false, false, false, true, // X, Y, Z, _
- false, true, true, true, true, true, true, true, // a, b, c, d, e, f, g
- true, true, true, true, true, true, true, true, // h, i, j, k, l, m, n, o
- true, true, true, true, true, true, true, true, // p, q, r, s, t, u, v, w
- true, true, true, false, false, false, false, false, // x, y, z
- // non-ASCII
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, false,
- }
|