From 2acfa83b76f00b67f36a36bc7408202f9604cd9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 28 Dec 2013 11:40:58 +0100 Subject: [PATCH] fixed sorting of functions, and lexing parsing fix for unclosed string params --- src/app/controllers/graphiteTarget.js | 4 +-- src/app/services/graphite/graphiteFuncs.js | 4 +++ src/app/services/graphite/lexer.js | 9 ++++++ src/app/services/graphite/parser.js | 36 +++++++++++----------- src/test/specs/lexer-specs.js | 13 ++++++++ src/test/specs/parser-specs.js | 21 +++++++------ 6 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js index 03f960fd006..e5997efdd3f 100644 --- a/src/app/controllers/graphiteTarget.js +++ b/src/app/controllers/graphiteTarget.js @@ -27,8 +27,8 @@ function (angular, _, config, graphiteFuncs, Parser) { var parser = new Parser($scope.target.target); var astNode = parser.getAst(); - if (parser.error) { - $scope.parserError = parser.error.text + " at position: " + parser.error.pos; + if (astNode.type === 'error') { + $scope.parserError = astNode.message + " at position: " + astNode.pos; $scope.showTextEditor = true; return; } diff --git a/src/app/services/graphite/graphiteFuncs.js b/src/app/services/graphite/graphiteFuncs.js index 7bd3762d00d..890632732f8 100644 --- a/src/app/services/graphite/graphiteFuncs.js +++ b/src/app/services/graphite/graphiteFuncs.js @@ -124,6 +124,10 @@ function (_) { defaultParams: ['1d'] }); + _.each(categories, function(funcList, catName) { + categories[catName] = _.sortBy(funcList, 'name'); + }); + function FuncInstance(funcDef) { this.def = funcDef; this.params = funcDef.defaultParams.slice(0); diff --git a/src/app/services/graphite/lexer.js b/src/app/services/graphite/lexer.js index 8ef0e186e89..482e7f7c635 100644 --- a/src/app/services/graphite/lexer.js +++ b/src/app/services/graphite/lexer.js @@ -591,6 +591,15 @@ define([ 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 diff --git a/src/app/services/graphite/parser.js b/src/app/services/graphite/parser.js index 11da206e695..1703aa7a4fa 100644 --- a/src/app/services/graphite/parser.js +++ b/src/app/services/graphite/parser.js @@ -3,25 +3,13 @@ define([ ], function (Lexer) { 'use strict'; - var NodeTypes = { - MetricExpression: 1, - MetricNode: 2, - FunctionCall: 4, - NumericLiteral: 5, - StringLiteral: 6 - }; - function Parser(expression) { this.expression = expression; this.lexer = new Lexer(expression); - this.state = "start"; - this.error = null; this.tokens = this.lexer.tokenize(); this.index = 0; } - Parser.Nodes = NodeTypes; - Parser.prototype = { getAst: function () { @@ -29,7 +17,16 @@ define([ }, start: function () { - return this.functionCall() || this.metricExpression(); + try { + return this.functionCall() || this.metricExpression(); + } + catch(e) { + return { + type: 'error', + message: e.message, + pos: e.pos + }; + } }, metricExpression: function() { @@ -52,7 +49,6 @@ define([ var rest = this.metricExpression(); if (!rest) { this.errorMark('Expected metric identifier'); - return null; } node.segments = node.segments.concat(rest.segments); @@ -77,7 +73,6 @@ define([ if (!this.match(')')) { this.errorMark('Expected closing paranthesis'); - return null; } this.index++; @@ -122,19 +117,24 @@ define([ return null; } + var token = this.tokens[this.index]; + if (token.isUnclosed) { + throw { message: 'Unclosed string parameter', pos: token.pos }; + } + this.index++; return { type: 'string', - value: this.tokens[this.index-1].value + value: token.value }; }, errorMark: function(text) { var currentToken = this.tokens[this.index]; var type = currentToken ? currentToken.type : 'end of string'; - this.error = { - text: text + " instead found " + type, + throw { + message: text + " instead found " + type, pos: currentToken ? currentToken.pos : this.lexer.char }; }, diff --git a/src/test/specs/lexer-specs.js b/src/test/specs/lexer-specs.js index ba01a584e56..21cc6a7e46c 100644 --- a/src/test/specs/lexer-specs.js +++ b/src/test/specs/lexer-specs.js @@ -30,6 +30,19 @@ define([ expect(tokens[tokens.length - 1].value).to.be(')'); }); + it('should handle error with unterminated string', function() { + var lexer = new Lexer("alias(metric, 'asd)"); + var tokens = lexer.tokenize(); + expect(tokens[0].value).to.be('alias'); + expect(tokens[1].value).to.be('('); + expect(tokens[2].value).to.be('metric'); + expect(tokens[3].value).to.be(','); + expect(tokens[4].type).to.be('string'); + expect(tokens[4].isUnclosed).to.be(true); + expect(tokens[4].pos).to.be(20); + }); + + }); }); diff --git a/src/test/specs/parser-specs.js b/src/test/specs/parser-specs.js index 7565de3a0d5..d8a72456871 100644 --- a/src/test/specs/parser-specs.js +++ b/src/test/specs/parser-specs.js @@ -8,7 +8,6 @@ define([ var parser = new Parser('metric.test.*.asd.count'); var rootNode = parser.getAst(); - expect(parser.error).to.be(null); expect(rootNode.type).to.be('metric'); expect(rootNode.segments.length).to.be(5); expect(rootNode.segments[0].value).to.be('metric'); @@ -17,7 +16,6 @@ define([ it('simple function', function() { var parser = new Parser('sum(test)'); var rootNode = parser.getAst(); - expect(parser.error).to.be(null); expect(rootNode.type).to.be('function'); expect(rootNode.params.length).to.be(1); }); @@ -26,7 +24,6 @@ define([ var parser = new Parser("sum(test, 1, 'test')"); var rootNode = parser.getAst(); - expect(parser.error).to.be(null); expect(rootNode.type).to.be('function'); expect(rootNode.params.length).to.be(3); expect(rootNode.params[0].type).to.be('metric'); @@ -38,7 +35,6 @@ define([ var parser = new Parser("sum(scaleToSeconds(test, 1))"); var rootNode = parser.getAst(); - expect(parser.error).to.be(null); expect(rootNode.type).to.be('function'); expect(rootNode.params.length).to.be(1); expect(rootNode.params[0].type).to.be('function'); @@ -52,7 +48,6 @@ define([ var parser = new Parser("sum(test.test.*.count, test.timers.*.count)"); var rootNode = parser.getAst(); - expect(parser.error).to.be(null); expect(rootNode.type).to.be('function'); expect(rootNode.params.length).to.be(2); expect(rootNode.params[0].type).to.be('metric'); @@ -63,16 +58,24 @@ define([ var parser = new Parser('metric.test.*.asd.'); var rootNode = parser.getAst(); - expect(parser.error.text).to.be('Expected metric identifier instead found end of string'); - expect(parser.error.pos).to.be(19); + expect(rootNode.message).to.be('Expected metric identifier instead found end of string'); + expect(rootNode.pos).to.be(19); }); it('invalid function expression missing closing paranthesis', function() { var parser = new Parser('sum(test'); var rootNode = parser.getAst(); - expect(parser.error.text).to.be('Expected closing paranthesis instead found end of string'); - expect(parser.error.pos).to.be(9); + expect(rootNode.message).to.be('Expected closing paranthesis instead found end of string'); + expect(rootNode.pos).to.be(9); + }); + + it('unclosed string in function', function() { + var parser = new Parser("sum('test)"); + var rootNode = parser.getAst(); + + expect(rootNode.message).to.be('Unclosed string parameter'); + expect(rootNode.pos).to.be(11); }); });