Complete rewrite of the CSS example grammar
This is a complete rewrite of the CSS example grammar. It is now based on CSS 2.1 *including the errata* and the generated parser builds a nicer syntax tree. There is also a number of cleanups, formatting changes, naming changes, and bug fixes. Beside this, the rewrite reflects how I write grammars today (as opposed to few years ago) and what style I would recommend to others.
This commit is contained in:
parent
18f92c5647
commit
2005345976
|
@ -1,48 +1,83 @@
|
|||
/*
|
||||
* CSS parser based on the grammar described at http://www.w3.org/TR/CSS2/grammar.html.
|
||||
* CSS Grammar
|
||||
* ===========
|
||||
*
|
||||
* The parser builds a tree representing the parsed CSS, composed of basic
|
||||
* JavaScript values, arrays and objects (basically JSON). It can be easily
|
||||
* used by various CSS processors, transformers, etc.
|
||||
* Based on grammar from CSS 2.1 specification [1] (including the errata [2]).
|
||||
* Generated parser builds a syntax tree composed of nested JavaScript objects,
|
||||
* vaguely inspired by CSS DOM [3]. The CSS DOM itself wasn't used as it is not
|
||||
* expressive enough (e.g. selectors are reflected as text, not structured
|
||||
* objects) and somewhat cumbersome.
|
||||
*
|
||||
* Note that the parser does not handle errors in CSS according to the
|
||||
* specification -- many errors which it should recover from (e.g. malformed
|
||||
* declarations or unexpected end of stylesheet) are simply fatal. This is a
|
||||
* result of straightforward rewrite of the CSS grammar to PEG.js and it should
|
||||
* be fixed sometimes.
|
||||
* Limitations:
|
||||
*
|
||||
* * Many errors which should be recovered from according to the specification
|
||||
* (e.g. malformed declarations or unexpected end of stylesheet) are fatal.
|
||||
* This is a result of straightforward rewrite of the CSS grammar to PEG.js.
|
||||
*
|
||||
* [1] http://www.w3.org/TR/2011/REC-CSS2-20110607
|
||||
* [2] http://www.w3.org/Style/css2-updates/REC-CSS2-20110607-errata.html
|
||||
* [3] http://www.w3.org/TR/DOM-Level-2-Style/css.html
|
||||
*/
|
||||
|
||||
/* ===== Syntactical Elements ===== */
|
||||
{
|
||||
function extractOptional(optional, index) {
|
||||
return optional ? optional[index] : null;
|
||||
}
|
||||
|
||||
function extractList(list, index) {
|
||||
var result = [], i;
|
||||
|
||||
for (i = 0; i < list.length; i++) {
|
||||
if (list[i][index] !== null) {
|
||||
result.push(list[i][index]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildList(first, rest, index) {
|
||||
return (first !== null ? [first] : []).concat(extractList(rest, index));
|
||||
}
|
||||
|
||||
function buildExpression(first, rest) {
|
||||
var result = first, i;
|
||||
|
||||
for (i = 0; i < rest.length; i++) {
|
||||
result = {
|
||||
type: "Expression",
|
||||
operator: rest[i][0],
|
||||
left: result,
|
||||
right: rest[i][1]
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
start
|
||||
= stylesheet:stylesheet comment* { return stylesheet; }
|
||||
|
||||
/* ----- G.1 Grammar ----- */
|
||||
|
||||
stylesheet
|
||||
= charset:(CHARSET_SYM STRING ";")? (S / CDO / CDC)*
|
||||
imports:(import (CDO S* / CDC S*)*)*
|
||||
rules:((ruleset / media / page) (CDO S* / CDC S*)*)* {
|
||||
var importsConverted = [];
|
||||
for (var i = 0; i < imports.length; i++) {
|
||||
importsConverted.push(imports[i][0]);
|
||||
}
|
||||
|
||||
var rulesConverted = [];
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
rulesConverted.push(rules[i][0]);
|
||||
}
|
||||
|
||||
rules:((ruleset / media / page) (CDO S* / CDC S*)*)*
|
||||
{
|
||||
return {
|
||||
type: "stylesheet",
|
||||
charset: charset !== null ? charset[1] : null,
|
||||
imports: importsConverted,
|
||||
rules: rulesConverted
|
||||
type: "StyleSheet",
|
||||
charset: extractOptional(charset, 1),
|
||||
imports: extractList(imports, 0),
|
||||
rules: extractList(rules, 0)
|
||||
};
|
||||
}
|
||||
|
||||
import
|
||||
= IMPORT_SYM S* href:(STRING / URI) S* media:media_list? ";" S* {
|
||||
return {
|
||||
type: "import_rule",
|
||||
type: "ImportRule",
|
||||
href: href,
|
||||
media: media !== null ? media : []
|
||||
};
|
||||
|
@ -51,46 +86,34 @@ import
|
|||
media
|
||||
= MEDIA_SYM S* media:media_list "{" S* rules:ruleset* "}" S* {
|
||||
return {
|
||||
type: "media_rule",
|
||||
type: "MediaRule",
|
||||
media: media,
|
||||
rules: rules
|
||||
};
|
||||
}
|
||||
|
||||
media_list
|
||||
= head:medium tail:("," S* medium)* {
|
||||
var result = [head];
|
||||
for (var i = 0; i < tail.length; i++) {
|
||||
result.push(tail[i][2]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
= first:medium rest:("," S* medium)* { return buildList(first, rest, 2); }
|
||||
|
||||
medium
|
||||
= ident:IDENT S* { return ident; }
|
||||
= name:IDENT S* { return name; }
|
||||
|
||||
page
|
||||
= PAGE_SYM S* qualifier:pseudo_page?
|
||||
= PAGE_SYM S* selector:pseudo_page?
|
||||
"{" S*
|
||||
declarationsHead:declaration?
|
||||
declarationsTail:(";" S* declaration?)*
|
||||
"}" S* {
|
||||
var declarations = declarationsHead !== null ? [declarationsHead] : [];
|
||||
for (var i = 0; i < declarationsTail.length; i++) {
|
||||
if (declarationsTail[i][2] !== null) {
|
||||
declarations.push(declarationsTail[i][2]);
|
||||
}
|
||||
}
|
||||
|
||||
declarationsFirst:declaration?
|
||||
declarationsRest:(";" S* declaration?)*
|
||||
"}" S*
|
||||
{
|
||||
return {
|
||||
type: "page_rule",
|
||||
qualifier: qualifier,
|
||||
declarations: declarations
|
||||
type: "PageRule",
|
||||
selector: selector,
|
||||
declarations: buildList(declarationsFirst, declarationsRest, 2)
|
||||
};
|
||||
}
|
||||
|
||||
pseudo_page
|
||||
= ":" ident:IDENT S* { return ident; }
|
||||
= ":" value:IDENT S* { return { type: "PseudoSelector", value: value }; }
|
||||
|
||||
operator
|
||||
= "/" S* { return "/"; }
|
||||
|
@ -100,51 +123,36 @@ combinator
|
|||
= "+" S* { return "+"; }
|
||||
/ ">" S* { return ">"; }
|
||||
|
||||
unary_operator
|
||||
= "+"
|
||||
/ "-"
|
||||
|
||||
property
|
||||
= ident:IDENT S* { return ident; }
|
||||
= name:IDENT S* { return name; }
|
||||
|
||||
ruleset
|
||||
= selectorsHead:selector
|
||||
selectorsTail:("," S* selector)*
|
||||
= selectorsFirst:selector
|
||||
selectorsRest:("," S* selector)*
|
||||
"{" S*
|
||||
declarationsHead:declaration?
|
||||
declarationsTail:(";" S* declaration?)*
|
||||
"}" S* {
|
||||
var selectors = [selectorsHead];
|
||||
for (var i = 0; i < selectorsTail.length; i++) {
|
||||
selectors.push(selectorsTail[i][2]);
|
||||
}
|
||||
|
||||
var declarations = declarationsHead !== null ? [declarationsHead] : [];
|
||||
for (i = 0; i < declarationsTail.length; i++) {
|
||||
if (declarationsTail[i][2] !== null) {
|
||||
declarations.push(declarationsTail[i][2]);
|
||||
}
|
||||
}
|
||||
|
||||
declarationsFirst:declaration?
|
||||
declarationsRest:(";" S* declaration?)*
|
||||
"}" S*
|
||||
{
|
||||
return {
|
||||
type: "ruleset",
|
||||
selectors: selectors,
|
||||
declarations: declarations
|
||||
type: "RuleSet",
|
||||
selectors: buildList(selectorsFirst, selectorsRest, 2),
|
||||
declarations: buildList(declarationsFirst, declarationsRest, 2)
|
||||
};
|
||||
}
|
||||
|
||||
selector
|
||||
= left:simple_selector S* combinator:combinator right:selector {
|
||||
return {
|
||||
type: "selector",
|
||||
type: "Selector",
|
||||
combinator: combinator,
|
||||
left: left,
|
||||
right: right
|
||||
};
|
||||
}
|
||||
/ left:simple_selector S* right:selector {
|
||||
/ left:simple_selector S+ right:selector {
|
||||
return {
|
||||
type: "selector",
|
||||
type: "Selector",
|
||||
combinator: " ",
|
||||
left: left,
|
||||
right: right
|
||||
|
@ -153,51 +161,42 @@ selector
|
|||
/ selector:simple_selector S* { return selector; }
|
||||
|
||||
simple_selector
|
||||
= element:element_name
|
||||
qualifiers:(
|
||||
id:HASH { return { type: "ID selector", id: id.substr(1) }; }
|
||||
/ class
|
||||
/ attrib
|
||||
/ pseudo
|
||||
)* {
|
||||
= element:element_name qualifiers:(id / class / attrib / pseudo)* {
|
||||
return {
|
||||
type: "simple_selector",
|
||||
type: "SimpleSelector",
|
||||
element: element,
|
||||
qualifiers: qualifiers
|
||||
};
|
||||
}
|
||||
/ qualifiers:(
|
||||
id:HASH { return { type: "ID selector", id: id.substr(1) }; }
|
||||
/ class
|
||||
/ attrib
|
||||
/ pseudo
|
||||
)+ {
|
||||
/ qualifiers:(id / class / attrib / pseudo)+ {
|
||||
return {
|
||||
type: "simple_selector",
|
||||
type: "SimpleSelector",
|
||||
element: "*",
|
||||
qualifiers: qualifiers
|
||||
};
|
||||
}
|
||||
|
||||
id
|
||||
= id:HASH { return { type: "IDSelector", id: id }; }
|
||||
|
||||
class
|
||||
= "." class_:IDENT { return { type: "class_selector", "class": class_ }; }
|
||||
= "." class_:IDENT { return { type: "ClassSelector", class: class_ }; }
|
||||
|
||||
element_name
|
||||
= IDENT / '*'
|
||||
= IDENT
|
||||
/ "*"
|
||||
|
||||
attrib
|
||||
= "[" S*
|
||||
attribute:IDENT S*
|
||||
operatorAndValue:(
|
||||
('=' / INCLUDES / DASHMATCH) S*
|
||||
(IDENT / STRING) S*
|
||||
)?
|
||||
"]" {
|
||||
operatorAndValue:(("=" / INCLUDES / DASHMATCH) S* (IDENT / STRING) S*)?
|
||||
"]"
|
||||
{
|
||||
return {
|
||||
type: "attribute_selector",
|
||||
type: "AttributeSelector",
|
||||
attribute: attribute,
|
||||
operator: operatorAndValue !== null ? operatorAndValue[0] : null,
|
||||
value: operatorAndValue !== null ? operatorAndValue[2] : null
|
||||
operator: extractOptional(operatorAndValue, 0),
|
||||
value: extractOptional(operatorAndValue, 2)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -206,31 +205,22 @@ pseudo
|
|||
value:(
|
||||
name:FUNCTION S* params:(IDENT S*)? ")" {
|
||||
return {
|
||||
type: "function",
|
||||
type: "Function",
|
||||
name: name,
|
||||
params: params !== null ? [params[0]] : []
|
||||
};
|
||||
}
|
||||
/ IDENT
|
||||
) {
|
||||
/*
|
||||
* The returned object has somewhat vague property names and values because
|
||||
* the rule matches both pseudo-classes and pseudo-elements (they look the
|
||||
* same at the syntactic level).
|
||||
*/
|
||||
return {
|
||||
type: "pseudo_selector",
|
||||
value: value
|
||||
};
|
||||
}
|
||||
)
|
||||
{ return { type: "PseudoSelector", value: value }; }
|
||||
|
||||
declaration
|
||||
= property:property ":" S* expression:expr important:prio? {
|
||||
= name:property ':' S* value:expr prio:prio? {
|
||||
return {
|
||||
type: "declaration",
|
||||
property: property,
|
||||
expression: expression,
|
||||
important: important !== null ? true : false
|
||||
type: "Declaration",
|
||||
name: name,
|
||||
value: value,
|
||||
important: prio !== null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -238,63 +228,41 @@ prio
|
|||
= IMPORTANT_SYM S*
|
||||
|
||||
expr
|
||||
= head:term tail:(operator? term)* {
|
||||
var result = head;
|
||||
for (var i = 0; i < tail.length; i++) {
|
||||
result = {
|
||||
type: "expression",
|
||||
operator: tail[i][0],
|
||||
left: result,
|
||||
right: tail[i][1]
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
= first:term rest:(operator? term)* { return buildExpression(first, rest); }
|
||||
|
||||
term
|
||||
= operator:unary_operator?
|
||||
value:(
|
||||
EMS S*
|
||||
/ EXS S*
|
||||
/ LENGTH S*
|
||||
/ ANGLE S*
|
||||
/ TIME S*
|
||||
/ FREQ S*
|
||||
/ PERCENTAGE S*
|
||||
/ NUMBER S*
|
||||
) {
|
||||
= quantity:(PERCENTAGE / LENGTH / EMS / EXS / ANGLE / TIME / FREQ / NUMBER)
|
||||
S*
|
||||
{
|
||||
return {
|
||||
type: "value",
|
||||
value: (operator !== null ? operator : "") + value[0]
|
||||
type: "Quantity",
|
||||
value: quantity.value,
|
||||
unit: quantity.unit
|
||||
};
|
||||
}
|
||||
/ value:URI S* { return { type: "uri", value: value }; }
|
||||
/ value:STRING S* { return { type: "String", value: value }; }
|
||||
/ value:URI S* { return { type: "URI", value: value }; }
|
||||
/ function
|
||||
/ hexcolor
|
||||
/ value:STRING S* { return { type: "string", value: value }; }
|
||||
/ value:IDENT S* { return { type: "ident", value: value }; }
|
||||
/ value:IDENT S* { return { type: "Ident", value: value }; }
|
||||
|
||||
function
|
||||
= name:FUNCTION S* params:expr ")" S* {
|
||||
return {
|
||||
type: "function",
|
||||
name: name,
|
||||
params: params
|
||||
};
|
||||
return { type: "Function", name: name, params: params };
|
||||
}
|
||||
|
||||
hexcolor
|
||||
= value:HASH S* { return { type: "hexcolor", value: value}; }
|
||||
= value:HASH S* { return { type: "Hexcolor", value: value }; }
|
||||
|
||||
/* ===== Lexical Elements ===== */
|
||||
/* ----- G.2 Lexical scanner ----- */
|
||||
|
||||
/* Macros */
|
||||
|
||||
h
|
||||
= [0-9a-fA-F]
|
||||
= [0-9a-f]i
|
||||
|
||||
nonascii
|
||||
= [\x80-\xFF]
|
||||
= [\x80-\uFFFF]
|
||||
|
||||
unicode
|
||||
= "\\" digits:$(h h? h? h? h? h?) ("\r\n" / [ \t\r\n\f])? {
|
||||
|
@ -303,31 +271,25 @@ unicode
|
|||
|
||||
escape
|
||||
= unicode
|
||||
/ "\\" char_:[^\r\n\f0-9a-fA-F] { return char_; }
|
||||
/ "\\" ch:[^\r\n\f0-9a-f]i { return ch; }
|
||||
|
||||
nmstart
|
||||
= [_a-zA-Z]
|
||||
= [_a-z]i
|
||||
/ nonascii
|
||||
/ escape
|
||||
|
||||
nmchar
|
||||
= [_a-zA-Z0-9-]
|
||||
= [_a-z0-9-]i
|
||||
/ nonascii
|
||||
/ escape
|
||||
|
||||
integer
|
||||
= parts:$[0-9]+ { return parts; }
|
||||
|
||||
float
|
||||
= parts:$([0-9]* "." [0-9]+) { return parts; }
|
||||
|
||||
string1
|
||||
= '"' chars:([^\n\r\f\\"] / "\\" nl:nl { return nl } / escape)* '"' {
|
||||
= '"' chars:([^\n\r\f\\"] / "\\" nl:nl { return ""; } / escape)* '"' {
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
string2
|
||||
= "'" chars:([^\n\r\f\\'] / "\\" nl:nl { return nl } / escape)* "'" {
|
||||
= "'" chars:([^\n\r\f\\'] / "\\" nl:nl { return ""; } / escape)* "'" {
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
|
@ -335,23 +297,24 @@ comment
|
|||
= "/*" [^*]* "*"+ ([^/*] [^*]* "*"+)* "/"
|
||||
|
||||
ident
|
||||
= dash:"-"? nmstart:nmstart nmchars:nmchar* {
|
||||
return (dash !== null ? dash : "") + nmstart + nmchars.join("");
|
||||
= prefix:$"-"? start:nmstart chars:nmchar* {
|
||||
return prefix + start + chars.join("");
|
||||
}
|
||||
|
||||
name
|
||||
= nmchars:nmchar+ { return nmchars.join(""); }
|
||||
= chars:nmchar+ { return chars.join(""); }
|
||||
|
||||
num
|
||||
= float
|
||||
/ integer
|
||||
= [+-]? ([0-9]+ / [0-9]* "." [0-9]+) ("e" [+-]? [0-9]+)? {
|
||||
return parseFloat(text());
|
||||
}
|
||||
|
||||
string
|
||||
= string1
|
||||
/ string2
|
||||
|
||||
url
|
||||
= chars:([!#$%&*-~] / nonascii / escape)* { return chars.join(""); }
|
||||
= chars:([!#$%&*-\[\]-~] / nonascii / escape)* { return chars.join(""); }
|
||||
|
||||
s
|
||||
= [ \t\r\n\f]+
|
||||
|
@ -365,115 +328,25 @@ nl
|
|||
/ "\r"
|
||||
/ "\f"
|
||||
|
||||
A
|
||||
= [aA]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "41" ("\r\n" / [ \t\r\n\f])? { return "A"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "61" ("\r\n" / [ \t\r\n\f])? { return "a"; }
|
||||
|
||||
C
|
||||
= [cC]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "43" ("\r\n" / [ \t\r\n\f])? { return "C"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "63" ("\r\n" / [ \t\r\n\f])? { return "c"; }
|
||||
|
||||
D
|
||||
= [dD]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "44" ("\r\n" / [ \t\r\n\f])? { return "D"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "64" ("\r\n" / [ \t\r\n\f])? { return "d"; }
|
||||
|
||||
E
|
||||
= [eE]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "45" ("\r\n" / [ \t\r\n\f])? { return "E"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "65" ("\r\n" / [ \t\r\n\f])? { return "e"; }
|
||||
|
||||
G
|
||||
= [gG]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "47" ("\r\n" / [ \t\r\n\f])? { return "G"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "67" ("\r\n" / [ \t\r\n\f])? { return "g"; }
|
||||
/ "\\" char_:[gG] { return char_; }
|
||||
|
||||
H
|
||||
= h:[hH]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "48" ("\r\n" / [ \t\r\n\f])? { return "H"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "68" ("\r\n" / [ \t\r\n\f])? { return "h"; }
|
||||
/ "\\" char_:[hH] { return char_; }
|
||||
|
||||
I
|
||||
= i:[iI]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "49" ("\r\n" / [ \t\r\n\f])? { return "I"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "69" ("\r\n" / [ \t\r\n\f])? { return "i"; }
|
||||
/ "\\" char_:[iI] { return char_; }
|
||||
|
||||
K
|
||||
= [kK]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "4" [bB] ("\r\n" / [ \t\r\n\f])? { return "K"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "6" [bB] ("\r\n" / [ \t\r\n\f])? { return "k"; }
|
||||
/ "\\" char_:[kK] { return char_; }
|
||||
|
||||
L
|
||||
= [lL]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "4" [cC] ("\r\n" / [ \t\r\n\f])? { return "L"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "6" [cC] ("\r\n" / [ \t\r\n\f])? { return "l"; }
|
||||
/ "\\" char_:[lL] { return char_; }
|
||||
|
||||
M
|
||||
= [mM]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "4" [dD] ("\r\n" / [ \t\r\n\f])? { return "M"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "6" [dD] ("\r\n" / [ \t\r\n\f])? { return "m"; }
|
||||
/ "\\" char_:[mM] { return char_; }
|
||||
|
||||
N
|
||||
= [nN]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "4" [eE] ("\r\n" / [ \t\r\n\f])? { return "N"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "6" [eE] ("\r\n" / [ \t\r\n\f])? { return "n"; }
|
||||
/ "\\" char_:[nN] { return char_; }
|
||||
|
||||
O
|
||||
= [oO]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "4" [fF] ("\r\n" / [ \t\r\n\f])? { return "O"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "6" [fF] ("\r\n" / [ \t\r\n\f])? { return "o"; }
|
||||
/ "\\" char_:[oO] { return char_; }
|
||||
|
||||
P
|
||||
= [pP]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "50" ("\r\n" / [ \t\r\n\f])? { return "P"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "70" ("\r\n" / [ \t\r\n\f])? { return "p"; }
|
||||
/ "\\" char_:[pP] { return char_; }
|
||||
|
||||
R
|
||||
= [rR]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "52" ("\r\n" / [ \t\r\n\f])? { return "R"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "72" ("\r\n" / [ \t\r\n\f])? { return "r"; }
|
||||
/ "\\" char_:[rR] { return char_; }
|
||||
|
||||
S_
|
||||
= [sS]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "53" ("\r\n" / [ \t\r\n\f])? { return "S"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "73" ("\r\n" / [ \t\r\n\f])? { return "s"; }
|
||||
/ "\\" char_:[sS] { return char_; }
|
||||
|
||||
T
|
||||
= [tT]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "54" ("\r\n" / [ \t\r\n\f])? { return "T"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "74" ("\r\n" / [ \t\r\n\f])? { return "t"; }
|
||||
/ "\\" char_:[tT] { return char_; }
|
||||
|
||||
U
|
||||
= [uU]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "55" ("\r\n" / [ \t\r\n\f])? { return "U"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "75" ("\r\n" / [ \t\r\n\f])? { return "u"; }
|
||||
/ "\\" char_:[uU] { return char_; }
|
||||
|
||||
X
|
||||
= [xX]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "58" ("\r\n" / [ \t\r\n\f])? { return "X"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "78" ("\r\n" / [ \t\r\n\f])? { return "x"; }
|
||||
/ "\\" char_:[xX] { return char_; }
|
||||
|
||||
Z
|
||||
= [zZ]
|
||||
/ "\\" "0"? "0"? "0"? "0"? "5" [aA] ("\r\n" / [ \t\r\n\f])? { return "Z"; }
|
||||
/ "\\" "0"? "0"? "0"? "0"? "7" [aA] ("\r\n" / [ \t\r\n\f])? { return "z"; }
|
||||
/ "\\" char_:[zZ] { return char_; }
|
||||
A = "a"i / "\\" "0"? "0"? "0"? "0"? [\x41\x61] ("\r\n" / [ \t\r\n\f])? { return "a"; }
|
||||
C = "c"i / "\\" "0"? "0"? "0"? "0"? [\x43\x63] ("\r\n" / [ \t\r\n\f])? { return "c"; }
|
||||
D = "d"i / "\\" "0"? "0"? "0"? "0"? [\x44\x64] ("\r\n" / [ \t\r\n\f])? { return "d"; }
|
||||
E = "e"i / "\\" "0"? "0"? "0"? "0"? [\x45\x65] ("\r\n" / [ \t\r\n\f])? { return "e"; }
|
||||
G = "g"i / "\\" "0"? "0"? "0"? "0"? [\x47\x67] ("\r\n" / [ \t\r\n\f])? / "\\g"i { return "g"; }
|
||||
H = "h"i / "\\" "0"? "0"? "0"? "0"? [\x48\x68] ("\r\n" / [ \t\r\n\f])? / "\\h"i { return "h"; }
|
||||
I = "i"i / "\\" "0"? "0"? "0"? "0"? [\x49\x69] ("\r\n" / [ \t\r\n\f])? / "\\i"i { return "i"; }
|
||||
K = "k"i / "\\" "0"? "0"? "0"? "0"? [\x4b\x6b] ("\r\n" / [ \t\r\n\f])? / "\\k"i { return "k"; }
|
||||
L = "l"i / "\\" "0"? "0"? "0"? "0"? [\x4c\x6c] ("\r\n" / [ \t\r\n\f])? / "\\l"i { return "l"; }
|
||||
M = "m"i / "\\" "0"? "0"? "0"? "0"? [\x4d\x6d] ("\r\n" / [ \t\r\n\f])? / "\\m"i { return "m"; }
|
||||
N = "n"i / "\\" "0"? "0"? "0"? "0"? [\x4e\x6e] ("\r\n" / [ \t\r\n\f])? / "\\n"i { return "n"; }
|
||||
O = "o"i / "\\" "0"? "0"? "0"? "0"? [\x4f\x6f] ("\r\n" / [ \t\r\n\f])? / "\\o"i { return "o"; }
|
||||
P = "p"i / "\\" "0"? "0"? "0"? "0"? [\x50\x70] ("\r\n" / [ \t\r\n\f])? / "\\p"i { return "p"; }
|
||||
R = "r"i / "\\" "0"? "0"? "0"? "0"? [\x52\x72] ("\r\n" / [ \t\r\n\f])? / "\\r"i { return "r"; }
|
||||
S_ = "s"i / "\\" "0"? "0"? "0"? "0"? [\x53\x73] ("\r\n" / [ \t\r\n\f])? / "\\s"i { return "s"; }
|
||||
T = "t"i / "\\" "0"? "0"? "0"? "0"? [\x54\x74] ("\r\n" / [ \t\r\n\f])? / "\\t"i { return "t"; }
|
||||
U = "u"i / "\\" "0"? "0"? "0"? "0"? [\x55\x75] ("\r\n" / [ \t\r\n\f])? / "\\u"i { return "u"; }
|
||||
X = "x"i / "\\" "0"? "0"? "0"? "0"? [\x58\x78] ("\r\n" / [ \t\r\n\f])? / "\\x"i { return "x"; }
|
||||
Z = "z"i / "\\" "0"? "0"? "0"? "0"? [\x5a\x7a] ("\r\n" / [ \t\r\n\f])? / "\\z"i { return "z"; }
|
||||
|
||||
/* Tokens */
|
||||
|
||||
|
@ -513,45 +386,46 @@ MEDIA_SYM "@media"
|
|||
CHARSET_SYM "@charset"
|
||||
= comment* "@charset "
|
||||
|
||||
/* Note: We replace "w" with "s" here to avoid infinite recursion. */
|
||||
/* We use |s| instead of |w| here to avoid infinite recursion. */
|
||||
IMPORTANT_SYM "!important"
|
||||
= comment* "!" (s / comment)* I M P O R T A N T { return "!important"; }
|
||||
= comment* "!" (s / comment)* I M P O R T A N T
|
||||
|
||||
EMS "length"
|
||||
= comment* num:num e:E m:M { return num + e + m; }
|
||||
= comment* value:num E M { return { value: value, unit: "em" }; }
|
||||
|
||||
EXS "length"
|
||||
= comment* num:num e:E x:X { return num + e + x; }
|
||||
= comment* value:num E X { return { value: value, unit: "ex" }; }
|
||||
|
||||
LENGTH "length"
|
||||
= comment* num:num unit:(P X / C M / M M / I N / P T / P C) {
|
||||
return num + unit.join("");
|
||||
}
|
||||
= comment* value:num P X { return { value: value, unit: "px" }; }
|
||||
/ comment* value:num C M { return { value: value, unit: "cm" }; }
|
||||
/ comment* value:num M M { return { value: value, unit: "mm" }; }
|
||||
/ comment* value:num I N { return { value: value, unit: "in" }; }
|
||||
/ comment* value:num P T { return { value: value, unit: "pt" }; }
|
||||
/ comment* value:num P C { return { value: value, unit: "pc" }; }
|
||||
|
||||
ANGLE "angle"
|
||||
= comment* num:num unit:(D E G / R A D / G R A D) {
|
||||
return num + unit.join("");
|
||||
}
|
||||
= comment* value:num D E G { return { value: value, unit: "deg" }; }
|
||||
/ comment* value:num R A D { return { value: value, unit: "rad" }; }
|
||||
/ comment* value:num G R A D { return { value: value, unit: "grad" }; }
|
||||
|
||||
TIME "time"
|
||||
= comment* num:num unit:(m:M s:S_ { return m + s; } / S_) {
|
||||
return num + unit;
|
||||
}
|
||||
= comment* value:num M S_ { return { value: value, unit: "ms" }; }
|
||||
/ comment* value:num S_ { return { value: value, unit: "s" }; }
|
||||
|
||||
FREQ "frequency"
|
||||
= comment* num:num unit:(H Z / K H Z) { return num + unit.join(""); }
|
||||
|
||||
DIMENSION "dimension"
|
||||
= comment* num:num unit:ident { return num + unit; }
|
||||
= comment* value:num H Z { return { value: value, unit: "hz" }; }
|
||||
/ comment* value:num K H Z { return { value: value, unit: "kh" }; }
|
||||
|
||||
PERCENTAGE "percentage"
|
||||
= comment* parts:$(num "%") { return parts; }
|
||||
= comment* value:num "%" { return { value: value, unit: "%" }; }
|
||||
|
||||
NUMBER "number"
|
||||
= comment* num:num { return num; }
|
||||
= comment* value:num { return { value: value, unit: null }; }
|
||||
|
||||
URI "uri"
|
||||
= comment* U R L "(" w value:(string / url) w ")" { return value; }
|
||||
= comment* U R L "("i w url:string w ")" { return url; }
|
||||
/ comment* U R L "("i w url:url w ")" { return url; }
|
||||
|
||||
FUNCTION "function"
|
||||
= comment* name:ident "(" { return name; }
|
||||
|
|
Loading…
Reference in a new issue