Comparing version 1.0.0-alpha7 to 1.0.0-alpha8
@@ -1,2 +0,3 @@ | ||
var mozilla = require('./mozilla-cssdata.json'); | ||
var mdnProperties = require('./mdn-data-properties.json'); | ||
var mdnSyntaxes = require('./mdn-data-syntaxes.json'); | ||
var patch = require('./patch.json'); | ||
@@ -12,2 +13,3 @@ var data = { | ||
.replace(/>/g, '>') | ||
.replace(/ /g, ' ') | ||
.replace(/&/g, '&'); | ||
@@ -18,6 +20,10 @@ } | ||
for (var key in patch.properties) { | ||
if (key in mozilla.properties) { | ||
mozilla.properties[key].syntax = patch.properties[key].syntax; | ||
if (key in mdnProperties) { | ||
if (patch.properties[key]) { | ||
mdnProperties[key].syntax = patch.properties[key].syntax; | ||
} else { | ||
delete mdnProperties[key]; | ||
} | ||
} else { | ||
mozilla.properties[key] = patch.properties[key]; | ||
mdnProperties[key] = patch.properties[key]; | ||
} | ||
@@ -28,17 +34,17 @@ } | ||
if (patch.syntaxes[key].syntax) { | ||
mozilla.syntaxes[key] = patch.syntaxes[key].syntax; | ||
mdnSyntaxes[key] = patch.syntaxes[key].syntax; | ||
} else { | ||
delete mozilla.syntaxes[key]; | ||
delete mdnSyntaxes[key]; | ||
} | ||
} | ||
// normalize source mozilla syntaxes, since it uses html token | ||
for (var key in mozilla.properties) { | ||
data.properties[key] = normalizeSyntax(mozilla.properties[key].syntax); | ||
// normalize source mdnProperties syntaxes, since it uses html token | ||
for (var key in mdnProperties) { | ||
data.properties[key] = normalizeSyntax(mdnProperties[key].syntax); | ||
} | ||
for (var key in mozilla.syntaxes) { | ||
data.types[key] = normalizeSyntax(mozilla.syntaxes[key]); | ||
for (var key in mdnSyntaxes) { | ||
data.types[key] = normalizeSyntax(mdnSyntaxes[key]); | ||
} | ||
module.exports = data; |
{ | ||
"properties": { | ||
"--*": null, | ||
"-moz-background-clip": { | ||
@@ -122,9 +123,5 @@ "comment": "deprecated syntax in old Firefox, https://developer.mozilla.org/en/docs/Web/CSS/background-clip", | ||
}, | ||
"-webkit-mask-attachment": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<attachment> [, <attachment> ]*" | ||
}, | ||
"-webkit-mask-box-image": { | ||
"comment": "missed; https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask-box-image", | ||
"syntax": "[ <uri> | <gradient> | none ] [ <length-percentage>{4} <-webkit-mask-box-repeat>{2} ]?" | ||
"syntax": "[ <url> | <gradient> | none ] [ <length-percentage>{4} <-webkit-mask-box-repeat>{2} ]?" | ||
}, | ||
@@ -135,26 +132,2 @@ "-webkit-mask-clip": { | ||
}, | ||
"-webkit-mask-composite": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<composite-style> [, <composite-style> ]*" | ||
}, | ||
"-webkit-mask-image": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<mask-image> [, <mask-image> ]*" | ||
}, | ||
"-webkit-mask-origin": { | ||
"comment": "missed spaces between brackets, extra space between [ and ,", | ||
"syntax": "[ padding | border | content ] [, [ border | padding | content ] ]*" | ||
}, | ||
"-webkit-mask-position": { | ||
"comment": "# instead of *, extra space between [ and ,", | ||
"syntax": "<mask-position> [, <mask-position> ]*" | ||
}, | ||
"-webkit-mask-position-y": { | ||
"comment": "extra space after `top`", | ||
"syntax": "[ <length-percentage> | top | center | bottom ]#" | ||
}, | ||
"-webkit-mask-repeat": { | ||
"comment": "extra space between [ and ,", | ||
"syntax": "<repeat-style> [, <repeat-style> ]*" | ||
}, | ||
"-webkit-overflow-scrolling": { | ||
@@ -171,6 +144,2 @@ "comment": "missed; https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling", | ||
}, | ||
"-webkit-tap-highlight-color": { | ||
"comment": "broken syntax", | ||
"syntax": "<color>#" | ||
}, | ||
"-webkit-text-security": { | ||
@@ -195,25 +164,2 @@ "comment": "missed; http://help.dottoro.com/lcbkewgt.php", | ||
}, | ||
"animation-timing-function": { | ||
"comment": "<timing-function> -> <single-timing-function> https://drafts.csswg.org/css-animations/#animation-timing-function", | ||
"syntax": "<single-timing-function>#" | ||
}, | ||
"appearance": { | ||
"comment": "CSS Basic User Interface Module Level 4", | ||
"references": [ | ||
"https://drafts.csswg.org/css-ui-4/#appearance-switching" | ||
], | ||
"syntax": "auto | none" | ||
}, | ||
"azimuth": { | ||
"comment": "missed space", | ||
"syntax": "<angle> | [ [ left-side | far-left | left | center-left | center | center-right | right | far-right | right-side ] || behind ] | leftwards | rightwards" | ||
}, | ||
"background-position-x": { | ||
"comment": "added missed multiplicator `?` https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands", | ||
"syntax": "[ center | [ left | right | x-start | x-end ]? <length-percentage>? ]#" | ||
}, | ||
"background-position-y": { | ||
"comment": "added missed multiplicator `?` https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands", | ||
"syntax": "[ center | [ top | bottom | y-start | y-end ]? <length-percentage>? ]#" | ||
}, | ||
"baseline-shift": { | ||
@@ -247,7 +193,7 @@ "comment": "added SVG property", | ||
"comment": "https://www.w3.org/TR/css3-speech/#property-index", | ||
"syntax": "<uri> <decibel>? | none" | ||
"syntax": "<url> <decibel>? | none" | ||
}, | ||
"cue-before": { | ||
"comment": "https://www.w3.org/TR/css3-speech/#property-index", | ||
"syntax": "<uri> <decibel>? | none" | ||
"syntax": "<url> <decibel>? | none" | ||
}, | ||
@@ -257,3 +203,3 @@ "cursor": { | ||
"refenrences": ["https://www.sitepoint.com/css3-cursor-styles/"], | ||
"syntax": "[ [ <uri> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing | hand | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out | -moz-grab | -moz-grabbing | -moz-zoom-in | -moz-zoom-out ] ]" | ||
"syntax": "[ [ <url> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing | hand | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out | -moz-grab | -moz-grabbing | -moz-zoom-in | -moz-zoom-out ] ]" | ||
}, | ||
@@ -305,17 +251,5 @@ "display": { | ||
"font": { | ||
"comment": "wrong quotes", | ||
"comment": "extend with non-standart fonts", | ||
"syntax": "[ [ <'font-style'> || <font-variant-css21> || <'font-weight'> || <'font-stretch'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ] | caption | icon | menu | message-box | small-caption | status-bar | <-non-standart-font>" | ||
}, | ||
"font-variant": { | ||
"comment": "# should stick to term, missed spaces for function body, trailing space", | ||
"syntax": "normal | none | [ <common-lig-values> || <discretionary-lig-values> || <historical-lig-values> || <contextual-alt-values> || stylistic( <feature-value-name> ) || historical-forms || styleset( <feature-value-name># ) || character-variant( <feature-value-name># ) || swash( <feature-value-name> ) || ornaments( <feature-value-name> ) || annotation( <feature-value-name> ) || [ small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps ] || <numeric-figure-values> || <numeric-spacing-values> || <numeric-fraction-values> || ordinal || slashed-zero || <east-asian-variant-values> || <east-asian-width-values> || ruby ]" | ||
}, | ||
"font-variant-alternates": { | ||
"comment": "# should stick to term, missed spaces for function body, trailing space", | ||
"syntax": "normal | [ stylistic( <feature-value-name> ) || historical-forms || styleset( <feature-value-name># ) || character-variant( <feature-value-name># ) || swash( <feature-value-name> ) || ornaments( <feature-value-name> ) || annotation( <feature-value-name> ) ]" | ||
}, | ||
"font-variant-east-asian": { | ||
"comment": "trailing space", | ||
"syntax": "normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]" | ||
}, | ||
"glyph-orientation-horizontal": { | ||
@@ -344,2 +278,5 @@ "comment": "added SVG property", | ||
"comment": "fix syntax <length> -> <length-percentage>", | ||
"references": [ | ||
"https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/letter-spacing" | ||
], | ||
"syntax": "normal | <length-percentage>" | ||
@@ -411,10 +348,2 @@ }, | ||
}, | ||
"scroll-snap-points-x": { | ||
"comment": "missed spaces in function body", | ||
"syntax": "none | repeat( <length> )" | ||
}, | ||
"scroll-snap-points-y": { | ||
"comment": "missed spaces in function body", | ||
"syntax": "none | repeat( <length> )" | ||
}, | ||
"shape-rendering": { | ||
@@ -502,18 +431,2 @@ "comment": "added SVG property", | ||
}, | ||
"text-emphasis-style": { | ||
"comment": "missed between brackets", | ||
"syntax": "none | [ [ filled | open ] || [ dot | circle | double-circle | triangle | sesame ] ] | <string>" | ||
}, | ||
"text-indent": { | ||
"comment": "wrong syntax, replaced for https://drafts.csswg.org/css-text-3/#text-indent-property", | ||
"syntax": "[ <length-percentage> ] && hanging? && each-line?" | ||
}, | ||
"text-size-adjust": { | ||
"comment": "trailing space", | ||
"syntax": "none | auto | <percentage>" | ||
}, | ||
"touch-action": { | ||
"comment": "missed between brackets", | ||
"syntax": "auto | none | [ [ pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] ] | manipulation" | ||
}, | ||
"transform-origin": { | ||
@@ -523,6 +436,2 @@ "comment": "move first group to the end since less collecting", | ||
}, | ||
"transition-timing-function": { | ||
"comment": "extra space in the beginning", | ||
"syntax": "<single-transition-timing-function>#" | ||
}, | ||
"unicode-bidi": { | ||
@@ -684,6 +593,2 @@ "comment": "added prefixed keywords https://developer.mozilla.org/en-US/docs/Web/CSS/unicode-bidi", | ||
}, | ||
"an-plus-b": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"attr()": { | ||
@@ -693,6 +598,2 @@ "comment": "drop it since it's a generic", | ||
}, | ||
"blend-mode": { | ||
"comment": "missed, https://drafts.fxtf.org/compositing-1/#ltblendmodegt", | ||
"syntax": "normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity" | ||
}, | ||
"border-radius": { | ||
@@ -714,10 +615,2 @@ "comment": "missed, https://drafts.csswg.org/css-backgrounds-3/#the-border-radius", | ||
}, | ||
"family-name": { | ||
"comment": "upper case", | ||
"syntax": "<string> | <ident>+" | ||
}, | ||
"feature-value-name": { | ||
"comment": "missed angle brackets", | ||
"syntax": "<ident>" | ||
}, | ||
"inset()": { | ||
@@ -754,6 +647,2 @@ "comment": "changed <border-radius> to <'border-radius'>", | ||
}, | ||
"mask-position": { | ||
"comment": "extra space after `top`", | ||
"syntax": "[ <length-percentage> | left | center | right ] [ <length-percentage> | top | center | bottom ]?" | ||
}, | ||
"matrix()": { | ||
@@ -775,6 +664,2 @@ "comment": "redundant max", | ||
}, | ||
"namespace-prefix": { | ||
"comment": "missed angle brackets", | ||
"syntax": "<ident>" | ||
}, | ||
"outline-radius": { | ||
@@ -800,6 +685,2 @@ "comment": "missed, looks like it's a similar to <border-radius> https://developer.mozilla.org/en/docs/Web/CSS/-moz-outline-radius", | ||
}, | ||
"scale3d()": { | ||
"comment": "missed space before comma", | ||
"syntax": "scale3d( <number> , <number> , <number> )" | ||
}, | ||
"shape": { | ||
@@ -809,6 +690,2 @@ "comment": "missed spaces in function body and add backwards compatible syntax", | ||
}, | ||
"single-animation-name": { | ||
"comment": "missed angle brackets", | ||
"syntax": "none | <ident>" | ||
}, | ||
"single-transition": { | ||
@@ -818,10 +695,2 @@ "comment": "moved <single-transition-timing-function> in the beginning to avoid wrong match to <single-transition-property>", | ||
}, | ||
"single-transition-property": { | ||
"comment": "missed angle brackets", | ||
"syntax": "all | <ident>" | ||
}, | ||
"single-transition-timing-function": { | ||
"comment": "missed spaces", | ||
"syntax": "ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps( <integer> [, [ start | end ] ]? ) | cubic-bezier( <number>, <number>, <number>, <number> )" | ||
}, | ||
"svg-length": { | ||
@@ -846,6 +715,2 @@ "comment": "All coordinates and lengths in SVG can be specified with or without a unit identifier", | ||
}, | ||
"uri": { | ||
"comment": "deprecated, add alias to not change syntaxes", | ||
"syntax": "<url>" | ||
}, | ||
"quote": { | ||
@@ -862,4 +727,121 @@ "comment": "missed -> https://drafts.csswg.org/css-content/#typedef-quote", | ||
"syntax": "<number>" | ||
}, | ||
"var()": { | ||
"comment": "drop it since it's a generic (also syntax is incorrect and can't be parsed)", | ||
"syntax": null | ||
}, | ||
"an-plus-b": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-type": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-block": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-declaration": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-block-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"feature-value-declaration-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"general-enclosed": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"keyframe-block": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"keyframe-block-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-plain": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-range": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"mf-value": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"minmax()": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-and": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-condition": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-not": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-or": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-in-parens": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-feature": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-condition-without-or": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-query": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"media-query-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-selector": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-selector-list": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-body": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-margin-box": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"page-margin-box-type": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
}, | ||
"pseudo-page": { | ||
"comment": "syntax is incorrect and can't be parsed, drop for now", | ||
"syntax": null | ||
} | ||
} | ||
} |
@@ -0,1 +1,16 @@ | ||
## 1.0.0-alpha8 (November 11, 2016) | ||
- Fixed `Scanner#skip()` issue method when cursor is moving to the end of source | ||
- Simplified `Progid` node | ||
- Changed behaviour for bad selector processing, now parsing fails instead of selector ignoring | ||
- Fixed `<id-selector>` generic syntax | ||
- Added `q` unit for `<length>` generic syntax | ||
- Refactored syntax parser (performance) | ||
- Reduced startup time by implementing lazy syntax parsing (default syntax doesn't parse on module load) | ||
- Updated syntax dictionaries and used [`mdn/data`](https://github.com/mdn/data) instead of `Template:CSSData` | ||
- Renamed `syntax.stringify()` method to `syntax.translate()` | ||
- Simplified generic syntax functions, those functions receive a single AST node for checking and should return `true` or `false` | ||
- Added exception for values that contains `var()`, those values are always valid for now | ||
- Added more tests and increase code coverage to `98.5%` | ||
## 1.0.0-alpha7 (October 7, 2016) | ||
@@ -2,0 +17,0 @@ |
@@ -78,5 +78,8 @@ 'use strict'; | ||
function getInfo() { | ||
function getInfo(start) { | ||
if (needPositions) { | ||
return scanner.getLocation(scanner.tokenStart, filename); | ||
return scanner.getLocation( | ||
start !== undefined ? start : scanner.tokenStart, | ||
filename | ||
); | ||
} | ||
@@ -88,9 +91,5 @@ | ||
function getStylesheet(nested) { | ||
var start = scanner.tokenStart; | ||
var rules = new List(); | ||
var child; | ||
var node = { | ||
type: 'StyleSheet', | ||
info: getInfo(), | ||
rules: rules | ||
}; | ||
@@ -104,2 +103,9 @@ scan: | ||
case RIGHTCURLYBRACKET: | ||
if (!nested) { | ||
scanner.error('Unexpected right curly brace'); | ||
} | ||
break scan; | ||
case COMMENT: | ||
@@ -119,9 +125,2 @@ // ignore comments except exclamation comments (i.e. /*! .. */) on top level | ||
case RIGHTCURLYBRACKET: | ||
if (!nested) { | ||
scanner.error('Unexpected right curly brace'); | ||
} | ||
break scan; | ||
default: | ||
@@ -134,3 +133,7 @@ child = getRule(); | ||
return node; | ||
return { | ||
type: 'StyleSheet', | ||
info: getInfo(start), | ||
rules: rules | ||
}; | ||
} | ||
@@ -154,10 +157,6 @@ | ||
function getAtruleExpression() { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'AtruleExpression', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -210,3 +209,7 @@ readSC(); | ||
return node; | ||
return { | ||
type: 'AtruleExpression', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -225,3 +228,2 @@ | ||
// at-rule expression can ends with semicolon, left curly bracket or eof | ||
switch (scanner.tokenType) { | ||
@@ -241,2 +243,4 @@ case SEMICOLON: | ||
break; | ||
// at-rule expression can ends with semicolon, left curly bracket or eof - no other options | ||
} | ||
@@ -248,20 +252,21 @@ | ||
function getRule() { | ||
return { | ||
var node = { | ||
type: 'Rule', | ||
info: getInfo(), | ||
selector: getSelector(), | ||
block: getBlockWithBrackets() | ||
block: null | ||
}; | ||
scanner.eat(LEFTCURLYBRACKET); | ||
node.block = getBlock(); | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
return node; | ||
} | ||
function getSelector() { | ||
var start = scanner.tokenStart; | ||
var selectors = new List(); | ||
var simpleSelector; | ||
var isBadSelector = false; | ||
var lastComma = true; | ||
var node = { | ||
type: 'Selector', | ||
info: getInfo(), | ||
selectors: selectors | ||
}; | ||
var lastComma = -2; | ||
@@ -275,7 +280,7 @@ scan: | ||
case COMMA: | ||
if (lastComma) { | ||
isBadSelector = true; | ||
if (lastComma !== -1) { | ||
scanner.error('Unexpected comma'); | ||
} | ||
lastComma = true; | ||
lastComma = scanner.tokenStart; | ||
scanner.next(); | ||
@@ -285,7 +290,3 @@ break; | ||
default: | ||
if (!lastComma) { | ||
isBadSelector = true; | ||
} | ||
lastComma = false; | ||
lastComma = -1; | ||
simpleSelector = getSimpleSelector(); | ||
@@ -295,3 +296,3 @@ selectors.appendData(simpleSelector); | ||
if (simpleSelector.sequence.isEmpty()) { | ||
isBadSelector = true; | ||
scanner.error('Simple selector expected'); | ||
} | ||
@@ -301,15 +302,15 @@ } | ||
if (lastComma) { | ||
isBadSelector = true; | ||
// scanner.error('Unexpected trailing comma'); | ||
if (lastComma !== -1 && lastComma !== -2) { // TODO: fail on empty selector rules? | ||
scanner.error('Unexpected trailing comma', lastComma); | ||
} | ||
if (isBadSelector) { | ||
selectors.clear(); | ||
} | ||
return node; | ||
return { | ||
type: 'Selector', | ||
info: getInfo(start), | ||
selectors: selectors | ||
}; | ||
} | ||
function getSimpleSelector(nested) { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
@@ -319,7 +320,2 @@ var combinator = null; | ||
var child; | ||
var node = { | ||
type: 'SimpleSelector', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -381,3 +377,3 @@ scan: | ||
case NUMBERSIGN: | ||
child = getShash(); | ||
child = getId(); | ||
break; | ||
@@ -397,3 +393,3 @@ | ||
case NUMBER: | ||
child = getPercentage(getInfo(), readNumber()); | ||
child = getPercentage(); | ||
break; | ||
@@ -410,3 +406,3 @@ | ||
type: 'Combinator', | ||
info: needPositions ? scanner.getLocation(combinatorOffset, filename) : null, | ||
info: getInfo(combinatorOffset), | ||
name: ' ' | ||
@@ -427,6 +423,11 @@ }; | ||
return node; | ||
return { | ||
type: 'SimpleSelector', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
function getDeclarations() { | ||
function getBlock() { | ||
var start = scanner.tokenStart; | ||
var declarations = new List(); | ||
@@ -451,16 +452,5 @@ | ||
return declarations; | ||
} | ||
function getBlockWithBrackets() { | ||
var info = getInfo(); | ||
var declarations; | ||
scanner.eat(LEFTCURLYBRACKET); | ||
declarations = getDeclarations(); | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
return { | ||
type: 'Block', | ||
info: info, | ||
info: getInfo(start), | ||
declarations: declarations | ||
@@ -470,12 +460,4 @@ }; | ||
function getBlock() { | ||
return { | ||
type: 'Block', | ||
info: getInfo(), | ||
declarations: getDeclarations() | ||
}; | ||
} | ||
function getDeclaration(nested) { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var property = readProperty(); | ||
@@ -485,2 +467,3 @@ var important = false; | ||
readSC(); | ||
scanner.eat(COLON); | ||
@@ -495,3 +478,3 @@ value = getValue(nested, property); | ||
type: 'Declaration', | ||
info: info, | ||
info: getInfo(start), | ||
important: important, | ||
@@ -505,7 +488,5 @@ property: property, | ||
var start = scanner.tokenStart; | ||
var name; | ||
var type; | ||
for (; !scanner.eof; scanner.next()) { | ||
var type = scanner.tokenType; | ||
for (; type = scanner.tokenType; scanner.next()) { | ||
if (type !== SOLIDUS && | ||
@@ -519,7 +500,4 @@ type !== ASTERISK && | ||
scanIdent(true); | ||
name = scanner.substrToCursor(start); | ||
readSC(); | ||
return name; | ||
return scanner.substrToCursor(start); | ||
} | ||
@@ -533,10 +511,6 @@ | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'Value', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
@@ -611,6 +585,29 @@ readSC(); | ||
return node; | ||
return { | ||
type: 'Value', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
// any = string | percentage | dimension | number | uri | functionExpression | funktion | unary | operator | ident | ||
function getFilterValue() { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var progid; | ||
while (progid = checkProgid()) { | ||
readSC(); | ||
sequence.appendData(getProgid(progid)); | ||
} | ||
readSC(); | ||
return { | ||
type: 'Value', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
// any = percentage | dimension | number | operator | ident | function | ||
function getAny(scope) { | ||
@@ -632,20 +629,17 @@ switch (scanner.tokenType) { | ||
case NUMBER: | ||
var info = getInfo(); | ||
var number = readNumber(); | ||
var type = scanner.tokenType; | ||
switch (scanner.lookupType(1)) { | ||
case PERCENTSIGN: | ||
return getPercentage(); | ||
if (type === PERCENTSIGN) { | ||
return getPercentage(info, number); | ||
} | ||
case IDENTIFIER: | ||
return getDimension(); | ||
if (type === IDENTIFIER) { | ||
return getDimension(info, number); | ||
default: | ||
return { | ||
type: 'Number', | ||
info: getInfo(), | ||
value: readNumber() | ||
}; | ||
} | ||
return { | ||
type: 'Number', | ||
info: info, | ||
value: number | ||
}; | ||
default: | ||
@@ -655,3 +649,2 @@ scanner.error(); | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -662,3 +655,3 @@ | ||
if (scanner.tokenType === LEFTPARENTHESIS) { | ||
return getFunction(scope, info, scanner.substrToCursor(start)); | ||
return getFunction(scope, getInfo(start), scanner.substrToCursor(start)); | ||
} | ||
@@ -668,3 +661,3 @@ | ||
type: 'Identifier', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -711,7 +704,5 @@ }; | ||
scanner.eat(LEFTSQUAREBRACKET); | ||
readSC(); | ||
node.name = getAttributeName(); | ||
readSC(); | ||
@@ -748,13 +739,9 @@ | ||
function getParentheses(scope) { | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var wasSpace = false; | ||
var child; | ||
var node = { | ||
type: 'Parentheses', | ||
info: getInfo(), | ||
sequence: sequence | ||
}; | ||
// left brace | ||
scanner.next(); | ||
scanner.eat(LEFTPARENTHESIS); | ||
readSC(); | ||
@@ -811,3 +798,7 @@ | ||
return node; | ||
return { | ||
type: 'Parentheses', | ||
info: getInfo(start), | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -817,3 +808,3 @@ | ||
function getClass() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -824,3 +815,3 @@ scanner.eat(FULLSTOP); | ||
type: 'Class', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false) | ||
@@ -831,4 +822,4 @@ }; | ||
// '#' ident | ||
function getShash() { | ||
var info = getInfo(); | ||
function getId() { | ||
var start = scanner.tokenStart; | ||
@@ -839,3 +830,3 @@ scanner.eat(NUMBERSIGN); | ||
type: 'Id', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false) | ||
@@ -847,3 +838,3 @@ }; | ||
function getCombinator() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var combinator; | ||
@@ -880,3 +871,3 @@ | ||
type: 'Combinator', | ||
info: info, | ||
info: getInfo(start), | ||
name: combinator | ||
@@ -888,3 +879,2 @@ }; | ||
function getComment() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 2; | ||
@@ -903,3 +893,3 @@ var end = scanner.tokenEnd; | ||
type: 'Comment', | ||
info: info, | ||
info: getInfo(start), | ||
value: scanner.source.substring(start, end) | ||
@@ -930,7 +920,7 @@ }; | ||
// number ident | ||
function getDimension(info, number) { | ||
function getDimension() { | ||
return { | ||
type: 'Dimension', | ||
info: info, | ||
value: number, | ||
info: getInfo(), | ||
value: readNumber(), | ||
unit: readUnit() | ||
@@ -940,3 +930,6 @@ }; | ||
function getPercentage(info, number) { | ||
function getPercentage() { | ||
var start = scanner.tokenStart; | ||
var number = readNumber(); | ||
scanner.eat(PERCENTSIGN); | ||
@@ -946,3 +939,3 @@ | ||
type: 'Percentage', | ||
info: info, | ||
info: getInfo(start), | ||
value: number | ||
@@ -1063,2 +1056,3 @@ }; | ||
// TODO: -> getSimpleSelectorList | ||
function getNotFunctionArguments() { | ||
@@ -1132,7 +1126,3 @@ var args = new List(); | ||
function getUri(scope, info) { | ||
var node = { | ||
type: 'Url', | ||
info: info, | ||
value: null | ||
}; | ||
var value; | ||
@@ -1143,7 +1133,7 @@ scanner.eat(LEFTPARENTHESIS); // ( | ||
if (scanner.tokenType === STRING) { | ||
node.value = getString(); | ||
value = getString(); | ||
} else { | ||
var rawInfo = getInfo(); | ||
var start = scanner.tokenStart; | ||
// TODO: fix me, looks like incorrect raw scan | ||
for (; !scanner.eof; scanner.next()) { | ||
@@ -1159,5 +1149,5 @@ var type = scanner.tokenType; | ||
node.value = { | ||
value = { | ||
type: 'Raw', | ||
info: rawInfo, | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
@@ -1170,3 +1160,7 @@ }; | ||
return node; | ||
return { | ||
type: 'Url', | ||
info: info, | ||
value: value | ||
}; | ||
} | ||
@@ -1271,4 +1265,2 @@ | ||
} | ||
return hexLength; | ||
} | ||
@@ -1278,3 +1270,2 @@ | ||
var start = scanner.tokenStart; | ||
var info = getInfo(); | ||
@@ -1286,3 +1277,3 @@ scanner.next(); // U or u | ||
type: 'UnicodeRange', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1319,3 +1310,2 @@ }; | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1356,3 +1346,3 @@ var expectIdentifier = false; | ||
type: 'Identifier', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1363,3 +1353,2 @@ }; | ||
function getTypeOrUniversal() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1391,3 +1380,3 @@ var universal = false; | ||
type: universal ? 'Universal' : 'Type', | ||
info: info, | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start) | ||
@@ -1419,15 +1408,8 @@ }; | ||
function getNthSelector() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var sequence = new List(); | ||
var node; | ||
var name; | ||
scanner.eat(COLON); | ||
node = { | ||
type: 'PseudoClass', | ||
info: info, | ||
name: readIdent(false), | ||
sequence: sequence | ||
}; | ||
name = readIdent(false); | ||
scanner.eat(LEFTPARENTHESIS); | ||
@@ -1443,3 +1425,3 @@ readSC(); | ||
type: 'Nth', | ||
info: getInfo(), | ||
info: info, | ||
value: scanner.substrToCursor(start) | ||
@@ -1583,3 +1565,8 @@ }); | ||
return node; | ||
return { | ||
type: 'PseudoClass', | ||
info: getInfo(start), | ||
name: name, | ||
sequence: sequence | ||
}; | ||
} | ||
@@ -1597,29 +1584,11 @@ | ||
function getOperator() { | ||
var node = { | ||
type: 'Operator', | ||
info: getInfo(), | ||
value: scanner.getTokenValue() | ||
}; | ||
var start = scanner.tokenStart; | ||
scanner.next(); | ||
return node; | ||
} | ||
function getFilterValue() { // TODO | ||
var sequence = new List(); | ||
var progid; | ||
var node = { | ||
type: 'Value', | ||
info: getInfo(), | ||
sequence: sequence | ||
return { | ||
type: 'Operator', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
while (progid = checkProgid()) { | ||
sequence.appendData(getProgid(progid)); | ||
} | ||
readSC(); | ||
return node; | ||
} | ||
@@ -1629,13 +1598,3 @@ | ||
function checkProgid() { | ||
function checkSC(offset) { | ||
for (var type; type = scanner.lookupType(offset); offset++) { | ||
if (type !== WHITESPACE && type !== COMMENT) { | ||
break; | ||
} | ||
} | ||
return offset; | ||
} | ||
var startOffset = checkSC(0); | ||
var startOffset = findNonSCOffset(0); | ||
var offset = startOffset; | ||
@@ -1653,3 +1612,3 @@ | ||
offset += 2; | ||
offset = checkSC(offset); | ||
offset = findNonSCOffset(offset); | ||
@@ -1665,3 +1624,3 @@ if (scanner.lookupValue(offset + 0, 'dximagetransform') === false || | ||
offset += 5; | ||
offset = checkSC(offset); | ||
offset = findNonSCOffset(offset); | ||
} | ||
@@ -1675,3 +1634,3 @@ | ||
if (type === RIGHTPARENTHESIS) { | ||
return offset - startOffset; | ||
return offset - startOffset + 1; | ||
} | ||
@@ -1684,25 +1643,11 @@ } | ||
function getProgid(progidEnd) { | ||
var node = { | ||
type: 'Progid', | ||
info: getInfo(), | ||
value: null | ||
}; | ||
readSC(); | ||
var rawInfo = getInfo(); | ||
var start = scanner.tokenStart; | ||
scanner.skip(progidEnd); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
node.value = { | ||
type: 'Raw', | ||
info: rawInfo, | ||
return { | ||
type: 'Progid', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
readSC(); | ||
return node; | ||
} | ||
@@ -1738,3 +1683,3 @@ | ||
function getPseudoElement() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1746,3 +1691,3 @@ scanner.eat(COLON); | ||
type: 'PseudoElement', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false), | ||
@@ -1759,3 +1704,3 @@ legacy: false | ||
function getLegacyPseudoElement() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
@@ -1766,3 +1711,3 @@ scanner.eat(COLON); | ||
type: 'PseudoElement', | ||
info: info, | ||
info: getInfo(start), | ||
name: readIdent(false), | ||
@@ -1775,4 +1720,3 @@ legacy: true | ||
function getPseudoClass() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 1; | ||
var start = scanner.tokenStart; | ||
@@ -1783,3 +1727,3 @@ scanner.eat(COLON); | ||
if (scanner.tokenType === LEFTPARENTHESIS) { | ||
return getFunction(SCOPE_SELECTOR, info, scanner.substrToCursor(start)); | ||
return getFunction(SCOPE_SELECTOR, getInfo(start), scanner.substrToCursor(start + 1)); | ||
} | ||
@@ -1789,4 +1733,4 @@ | ||
type: 'PseudoClass', | ||
info: info, | ||
name: scanner.substrToCursor(start), | ||
info: getInfo(start), | ||
name: scanner.substrToCursor(start + 1), | ||
sequence: null | ||
@@ -1796,2 +1740,12 @@ }; | ||
function findNonSCOffset(offset) { | ||
for (var type; type = scanner.lookupType(offset); offset++) { | ||
if (type !== WHITESPACE && type !== COMMENT) { | ||
break; | ||
} | ||
} | ||
return offset; | ||
} | ||
function readSC() { | ||
@@ -1801,4 +1755,2 @@ while (scanner.tokenType === WHITESPACE || scanner.tokenType === COMMENT) { | ||
} | ||
return null; | ||
} | ||
@@ -1808,11 +1760,11 @@ | ||
function getString() { | ||
var node = { | ||
type: 'String', | ||
info: getInfo(), | ||
value: scanner.getTokenValue() | ||
}; | ||
var start = scanner.tokenStart; | ||
scanner.next(); | ||
return node; | ||
return { | ||
type: 'String', | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start) | ||
}; | ||
} | ||
@@ -1822,4 +1774,3 @@ | ||
function getHash() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart + 1; | ||
var start = scanner.tokenStart; | ||
@@ -1842,4 +1793,4 @@ scanner.eat(NUMBERSIGN); | ||
type: 'Hash', | ||
info: info, | ||
value: scanner.substrToCursor(start) | ||
info: getInfo(start), | ||
value: scanner.substrToCursor(start + 1) // skip # | ||
}; | ||
@@ -1879,4 +1830,4 @@ } | ||
// fix soft deoptimizations (insufficient type feedback) | ||
parse('a.b#c:e:NOT(a)::g,* b>c+d~e/deep/f,100%{v:1 2em t a(2%, var(--a)) url(..) -foo-bar !important}'); | ||
parse('a.b#c:e:NOT(a)::g,* b >c+d~e/deep/f,100%{v:1 2em t a(2%, var(--a)) url(..) -foo-bar !important}'); | ||
module.exports = parse; |
@@ -33,8 +33,9 @@ 'use strict'; | ||
var MIN_ARRAY_SIZE = 16 * 1024; | ||
var MIN_BUFFER_SIZE = 16 * 1024; | ||
var OFFSET_MASK = 0x00FFFFFF; | ||
var TYPE_OFFSET = 24; | ||
var SafeUint32Array = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; // fallback on Array when TypedArray is not supported | ||
var lastIndexOf = Array.prototype.lastIndexOf; // some browser implementations have no TypedArray#lastIndexOf | ||
var LongArray = typeof Uint32Array !== 'undefined' ? Uint32Array : Array; | ||
var offsetAndType = new LongArray(MIN_ARRAY_SIZE); | ||
var offsetAndType = new SafeUint32Array(MIN_BUFFER_SIZE); | ||
var lines = null; | ||
@@ -56,3 +57,3 @@ | ||
if (lines === null || lines.length < sourceLength + 1) { | ||
lines = new LongArray(Math.max(sourceLength + 1024, MIN_ARRAY_SIZE)); | ||
lines = new SafeUint32Array(Math.max(sourceLength + 1024, MIN_BUFFER_SIZE)); | ||
} | ||
@@ -231,3 +232,3 @@ | ||
if (offsetAndType.length < sourceLength + 1) { | ||
offsetAndType = new LongArray(sourceLength + 1024); | ||
offsetAndType = new SafeUint32Array(sourceLength + 1024); | ||
} | ||
@@ -283,3 +284,3 @@ | ||
offsetAndType[tokenCount++] = (type << 24) | offset; | ||
offsetAndType[tokenCount++] = (type << TYPE_OFFSET) | offset; | ||
prevType = type; | ||
@@ -323,6 +324,6 @@ } | ||
if (offset < this.tokenCount) { | ||
return this.offsetAndType[offset] >> 24; | ||
return this.offsetAndType[offset] >> TYPE_OFFSET; | ||
} | ||
return 0; | ||
return NULL; | ||
}, | ||
@@ -358,5 +359,6 @@ lookupValue: function(offset, referenceStr) { | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenType = next >> TYPE_OFFSET; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
} else { | ||
this.currentToken = this.tokenCount; | ||
this.next(); | ||
@@ -372,3 +374,3 @@ } | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenType = next >> TYPE_OFFSET; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
@@ -384,16 +386,14 @@ } else { | ||
eat: function(tokenType) { | ||
if (this.tokenType === tokenType) { | ||
this.next(); | ||
return true; | ||
if (this.tokenType !== tokenType) { | ||
this.error(TokenName[tokenType] + ' is expected'); | ||
} | ||
this.error(TokenName[tokenType] + ' is expected'); | ||
this.next(); | ||
}, | ||
expectIdentifier: function(name) { | ||
if (this.tokenType === IDENTIFIER && cmpStr(this.source, this.tokenStart, this.tokenEnd, name)) { | ||
this.next(); | ||
return true; | ||
if (this.tokenType !== IDENTIFIER || cmpStr(this.source, this.tokenStart, this.tokenEnd, name) === false) { | ||
this.error('Identifier `' + name + '` is expected'); | ||
} | ||
this.error('Identifier `' + name + '` is expected'); | ||
this.next(); | ||
}, | ||
@@ -452,3 +452,3 @@ | ||
return Array.prototype.slice.call(this.offsetAndType, 0, this.tokenCount).map(function(item) { | ||
return TokenName[item >> 24]; | ||
return TokenName[item >> TYPE_OFFSET]; | ||
}); | ||
@@ -455,0 +455,0 @@ } |
@@ -1,2 +0,2 @@ | ||
var stringifySyntax = require('./stringify'); | ||
var translateSyntax = require('./translate'); | ||
var translateCss = require('../utils/translate'); | ||
@@ -49,3 +49,3 @@ var walk = require('../utils/walk').all; | ||
error.rawMessage = message; | ||
error.syntax = syntax ? stringifySyntax(syntax) : '<generic>'; | ||
error.syntax = syntax ? translateSyntax(syntax) : '<generic>'; | ||
error.css = css; | ||
@@ -52,0 +52,0 @@ error.line = badNode && badNode.info ? badNode.info.line : undefined; |
var names = require('../utils/names.js'); | ||
// https://www.w3.org/TR/css-values-3/#lengths | ||
var LENGTH = { | ||
@@ -10,2 +12,3 @@ // absolute length units | ||
'pc': true, | ||
'q': true, | ||
@@ -43,2 +46,3 @@ // relative length units | ||
// https://www.w3.org/TR/css-values-3/#resolution | ||
// https://www.w3.org/TR/css3-images/#resolution-type | ||
@@ -80,13 +84,17 @@ var RESOLUTION = { | ||
// there were some prefixed implementations | ||
return keyword.vendor === '' || keyword.vendor === '-moz-' || keyword.vendor === '-webkit-'; | ||
return keyword.vendor === '' || | ||
keyword.vendor === '-moz-' || | ||
keyword.vendor === '-webkit-'; | ||
} | ||
function astNode(type) { | ||
return function(node) { | ||
return node.data.type === type; | ||
}; | ||
} | ||
function dimension(type) { | ||
return function(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Dimension' && type.hasOwnProperty(node.data.unit.toLowerCase())); | ||
}; | ||
@@ -96,137 +104,63 @@ } | ||
function length(node) { | ||
if (node && ( | ||
isCalc(node) | ||
|| | ||
(node.data.type === 'Dimension' && LENGTH.hasOwnProperty(node.data.unit.toLowerCase())) | ||
|| | ||
(node.data.type === 'Number' && Number(node.data.value) === 0) | ||
) | ||
) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Dimension' && LENGTH.hasOwnProperty(node.data.unit.toLowerCase())) || | ||
(node.data.type === 'Number' && Number(node.data.value) === 0); | ||
} | ||
function attr(node) { | ||
if (node && node.data.type === 'Function' && node.data.name.toLowerCase() === 'attr') { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return node.data.type === 'Function' && node.data.name.toLowerCase() === 'attr'; | ||
} | ||
function astNode(type) { | ||
return function(node) { | ||
if (node && node.data.type === type) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
}; | ||
} | ||
function number(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || node.data.type === 'Number'; | ||
} | ||
function numberZeroOne(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
if (isCalc(node) || node.data.type === 'Number') { | ||
var value = Number(node.data.value); | ||
if (value >= 0 && value <= 1) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return value >= 0 && value <= 1; | ||
} | ||
return false; | ||
} | ||
function numberOneOrGreater(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Number')) { | ||
var value = Number(node.data.value); | ||
if (value >= 1) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
if (isCalc(node) || node.data.type === 'Number') { | ||
return Number(node.data.value) >= 1; | ||
} | ||
return false; | ||
} | ||
// TODO: fail on 10e-2 | ||
function integer(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Number' && node.data.value.indexOf('.') === -1); | ||
} | ||
// TODO: fail on 10e-2 | ||
function positiveInteger(node) { | ||
if (node && (isCalc(node) || (node.data.type === 'Number' && node.data.value.indexOf('.') === -1 && node.data.value.charAt(0) !== '-'))) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
(node.data.type === 'Number' && node.data.value.indexOf('.') === -1 && node.data.value.charAt(0) !== '-'); | ||
} | ||
function percentage(node) { | ||
if (node && (isCalc(node) || node.data.type === 'Percentage')) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return isCalc(node) || | ||
node.data.type === 'Percentage'; | ||
} | ||
function hexColor(node) { | ||
if (!node || node.data.type !== 'Hash') { | ||
return; | ||
if (node.data.type !== 'Hash') { | ||
return false; | ||
} | ||
var hex = node.data.value; | ||
if (/^[0-9a-fA-F]{3,8}$/.test(hex) && | ||
(hex.length === 3 || hex.length === 4 || hex.length === 6 || hex.length === 8)) { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
} | ||
function idSelector(node) { | ||
if (!node || node.data.type !== 'Hash') { | ||
return; | ||
} | ||
var cursor = node.next; | ||
var match = [node.data]; | ||
while (cursor && (cursor.data.type === 'Number' || cursor.data.type === 'Identifier')) { | ||
match.push(cursor.data); | ||
cursor = cursor.next; | ||
} | ||
return { | ||
next: cursor, | ||
match: match | ||
}; | ||
return /^[0-9a-fA-F]{3,8}$/.test(hex) && | ||
(hex.length === 3 || hex.length === 4 || hex.length === 6 || hex.length === 8); | ||
} | ||
function expression(node) { | ||
if (node && node.data.type === 'Function' && node.data.name.toLowerCase() === 'expression') { | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return node.data.type === 'Function' && node.data.name.toLowerCase() === 'expression'; | ||
} | ||
@@ -236,4 +170,4 @@ | ||
function customIdent(node) { | ||
if (!node || node.data.type !== 'Identifier') { | ||
return; | ||
if (node.data.type !== 'Identifier') { | ||
return false; | ||
} | ||
@@ -245,3 +179,3 @@ | ||
if (name === 'unset' || name === 'initial' || name === 'inherit') { | ||
return; | ||
return false; | ||
} | ||
@@ -251,6 +185,3 @@ | ||
return { | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
return true; | ||
} | ||
@@ -267,3 +198,3 @@ | ||
'hex-color': hexColor, | ||
'id-selector': idSelector, // element( <id-selector> ) | ||
'id-selector': astNode('Hash'), // element( <id-selector> ) | ||
'ident': astNode('Identifier'), | ||
@@ -270,0 +201,0 @@ 'integer': integer, |
@@ -6,4 +6,4 @@ module.exports = { | ||
parse: require('./parse'), | ||
stringify: require('./stringify'), | ||
translate: require('./translate'), | ||
walk: require('./walk') | ||
}; |
var names = require('../utils/names'); | ||
var MULTIPLIER_DEFAULT = { | ||
comma: false, | ||
min: 1, | ||
max: 1, | ||
value: '' | ||
}; | ||
@@ -13,4 +19,5 @@ function skipSpaces(node) { | ||
var result = []; | ||
var min = syntaxNode.min === 0 || syntaxNode.min ? syntaxNode.min : 1; | ||
var max = syntaxNode.max === null ? Infinity : (syntaxNode.max || 1); | ||
var multiplier = syntaxNode.multiplier || MULTIPLIER_DEFAULT; | ||
var min = multiplier.min; | ||
var max = multiplier.max === 0 ? Infinity : multiplier.max; | ||
var lastCommaTermCount; | ||
@@ -351,3 +358,3 @@ var lastComma; | ||
if (syntaxNode.comma) { | ||
if (multiplier.comma) { | ||
if (lastComma && lastCommaTermCount === result.length) { | ||
@@ -354,0 +361,0 @@ // nothing match after comma |
var SyntaxParseError = require('./error').SyntaxParseError; | ||
var TAB = 9; | ||
var N = 10; | ||
var F = 12; | ||
var R = 13; | ||
var SPACE = 32; | ||
var EXCLAMATIONMARK = 33; // ! | ||
var NUMBERSIGN = 35; // # | ||
var PERCENTSIGN = 37; // % | ||
var AMPERSAND = 38; // & | ||
var APOSTROPHE = 39; // ' | ||
var LEFTPARENTHESIS = 40; // ( | ||
var RIGHTPARENTHESIS = 41; // ) | ||
var ASTERISK = 42; // * | ||
var PLUSSIGN = 43; // + | ||
var COMMA = 44; // , | ||
var SOLIDUS = 47; // / | ||
var LESSTHANSIGN = 60; // < | ||
var GREATERTHANSIGN = 62; // > | ||
var QUESTIONMARK = 63; // ? | ||
var LEFTSQUAREBRACKET = 91; // [ | ||
var RIGHTSQUAREBRACKET = 93; // ] | ||
var LEFTCURLYBRACKET = 123; // { | ||
var VERTICALLINE = 124; // | | ||
var RIGHTCURLYBRACKET = 125; // } | ||
var COMBINATOR_PRECEDENCE = { | ||
@@ -8,223 +33,234 @@ ' ': 1, | ||
}; | ||
var MULTIPLIER_DEFAULT = { | ||
comma: false, | ||
min: 1, | ||
max: 1 | ||
}; | ||
var MULTIPLIER_ZERO_OR_MORE = { | ||
comma: false, | ||
min: 0, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ONE_OR_MORE = { | ||
comma: false, | ||
min: 1, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ONE_OR_MORE_COMMA_SEPARATED = { | ||
comma: true, | ||
min: 1, | ||
max: 0 | ||
}; | ||
var MULTIPLIER_ZERO_OR_ONE = { | ||
comma: false, | ||
min: 0, | ||
max: 1 | ||
}; | ||
var NAME_CHAR = (function() { | ||
var array = typeof Uint32Array === 'function' ? new Uint32Array(128) : new Array(128); | ||
for (var i = 0; i < 128; i++) { | ||
array[i] = /[a-zA-Z0-9\-]/.test(String.fromCharCode(i)) ? 1 : 0; | ||
}; | ||
return array; | ||
})(); | ||
var spaces = /[ \r\n\f]/; | ||
var nameChar = /[a-zA-Z0-9\-]/; | ||
var number = /\d/; | ||
var Scanner = function(str) { | ||
this.str = str; | ||
this.pos = 0; | ||
}; | ||
Scanner.prototype = { | ||
charCode: function() { | ||
return this.pos < this.str.length ? this.str.charCodeAt(this.pos) : 0; | ||
}, | ||
nextCharCode: function() { | ||
return this.pos + 1 < this.str.length ? this.str.charCodeAt(this.pos + 1) : 0; | ||
}, | ||
function readWord(str, start) { | ||
var end = start; | ||
substringToPos: function(end) { | ||
return this.str.substring(this.pos, this.pos = end); | ||
}, | ||
eat: function(code) { | ||
if (this.charCode() !== code) { | ||
error(this, this.pos, 'Expect `' + String.fromCharCode(code) + '`'); | ||
} | ||
while (nameChar.test(str.charAt(end))) { | ||
end++; | ||
this.pos++; | ||
} | ||
}; | ||
return str.substring(start, end); | ||
function scanSpaces(scanner) { | ||
var end = scanner.pos + 1; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code !== R && code !== N && code !== F && code !== SPACE && code !== TAB) { | ||
break; | ||
} | ||
} | ||
return scanner.substringToPos(end); | ||
} | ||
function readNumber(str, start) { | ||
var end = start; | ||
function scanWord(scanner) { | ||
var end = scanner.pos; | ||
while (number.test(str.charAt(end))) { | ||
end++; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code >= 128 || NAME_CHAR[code] === 0) { | ||
break; | ||
} | ||
} | ||
return str.substring(start, end); | ||
if (scanner.pos === end) { | ||
error(scanner, scanner.pos, 'Expect a keyword'); | ||
} | ||
return scanner.substringToPos(end); | ||
} | ||
function readString(str, start) { | ||
var end = start + 1; | ||
function scanNumber(scanner) { | ||
var end = scanner.pos; | ||
while (str.charAt(end) !== '\'') { | ||
end++; | ||
for (; end < scanner.str.length; end++) { | ||
var code = scanner.str.charCodeAt(end); | ||
if (code < 48 || code > 57) { | ||
break; | ||
} | ||
} | ||
end += eat(str, end, '\''); | ||
if (scanner.pos === end) { | ||
error(scanner, scanner.pos, 'Expect a number'); | ||
} | ||
return str.substring(start, end); | ||
return scanner.substringToPos(end); | ||
} | ||
function readMultiplier(str, pos) { | ||
function readRange() { | ||
var min = null; | ||
var max = null; | ||
function scanString(scanner) { | ||
var end = scanner.str.indexOf('\'', scanner.pos + 1); | ||
pos += eat(str, pos, '{'); | ||
if (end === -1) { | ||
error(scanner, scanner.str.length, 'Expect a quote'); | ||
} | ||
min = readNumber(str, pos) || error(str, pos); | ||
pos += min.length; | ||
return scanner.substringToPos(end + 1); | ||
} | ||
if (str.charAt(pos) === ',') { | ||
pos++; | ||
if (str.charAt(pos) !== '}') { | ||
max = readNumber(str, pos) || error(str, pos); | ||
pos += max.length; | ||
} | ||
} else { | ||
max = min; | ||
} | ||
function readMultiplierRange(scanner, comma) { | ||
var min = null; | ||
var max = null; | ||
pos += eat(str, pos, '}'); | ||
scanner.eat(LEFTCURLYBRACKET); | ||
return { | ||
min: Number(min), | ||
max: max ? Number(max) : null | ||
}; | ||
min = scanNumber(scanner); | ||
if (scanner.charCode() === COMMA) { | ||
scanner.pos++; | ||
if (scanner.charCode() !== RIGHTCURLYBRACKET) { | ||
max = scanNumber(scanner); | ||
} | ||
} else { | ||
max = min; | ||
} | ||
switch (str.charAt(pos)) { | ||
case '*': | ||
return { | ||
comma: false, | ||
min: 0, | ||
max: null, | ||
value: '*' | ||
}; | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
case '+': | ||
return { | ||
comma: false, | ||
min: 1, | ||
max: null, | ||
value: '+' | ||
}; | ||
return { | ||
comma: comma, | ||
min: Number(min), | ||
max: max ? Number(max) : 0 | ||
}; | ||
} | ||
case '?': | ||
return { | ||
comma: false, | ||
min: 0, | ||
max: 1, | ||
value: '?' | ||
}; | ||
function readMultiplier(scanner) { | ||
switch (scanner.charCode()) { | ||
case ASTERISK: | ||
scanner.pos++; | ||
return MULTIPLIER_ZERO_OR_MORE; | ||
case '#': | ||
var start = pos; | ||
case PLUSSIGN: | ||
scanner.pos++; | ||
return MULTIPLIER_ONE_OR_MORE; | ||
pos++; // # | ||
case QUESTIONMARK: | ||
scanner.pos++; | ||
return MULTIPLIER_ZERO_OR_ONE; | ||
if (str.charAt(pos) === '{') { | ||
var range = readRange(); | ||
if (range) { | ||
return { | ||
comma: true, | ||
min: range.min, | ||
max: range.max, | ||
value: str.substring(start, pos) | ||
}; | ||
} | ||
case NUMBERSIGN: | ||
scanner.pos++; | ||
if (scanner.charCode() !== LEFTCURLYBRACKET) { | ||
return MULTIPLIER_ONE_OR_MORE_COMMA_SEPARATED; | ||
} | ||
return { | ||
comma: true, | ||
min: 1, | ||
max: null, | ||
value: '#' | ||
}; | ||
return readMultiplierRange(scanner, true); | ||
case '{': | ||
var start = pos; | ||
var range = readRange(); | ||
if (range) { | ||
return { | ||
comma: false, | ||
min: range.min, | ||
max: range.max, | ||
value: str.substring(start, pos) | ||
}; | ||
} | ||
break; | ||
case LEFTCURLYBRACKET: | ||
return readMultiplierRange(scanner, false); | ||
} | ||
return { | ||
comma: false, | ||
min: 1, | ||
max: 1, | ||
value: '' | ||
}; | ||
return MULTIPLIER_DEFAULT; | ||
} | ||
function readProperty(str, start) { | ||
var end = start; | ||
var multiplier; | ||
function readProperty(scanner) { | ||
var name; | ||
end += eat(str, end, '<'); | ||
end += eat(str, end, '\''); | ||
scanner.eat(LESSTHANSIGN); | ||
scanner.eat(APOSTROPHE); | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
name = scanWord(scanner); | ||
end += eat(str, end, '\''); | ||
end += eat(str, end, '>'); | ||
scanner.eat(APOSTROPHE); | ||
scanner.eat(GREATERTHANSIGN); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: 'Property', | ||
name: name, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
function readType(str, start) { | ||
var end = start; | ||
var multiplier; | ||
function readType(scanner) { | ||
var name; | ||
end += eat(str, end, '<'); | ||
scanner.eat(LESSTHANSIGN); | ||
name = scanWord(scanner); | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
if (str.charAt(end) === '(' && str.charAt(end + 1) === ')') { | ||
if (scanner.charCode() === LEFTPARENTHESIS && | ||
scanner.nextCharCode() === RIGHTPARENTHESIS) { | ||
scanner.pos += 2; | ||
name += '()'; | ||
end += 2; | ||
} | ||
end += eat(str, end, '>'); | ||
scanner.eat(GREATERTHANSIGN); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: 'Type', | ||
name: name, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
function readKeywordOrFunction(str, start) { | ||
var end = start; | ||
function readKeywordOrFunction(scanner) { | ||
var sequence = null; | ||
var multiplier; | ||
var name; | ||
name = readWord(str, end) || error(str, end); | ||
end += name.length; | ||
name = scanWord(scanner); | ||
if (str.charAt(end) === '(') { | ||
sequence = readSequence(str, end + 1); | ||
end += sequence.value.length + 1; | ||
if (scanner.charCode() === LEFTPARENTHESIS) { | ||
scanner.pos++; | ||
sequence = readSequence(scanner); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
end += eat(str, end, ')'); | ||
return { | ||
type: 'Function', | ||
name: name, | ||
sequence: sequence, | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
} | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
return { | ||
type: sequence ? 'Function' : 'Keyword', | ||
type: 'Keyword', | ||
name: name, | ||
sequence: sequence, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: readMultiplier(scanner) | ||
}; | ||
@@ -283,19 +319,19 @@ } | ||
function readSequence(str, start) { | ||
function readSequence(scanner) { | ||
var terms = []; | ||
var combinators = {}; | ||
var end = start; | ||
var token; | ||
var lastToken; | ||
var prevToken = null; | ||
var prevTokenPos = scanner.pos; | ||
while (token = peek(str, end)) { | ||
while (token = peek(scanner)) { | ||
if (token.type !== 'Spaces') { | ||
if (token.type === 'Combinator') { | ||
// check for combinator in group beginning and double combinator sequence | ||
if (!lastToken || lastToken.type === 'Combinator') { | ||
error(str, end); | ||
if (prevToken === null || prevToken.type === 'Combinator') { | ||
error(scanner, prevTokenPos, 'Unexpected combinator'); | ||
} | ||
combinators[token.value] = true; | ||
} else if (lastToken && lastToken.type !== 'Combinator') { | ||
} else if (prevToken !== null && prevToken.type !== 'Combinator') { | ||
combinators[' '] = true; // a b | ||
@@ -309,11 +345,10 @@ terms.push({ | ||
terms.push(token); | ||
lastToken = token; | ||
prevToken = token; | ||
prevTokenPos = scanner.pos; | ||
} | ||
end += token.value.length; | ||
} | ||
// check for combinator in group ending | ||
if (lastToken && lastToken.type === 'Combinator') { | ||
error(str, end - lastToken.value.length); | ||
if (prevToken !== null && prevToken.type === 'Combinator') { | ||
error(scanner, scanner.pos - prevTokenPos, 'Unexpected combinator'); | ||
} | ||
@@ -324,25 +359,19 @@ | ||
terms: terms, | ||
combinator: regroupTerms(terms, combinators) || ' ', | ||
value: str.substring(start, end) | ||
combinator: regroupTerms(terms, combinators) || ' ' | ||
}; | ||
} | ||
function readGroup(str, start) { | ||
function readGroup(scanner) { | ||
var sequence; | ||
var nonEmpty = false; | ||
var multiplier; | ||
var end = start; | ||
end += eat(str, end, '['); | ||
scanner.eat(LEFTSQUAREBRACKET); | ||
sequence = readSequence(scanner); | ||
sequence = readSequence(str, end); | ||
end += sequence.value.length; | ||
scanner.eat(RIGHTSQUAREBRACKET); | ||
multiplier = readMultiplier(scanner); | ||
end += eat(str, end, ']'); | ||
multiplier = readMultiplier(str, end); | ||
end += multiplier.value.length; | ||
if (str.charAt(end) === '!') { | ||
end++; | ||
if (scanner.charCode() === EXCLAMATIONMARK) { | ||
scanner.pos++; | ||
nonEmpty = true; | ||
@@ -356,60 +385,40 @@ } | ||
nonEmpty: nonEmpty, | ||
comma: multiplier.comma, | ||
min: multiplier.min, | ||
max: multiplier.max, | ||
value: str.substring(start, end) | ||
multiplier: multiplier | ||
}; | ||
} | ||
function readSpaces(str, start) { | ||
var end = start + 1; | ||
while (spaces.test(str.charAt(end))) { | ||
end++; | ||
} | ||
return { | ||
type: 'Spaces', | ||
value: str.substring(start, end) | ||
}; | ||
} | ||
function peek(scanner) { | ||
var code = scanner.charCode(); | ||
function peek(str, start) { | ||
var c = str.charAt(start); | ||
var end = start + 1; | ||
if (spaces.test(c)) { | ||
return readSpaces(str, start); | ||
if (code < 128 && NAME_CHAR[code] === 1) { | ||
return readKeywordOrFunction(scanner); | ||
} | ||
if (nameChar.test(c)) { | ||
return readKeywordOrFunction(str, start); | ||
} | ||
switch (code) { | ||
case LEFTSQUAREBRACKET: | ||
return readGroup(scanner); | ||
switch (c) { | ||
case '[': | ||
return readGroup(str, start); | ||
case '<': | ||
if (str.charAt(start + 1) === '\'') { | ||
return readProperty(str, start); | ||
case LESSTHANSIGN: | ||
if (scanner.nextCharCode() === APOSTROPHE) { | ||
return readProperty(scanner); | ||
} else { | ||
return readType(str, start); | ||
return readType(scanner); | ||
} | ||
case '|': | ||
end += str.charAt(end) === '|'; | ||
case VERTICALLINE: | ||
return { | ||
type: 'Combinator', | ||
value: str.substring(start, end) | ||
value: scanner.substringToPos(scanner.nextCharCode() === VERTICALLINE ? scanner.pos + 2 : scanner.pos + 1) | ||
}; | ||
case '&': | ||
if (str.charAt(end) === '&') { | ||
return { | ||
type: 'Combinator', | ||
value: '&&' | ||
}; | ||
} | ||
break; | ||
case AMPERSAND: | ||
scanner.pos++; | ||
scanner.eat(AMPERSAND); | ||
return { | ||
type: 'Combinator', | ||
value: '&&' | ||
}; | ||
case ',': | ||
case COMMA: | ||
scanner.pos++; | ||
return { | ||
@@ -420,3 +429,4 @@ type: 'Comma', | ||
case '/': | ||
case SOLIDUS: | ||
scanner.pos++; | ||
return { | ||
@@ -427,3 +437,4 @@ type: 'Slash', | ||
case '%': // looks like exception, needs for attr()'s <type-or-unit> | ||
case PERCENTSIGN: // looks like exception, needs for attr()'s <type-or-unit> | ||
scanner.pos++; | ||
return { | ||
@@ -434,38 +445,40 @@ type: 'Percent', | ||
case '(': | ||
var sequence = readSequence(str, end); | ||
end += sequence.value.length; | ||
end += eat(str, end, ')'); | ||
case LEFTPARENTHESIS: | ||
scanner.pos++; | ||
var sequence = readSequence(scanner); | ||
scanner.eat(RIGHTPARENTHESIS); | ||
return { | ||
type: 'Parentheses', | ||
sequence: sequence, | ||
value: str.substring(start, end) | ||
sequence: sequence | ||
}; | ||
case '\'': | ||
case APOSTROPHE: | ||
return { | ||
type: 'String', | ||
value: readString(str, start) | ||
value: scanString(scanner) | ||
}; | ||
} | ||
} | ||
function eat(str, pos, ch) { | ||
if (str.charAt(pos) !== ch) { | ||
error(str, pos, 'Expect `' + ch + '`'); | ||
case SPACE: | ||
case TAB: | ||
case N: | ||
case R: | ||
case F: | ||
return { | ||
type: 'Spaces', | ||
value: scanSpaces(scanner) | ||
}; | ||
} | ||
return 1; | ||
} | ||
function error(str, pos, msg) { | ||
throw new SyntaxParseError(msg || 'Unexpected input', str, pos); | ||
function error(scanner, pos, msg) { | ||
throw new SyntaxParseError(msg || 'Unexpected input', scanner.str, pos); | ||
} | ||
module.exports = function parse(str) { | ||
var result = readSequence(str, 0); | ||
function parse(str) { | ||
var scanner = new Scanner(str); | ||
var result = readSequence(scanner); | ||
if (result.value !== str) { | ||
error(str, result.value.length); | ||
if (scanner.pos !== str.length) { | ||
error(scanner, scanner.pos); | ||
} | ||
@@ -479,2 +492,8 @@ | ||
return result; | ||
}; | ||
} | ||
// warm up parse to elimitate code branches that never execute | ||
// fix soft deoptimizations (insufficient type feedback) | ||
parse('[a&&<b>#|<\'c\'>*||e(){2,} f{2} /,(% g#{1,2})]!'); | ||
module.exports = parse; |
var MatchError = require('./error').MatchError; | ||
var walkAst = require('../utils/walk').all; | ||
var names = require('../utils/names'); | ||
var generic = require('./generic'); | ||
var parse = require('./parse'); | ||
var stringify = require('./stringify'); | ||
var translate = require('./translate'); | ||
var walk = require('./walk'); | ||
@@ -16,3 +17,3 @@ var match = require('./match'); | ||
if (map[name].syntax) { | ||
result[name] = syntaxAsAst ? map[name].syntax : stringify(map[name].syntax); | ||
result[name] = syntaxAsAst ? map[name].syntax : translate(map[name].syntax); | ||
} | ||
@@ -28,5 +29,26 @@ } | ||
function valueHasVar(value) { | ||
var hasVar = false; | ||
walkAst(value, function(node) { | ||
if (node.type === 'Function' && node.name.toLowerCase() === 'var') { | ||
hasVar = true; | ||
} | ||
}); | ||
return hasVar; | ||
} | ||
function matchSyntax(dictionary, syntax, value) { | ||
var result = match(dictionary, dictionary.valueCommonSyntax, value.sequence.head); | ||
var result; | ||
if (valueHasVar(value)) { | ||
return { | ||
type: 'NoMatch', | ||
comment: 'Due to matching for value with var() is very complex those values are always valid for now' | ||
}; | ||
} | ||
result = match(dictionary, dictionary.valueCommonSyntax, value.sequence.head); | ||
if (!result.match) { | ||
@@ -83,2 +105,3 @@ result = syntax.match(value.sequence.head); | ||
createDescriptor: function(syntax) { | ||
var self = this; | ||
var descriptor = { | ||
@@ -90,6 +113,32 @@ syntax: null, | ||
if (typeof syntax === 'function') { | ||
descriptor.match = syntax; | ||
descriptor.match = function(node) { | ||
if (node && syntax(node)) { | ||
return { | ||
badNode: null, | ||
lastNode: null, | ||
next: node.next, | ||
match: [node.data] | ||
}; | ||
} | ||
return null; | ||
}; | ||
} else { | ||
descriptor.syntax = typeof syntax === 'string' ? parse(syntax) : syntax; | ||
descriptor.match = match.bind(null, this, descriptor.syntax); | ||
if (typeof syntax === 'string') { | ||
Object.defineProperty(descriptor, 'syntax', { | ||
get: function() { | ||
Object.defineProperty(descriptor, 'syntax', { | ||
value: parse(syntax) | ||
}); | ||
return descriptor.syntax; | ||
} | ||
}); | ||
} else { | ||
descriptor.syntax = syntax; | ||
} | ||
descriptor.match = function(ast) { | ||
return match(self, descriptor.syntax, ast); | ||
}; | ||
} | ||
@@ -96,0 +145,0 @@ |
@@ -30,18 +30,16 @@ // | ||
Object.defineProperty(List.prototype, 'size', { | ||
get: function() { | ||
var size = 0; | ||
var cursor = this.head; | ||
List.createItem = createItem; | ||
List.prototype.createItem = createItem; | ||
while (cursor) { | ||
size++; | ||
cursor = cursor.next; | ||
} | ||
List.prototype.getSize = function() { | ||
var size = 0; | ||
var cursor = this.head; | ||
return size; | ||
while (cursor) { | ||
size++; | ||
cursor = cursor.next; | ||
} | ||
}); | ||
List.createItem = createItem; | ||
List.prototype.createItem = createItem; | ||
return size; | ||
}; | ||
@@ -82,6 +80,5 @@ List.prototype.fromArray = function(array) { | ||
}; | ||
List.prototype.toJSON = function() { | ||
return this.toArray(); | ||
}; | ||
List.prototype.toJSON = List.prototype.toArray; | ||
List.prototype.isEmpty = function() { | ||
@@ -88,0 +85,0 @@ return this.head === null; |
@@ -153,3 +153,3 @@ function each(list) { | ||
case 'Progid': | ||
return translate(node.value); | ||
return node.value; | ||
@@ -156,0 +156,0 @@ case 'Combinator': |
@@ -227,3 +227,3 @@ var SourceMapGenerator = require('source-map').SourceMapGenerator; | ||
case 'Progid': | ||
return translate(node.value); | ||
return node.value; | ||
@@ -230,0 +230,0 @@ case 'Combinator': |
@@ -175,3 +175,2 @@ function walkRules(node, item, list) { | ||
case 'Url': | ||
case 'Progid': | ||
walkAll.call(this, node.value); | ||
@@ -181,2 +180,3 @@ break; | ||
// nothig to do with | ||
// case 'Progid': | ||
// case 'Property': | ||
@@ -183,0 +183,0 @@ // case 'Combinator': |
{ | ||
"name": "css-tree", | ||
"version": "1.0.0-alpha7", | ||
"version": "1.0.0-alpha8", | ||
"description": "Detailed CSS parser", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -0,1 +1,7 @@ | ||
<img align="right" width="111" height="111" | ||
alt="CSSTree logo" | ||
src="https://cloud.githubusercontent.com/assets/270491/19243723/6f9136c6-8f21-11e6-82ac-eeeee4c6c452.png"/> | ||
# CSSTree | ||
[![NPM version](https://img.shields.io/npm/v/css-tree.svg)](https://www.npmjs.com/package/css-tree) | ||
@@ -7,6 +13,12 @@ [![Build Status](https://travis-ci.org/csstree/csstree.svg?branch=master)](https://travis-ci.org/csstree/csstree) | ||
Fast detailed CSS parser | ||
> Work in progress | ||
Docs and tools: | ||
* [CSS syntax reference](https://csstree.github.io/docs/syntax.html) | ||
* [CSS syntax validator](https://csstree.github.io/docs/validator.html) | ||
Related: | ||
Related projects: | ||
@@ -18,4 +30,4 @@ * [csstree-validator](https://github.com/csstree/validator) – NPM package to validate CSS | ||
* [Sublime plugin](https://github.com/csstree/SublimeLinter-contrib-csstree) | ||
* [VS Code plugin](https://github.com/csstree/vscode-csstree) | ||
* [Atom plugin](https://github.com/csstree/atom-csstree-validator) | ||
* [VS Code plugin](https://github.com/csstree/vscode-plugin) | ||
* [Atom plugin](https://github.com/csstree/atom-plugin) | ||
@@ -191,2 +203,2 @@ ## Install | ||
[Template:CSSData](https://developer.mozilla.org/en-US/docs/Template:CSSData) by Mozilla Contributors is licensed under [CC-BY-SA 2.5](http://creativecommons.org/licenses/by-sa/2.5/) | ||
Syntax matching use [mdn/data](https://github.com/mdn/data) dictionaries by Mozilla Contributors |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33
202
526790
11989
1