mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
fix(graphite): fixed minor graphite lexer issue when nodes begin with octal syntax and end with dash identifier, fixes #6049#
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
|
<div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
|
<label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
|
||||||
{{variable.label || variable.name}}:
|
{{variable.label || variable.name}}
|
||||||
</label>
|
</label>
|
||||||
<value-select-dropdown ng-if="variable.type !== 'adhoc'" variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
|
<value-select-dropdown ng-if="variable.type !== 'adhoc'" variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -134,13 +134,7 @@ for (var i = 0; i < 128; i++) {
|
|||||||
i >= 97 && i <= 122; // a-z
|
i >= 97 && i <= 122; // a-z
|
||||||
}
|
}
|
||||||
|
|
||||||
var identifierPartTable = [];
|
var identifierPartTable = identifierStartTable;
|
||||||
|
|
||||||
for (var i2 = 0; i2 < 128; i2++) {
|
|
||||||
identifierPartTable[i2] =
|
|
||||||
identifierStartTable[i2] || // $, _, A-Z, a-z
|
|
||||||
i2 >= 48 && i2 <= 57; // 0-9
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Lexer(expression) {
|
export function Lexer(expression) {
|
||||||
this.input = expression;
|
this.input = expression;
|
||||||
@@ -423,256 +417,260 @@ Lexer.prototype = {
|
|||||||
if (char === '-') {
|
if (char === '-') {
|
||||||
value += char;
|
value += char;
|
||||||
index += 1;
|
index += 1;
|
||||||
char = this.peek(index);
|
char = this.peek(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Numbers must start either with a decimal digit or a point.
|
// Numbers must start either with a decimal digit or a point.
|
||||||
if (char !== "." && !isDecimalDigit(char)) {
|
if (char !== "." && !isDecimalDigit(char)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char !== ".") {
|
if (char !== ".") {
|
||||||
value += this.peek(index);
|
value += this.peek(index);
|
||||||
index += 1;
|
index += 1;
|
||||||
char = this.peek(index);
|
char = this.peek(index);
|
||||||
|
|
||||||
if (value === "0") {
|
if (value === "0") {
|
||||||
// Base-16 numbers.
|
// Base-16 numbers.
|
||||||
if (char === "x" || char === "X") {
|
if (char === "x" || char === "X") {
|
||||||
index += 1;
|
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;
|
|
||||||
} else if (!isOctalDigit(char)) {
|
|
||||||
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: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
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) {
|
while (index < length) {
|
||||||
char = this.peek(index);
|
char = this.peek(index);
|
||||||
if (!isDecimalDigit(char)) {
|
if (!isHexDigit(char)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
value += char;
|
value += char;
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < length) {
|
if (value.length <= 2) { // 0x
|
||||||
char = this.peek(index);
|
return {
|
||||||
if (!this.isPunctuator(char)) {
|
type: 'number',
|
||||||
return null;
|
value: value,
|
||||||
}
|
isMalformed: true,
|
||||||
}
|
pos: this.char
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
if (index < length) {
|
||||||
type: 'number',
|
char = this.peek(index);
|
||||||
value: value,
|
if (isIdentifierStart(char)) {
|
||||||
base: 10,
|
return null;
|
||||||
pos: this.char,
|
}
|
||||||
isMalformed: !isFinite(+value)
|
}
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
isPunctuator: function (ch1) {
|
|
||||||
switch (ch1) {
|
|
||||||
case ".":
|
|
||||||
case "(":
|
|
||||||
case ")":
|
|
||||||
case ",":
|
|
||||||
case "{":
|
|
||||||
case "}":
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
scanPunctuator: function () {
|
|
||||||
var 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: function () {
|
|
||||||
/*jshint loopfunc:true */
|
|
||||||
var quote = this.peek();
|
|
||||||
|
|
||||||
// String must start with a quote.
|
|
||||||
if (quote !== "\"" && quote !== "'") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = "";
|
|
||||||
|
|
||||||
this.skip();
|
|
||||||
|
|
||||||
while (this.peek() !== quote) {
|
|
||||||
if (this.peek() === "") { // End Of Line
|
|
||||||
return {
|
return {
|
||||||
type: 'string',
|
type: 'number',
|
||||||
value: value,
|
value: value,
|
||||||
isUnclosed: true,
|
base: 16,
|
||||||
quote: quote,
|
isMalformed: false,
|
||||||
pos: this.char
|
pos: this.char
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var char = this.peek();
|
// Base-8 numbers.
|
||||||
var jump = 1; // A length of a jump, after we're done
|
if (isOctalDigit(char)) {
|
||||||
// parsing this character.
|
index += 1;
|
||||||
|
value += char;
|
||||||
|
bad = false;
|
||||||
|
|
||||||
value += char;
|
while (index < length) {
|
||||||
this.skip(jump);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.skip();
|
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: function (ch1) {
|
||||||
|
switch (ch1) {
|
||||||
|
case ".":
|
||||||
|
case "(":
|
||||||
|
case ")":
|
||||||
|
case ",":
|
||||||
|
case "{":
|
||||||
|
case "}":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
scanPunctuator: function () {
|
||||||
|
var ch1 = this.peek();
|
||||||
|
|
||||||
|
if (this.isPunctuator(ch1)) {
|
||||||
return {
|
return {
|
||||||
type: 'string',
|
type: ch1,
|
||||||
value: value,
|
value: ch1,
|
||||||
isUnclosed: false,
|
|
||||||
quote: quote,
|
|
||||||
pos: this.char
|
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: function () {
|
||||||
|
/*jshint loopfunc:true */
|
||||||
|
var quote = this.peek();
|
||||||
|
|
||||||
|
// String must start with a quote.
|
||||||
|
if (quote !== "\"" && quote !== "'") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var char = this.peek();
|
||||||
|
var 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
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -100,10 +100,7 @@ Parser.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
metricExpression: function() {
|
metricExpression: function() {
|
||||||
if (!this.match('templateStart') &&
|
if (!this.match('templateStart') && !this.match('identifier') && !this.match('number') && !this.match('{')) {
|
||||||
!this.match('identifier') &&
|
|
||||||
!this.match('number') &&
|
|
||||||
!this.match('{')) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ describe('when lexing graphite expression', function() {
|
|||||||
expect(tokens[4].type).to.be('identifier');
|
expect(tokens[4].type).to.be('identifier');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should tokenize metric expression with segment that start with number', function() {
|
||||||
|
var lexer = new Lexer("metric.001-server");
|
||||||
|
var tokens = lexer.tokenize();
|
||||||
|
expect(tokens[0].type).to.be('identifier');
|
||||||
|
expect(tokens[2].type).to.be('identifier');
|
||||||
|
expect(tokens.length).to.be(3);
|
||||||
|
});
|
||||||
|
|
||||||
it('should tokenize func call with numbered metric and number arg', function() {
|
it('should tokenize func call with numbered metric and number arg', function() {
|
||||||
var lexer = new Lexer("scale(metric.10, 15)");
|
var lexer = new Lexer("scale(metric.10, 15)");
|
||||||
var tokens = lexer.tokenize();
|
var tokens = lexer.tokenize();
|
||||||
|
|||||||
Reference in New Issue
Block a user