mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(plugins): migrating graphite query editor to new model
This commit is contained in:
258
public/app/plugins/datasource/graphite/parser.ts
Normal file
258
public/app/plugins/datasource/graphite/parser.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
|
||||
import {Lexer} from './lexer';
|
||||
|
||||
export function Parser(expression) {
|
||||
this.expression = expression;
|
||||
this.lexer = new Lexer(expression);
|
||||
this.tokens = this.lexer.tokenize();
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
Parser.prototype = {
|
||||
|
||||
getAst: function () {
|
||||
return this.start();
|
||||
},
|
||||
|
||||
start: function () {
|
||||
try {
|
||||
return this.functionCall() || this.metricExpression();
|
||||
} catch (e) {
|
||||
return {
|
||||
type: 'error',
|
||||
message: e.message,
|
||||
pos: e.pos
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
curlyBraceSegment: function() {
|
||||
if (this.match('identifier', '{') || this.match('{')) {
|
||||
|
||||
var curlySegment = "";
|
||||
|
||||
while (!this.match('') && !this.match('}')) {
|
||||
curlySegment += this.consumeToken().value;
|
||||
}
|
||||
|
||||
if (!this.match('}')) {
|
||||
this.errorMark("Expected closing '}'");
|
||||
}
|
||||
|
||||
curlySegment += this.consumeToken().value;
|
||||
|
||||
// if curly segment is directly followed by identifier
|
||||
// include it in the segment
|
||||
if (this.match('identifier')) {
|
||||
curlySegment += this.consumeToken().value;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'segment',
|
||||
value: curlySegment
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
metricSegment: function() {
|
||||
var curly = this.curlyBraceSegment();
|
||||
if (curly) {
|
||||
return curly;
|
||||
}
|
||||
|
||||
if (this.match('identifier') || this.match('number')) {
|
||||
// hack to handle float numbers in metric segments
|
||||
var parts = this.consumeToken().value.split('.');
|
||||
if (parts.length === 2) {
|
||||
this.tokens.splice(this.index, 0, { type: '.' });
|
||||
this.tokens.splice(this.index + 1, 0, { type: 'number', value: parts[1] });
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'segment',
|
||||
value: parts[0]
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.match('templateStart')) {
|
||||
this.errorMark('Expected metric identifier');
|
||||
}
|
||||
|
||||
this.consumeToken();
|
||||
|
||||
if (!this.match('identifier')) {
|
||||
this.errorMark('Expected identifier after templateStart');
|
||||
}
|
||||
|
||||
var node = {
|
||||
type: 'template',
|
||||
value: this.consumeToken().value
|
||||
};
|
||||
|
||||
if (!this.match('templateEnd')) {
|
||||
this.errorMark('Expected templateEnd');
|
||||
}
|
||||
|
||||
this.consumeToken();
|
||||
return node;
|
||||
},
|
||||
|
||||
metricExpression: function() {
|
||||
if (!this.match('templateStart') &&
|
||||
!this.match('identifier') &&
|
||||
!this.match('number') &&
|
||||
!this.match('{')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var node = {
|
||||
type: 'metric',
|
||||
segments: []
|
||||
};
|
||||
|
||||
node.segments.push(this.metricSegment());
|
||||
|
||||
while (this.match('.')) {
|
||||
this.consumeToken();
|
||||
|
||||
var segment = this.metricSegment();
|
||||
if (!segment) {
|
||||
this.errorMark('Expected metric identifier');
|
||||
}
|
||||
|
||||
node.segments.push(segment);
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
functionCall: function() {
|
||||
if (!this.match('identifier', '(')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var node: any = {
|
||||
type: 'function',
|
||||
name: this.consumeToken().value,
|
||||
};
|
||||
|
||||
// consume left parenthesis
|
||||
this.consumeToken();
|
||||
|
||||
node.params = this.functionParameters();
|
||||
|
||||
if (!this.match(')')) {
|
||||
this.errorMark('Expected closing parenthesis');
|
||||
}
|
||||
|
||||
this.consumeToken();
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
boolExpression: function() {
|
||||
if (!this.match('bool')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'bool',
|
||||
value: this.consumeToken().value === 'true',
|
||||
};
|
||||
},
|
||||
|
||||
functionParameters: function () {
|
||||
if (this.match(')') || this.match('')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var param =
|
||||
this.functionCall() ||
|
||||
this.numericLiteral() ||
|
||||
this.seriesRefExpression() ||
|
||||
this.boolExpression() ||
|
||||
this.metricExpression() ||
|
||||
this.stringLiteral();
|
||||
|
||||
if (!this.match(',')) {
|
||||
return [param];
|
||||
}
|
||||
|
||||
this.consumeToken();
|
||||
return [param].concat(this.functionParameters());
|
||||
},
|
||||
|
||||
seriesRefExpression: function() {
|
||||
if (!this.match('identifier')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = this.tokens[this.index].value;
|
||||
if (!value.match(/\#[A-Z]/)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var token = this.consumeToken();
|
||||
|
||||
return {
|
||||
type: 'series-ref',
|
||||
value: token.value
|
||||
};
|
||||
},
|
||||
|
||||
numericLiteral: function () {
|
||||
if (!this.match('number')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'number',
|
||||
value: parseFloat(this.consumeToken().value)
|
||||
};
|
||||
},
|
||||
|
||||
stringLiteral: function () {
|
||||
if (!this.match('string')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var token = this.consumeToken();
|
||||
if (token.isUnclosed) {
|
||||
throw { message: 'Unclosed string parameter', pos: token.pos };
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'string',
|
||||
value: token.value
|
||||
};
|
||||
},
|
||||
|
||||
errorMark: function(text) {
|
||||
var currentToken = this.tokens[this.index];
|
||||
var type = currentToken ? currentToken.type : 'end of string';
|
||||
throw {
|
||||
message: text + " instead found " + type,
|
||||
pos: currentToken ? currentToken.pos : this.lexer.char
|
||||
};
|
||||
},
|
||||
|
||||
// returns token value and incre
|
||||
consumeToken: function() {
|
||||
this.index++;
|
||||
return this.tokens[this.index - 1];
|
||||
},
|
||||
|
||||
matchToken: function(type, index) {
|
||||
var token = this.tokens[this.index + index];
|
||||
return (token === undefined && type === '') ||
|
||||
token && token.type === type;
|
||||
},
|
||||
|
||||
match: function(token1, token2) {
|
||||
return this.matchToken(token1, 0) &&
|
||||
(!token2 || this.matchToken(token2, 1));
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user