@grammar/css
Version:
CSS3 grammar.
715 lines (701 loc) • 29.6 kB
JavaScript
import $ from './spellu-engine.mjs';
/**
* CSS
* 3.0 - https://www.w3.org/TR/css-syntax-3/
* 2.2 - 2016/4/12 https://www.w3.org/TR/CSS22/
* 1.0 - 2008/4/11 https://www.w3.org/TR/CSS1/
*/
var grammar$css;
(function (grammar$css) {
let ElementCombinator;
(function (ElementCombinator) {
ElementCombinator["Descendant"] = " ";
ElementCombinator["Child"] = ">";
ElementCombinator["NextSibling"] = "+";
ElementCombinator["SubsequentSibling"] = "~";
})(ElementCombinator = grammar$css.ElementCombinator || (grammar$css.ElementCombinator = {}));
let MatchOperator;
(function (MatchOperator) {
MatchOperator["WholeMatch"] = "=";
MatchOperator["PrefixMatch"] = "^=";
MatchOperator["SuffixMatch"] = "$=";
MatchOperator["SubstringMatch"] = "*=";
MatchOperator["Includes"] = "~=";
MatchOperator["DashMatch"] = "|=";
})(MatchOperator = grammar$css.MatchOperator || (grammar$css.MatchOperator = {}));
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
function selectProcessorSuite(suite) {
const items = grammar$css.processors[suite];
return [
items.css,
items.selectors,
items.values,
];
}
grammar$css.selectProcessorSuite = selectProcessorSuite;
function scan(source, suite = grammar$css.ProcessorSuite.AST, rule, options = {}) {
options.processors = selectProcessorSuite(suite);
return $.scan($.createSource(source), rule ?? grammar$css.recipes.css.name, options);
}
grammar$css.scan = scan;
function tokens(source, options = {}) {
return $.flatten(scan(source, grammar$css.ProcessorSuite.CST, undefined, options));
}
grammar$css.tokens = tokens;
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
const printOptions = {
format: 1 /* Pretty */,
};
function print(value, options = {}) {
options = { ...printOptions, ...options };
const printer = new CSSPrinter(options);
return printer.print(value);
}
grammar$css.print = print;
class CSSPrinter extends $.TextPrinter {
constructor(options) {
super({
space: options.format === 1 /* Pretty */ ? ' ' : '',
lineBreak: options.format === 1 /* Pretty */ ? '\n' : '',
indent: options.format === 1 /* Pretty */ ? 'tab' : 0,
});
}
print(value) {
this.node(value);
return this.flash();
}
node(...values) {
for (const value of values) {
const block = this._blocks[value.syntax];
if (!block) {
throw new Error(`syntax "${value.syntax}" is not defined.`);
}
block(this, value);
}
return this;
}
nodeList(list, options, callback) {
if (list.length === 0)
return this;
const nodes = [...list];
const lastNode = nodes.pop();
for (const node of nodes) {
this.node(node);
callback();
}
if (options.tralingSeparator) {
this.node(lastNode);
callback();
}
else {
this.node(lastNode);
}
return this;
}
_blocks = {
["css.StyleSheet" /* StyleSheet */](printer, _) {
printer.node(..._.statements);
},
["css.Import" /* Import */](printer, _) {
printer.text('uu');
},
["css.Ruleset" /* Ruleset */](printer, _) {
printer.nodeList(_.selectors, { tralingSeparator: false }, () => printer.text(',')._())._().text('{').newLine().indentUp();
printer.node(..._.declarations);
printer.indentDown().text('}');
},
["css.Selector" /* Selector */](printer, _) {
_.items.map(_ => printer.node(_));
},
["css.Combinator" /* Combinator */](printer, _) {
printer.text(_.combinator);
},
["css.Tag" /* Tag */](printer, _) {
printer.text(_.name);
},
["css.Id" /* Id */](printer, _) {
printer.text('#', _.name);
},
["css.Class" /* Class */](printer, _) {
printer.text('.', _.name);
},
["css.Attribute" /* Attribute */](printer, _) {
printer.text('[', _.name.value);
if (_.operator)
printer.text(_.operator);
if (_.value)
printer.text('"', _.value.value, '"');
printer.text(']');
},
["css.Declaration" /* Declaration */](printer, _) {
printer.text(_.property.name.value, ':')._().node(_.value)
._(_.important).text(_.important ? '!important' : '', ';').newLine();
},
["css.Identifier" /* Identifier */](printer, _) {
printer.text(_.name.value);
},
["css.ValueList" /* ValueList */](printer, _) {
printer.nodeList(_.items, { tralingSeparator: false }, () => {
if (_.separator === ' ') {
printer.text(_.separator);
}
else {
printer.text(_.separator)._();
}
});
},
["css.Function" /* Function */](printer, _) {
printer.text(_.name.value, '(').nodeList(_.parameters, { tralingSeparator: false }, () => {
printer.text(',')._();
}).text(')');
},
["css.Value" /* Value */](printer, _) {
if (_.token.syntax === "css.token.loose-slash" /* LooseSlashToken */) {
printer.text(_.value);
}
else if (_.token.syntax === "css.token.string" /* StringToken */) {
printer.text('"', _.value, '"');
}
else {
printer.text(_.value);
}
},
};
}
})(grammar$css || (grammar$css = {}));
/**
* CSS 3
*
* stylesheet = statement-list
* statement-list = ( ruleset )*
* ruleset = selectorGroup '{' propertyList '}'
* selectorGroup = selector ( ',' selector )*
* selector = selectorItem ( combinator )? selectorItem
* selectorItem = id | class | attribute | pseudo | negation
* id = '#' [a-zA-Z][a-zA-Z0-9]*
* class = '.' [a-zA-Z][a-zA-Z0-9]*
* combinator = '+' | '>' | '~'
* declarationList = ( declaration? ( ';' declaration? )* )?
* declaration = property ':' value
* property = ident
* value = ...
* comment = '/' '*' ... '*' '/'
*/
(function (grammar$css) {
var recipes;
(function (recipes) {
recipes.css = {
name: 'css',
spacePattern: /[ \t\r\n]*/,
commentRule: 'comment',
rules: {
'root': {
parser: 'statement-list',
},
'statement-list': {
parser: '|', argument: [
{ parser: 'statement-import' },
{ parser: 'statement-ruleset' },
],
multiplicity: 0,
},
'statement-import': {
parser: '!',
},
'statement-ruleset': {
parser: '&', argument: [
{ parser: 'css-selectors/group' },
{ parser: '?', argument: { syntax: "css.token.{" /* OpenBraceToken */, pattern: '{' } },
{ parser: 'block' },
{ parser: '?', argument: { syntax: "css.token.}" /* CloseBraceToken */, pattern: '}' } },
],
},
'block': {
parser: '*', argument: [
{ parser: 'declaration', optional: true },
{ parser: '?', argument: { syntax: "css.token.;" /* SemiColonToken */, pattern: ';' } },
],
options: { allowTrailingSeparator: true },
},
'declaration': {
parser: '&', argument: [
{ parser: 'property' },
{ parser: '?', argument: { syntax: "css.token.:" /* ColonToken */, pattern: ':' } },
{ parser: 'css-values/comma-separated-list' },
{ parser: '?', argument: { syntax: "css.token.important" /* ImportantToken */, pattern: /!\s*(important)/i, label: '!IMPORTANT' }, optional: true },
],
},
'property': {
parser: 'ident',
},
'ident': {
parser: '?', argument: { syntax: "css.token.ident" /* IdentToken */, pattern: /[-]?[a-zA-Z_][a-zA-Z0-9_\-]*/, label: 'IDENT' },
},
'comment': {
parser: '&', argument: [
{ parser: '#', argument: { syntax: "css.token.open-comment" /* OpenCommentToken */, pattern: '/*' } },
{ parser: '#', argument: { syntax: "css.token.comment" /* CommentToken */, pattern: /[\s\S]*?(?=\*\/)/m } },
{ parser: '#', argument: { syntax: "css.token.close-comment" /* CloseCommentToken */, pattern: '*/' } },
], optional: true,
},
},
};
})(recipes = grammar$css.recipes || (grammar$css.recipes = {}));
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
var recipes;
(function (recipes) {
recipes.selectors = {
name: 'css-selectors',
rules: {
'group': {
parser: '*', argument: [
{ parser: 'selector' },
{ parser: '?', argument: { syntax: "css.token.," /* CommaToken */, pattern: ',' } },
],
options: { allowTrailingSeparator: false },
},
'selector': {
parser: '+', argument: [
{ parser: 'item' },
{ parser: 'combinator', optional: true },
],
},
'combinator': {
parser: '|', argument: [
{ parser: '?', argument: { syntax: '>', pattern: '>' } },
{ parser: '?', argument: { syntax: '+', pattern: '+' } },
{ parser: '?', argument: { syntax: '~', pattern: '~' } },
{ parser: '#', argument: { syntax: ' ', pattern: /[ \t]+/, label: ' ' } },
],
},
'item': {
parser: '|', argument: [
{ parser: 'tag' },
{ parser: 'id' },
{ parser: 'class' },
{ parser: 'attribute' },
{ parser: 'pseudo' },
{ parser: 'negate' }, // :not(...)
],
},
'tag': {
parser: '|', argument: [
{ parser: 'css/ident', },
{ parser: '?', argument: { syntax: "css.token.*" /* AsteriskToken */, pattern: '*' } },
],
},
'id': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.id" /* IdToken */, pattern: '#' } },
{ parser: 'css/ident' },
],
},
'class': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.id" /* IdToken */, pattern: '.' } },
{ parser: 'css/ident' },
],
},
'attribute': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.[" /* OpenBracketToken */, pattern: '[' } },
{ parser: 'css/ident', optional: true },
{
parser: '&', argument: [
{ parser: 'match-operator' },
{
parser: '|', argument: [
{ parser: 'css/ident' },
{ parser: 'css-values/string' },
]
},
], optional: true
},
{ parser: '?', argument: { syntax: "css.token.]" /* CloseBracketToken */, pattern: ']' } },
],
},
'match-operator': {
parser: '|', argument: [
{ parser: '?', argument: { syntax: "css.token.=" /* EqualToken */, pattern: '=' } },
{ parser: '?', argument: { syntax: "css.token.^=" /* HatEqualToken */, pattern: '^=' } },
{ parser: '?', argument: { syntax: "css.token.$=" /* DollerEqualToken */, pattern: '$=' } },
{ parser: '?', argument: { syntax: "css.token.*=" /* AsteriskEqualToken */, pattern: '*=' } },
{ parser: '?', argument: { syntax: "css.token.~=" /* TildaEqualToken */, pattern: '~=' } },
{ parser: '?', argument: { syntax: "css.token.|=" /* VerticalBarEqualToken */, pattern: '|=' } },
],
},
'pseudo': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.:" /* ColonToken */, pattern: ':' } },
{ parser: 'css/ident' },
],
},
'negate': {
parser: '!',
},
},
};
})(recipes = grammar$css.recipes || (grammar$css.recipes = {}));
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
var recipes;
(function (recipes) {
recipes.values = {
name: 'css-values',
rules: {
'comma-separated-list': {
parser: '*', argument: [
{ parser: 'space-separated-list' },
{ parser: '?', argument: { syntax: "css.token.," /* CommaToken */, pattern: ',' } },
],
options: { allowTrailingSeparator: false },
},
'space-separated-list': {
parser: 'expression', multiplicity: 1,
},
'expression': {
parser: '|', argument: [
{ parser: 'function' },
{ parser: '-url' },
{ parser: 'value' },
],
},
'function': {
parser: '&', argument: [
{ parser: 'css/ident' },
{ parser: '?', argument: { syntax: "css.token.(" /* OpenParenToken */, pattern: '(' } },
{
parser: '*', argument: [
{ parser: 'value' },
{ parser: '?', argument: { syntax: "css.token.," /* CommaToken */, pattern: ',' } },
],
options: { allowTrailingSeparator: false },
},
{ parser: '?', argument: { syntax: "css.token.)" /* CloseParenToken */, pattern: ')' } },
],
},
'-url': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.ident" /* IdentToken */, pattern: 'url', label: 'URL' } },
{ parser: '?', argument: { syntax: "css.token.(" /* OpenParenToken */, pattern: '(' } },
{ parser: '?', argument: { syntax: "css.token.string" /* StringToken */, pattern: /.*(?=\))/ } },
{ parser: '?', argument: { syntax: "css.token.)" /* CloseParenToken */, pattern: ')' } },
],
},
'value': {
parser: '|', argument: [
{ parser: 'css/ident' },
{ parser: 'dimension' },
{ parser: 'percentage' },
{ parser: 'number' },
{ parser: 'hash' },
{ parser: 'string' },
{ parser: 'loose-slash' }
],
},
// 'ident': {
// parser: '?', argument: { syntax: Syntax.IdentToken, pattern: /[-]?[a-zA-Z_][a-zA-Z0-9_\-]*/, label: 'IDENT' },
// },
'dimension': {
parser: '?', argument: { syntax: "css.token.dimension" /* DimensionToken */, pattern: /[.](?:[0-9])+|[0-9]+(?:\.[0-9]+)?[a-zA-Z]+/, label: 'DIMENSION' },
},
'percentage': {
parser: '?', argument: { syntax: "css.token.percentage" /* PercentageToken */, pattern: /[.](?:[0-9])+|[0-9]+(?:\.[0-9]+)?%/, label: 'PERCENTAGE' },
},
'number': {
parser: '?', argument: { syntax: "css.token.number" /* NumberToken */, pattern: /[.](?:[0-9])+|[0-9]+(?:\.[0-9]+)?/, label: 'NUMBER' },
},
'hash': {
parser: '?', argument: { syntax: "css.token.hash" /* HashToken */, pattern: /[#][a-zA-Z0-9_\-]+/, label: 'HASH' },
},
'string': {
parser: '|', argument: [
{ parser: 'double-quoted-string' },
{ parser: 'single-quoted-string' },
],
},
'double-quoted-string': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.\"" /* DoublequoteToken */, pattern: '"' } },
{ parser: '#', argument: { syntax: "css.token.string" /* StringToken */, pattern: /[^"]*/ } },
{ parser: '#', argument: { syntax: "css.token.\"" /* DoublequoteToken */, pattern: '"' } },
],
},
'single-quoted-string': {
parser: '&', argument: [
{ parser: '?', argument: { syntax: "css.token.'" /* SingleQuoteToken */, pattern: "'" } },
{ parser: '#', argument: { syntax: "css.token.string" /* StringToken */, pattern: /[^']*/ } },
{ parser: '#', argument: { syntax: "css.token.'" /* SingleQuoteToken */, pattern: "'" } },
],
},
'loose-slash': {
parser: '?', argument: { syntax: "css.token.loose-slash" /* LooseSlashToken */, pattern: '/' },
},
},
};
})(recipes = grammar$css.recipes || (grammar$css.recipes = {}));
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
let ProcessorSuite;
(function (ProcessorSuite) {
ProcessorSuite["CST"] = "cst";
ProcessorSuite["AST"] = "ast";
})(ProcessorSuite = grammar$css.ProcessorSuite || (grammar$css.ProcessorSuite = {}));
grammar$css.processors = {
[ProcessorSuite.CST]: {},
[ProcessorSuite.AST]: {},
};
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
grammar$css.processors[grammar$css.ProcessorSuite.CST].css = {
recipe: grammar$css.recipes.css,
parts: {},
};
grammar$css.processors[grammar$css.ProcessorSuite.CST].selectors = {
recipe: grammar$css.recipes.selectors,
parts: {},
};
grammar$css.processors[grammar$css.ProcessorSuite.CST].values = {
recipe: grammar$css.recipes.values,
parts: {},
};
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
grammar$css.processors[grammar$css.ProcessorSuite.AST].css = {
recipe: grammar$css.recipes.css,
parts: {
'root'(_) {
return $.createNode(_, "css.StyleSheet" /* StyleSheet */, {
statements: _,
});
},
'statement-ruleset'(_) {
return $.createNode(_, "css.Ruleset" /* Ruleset */, {
selectors: _[0],
declarations: _[2],
});
},
'block'(_) {
return _.filter((decl) => decl != null);
},
'declaration'(_) {
return $.createNode(_, "css.Declaration" /* Declaration */, {
property: _[0],
value: _[2],
important: Boolean(_[3]),
});
},
'property'(_) {
return $.createNode(_, "css.Identifier" /* Identifier */, {
name: _,
});
},
},
};
/*
export const extracted: $.Processor = {
recipe,
parts: {
// 'root'(_: {}): {} {
// return {
// type: Syntax.StyleSheet,
// statements: _,
// }
// },
// 'statement-ruleset'(_: [{}[], Token, {}[], Token]): {} {
// return {
// type: Syntax.Ruleset,
// selectors: _[0],
// declarations: _[2],
// }
// },
// 'selector'(_: any[]) {
// return {
// type: Syntax.Selector,
// items: _.filter(_ => _ != null),
// }
// },
// 'combinator'(_: Token): {} {
// return {
// type: Syntax.Combinator,
// name: _.value,
// }
// },
// 'selector-tag'(_: Token): {} {
// return {
// type: Syntax.Tag,
// name: _.value,
// }
// },
// 'selector-id'(_: [Token, Token]): {} {
// return {
// type: Syntax.Id,
// name: _[1].value,
// }
// },
// 'selector-class'(_: [Token, Token]): {} {
// return {
// type: Syntax.Class,
// name: _[1].value,
// }
// },
// 'selector-attribute'(_: Token): {} {
// return {
// type: Syntax.Attribute,
// name: _.value,
// operator: null,
// value: null,
// }
// },
// 'selector-pseudo'(_: Token): string {
// return _.value
// },
// 'selector-negate'(_: Token): string {
// return _.value
// },
// 'declaration'(_: (any | null)[]): {}[] {
// return _.filter((decl): decl is Declaration => decl != null)
// },
// 'property'(_: [Token, Token, any[], Token | null]): any {
// return {
// type: Syntax.Declaration,
// name: _[0].value,
// value: _[2],
// important: _[3] !== null,
// }
// },
// 'comma-separated-list'(_: any[]): any {
// return _.length == 1 ? _[0] : {
// type: Syntax.ValueList,
// items: _,
// separator: ',',
// }
// },
// 'space-separated-list'(_: Token[]): any {
// return _.length == 1 ? _[0] : {
// type: Syntax.ValueList,
// items: _,
// separator: ' ',
// }
// },
// 'value'(_: Token): any {
// return _.value
// }
},
}
*/
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
grammar$css.processors[grammar$css.ProcessorSuite.AST].selectors = {
recipe: grammar$css.recipes.selectors,
parts: {
'selector'(_) {
return $.createNode(_, "css.Selector" /* Selector */, {
items: _.filter(_ => _ != null),
});
},
'combinator'(_) {
return $.createNode(_, "css.Combinator" /* Combinator */, {
combinator: convertToElementCombinator(_),
});
},
'tag'(_) {
return $.createNode(_, "css.Tag" /* Tag */, {
name: _.value,
});
},
'id'(_) {
return $.createNode(_, "css.Id" /* Id */, {
name: _[1].value,
});
},
'class'(_) {
return $.createNode(_, "css.Class" /* Class */, {
name: _[1].value,
});
},
'attribute'(_) {
return $.createNode(_, "css.Attribute" /* Attribute */, {
name: _[1],
operator: _[2] ? convertToMatchOperator(_[2][0]) : null,
value: _[2] ? $.createNode(_[2][1], "css.Value" /* Value */, { value: _[2][1].value, token: _[2][1] }) : null,
});
},
'pseudo'(_) {
return $.createNode(_, "css.Pseudo" /* Pseudo */, {
name: _[1],
parameter: null,
});
},
'negate'(_) {
return _;
},
},
};
function convertToElementCombinator(token) {
switch (token.syntax) {
case ' ': return grammar$css.ElementCombinator.Descendant;
case '>': return grammar$css.ElementCombinator.Child;
case '+': return grammar$css.ElementCombinator.NextSibling;
case '~': return grammar$css.ElementCombinator.SubsequentSibling;
default: throw new Error('Unknown combinator syntax.');
}
}
function convertToMatchOperator(token) {
switch (token.syntax) {
case "css.token.=" /* EqualToken */: return grammar$css.MatchOperator.WholeMatch;
case "css.token.^=" /* HatEqualToken */: return grammar$css.MatchOperator.PrefixMatch;
case "css.token.$=" /* DollerEqualToken */: return grammar$css.MatchOperator.SuffixMatch;
case "css.token.*=" /* AsteriskEqualToken */: return grammar$css.MatchOperator.SubstringMatch;
case "css.token.~=" /* TildaEqualToken */: return grammar$css.MatchOperator.Includes;
case "css.token.|=" /* VerticalBarEqualToken */: return grammar$css.MatchOperator.DashMatch;
default: throw new Error('Unknown match operator syntax.');
}
}
})(grammar$css || (grammar$css = {}));
(function (grammar$css) {
grammar$css.processors[grammar$css.ProcessorSuite.AST].values = {
recipe: grammar$css.recipes.values,
parts: {
'comma-separated-list'(_) {
return _.length == 1 ? _[0] : $.createNode(_, "css.ValueList" /* ValueList */, {
items: _,
separator: ',',
});
},
'space-separated-list'(_) {
return _.length == 1 ? _[0] : $.createNode(_, "css.ValueList" /* ValueList */, {
items: _,
separator: ' ',
});
},
'function'(_) {
return $.createNode(_, "css.Function" /* Function */, {
name: _[0],
parameters: _[2],
});
},
'-url'(_) {
const value = $.createNode(_[2], "css.Value" /* Value */, { value: _[2].value, token: _[2] });
return $.createNode(_, "css.Function" /* Function */, {
name: _[0],
parameters: [value],
});
},
'value'(_) {
return $.createNode(_, "css.Value" /* Value */, {
value: _.value,
token: _,
});
},
'string'(_) {
return _[1];
},
},
};
})(grammar$css || (grammar$css = {}));
export default grammar$css;
//# sourceMappingURL=grammar-css.mjs.map