UNPKG

@grammar/css

Version:

CSS3 grammar.

715 lines (701 loc) 29.6 kB
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