grafana/public/app/plugins/datasource/graphite/lexer.ts
kay delaney bad048b7ba
Performance: Standardize lodash imports to use destructured members (#33040)
* Performance: Standardize lodash imports to use destructured members
Changes lodash imports of the form `import x from 'lodash/x'` to
`import { x } from 'lodash'` to reduce bundle size.

* Remove unnecessary _ import from Graph component

* Enforce lodash import style

* Fix remaining lodash imports
2021-04-21 09:38:00 +02:00

1456 lines
19 KiB
TypeScript

import { bind } from 'lodash';
// This is auto generated from the unicode tables.
// The tables are at:
// http://www.fileformat.info/info/unicode/category/Lu/list.htm
// http://www.fileformat.info/info/unicode/category/Ll/list.htm
// http://www.fileformat.info/info/unicode/category/Lt/list.htm
// http://www.fileformat.info/info/unicode/category/Lm/list.htm
// http://www.fileformat.info/info/unicode/category/Lo/list.htm
// http://www.fileformat.info/info/unicode/category/Nl/list.htm
const unicodeLetterTable = [
170,
170,
181,
181,
186,
186,
192,
214,
216,
246,
248,
705,
710,
721,
736,
740,
748,
748,
750,
750,
880,
884,
886,
887,
890,
893,
902,
902,
904,
906,
908,
908,
910,
929,
931,
1013,
1015,
1153,
1162,
1319,
1329,
1366,
1369,
1369,
1377,
1415,
1488,
1514,
1520,
1522,
1568,
1610,
1646,
1647,
1649,
1747,
1749,
1749,
1765,
1766,
1774,
1775,
1786,
1788,
1791,
1791,
1808,
1808,
1810,
1839,
1869,
1957,
1969,
1969,
1994,
2026,
2036,
2037,
2042,
2042,
2048,
2069,
2074,
2074,
2084,
2084,
2088,
2088,
2112,
2136,
2308,
2361,
2365,
2365,
2384,
2384,
2392,
2401,
2417,
2423,
2425,
2431,
2437,
2444,
2447,
2448,
2451,
2472,
2474,
2480,
2482,
2482,
2486,
2489,
2493,
2493,
2510,
2510,
2524,
2525,
2527,
2529,
2544,
2545,
2565,
2570,
2575,
2576,
2579,
2600,
2602,
2608,
2610,
2611,
2613,
2614,
2616,
2617,
2649,
2652,
2654,
2654,
2674,
2676,
2693,
2701,
2703,
2705,
2707,
2728,
2730,
2736,
2738,
2739,
2741,
2745,
2749,
2749,
2768,
2768,
2784,
2785,
2821,
2828,
2831,
2832,
2835,
2856,
2858,
2864,
2866,
2867,
2869,
2873,
2877,
2877,
2908,
2909,
2911,
2913,
2929,
2929,
2947,
2947,
2949,
2954,
2958,
2960,
2962,
2965,
2969,
2970,
2972,
2972,
2974,
2975,
2979,
2980,
2984,
2986,
2990,
3001,
3024,
3024,
3077,
3084,
3086,
3088,
3090,
3112,
3114,
3123,
3125,
3129,
3133,
3133,
3160,
3161,
3168,
3169,
3205,
3212,
3214,
3216,
3218,
3240,
3242,
3251,
3253,
3257,
3261,
3261,
3294,
3294,
3296,
3297,
3313,
3314,
3333,
3340,
3342,
3344,
3346,
3386,
3389,
3389,
3406,
3406,
3424,
3425,
3450,
3455,
3461,
3478,
3482,
3505,
3507,
3515,
3517,
3517,
3520,
3526,
3585,
3632,
3634,
3635,
3648,
3654,
3713,
3714,
3716,
3716,
3719,
3720,
3722,
3722,
3725,
3725,
3732,
3735,
3737,
3743,
3745,
3747,
3749,
3749,
3751,
3751,
3754,
3755,
3757,
3760,
3762,
3763,
3773,
3773,
3776,
3780,
3782,
3782,
3804,
3805,
3840,
3840,
3904,
3911,
3913,
3948,
3976,
3980,
4096,
4138,
4159,
4159,
4176,
4181,
4186,
4189,
4193,
4193,
4197,
4198,
4206,
4208,
4213,
4225,
4238,
4238,
4256,
4293,
4304,
4346,
4348,
4348,
4352,
4680,
4682,
4685,
4688,
4694,
4696,
4696,
4698,
4701,
4704,
4744,
4746,
4749,
4752,
4784,
4786,
4789,
4792,
4798,
4800,
4800,
4802,
4805,
4808,
4822,
4824,
4880,
4882,
4885,
4888,
4954,
4992,
5007,
5024,
5108,
5121,
5740,
5743,
5759,
5761,
5786,
5792,
5866,
5870,
5872,
5888,
5900,
5902,
5905,
5920,
5937,
5952,
5969,
5984,
5996,
5998,
6000,
6016,
6067,
6103,
6103,
6108,
6108,
6176,
6263,
6272,
6312,
6314,
6314,
6320,
6389,
6400,
6428,
6480,
6509,
6512,
6516,
6528,
6571,
6593,
6599,
6656,
6678,
6688,
6740,
6823,
6823,
6917,
6963,
6981,
6987,
7043,
7072,
7086,
7087,
7104,
7141,
7168,
7203,
7245,
7247,
7258,
7293,
7401,
7404,
7406,
7409,
7424,
7615,
7680,
7957,
7960,
7965,
7968,
8005,
8008,
8013,
8016,
8023,
8025,
8025,
8027,
8027,
8029,
8029,
8031,
8061,
8064,
8116,
8118,
8124,
8126,
8126,
8130,
8132,
8134,
8140,
8144,
8147,
8150,
8155,
8160,
8172,
8178,
8180,
8182,
8188,
8305,
8305,
8319,
8319,
8336,
8348,
8450,
8450,
8455,
8455,
8458,
8467,
8469,
8469,
8473,
8477,
8484,
8484,
8486,
8486,
8488,
8488,
8490,
8493,
8495,
8505,
8508,
8511,
8517,
8521,
8526,
8526,
8544,
8584,
11264,
11310,
11312,
11358,
11360,
11492,
11499,
11502,
11520,
11557,
11568,
11621,
11631,
11631,
11648,
11670,
11680,
11686,
11688,
11694,
11696,
11702,
11704,
11710,
11712,
11718,
11720,
11726,
11728,
11734,
11736,
11742,
11823,
11823,
12293,
12295,
12321,
12329,
12337,
12341,
12344,
12348,
12353,
12438,
12445,
12447,
12449,
12538,
12540,
12543,
12549,
12589,
12593,
12686,
12704,
12730,
12784,
12799,
13312,
13312,
19893,
19893,
19968,
19968,
40907,
40907,
40960,
42124,
42192,
42237,
42240,
42508,
42512,
42527,
42538,
42539,
42560,
42606,
42623,
42647,
42656,
42735,
42775,
42783,
42786,
42888,
42891,
42894,
42896,
42897,
42912,
42921,
43002,
43009,
43011,
43013,
43015,
43018,
43020,
43042,
43072,
43123,
43138,
43187,
43250,
43255,
43259,
43259,
43274,
43301,
43312,
43334,
43360,
43388,
43396,
43442,
43471,
43471,
43520,
43560,
43584,
43586,
43588,
43595,
43616,
43638,
43642,
43642,
43648,
43695,
43697,
43697,
43701,
43702,
43705,
43709,
43712,
43712,
43714,
43714,
43739,
43741,
43777,
43782,
43785,
43790,
43793,
43798,
43808,
43814,
43816,
43822,
43968,
44002,
44032,
44032,
55203,
55203,
55216,
55238,
55243,
55291,
63744,
64045,
64048,
64109,
64112,
64217,
64256,
64262,
64275,
64279,
64285,
64285,
64287,
64296,
64298,
64310,
64312,
64316,
64318,
64318,
64320,
64321,
64323,
64324,
64326,
64433,
64467,
64829,
64848,
64911,
64914,
64967,
65008,
65019,
65136,
65140,
65142,
65276,
65313,
65338,
65345,
65370,
65382,
65470,
65474,
65479,
65482,
65487,
65490,
65495,
65498,
65500,
65536,
65547,
65549,
65574,
65576,
65594,
65596,
65597,
65599,
65613,
65616,
65629,
65664,
65786,
65856,
65908,
66176,
66204,
66208,
66256,
66304,
66334,
66352,
66378,
66432,
66461,
66464,
66499,
66504,
66511,
66513,
66517,
66560,
66717,
67584,
67589,
67592,
67592,
67594,
67637,
67639,
67640,
67644,
67644,
67647,
67669,
67840,
67861,
67872,
67897,
68096,
68096,
68112,
68115,
68117,
68119,
68121,
68147,
68192,
68220,
68352,
68405,
68416,
68437,
68448,
68466,
68608,
68680,
69635,
69687,
69763,
69807,
73728,
74606,
74752,
74850,
77824,
78894,
92160,
92728,
110592,
110593,
119808,
119892,
119894,
119964,
119966,
119967,
119970,
119970,
119973,
119974,
119977,
119980,
119982,
119993,
119995,
119995,
119997,
120003,
120005,
120069,
120071,
120074,
120077,
120084,
120086,
120092,
120094,
120121,
120123,
120126,
120128,
120132,
120134,
120134,
120138,
120144,
120146,
120485,
120488,
120512,
120514,
120538,
120540,
120570,
120572,
120596,
120598,
120628,
120630,
120654,
120656,
120686,
120688,
120712,
120714,
120744,
120746,
120770,
120772,
120779,
131072,
131072,
173782,
173782,
173824,
173824,
177972,
177972,
177984,
177984,
178205,
178205,
194560,
195101,
];
const identifierStartTable: any[] = [];
for (let i = 0; i < 128; i++) {
identifierStartTable[i] =
(i >= 48 && i <= 57) || // 0-9
i === 36 || // $
i === 126 || // ~
i === 124 || // |
(i >= 65 && i <= 90) || // A-Z
i === 95 || // _
i === 45 || // -
i === 42 || // *
i === 58 || // :
i === 91 || // templateStart [
i === 93 || // templateEnd ]
i === 63 || // ?
i === 37 || // %
i === 35 || // #
i === 61 || // =
(i >= 97 && i <= 122); // a-z
}
const identifierPartTable = identifierStartTable;
export class Lexer {
input: any;
char: number;
from: number;
constructor(expression: any) {
this.input = expression;
this.char = 1;
this.from = 1;
}
peek(i?: number) {
return this.input.charAt(i || 0);
}
skip(i?: number) {
i = i || 1;
this.char += i;
this.input = this.input.slice(i);
}
tokenize() {
const list = [];
let token = this.next();
while (token) {
list.push(token);
token = this.next();
}
return list;
}
next() {
this.from = this.char;
// Move to the next non-space character.
if (/\s/.test(this.peek())) {
while (/\s/.test(this.peek())) {
this.from += 1;
this.skip();
}
if (this.peek() === '') {
// EOL
return null;
}
}
let match = this.scanStringLiteral();
if (match) {
return match;
}
match = this.scanPunctuator() || this.scanNumericLiteral() || this.scanIdentifier() || this.scanTemplateSequence();
if (match) {
this.skip(match.value.length);
return match;
}
// No token could be matched, give up.
return null;
}
scanTemplateSequence() {
if (this.peek() === '[' && this.peek(1) === '[') {
return {
type: 'templateStart',
value: '[[',
pos: this.char,
};
}
if (this.peek() === ']' && this.peek(1) === ']') {
return {
type: 'templateEnd',
value: '[[',
pos: this.char,
};
}
return null;
}
/*
* Extract a JavaScript identifier out of the next sequence of
* characters or return 'null' if its not possible. In addition,
* to Identifier this method can also produce BooleanLiteral
* (true/false) and NullLiteral (null).
*/
scanIdentifier() {
let id = '';
let index = 0;
let type, char;
// Detects any character in the Unicode categories "Uppercase
// letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter
// (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or
// "Letter number (Nl)".
//
// Both approach and unicodeLetterTable were borrowed from
// Google's Traceur.
function isUnicodeLetter(code: number) {
for (let i = 0; i < unicodeLetterTable.length; ) {
if (code < unicodeLetterTable[i++]) {
return false;
}
if (code <= unicodeLetterTable[i++]) {
return true;
}
}
return false;
}
function isHexDigit(str: string) {
return /^[0-9a-fA-F]$/.test(str);
}
const readUnicodeEscapeSequence = bind(function (this: any) {
index += 1;
if (this.peek(index) !== 'u') {
return null;
}
const ch1 = this.peek(index + 1);
const ch2 = this.peek(index + 2);
const ch3 = this.peek(index + 3);
const ch4 = this.peek(index + 4);
let code;
if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) {
code = parseInt(ch1 + ch2 + ch3 + ch4, 16);
if (isUnicodeLetter(code)) {
index += 5;
return '\\u' + ch1 + ch2 + ch3 + ch4;
}
return null;
}
return null;
}, this);
const getIdentifierStart = bind(function (this: any) {
const chr = this.peek(index);
const code = chr.charCodeAt(0);
if (chr === '*') {
index += 1;
return chr;
}
if (code === 92) {
return readUnicodeEscapeSequence();
}
if (code < 128) {
if (identifierStartTable[code]) {
index += 1;
return chr;
}
return null;
}
if (isUnicodeLetter(code)) {
index += 1;
return chr;
}
return null;
}, this);
const getIdentifierPart = bind(function (this: any) {
const chr = this.peek(index);
const code = chr.charCodeAt(0);
if (code === 92) {
return readUnicodeEscapeSequence();
}
if (code < 128) {
if (identifierPartTable[code]) {
index += 1;
return chr;
}
return null;
}
if (isUnicodeLetter(code)) {
index += 1;
return chr;
}
return null;
}, this);
char = getIdentifierStart();
if (char === null) {
return null;
}
id = char;
for (;;) {
char = getIdentifierPart();
if (char === null) {
break;
}
id += char;
}
switch (id) {
case 'true': {
type = 'bool';
break;
}
case 'false': {
type = 'bool';
break;
}
default:
type = 'identifier';
}
return {
type: type,
value: id,
pos: this.char,
};
}
/*
* Extract a numeric literal out of the next sequence of
* characters or return 'null' if its not possible. This method
* supports all numeric literals described in section 7.8.3
* of the EcmaScript 5 specification.
*
* This method's implementation was heavily influenced by the
* scanNumericLiteral function in the Esprima parser's source code.
*/
scanNumericLiteral(): any {
let index = 0;
let value = '';
const length = this.input.length;
let char = this.peek(index);
let bad;
function isDecimalDigit(str: string) {
return /^[0-9]$/.test(str);
}
function isOctalDigit(str: string) {
return /^[0-7]$/.test(str);
}
function isHexDigit(str: string) {
return /^[0-9a-fA-F]$/.test(str);
}
function isIdentifierStart(ch: string) {
return ch === '$' || ch === '_' || ch === '\\' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
// handle negative num literals
if (char === '-') {
value += char;
index += 1;
char = this.peek(index);
}
// Numbers must start either with a decimal digit or a point.
if (char !== '.' && !isDecimalDigit(char)) {
return null;
}
if (char !== '.') {
value += this.peek(index);
index += 1;
char = this.peek(index);
if (value === '0') {
// Base-16 numbers.
if (char === 'x' || char === 'X') {
index += 1;
value += char;
while (index < length) {
char = this.peek(index);
if (!isHexDigit(char)) {
break;
}
value += char;
index += 1;
}
if (value.length <= 2) {
// 0x
return {
type: 'number',
value: value,
isMalformed: true,
pos: this.char,
};
}
if (index < length) {
char = this.peek(index);
if (isIdentifierStart(char)) {
return null;
}
}
return {
type: 'number',
value: value,
base: 16,
isMalformed: false,
pos: this.char,
};
}
// Base-8 numbers.
if (isOctalDigit(char)) {
index += 1;
value += char;
bad = false;
while (index < length) {
char = this.peek(index);
// Numbers like '019' (note the 9) are not valid octals
// but we still parse them and mark as malformed.
if (isDecimalDigit(char)) {
bad = true;
}
if (!isOctalDigit(char)) {
// if the char is a non punctuator then its not a valid number
if (!this.isPunctuator(char)) {
return null;
}
break;
}
value += char;
index += 1;
}
if (index < length) {
char = this.peek(index);
if (isIdentifierStart(char)) {
return null;
}
}
return {
type: 'number',
value: value,
base: 8,
isMalformed: bad,
};
}
// Decimal numbers that start with '0' such as '09' are illegal
// but we still parse them and return as malformed.
if (isDecimalDigit(char)) {
index += 1;
value += char;
}
}
while (index < length) {
char = this.peek(index);
if (!isDecimalDigit(char)) {
break;
}
value += char;
index += 1;
}
}
// Decimal digits.
if (char === '.') {
value += char;
index += 1;
while (index < length) {
char = this.peek(index);
if (!isDecimalDigit(char)) {
break;
}
value += char;
index += 1;
}
}
// Exponent part.
if (char === 'e' || char === 'E') {
value += char;
index += 1;
char = this.peek(index);
if (char === '+' || char === '-') {
value += this.peek(index);
index += 1;
}
char = this.peek(index);
if (isDecimalDigit(char)) {
value += char;
index += 1;
while (index < length) {
char = this.peek(index);
if (!isDecimalDigit(char)) {
break;
}
value += char;
index += 1;
}
} else {
return null;
}
}
if (index < length) {
char = this.peek(index);
if (!this.isPunctuator(char)) {
return null;
}
}
return {
type: 'number',
value: value,
base: 10,
pos: this.char,
isMalformed: !isFinite(+value),
};
}
isPunctuator(ch1: string) {
switch (ch1) {
case '.':
case '(':
case ')':
case ',':
case '{':
case '}':
return true;
}
return false;
}
scanPunctuator() {
const ch1 = this.peek();
if (this.isPunctuator(ch1)) {
return {
type: ch1,
value: ch1,
pos: this.char,
};
}
return null;
}
/*
* Extract a string out of the next sequence of characters and/or
* lines or return 'null' if its not possible. Since strings can
* span across multiple lines this method has to move the char
* pointer.
*
* This method recognizes pseudo-multiline JavaScript strings:
*
* var str = "hello\
* world";
*/
scanStringLiteral() {
const quote = this.peek();
// String must start with a quote.
if (quote !== '"' && quote !== "'") {
return null;
}
let value = '';
this.skip();
while (this.peek() !== quote) {
if (this.peek() === '') {
// End Of Line
return {
type: 'string',
value: value,
isUnclosed: true,
quote: quote,
pos: this.char,
};
}
const char = this.peek();
const jump = 1; // A length of a jump, after we're done
// parsing this character.
value += char;
this.skip(jump);
}
this.skip();
return {
type: 'string',
value: value,
isUnclosed: false,
quote: quote,
pos: this.char,
};
}
}