From 822c8f15759a93a18a70a993fca185829affc2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 2 Feb 2016 12:52:43 +0100 Subject: [PATCH] feat(plugins): migrating graphite query editor to new model --- .../app/core/directives/plugin_component.ts | 2 +- public/app/features/dashboard/dashboardSrv.js | 10 + public/app/features/panel/all.js | 2 +- public/app/features/panel/panel.ts | 4 +- .../panel/{query_editor.ts => query_ctrl.ts} | 16 +- .../datasource/graphite/datasource.d.ts | 3 - .../plugins/datasource/graphite/datasource.js | 296 -------- .../plugins/datasource/graphite/datasource.ts | 281 ++++++++ .../app/plugins/datasource/graphite/lexer.js | 682 ------------------ .../app/plugins/datasource/graphite/lexer.ts | 678 +++++++++++++++++ .../app/plugins/datasource/graphite/module.js | 38 - .../app/plugins/datasource/graphite/module.ts | 51 ++ .../app/plugins/datasource/graphite/parser.js | 265 ------- .../app/plugins/datasource/graphite/parser.ts | 258 +++++++ .../graphite/partials/query.editor.html | 34 +- .../plugins/datasource/graphite/query_ctrl.js | 292 -------- .../plugins/datasource/graphite/query_ctrl.ts | 275 +++++++ .../graphite/specs/datasource_specs.ts | 4 +- .../datasource/graphite/specs/lexer_specs.ts | 118 +++ .../datasource/graphite/specs/parser_specs.ts | 183 +++++ .../graphite/specs/query_ctrl_specs.ts | 115 ++- .../datasource/prometheus/datasource.ts | 6 +- .../plugins/datasource/prometheus/module.ts | 13 +- .../datasource/prometheus/query_ctrl.ts | 6 +- .../prometheus/specs/datasource_specs.ts | 4 +- public/test/specs/dashboardSrv-specs.js | 33 - public/test/specs/lexer-specs.js | 122 ---- public/test/specs/parser-specs.js | 188 ----- tslint.json | 2 +- 29 files changed, 1946 insertions(+), 2035 deletions(-) rename public/app/features/panel/{query_editor.ts => query_ctrl.ts} (86%) delete mode 100644 public/app/plugins/datasource/graphite/datasource.d.ts delete mode 100644 public/app/plugins/datasource/graphite/datasource.js create mode 100644 public/app/plugins/datasource/graphite/datasource.ts delete mode 100644 public/app/plugins/datasource/graphite/lexer.js create mode 100644 public/app/plugins/datasource/graphite/lexer.ts delete mode 100644 public/app/plugins/datasource/graphite/module.js create mode 100644 public/app/plugins/datasource/graphite/module.ts delete mode 100644 public/app/plugins/datasource/graphite/parser.js create mode 100644 public/app/plugins/datasource/graphite/parser.ts delete mode 100644 public/app/plugins/datasource/graphite/query_ctrl.js create mode 100644 public/app/plugins/datasource/graphite/query_ctrl.ts create mode 100644 public/app/plugins/datasource/graphite/specs/lexer_specs.ts create mode 100644 public/app/plugins/datasource/graphite/specs/parser_specs.ts delete mode 100644 public/test/specs/lexer-specs.js delete mode 100644 public/test/specs/parser-specs.js diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 3b18ac62acf..d4722c6d2f5 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -37,7 +37,7 @@ function pluginDirectiveLoader($compile, datasourceSrv) { name: 'metrics-query-editor-' + ds.meta.id, bindings: {target: "=", panelCtrl: "=", datasource: "="}, attrs: {"target": "target", "panel-ctrl": "ctrl", datasource: "datasource"}, - Component: dsModule.MetricsQueryEditor + Component: dsModule.QueryCtrl }; }); }); diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js index ad45ce64a70..2f64fec9be1 100644 --- a/public/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -194,6 +194,16 @@ function (angular, $, _, moment) { moment.utc(date).fromNow(); }; + p.getNextQueryLetter = function(panel) { + var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + return _.find(letters, function(refId) { + return _.every(panel.targets, function(other) { + return other.refId !== refId; + }); + }); + }; + p._updateSchema = function(old) { var i, j, k; var oldVersion = this.schemaVersion; diff --git a/public/app/features/panel/all.js b/public/app/features/panel/all.js index c03089bbf9b..96a119c8b48 100644 --- a/public/app/features/panel/all.js +++ b/public/app/features/panel/all.js @@ -3,6 +3,6 @@ define([ './panel_directive', './solo_panel_ctrl', './panel_loader', - './query_editor', + './query_ctrl', './panel_editor_tab', ], function () {}); diff --git a/public/app/features/panel/panel.ts b/public/app/features/panel/panel.ts index 3f98b77ff1b..634591801b6 100644 --- a/public/app/features/panel/panel.ts +++ b/public/app/features/panel/panel.ts @@ -5,11 +5,11 @@ import config from 'app/core/config'; import {PanelCtrl} from './panel_ctrl'; import {MetricsPanelCtrl} from './metrics_panel_ctrl'; import {PanelDirective} from './panel_directive'; -import {QueryEditorCtrl} from './query_editor'; +import {QueryCtrl} from './query_ctrl'; export { PanelCtrl, MetricsPanelCtrl, PanelDirective, - QueryEditorCtrl, + QueryCtrl, } diff --git a/public/app/features/panel/query_editor.ts b/public/app/features/panel/query_ctrl.ts similarity index 86% rename from public/app/features/panel/query_editor.ts rename to public/app/features/panel/query_ctrl.ts index a186266ecaf..5227febed2f 100644 --- a/public/app/features/panel/query_editor.ts +++ b/public/app/features/panel/query_ctrl.ts @@ -3,7 +3,7 @@ import angular from 'angular'; import _ from 'lodash'; -export class QueryEditorCtrl { +export class QueryCtrl { target: any; datasource: any; panelCtrl: any; @@ -27,22 +27,26 @@ export class QueryEditorCtrl { }); } - removeDataQuery(query) { - this.panel.targets = _.without(this.panel.targets, query); + removeQuery() { + this.panel.targets = _.without(this.panel.targets, this.target); this.panelCtrl.refresh(); }; - duplicateDataQuery(query) { - var clone = angular.copy(query); + duplicateQuery() { + var clone = angular.copy(this.target); clone.refId = this.getNextQueryLetter(); this.panel.targets.push(clone); } - moveDataQuery(direction) { + moveQuery(direction) { var index = _.indexOf(this.panel.targets, this.target); _.move(this.panel.targets, index, index + direction); } + refresh() { + this.panelCtrl.refresh(); + } + toggleHideQuery() { this.target.hide = !this.target.hide; this.panelCtrl.refresh(); diff --git a/public/app/plugins/datasource/graphite/datasource.d.ts b/public/app/plugins/datasource/graphite/datasource.d.ts deleted file mode 100644 index a50d7ca49cc..00000000000 --- a/public/app/plugins/datasource/graphite/datasource.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare var Datasource: any; -export default Datasource; - diff --git a/public/app/plugins/datasource/graphite/datasource.js b/public/app/plugins/datasource/graphite/datasource.js deleted file mode 100644 index 3a169eaae4e..00000000000 --- a/public/app/plugins/datasource/graphite/datasource.js +++ /dev/null @@ -1,296 +0,0 @@ -define([ - 'angular', - 'lodash', - 'jquery', - 'app/core/config', - 'app/core/utils/datemath', - './query_ctrl', - './func_editor', - './add_graphite_func', -], -function (angular, _, $, config, dateMath) { - 'use strict'; - - /** @ngInject */ - function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) { - this.basicAuth = instanceSettings.basicAuth; - this.url = instanceSettings.url; - this.name = instanceSettings.name; - this.cacheTimeout = instanceSettings.cacheTimeout; - this.withCredentials = instanceSettings.withCredentials; - this.render_method = instanceSettings.render_method || 'POST'; - - this.query = function(options) { - try { - var graphOptions = { - from: this.translateTime(options.rangeRaw.from, false), - until: this.translateTime(options.rangeRaw.to, true), - targets: options.targets, - format: options.format, - cacheTimeout: options.cacheTimeout || this.cacheTimeout, - maxDataPoints: options.maxDataPoints, - }; - - var params = this.buildGraphiteParams(graphOptions, options.scopedVars); - if (params.length === 0) { - return $q.when([]); - } - - if (options.format === 'png') { - return $q.when(this.url + '/render' + '?' + params.join('&')); - } - - var httpOptions = { method: this.render_method, url: '/render' }; - - if (httpOptions.method === 'GET') { - httpOptions.url = httpOptions.url + '?' + params.join('&'); - } - else { - httpOptions.data = params.join('&'); - httpOptions.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; - } - - return this.doGraphiteRequest(httpOptions).then(this.convertDataPointsToMs); - } - catch(err) { - return $q.reject(err); - } - }; - - this.convertDataPointsToMs = function(result) { - if (!result || !result.data) { return []; } - for (var i = 0; i < result.data.length; i++) { - var series = result.data[i]; - for (var y = 0; y < series.datapoints.length; y++) { - series.datapoints[y][1] *= 1000; - } - } - return result; - }; - - this.annotationQuery = function(options) { - // Graphite metric as annotation - if (options.annotation.target) { - var target = templateSrv.replace(options.annotation.target); - var graphiteQuery = { - rangeRaw: options.rangeRaw, - targets: [{ target: target }], - format: 'json', - maxDataPoints: 100 - }; - - return this.query(graphiteQuery) - .then(function(result) { - var list = []; - - for (var i = 0; i < result.data.length; i++) { - var target = result.data[i]; - - for (var y = 0; y < target.datapoints.length; y++) { - var datapoint = target.datapoints[y]; - if (!datapoint[0]) { continue; } - - list.push({ - annotation: options.annotation, - time: datapoint[1], - title: target.target - }); - } - } - - return list; - }); - } - // Graphite event as annotation - else { - var tags = templateSrv.replace(options.annotation.tags); - return this.events({range: options.rangeRaw, tags: tags}).then(function(results) { - var list = []; - for (var i = 0; i < results.data.length; i++) { - var e = results.data[i]; - - list.push({ - annotation: options.annotation, - time: e.when * 1000, - title: e.what, - tags: e.tags, - text: e.data - }); - } - return list; - }); - } - }; - - this.events = function(options) { - try { - var tags = ''; - if (options.tags) { - tags = '&tags=' + options.tags; - } - - return this.doGraphiteRequest({ - method: 'GET', - url: '/events/get_data?from=' + this.translateTime(options.range.from, false) + - '&until=' + this.translateTime(options.range.to, true) + tags, - }); - } - catch(err) { - return $q.reject(err); - } - }; - - this.translateTime = function(date, roundUp) { - if (_.isString(date)) { - if (date === 'now') { - return 'now'; - } - else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) { - date = date.substring(3); - date = date.replace('m', 'min'); - date = date.replace('M', 'mon'); - return date; - } - date = dateMath.parse(date, roundUp); - } - - // graphite' s from filter is exclusive - // here we step back one minute in order - // to guarantee that we get all the data that - // exists for the specified range - if (roundUp) { - if (date.get('s')) { - date.add(1, 'm'); - } - } - else if (roundUp === false) { - if (date.get('s')) { - date.subtract(1, 'm'); - } - } - - return date.unix(); - }; - - this.metricFindQuery = function(query) { - var interpolated; - try { - interpolated = encodeURIComponent(templateSrv.replace(query)); - } - catch(err) { - return $q.reject(err); - } - - return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated }) - .then(function(results) { - return _.map(results.data, function(metric) { - return { - text: metric.text, - expandable: metric.expandable ? true : false - }; - }); - }); - }; - - this.testDatasource = function() { - return this.metricFindQuery('*').then(function () { - return { status: "success", message: "Data source is working", title: "Success" }; - }); - }; - - this.listDashboards = function(query) { - return this.doGraphiteRequest({ method: 'GET', url: '/dashboard/find/', params: {query: query || ''} }) - .then(function(results) { - return results.data.dashboards; - }); - }; - - this.loadDashboard = function(dashName) { - return this.doGraphiteRequest({method: 'GET', url: '/dashboard/load/' + encodeURIComponent(dashName) }); - }; - - this.doGraphiteRequest = function(options) { - if (this.basicAuth || this.withCredentials) { - options.withCredentials = true; - } - if (this.basicAuth) { - options.headers = options.headers || {}; - options.headers.Authorization = this.basicAuth; - } - - options.url = this.url + options.url; - options.inspect = { type: 'graphite' }; - - return backendSrv.datasourceRequest(options); - }; - - this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - - this.buildGraphiteParams = function(options, scopedVars) { - var graphite_options = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout']; - var clean_options = [], targets = {}; - var target, targetValue, i; - var regex = /\#([A-Z])/g; - var intervalFormatFixRegex = /'(\d+)m'/gi; - var hasTargets = false; - - if (options.format !== 'png') { - options['format'] = 'json'; - } - - function fixIntervalFormat(match) { - return match.replace('m', 'min').replace('M', 'mon'); - } - - for (i = 0; i < options.targets.length; i++) { - target = options.targets[i]; - if (!target.target) { - continue; - } - - if (!target.refId) { - target.refId = this._seriesRefLetters[i]; - } - - targetValue = templateSrv.replace(target.target, scopedVars); - targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat); - targets[target.refId] = targetValue; - } - - function nestedSeriesRegexReplacer(match, g1) { - return targets[g1]; - } - - for (i = 0; i < options.targets.length; i++) { - target = options.targets[i]; - if (!target.target) { - continue; - } - - targetValue = targets[target.refId]; - targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer); - targets[target.refId] = targetValue; - - if (!target.hide) { - hasTargets = true; - clean_options.push("target=" + encodeURIComponent(targetValue)); - } - } - - _.each(options, function (value, key) { - if ($.inArray(key, graphite_options) === -1) { return; } - if (value) { - clean_options.push(key + "=" + encodeURIComponent(value)); - } - }); - - if (!hasTargets) { - return []; - } - - return clean_options; - }; - } - - return GraphiteDatasource; -}); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts new file mode 100644 index 00000000000..c57e5fcfd26 --- /dev/null +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -0,0 +1,281 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; +import moment from 'moment'; + +import * as dateMath from 'app/core/utils/datemath'; + +/** @ngInject */ +export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) { + this.basicAuth = instanceSettings.basicAuth; + this.url = instanceSettings.url; + this.name = instanceSettings.name; + this.cacheTimeout = instanceSettings.cacheTimeout; + this.withCredentials = instanceSettings.withCredentials; + this.render_method = instanceSettings.render_method || 'POST'; + + this.query = function(options) { + try { + var graphOptions = { + from: this.translateTime(options.rangeRaw.from, false), + until: this.translateTime(options.rangeRaw.to, true), + targets: options.targets, + format: options.format, + cacheTimeout: options.cacheTimeout || this.cacheTimeout, + maxDataPoints: options.maxDataPoints, + }; + + var params = this.buildGraphiteParams(graphOptions, options.scopedVars); + if (params.length === 0) { + return $q.when([]); + } + + if (options.format === 'png') { + return $q.when(this.url + '/render' + '?' + params.join('&')); + } + + var httpOptions: any = {method: this.render_method, url: '/render'}; + + if (httpOptions.method === 'GET') { + httpOptions.url = httpOptions.url + '?' + params.join('&'); + } else { + httpOptions.data = params.join('&'); + httpOptions.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + + return this.doGraphiteRequest(httpOptions).then(this.convertDataPointsToMs); + } catch (err) { + return $q.reject(err); + } + }; + + this.convertDataPointsToMs = function(result) { + if (!result || !result.data) { return []; } + for (var i = 0; i < result.data.length; i++) { + var series = result.data[i]; + for (var y = 0; y < series.datapoints.length; y++) { + series.datapoints[y][1] *= 1000; + } + } + return result; + }; + + this.annotationQuery = function(options) { + // Graphite metric as annotation + if (options.annotation.target) { + var target = templateSrv.replace(options.annotation.target); + var graphiteQuery = { + rangeRaw: options.rangeRaw, + targets: [{ target: target }], + format: 'json', + maxDataPoints: 100 + }; + + return this.query(graphiteQuery) + .then(function(result) { + var list = []; + + for (var i = 0; i < result.data.length; i++) { + var target = result.data[i]; + + for (var y = 0; y < target.datapoints.length; y++) { + var datapoint = target.datapoints[y]; + if (!datapoint[0]) { continue; } + + list.push({ + annotation: options.annotation, + time: datapoint[1], + title: target.target + }); + } + } + + return list; + }); + } else { + // Graphite event as annotation + var tags = templateSrv.replace(options.annotation.tags); + return this.events({range: options.rangeRaw, tags: tags}).then(function(results) { + var list = []; + for (var i = 0; i < results.data.length; i++) { + var e = results.data[i]; + + list.push({ + annotation: options.annotation, + time: e.when * 1000, + title: e.what, + tags: e.tags, + text: e.data + }); + } + return list; + }); + } + }; + + this.events = function(options) { + try { + var tags = ''; + if (options.tags) { + tags = '&tags=' + options.tags; + } + + return this.doGraphiteRequest({ + method: 'GET', + url: '/events/get_data?from=' + this.translateTime(options.range.from, false) + + '&until=' + this.translateTime(options.range.to, true) + tags, + }); + } catch (err) { + return $q.reject(err); + } + }; + + this.translateTime = function(date, roundUp) { + if (_.isString(date)) { + if (date === 'now') { + return 'now'; + } else if (date.indexOf('now-') >= 0 && date.indexOf('/') === -1) { + date = date.substring(3); + date = date.replace('m', 'min'); + date = date.replace('M', 'mon'); + return date; + } + date = dateMath.parse(date, roundUp); + } + + // graphite' s from filter is exclusive + // here we step back one minute in order + // to guarantee that we get all the data that + // exists for the specified range + if (roundUp) { + if (date.get('s')) { + date.add(1, 'm'); + } + } else if (roundUp === false) { + if (date.get('s')) { + date.subtract(1, 'm'); + } + } + + return date.unix(); + }; + + this.metricFindQuery = function(query) { + var interpolated; + try { + interpolated = encodeURIComponent(templateSrv.replace(query)); + } catch (err) { + return $q.reject(err); + } + + return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated }) + .then(function(results) { + return _.map(results.data, function(metric) { + return { + text: metric.text, + expandable: metric.expandable ? true : false + }; + }); + }); + }; + + this.testDatasource = function() { + return this.metricFindQuery('*').then(function () { + return { status: "success", message: "Data source is working", title: "Success" }; + }); + }; + + this.listDashboards = function(query) { + return this.doGraphiteRequest({ method: 'GET', url: '/dashboard/find/', params: {query: query || ''} }) + .then(function(results) { + return results.data.dashboards; + }); + }; + + this.loadDashboard = function(dashName) { + return this.doGraphiteRequest({method: 'GET', url: '/dashboard/load/' + encodeURIComponent(dashName) }); + }; + + this.doGraphiteRequest = function(options) { + if (this.basicAuth || this.withCredentials) { + options.withCredentials = true; + } + if (this.basicAuth) { + options.headers = options.headers || {}; + options.headers.Authorization = this.basicAuth; + } + + options.url = this.url + options.url; + options.inspect = { type: 'graphite' }; + + return backendSrv.datasourceRequest(options); + }; + + this._seriesRefLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + this.buildGraphiteParams = function(options, scopedVars) { + var graphite_options = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout']; + var clean_options = [], targets = {}; + var target, targetValue, i; + var regex = /\#([A-Z])/g; + var intervalFormatFixRegex = /'(\d+)m'/gi; + var hasTargets = false; + + if (options.format !== 'png') { + options['format'] = 'json'; + } + + function fixIntervalFormat(match) { + return match.replace('m', 'min').replace('M', 'mon'); + } + + for (i = 0; i < options.targets.length; i++) { + target = options.targets[i]; + if (!target.target) { + continue; + } + + if (!target.refId) { + target.refId = this._seriesRefLetters[i]; + } + + targetValue = templateSrv.replace(target.target, scopedVars); + targetValue = targetValue.replace(intervalFormatFixRegex, fixIntervalFormat); + targets[target.refId] = targetValue; + } + + function nestedSeriesRegexReplacer(match, g1) { + return targets[g1]; + } + + for (i = 0; i < options.targets.length; i++) { + target = options.targets[i]; + if (!target.target) { + continue; + } + + targetValue = targets[target.refId]; + targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer); + targets[target.refId] = targetValue; + + if (!target.hide) { + hasTargets = true; + clean_options.push("target=" + encodeURIComponent(targetValue)); + } + } + + _.each(options, function (value, key) { + if (_.indexOf(graphite_options, key) === -1) { return; } + if (value) { + clean_options.push(key + "=" + encodeURIComponent(value)); + } + }); + + if (!hasTargets) { + return []; + } + + return clean_options; + }; +} diff --git a/public/app/plugins/datasource/graphite/lexer.js b/public/app/plugins/datasource/graphite/lexer.js deleted file mode 100644 index 2b8affd04e2..00000000000 --- a/public/app/plugins/datasource/graphite/lexer.js +++ /dev/null @@ -1,682 +0,0 @@ -define([ - 'lodash' -], function(_) { - 'use strict'; - - // 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 - - var 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 - ]; - - var identifierStartTable = []; - - for (var 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 - } - - var identifierPartTable = []; - - for (var i2 = 0; i2 < 128; i2++) { - identifierPartTable[i2] = - identifierStartTable[i2] || // $, _, A-Z, a-z - i2 >= 48 && i2 <= 57; // 0-9 - } - - function Lexer(expression) { - this.input = expression; - this.char = 1; - this.from = 1; - } - - Lexer.prototype = { - - peek: function (i) { - return this.input.charAt(i || 0); - }, - - skip: function (i) { - i = i || 1; - this.char += i; - this.input = this.input.slice(i); - }, - - tokenize: function() { - var list = []; - var token; - while (token = this.next()) { - list.push(token); - } - return list; - }, - - next: function() { - this.from = this.char; - - // Move to the next non-space character. - var start; - if (/\s/.test(this.peek())) { - start = this.char; - - while (/\s/.test(this.peek())) { - this.from += 1; - this.skip(); - } - - if (this.peek() === "") { // EOL - return null; - } - } - - var 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: function() { - 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: function() { - var id = ""; - var index = 0; - var 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) { - for (var i = 0; i < unicodeLetterTable.length;) { - if (code < unicodeLetterTable[i++]) { - return false; - } - - if (code <= unicodeLetterTable[i++]) { - return true; - } - } - - return false; - } - - function isHexDigit(str) { - return (/^[0-9a-fA-F]$/).test(str); - } - - var readUnicodeEscapeSequence = _.bind(function () { - /*jshint validthis:true */ - index += 1; - - if (this.peek(index) !== "u") { - return null; - } - - var ch1 = this.peek(index + 1); - var ch2 = this.peek(index + 2); - var ch3 = this.peek(index + 3); - var ch4 = this.peek(index + 4); - var 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); - - var getIdentifierStart = _.bind(function () { - /*jshint validthis:true */ - var chr = this.peek(index); - var 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); - - var getIdentifierPart = _.bind(function () { - /*jshint validthis:true */ - var chr = this.peek(index); - var 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: function () { - var index = 0; - var value = ""; - var length = this.input.length; - var char = this.peek(index); - var bad; - - function isDecimalDigit(str) { - return (/^[0-9]$/).test(str); - } - - function isOctalDigit(str) { - return (/^[0-7]$/).test(str); - } - - function isHexDigit(str) { - return (/^[0-9a-fA-F]$/).test(str); - } - - function isIdentifierStart(ch) { - 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; - } 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; - 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 { - 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 { - 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 - }; - }, - - }; - - return Lexer; - -}); diff --git a/public/app/plugins/datasource/graphite/lexer.ts b/public/app/plugins/datasource/graphite/lexer.ts new file mode 100644 index 00000000000..1835921d40b --- /dev/null +++ b/public/app/plugins/datasource/graphite/lexer.ts @@ -0,0 +1,678 @@ +/// + +import _ 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 + +var 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 +]; + +var identifierStartTable = []; + +for (var 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 +} + +var identifierPartTable = []; + +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) { + this.input = expression; + this.char = 1; + this.from = 1; +} + +Lexer.prototype = { + + peek: function (i) { + return this.input.charAt(i || 0); + }, + + skip: function (i) { + i = i || 1; + this.char += i; + this.input = this.input.slice(i); + }, + + tokenize: function() { + var list = []; + var token; + while (token = this.next()) { + list.push(token); + } + return list; + }, + + next: function() { + this.from = this.char; + + // Move to the next non-space character. + var start; + if (/\s/.test(this.peek())) { + start = this.char; + + while (/\s/.test(this.peek())) { + this.from += 1; + this.skip(); + } + + if (this.peek() === "") { // EOL + return null; + } + } + + var 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: function() { + 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: function() { + var id = ""; + var index = 0; + var 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) { + for (var i = 0; i < unicodeLetterTable.length;) { + if (code < unicodeLetterTable[i++]) { + return false; + } + + if (code <= unicodeLetterTable[i++]) { + return true; + } + } + + return false; + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + var readUnicodeEscapeSequence = _.bind(function () { + /*jshint validthis:true */ + index += 1; + + if (this.peek(index) !== "u") { + return null; + } + + var ch1 = this.peek(index + 1); + var ch2 = this.peek(index + 2); + var ch3 = this.peek(index + 3); + var ch4 = this.peek(index + 4); + var 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); + + var getIdentifierStart = _.bind(function () { + /*jshint validthis:true */ + var chr = this.peek(index); + var 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); + + var getIdentifierPart = _.bind(function () { + /*jshint validthis:true */ + var chr = this.peek(index); + var 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: function (): any { + var index = 0; + var value = ""; + var length = this.input.length; + var char = this.peek(index); + var bad; + + function isDecimalDigit(str) { + return (/^[0-9]$/).test(str); + } + + function isOctalDigit(str) { + return (/^[0-7]$/).test(str); + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + function isIdentifierStart(ch) { + 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; + } 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; + 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 { + 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 { + 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 + }; + }, + + }; + diff --git a/public/app/plugins/datasource/graphite/module.js b/public/app/plugins/datasource/graphite/module.js deleted file mode 100644 index a1d4ed5fc2b..00000000000 --- a/public/app/plugins/datasource/graphite/module.js +++ /dev/null @@ -1,38 +0,0 @@ -define([ - './datasource', -], -function (GraphiteDatasource) { - 'use strict'; - - function metricsQueryEditor() { - return { - controller: 'GraphiteQueryCtrl', - templateUrl: 'public/app/plugins/datasource/graphite/partials/query.editor.html' - }; - } - - function metricsQueryOptions() { - return {templateUrl: 'public/app/plugins/datasource/graphite/partials/query.options.html'}; - } - - function annotationsQueryEditor() { - return {templateUrl: 'public/app/plugins/datasource/graphite/partials/annotations.editor.html'}; - } - - function configView() { - return {templateUrl: 'public/app/plugins/datasource/graphite/partials/config.html'}; - } - - function ConfigView() { - } - ConfigView.templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html'; - - return { - Datasource: GraphiteDatasource, - configView: configView, - annotationsQueryEditor: annotationsQueryEditor, - metricsQueryEditor: metricsQueryEditor, - metricsQueryOptions: metricsQueryOptions, - ConfigView: ConfigView - }; -}); diff --git a/public/app/plugins/datasource/graphite/module.ts b/public/app/plugins/datasource/graphite/module.ts new file mode 100644 index 00000000000..abab560aafd --- /dev/null +++ b/public/app/plugins/datasource/graphite/module.ts @@ -0,0 +1,51 @@ +import {GraphiteDatasource} from './datasource'; +import {GraphiteQueryCtrl} from './query_ctrl'; + +class GraphiteConfigView { + static templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html'; +} + +export { + GraphiteDatasource as Datasource, + GraphiteQueryCtrl as QueryCtrl, + GraphiteConfigView as ConfigView +}; + +// define([ +// './datasource', +// ], +// function (GraphiteDatasource) { +// 'use strict'; +// +// function metricsQueryEditor() { +// return { +// controller: 'GraphiteQueryCtrl', +// templateUrl: 'public/app/plugins/datasource/graphite/partials/query.editor.html' +// }; +// } +// +// function metricsQueryOptions() { +// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/query.options.html'}; +// } +// +// function annotationsQueryEditor() { +// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/annotations.editor.html'}; +// } +// +// function configView() { +// return {templateUrl: 'public/app/plugins/datasource/graphite/partials/config.html'}; +// } +// +// function ConfigView() { +// } +// ConfigView.templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html'; +// +// return { +// Datasource: GraphiteDatasource, +// configView: configView, +// annotationsQueryEditor: annotationsQueryEditor, +// metricsQueryEditor: metricsQueryEditor, +// metricsQueryOptions: metricsQueryOptions, +// ConfigView: ConfigView +// }; +// }); diff --git a/public/app/plugins/datasource/graphite/parser.js b/public/app/plugins/datasource/graphite/parser.js deleted file mode 100644 index 43fd148f1ea..00000000000 --- a/public/app/plugins/datasource/graphite/parser.js +++ /dev/null @@ -1,265 +0,0 @@ -define([ - './lexer' -], function (Lexer) { - 'use strict'; - - 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 = { - 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)); - }, - - }; - - return Parser; -}); diff --git a/public/app/plugins/datasource/graphite/parser.ts b/public/app/plugins/datasource/graphite/parser.ts new file mode 100644 index 00000000000..bfafdda7815 --- /dev/null +++ b/public/app/plugins/datasource/graphite/parser.ts @@ -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)); + }, +}; + diff --git a/public/app/plugins/datasource/graphite/partials/query.editor.html b/public/app/plugins/datasource/graphite/partials/query.editor.html index 9cb2b454132..2557dfbf0a2 100755 --- a/public/app/plugins/datasource/graphite/partials/query.editor.html +++ b/public/app/plugins/datasource/graphite/partials/query.editor.html @@ -1,15 +1,15 @@
  • - +
  • @@ -45,24 +45,24 @@
    • - {{target.refId}} + {{ctrl.target.refId}}
    • - +
    - + -