diff --git a/lib/syntax/parse.js b/lib/syntax/parse.js index 5c1466a2..a9ebd753 100644 --- a/lib/syntax/parse.js +++ b/lib/syntax/parse.js @@ -20,6 +20,10 @@ function readWord(str, start) { end++; } + if (start === end) { + error(str, start, 'Expect a keyword'); + } + return str.substring(start, end); } @@ -111,14 +115,12 @@ function readMultiplier(str, pos) { if (charAt(str, pos) === '{') { var range = readMultiplierRange(str, pos); - if (range) { - return { - comma: true, - min: range.min, - max: range.max, - value: str.substring(start, range.pos) - }; - } + return { + comma: true, + min: range.min, + max: range.max, + value: str.substring(start, range.pos) + }; } return { @@ -132,15 +134,12 @@ function readMultiplier(str, pos) { var start = pos; var range = readMultiplierRange(str, pos); - if (range) { - return { - comma: false, - min: range.min, - max: range.max, - value: str.substring(start, range.pos) - }; - } - break; + return { + comma: false, + min: range.min, + max: range.max, + value: str.substring(start, range.pos) + }; } return { @@ -159,7 +158,7 @@ function readProperty(str, start) { end += eat(str, end, '<'); end += eat(str, end, '\''); - name = readWord(str, end) || error(str, end); + name = readWord(str, end); end += name.length; end += eat(str, end, '\''); @@ -185,7 +184,7 @@ function readType(str, start) { end += eat(str, end, '<'); - name = readWord(str, end) || error(str, end); + name = readWord(str, end); end += name.length; if (charAt(str, end) === '(' && charAt(str, end + 1) === ')') { @@ -214,7 +213,7 @@ function readKeywordOrFunction(str, start) { var multiplier; var name; - name = readWord(str, end) || error(str, end); + name = readWord(str, end); end += name.length; if (charAt(str, end) === '(') { @@ -300,7 +299,7 @@ function readSequence(str, start) { if (token.type === 'Combinator') { // check for combinator in group beginning and double combinator sequence if (!lastToken || lastToken.type === 'Combinator') { - error(str, end); + error(str, end, 'Unexpected combinator'); } combinators[token.value] = true; @@ -321,7 +320,7 @@ function readSequence(str, start) { // check for combinator in group ending if (lastToken && lastToken.type === 'Combinator') { - error(str, end - lastToken.value.length); + error(str, end - lastToken.value.length, 'Unexpected combinator'); } return { @@ -409,13 +408,12 @@ function peek(str, start) { }; case '&': - if (charAt(str, end) === '&') { - return { - type: 'Combinator', - value: '&&' - }; - } - break; + eat(str, end, '&'); + + return { + type: 'Combinator', + value: '&&' + }; case ',': return { diff --git a/test/syntax.js b/test/syntax.js index 29f9dd6d..6cec3301 100755 --- a/test/syntax.js +++ b/test/syntax.js @@ -55,6 +55,73 @@ describe('CSS syntax', function() { assert.equal(stringify(ast, true), '[ [ a b ] | [ c || [ d && [ e f ] ] ] ]'); }); + describe('bad syntax', function() { + it('expected a quote', function() { + assert.throws(function() { + parse('\'x'); + }, /^SyntaxParseError: Expect a quote\n/); + }); + + it('expected a number', function() { + var tests = [ + '{}', + '{,2}', + '{ 2}', + '{1, }' + ]; + tests.forEach(function(test) { + assert.throws(function() { + parse(test); + }, /^SyntaxParseError: Expect a number\n/, test); + }); + }); + + it('missed keyword', function() { + var tests = [ + '<>', + '<\'\'>' + ]; + tests.forEach(function(test) { + assert.throws(function() { + parse(test); + }, /^SyntaxParseError: Expect a keyword\n/, test); + }); + }); + + it('unexpected combinator', function() { + var tests = [ + '&&', + '&&', + '&&||' + ]; + tests.forEach(function(test) { + assert.throws(function() { + parse(test); + }, /^SyntaxParseError: Unexpected combinator\n/, test); + }); + }); + + it('unexpected input', function() { + assert.throws(function() { + parse('!'); + }, /^SyntaxParseError: Unexpected input\n/); + }); + + it('bad syntax', function() { + var tests = [ + 'a&b', + '