├── .npmignore ├── .gitignore ├── dist ├── index.d.ts ├── index.es.js └── index.cjs ├── README.md ├── rollup.config.js ├── test ├── test-css.js ├── selector.txt ├── statements.txt └── declarations.txt ├── package.json ├── LICENSE ├── src ├── highlight.js ├── tokens.js └── css.grammar └── CHANGELOG.md /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /src/parser.* 3 | .tern-* 4 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import {LRParser} from "@lezer/lr" 2 | 3 | export const parser: LRParser 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @lezer/css 2 | 3 | This is a CSS grammar for the 4 | [lezer](https://lezer.codemirror.net/) parser system. 5 | 6 | The code is licensed under an MIT license. 7 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from "@rollup/plugin-node-resolve" 2 | 3 | export default { 4 | input: "./src/parser.js", 5 | output: [{ 6 | format: "cjs", 7 | file: "./dist/index.cjs" 8 | }, { 9 | format: "es", 10 | file: "./dist/index.es.js" 11 | }], 12 | external(id) { return !/^[\.\/]/.test(id) }, 13 | plugins: [ 14 | nodeResolve() 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test/test-css.js: -------------------------------------------------------------------------------- 1 | import {parser} from "../dist/index.es.js" 2 | import {fileTests} from "@lezer/generator/dist/test" 3 | 4 | import * as fs from "fs" 5 | import * as path from "path" 6 | import { fileURLToPath } from 'url'; 7 | let caseDir = path.dirname(fileURLToPath(import.meta.url)) 8 | 9 | for (let file of fs.readdirSync(caseDir)) { 10 | if (!/\.txt$/.test(file)) continue 11 | let name = /^[^\.]*/.exec(file)[0] 12 | describe(name, () => { 13 | for (let {name, run} of fileTests(fs.readFileSync(path.join(caseDir, file), "utf8"), file)) 14 | it(name, () => run(parser)) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lezer/css", 3 | "version": "1.0.1", 4 | "description": "lezer-based CSS grammar", 5 | "main": "dist/index.cjs", 6 | "type": "module", 7 | "exports": { 8 | "import": "./dist/index.es.js", 9 | "require": "./dist/index.cjs" 10 | }, 11 | "module": "dist/index.es.js", 12 | "types": "dist/index.d.ts", 13 | "author": "Marijn Haverbeke ", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@lezer/generator": "^1.0.0", 17 | "mocha": "^9.0.1", 18 | "rollup": "^2.52.2", 19 | "@rollup/plugin-node-resolve": "^9.0.0" 20 | }, 21 | "dependencies": { 22 | "@lezer/lr": "^1.0.0", 23 | "@lezer/highlight": "^1.0.0" 24 | }, 25 | "repository": { 26 | "type" : "git", 27 | "url" : "https://github.com/lezer-parser/css.git" 28 | }, 29 | "scripts": { 30 | "build": "lezer-generator src/css.grammar -o src/parser && rollup -c", 31 | "build-debug": "lezer-generator src/css.grammar --names -o src/parser && rollup -c", 32 | "prepare": "npm run build", 33 | "test": "mocha test/test-*.js" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2018 by Marijn Haverbeke and others 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/highlight.js: -------------------------------------------------------------------------------- 1 | import {styleTags, tags as t} from "@lezer/highlight" 2 | 3 | export const cssHighlighting = styleTags({ 4 | "AtKeyword import charset namespace keyframes media supports": t.definitionKeyword, 5 | "from to selector": t.keyword, 6 | NamespaceName: t.namespace, 7 | KeyframeName: t.labelName, 8 | TagName: t.tagName, 9 | ClassName: t.className, 10 | PseudoClassName: t.constant(t.className), 11 | IdName: t.labelName, 12 | "FeatureName PropertyName": t.propertyName, 13 | AttributeName: t.attributeName, 14 | NumberLiteral: t.number, 15 | KeywordQuery: t.keyword, 16 | UnaryQueryOp: t.operatorKeyword, 17 | "CallTag ValueName": t.atom, 18 | VariableName: t.variableName, 19 | Callee: t.operatorKeyword, 20 | Unit: t.unit, 21 | "UniversalSelector NestingSelector": t.definitionOperator, 22 | MatchOp: t.compareOperator, 23 | "ChildOp SiblingOp, LogicOp": t.logicOperator, 24 | BinOp: t.arithmeticOperator, 25 | Important: t.modifier, 26 | Comment: t.blockComment, 27 | ParenthesizedContent: t.special(t.name), 28 | ColorLiteral: t.color, 29 | StringLiteral: t.string, 30 | ":": t.punctuation, 31 | "PseudoOp #": t.derefOperator, 32 | "; ,": t.separator, 33 | "( )": t.paren, 34 | "[ ]": t.squareBracket, 35 | "{ }": t.brace 36 | }) 37 | -------------------------------------------------------------------------------- /src/tokens.js: -------------------------------------------------------------------------------- 1 | /* Hand-written tokenizers for CSS tokens that can't be 2 | expressed by Lezer's built-in tokenizer. */ 3 | 4 | import {ExternalTokenizer} from "@lezer/lr" 5 | import {callee, identifier, VariableName, descendantOp, Unit} from "./parser.terms.js" 6 | 7 | const space = [9, 10, 11, 12, 13, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8 | 8198, 8199, 8200, 8201, 8202, 8232, 8233, 8239, 8287, 12288] 9 | const colon = 58, parenL = 40, underscore = 95, bracketL = 91, dash = 45, period = 46, 10 | hash = 35, percent = 37 11 | 12 | function isAlpha(ch) { return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch >= 161 } 13 | 14 | function isDigit(ch) { return ch >= 48 && ch <= 57 } 15 | 16 | export const identifiers = new ExternalTokenizer((input, stack) => { 17 | for (let inside = false, dashes = 0, i = 0;; i++) { 18 | let {next} = input 19 | if (isAlpha(next) || next == dash || next == underscore || (inside && isDigit(next))) { 20 | if (!inside && (next != dash || i > 0)) inside = true 21 | if (dashes === i && next == dash) dashes++ 22 | input.advance() 23 | } else { 24 | if (inside) 25 | input.acceptToken(next == parenL ? callee : dashes == 2 && stack.canShift(VariableName) ? VariableName : identifier) 26 | break 27 | } 28 | } 29 | }) 30 | 31 | export const descendant = new ExternalTokenizer(input => { 32 | if (space.includes(input.peek(-1))) { 33 | let {next} = input 34 | if (isAlpha(next) || next == underscore || next == hash || next == period || 35 | next == bracketL || next == colon || next == dash) 36 | input.acceptToken(descendantOp) 37 | } 38 | }) 39 | 40 | export const unitToken = new ExternalTokenizer(input => { 41 | if (!space.includes(input.peek(-1))) { 42 | let {next} = input 43 | if (next == percent) { input.advance(); input.acceptToken(Unit) } 44 | if (isAlpha(next)) { 45 | do { input.advance() } while (isAlpha(input.next)) 46 | input.acceptToken(Unit) 47 | } 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1 (2022-10-10) 2 | 3 | ### Bug fixes 4 | 5 | Add support for the `is`, `where`, `host-context`, `nth-last-of-type`, and `nth-of-type` pseudo classes. 6 | 7 | Apply a consistent highlighting tag (`definitionKeyword`) to all @ keywords. 8 | 9 | ## 1.0.0 (2022-06-06) 10 | 11 | ### New features 12 | 13 | First stable version. 14 | 15 | ## 0.16.0 (2022-04-20) 16 | 17 | ### Breaking changes 18 | 19 | Move to 0.16 serialized parser format. 20 | 21 | ### New features 22 | 23 | The parser now includes syntax highlighting information in its node types. 24 | 25 | ## 0.15.2 (2021-09-24) 26 | 27 | ### Bug fixes 28 | 29 | Distinguish between variable names and other names. 30 | 31 | Fix the name of nodes for the `selector` keyword (which by accident was `callee` before). 32 | 33 | ## 0.15.1 (2021-08-31) 34 | 35 | ### Bug fixes 36 | 37 | Fix parsing of selector arguments to pseudo selectors. 38 | 39 | ## 0.15.0 (2021-08-11) 40 | 41 | ### Breaking changes 42 | 43 | The module's name changed from `lezer-css` to `@lezer/css`. 44 | 45 | Upgrade to the 0.15.0 lezer interfaces. 46 | 47 | ## 0.13.1 (2020-12-04) 48 | 49 | ### Bug fixes 50 | 51 | Fix versions of lezer packages depended on. 52 | 53 | ## 0.13.0 (2020-12-04) 54 | 55 | ## 0.12.0 (2020-10-23) 56 | 57 | ### Breaking changes 58 | 59 | Adjust to changed serialized parser format. 60 | 61 | ## 0.11.1 (2020-09-26) 62 | 63 | ### Bug fixes 64 | 65 | Fix lezer depencency versions 66 | 67 | ## 0.11.0 (2020-09-26) 68 | 69 | ### Breaking changes 70 | 71 | Follow change in serialized parser format. 72 | 73 | ## 0.10.1 (2020-09-02) 74 | 75 | ### Bug fixes 76 | 77 | Fix a conflicting pair of tokens that the generator previously didn't catch. 78 | 79 | ## 0.10.0 (2020-08-07) 80 | 81 | ### Breaking changes 82 | 83 | Upgrade to 0.10 parser serialization 84 | 85 | ## 0.9.0 (2020-06-08) 86 | 87 | ### Breaking changes 88 | 89 | Upgrade to 0.9 parser serialization 90 | 91 | ## 0.8.3 (2020-04-09) 92 | 93 | ### Bug fixes 94 | 95 | Regenerate parser with a fix in lezer-generator so that the top node prop is properly assigned. 96 | 97 | ## 0.8.2 (2020-04-01) 98 | 99 | ### Bug fixes 100 | 101 | Make the package load as an ES module on node 102 | 103 | ## 0.8.1 (2020-02-28) 104 | 105 | ### New features 106 | 107 | Provide an ES module file. 108 | 109 | ## 0.8.0 (2020-02-03) 110 | 111 | ### New features 112 | 113 | Follow 0.8.0 release of the library. 114 | 115 | ## 0.7.0 (2020-01-20) 116 | 117 | ### Breaking changes 118 | 119 | Use the lezer 0.7.0 parser format. 120 | 121 | ## 0.5.2 (2020-01-15) 122 | 123 | ### Bug fixes 124 | 125 | Regenerate with lezer-generator 0.5.2 to avoid cyclic forced reductions. 126 | 127 | ## 0.5.1 (2019-10-22) 128 | 129 | ### Bug fixes 130 | 131 | Fix top prop missing from build output. 132 | 133 | ## 0.5.0 (2019-10-22) 134 | 135 | ### Breaking changes 136 | 137 | Move from `lang` to `top` prop on document node. 138 | 139 | ## 0.4.0 (2019-09-10) 140 | 141 | ### Breaking changes 142 | 143 | Adjust to 0.4.0 parse table format. 144 | 145 | ## 0.3.0 (2019-08-22) 146 | 147 | ### New features 148 | 149 | Go back to node names, add props, follow changes in grammar syntax. 150 | 151 | ## 0.2.0 (2019-08-02) 152 | 153 | ### New Features 154 | 155 | First documented release. 156 | -------------------------------------------------------------------------------- /test/selector.txt: -------------------------------------------------------------------------------- 1 | # Universal selectors 2 | 3 | * {} 4 | 5 | ==> 6 | 7 | StyleSheet(RuleSet(UniversalSelector,Block)) 8 | 9 | # Type selectors 10 | 11 | div, span {} 12 | h1, h2, h3, h4 {} 13 | 14 | ==> 15 | 16 | StyleSheet( 17 | RuleSet(TagSelector(TagName),TagSelector(TagName),Block), 18 | RuleSet(TagSelector(TagName),TagSelector(TagName),TagSelector(TagName),TagSelector(TagName),Block)) 19 | 20 | # Class selectors 21 | 22 | .class-a {} 23 | div.class-b, .class-c.class-d {} 24 | 25 | ==> 26 | 27 | StyleSheet( 28 | RuleSet(ClassSelector(ClassName),Block), 29 | RuleSet(ClassSelector(TagSelector(TagName),ClassName),ClassSelector(ClassSelector(ClassName),ClassName),Block)) 30 | 31 | # Id selectors 32 | 33 | #some-id, a#another-id {} 34 | 35 | ==> 36 | 37 | StyleSheet(RuleSet(IdSelector(IdName),IdSelector(TagSelector(TagName),IdName),Block)) 38 | 39 | # Attribute selectors 40 | 41 | [a] {} 42 | [b=c] {} 43 | [d~=e] {} 44 | a[b] {} 45 | 46 | ==> 47 | 48 | StyleSheet( 49 | RuleSet(AttributeSelector(AttributeName),Block), 50 | RuleSet(AttributeSelector(AttributeName,MatchOp,ValueName),Block), 51 | RuleSet(AttributeSelector(AttributeName,MatchOp,ValueName),Block), 52 | RuleSet(AttributeSelector(TagSelector(TagName),AttributeName),Block)) 53 | 54 | # Pseudo-class selectors 55 | 56 | a:hover {} 57 | :nth-child(2) {} 58 | 59 | ==> 60 | 61 | StyleSheet( 62 | RuleSet(PseudoClassSelector(TagSelector(TagName),":",PseudoClassName),Block), 63 | RuleSet(PseudoClassSelector(":",PseudoClassName,ArgList(NumberLiteral)),Block)) 64 | 65 | # Pseudo-element selectors 66 | 67 | a::first-line {} 68 | 69 | ==> 70 | 71 | StyleSheet(RuleSet(PseudoClassSelector(TagSelector(TagName),"::",PseudoClassName),Block)) 72 | 73 | # Child selectors 74 | 75 | a > b {} 76 | c > d > e {} 77 | 78 | ==> 79 | 80 | StyleSheet( 81 | RuleSet(ChildSelector(TagSelector(TagName),ChildOp,TagSelector(TagName)),Block), 82 | RuleSet(ChildSelector(ChildSelector(TagSelector(TagName),ChildOp,TagSelector(TagName)),ChildOp,TagSelector(TagName)),Block)) 83 | 84 | # Descendant selectors 85 | 86 | a b {} 87 | c d e {} 88 | 89 | ==> 90 | 91 | StyleSheet( 92 | RuleSet(DescendantSelector(TagSelector(TagName),TagSelector(TagName)),Block), 93 | RuleSet(DescendantSelector(DescendantSelector(TagSelector(TagName),TagSelector(TagName)),TagSelector(TagName)),Block)) 94 | 95 | # Nesting selectors 96 | 97 | a { 98 | &.b {} 99 | & c {} 100 | & > d {} 101 | } 102 | 103 | ==> 104 | 105 | StyleSheet(RuleSet(TagSelector(TagName),Block( 106 | RuleSet(ClassSelector(NestingSelector,ClassName),Block), 107 | RuleSet(DescendantSelector(NestingSelector,TagSelector(TagName)),Block), 108 | RuleSet(ChildSelector(NestingSelector,ChildOp,TagSelector(TagName)),Block)))) 109 | 110 | # Sibling selectors 111 | 112 | a.b ~ c.d {} 113 | .e.f + .g.h {} 114 | 115 | ==> 116 | 117 | StyleSheet( 118 | RuleSet(SiblingSelector(ClassSelector(TagSelector(TagName),ClassName),SiblingOp,ClassSelector(TagSelector(TagName),ClassName)),Block), 119 | RuleSet(SiblingSelector(ClassSelector(ClassSelector(ClassName),ClassName),SiblingOp,ClassSelector(ClassSelector(ClassName),ClassName)),Block)) 120 | 121 | # The :not selector 122 | 123 | a:not(:hover) {} 124 | .b:not(c > .d) {} 125 | 126 | ==> 127 | 128 | StyleSheet( 129 | RuleSet(PseudoClassSelector(TagSelector(TagName),":",PseudoClassName,ArgList(PseudoClassSelector(":",PseudoClassName))),Block), 130 | RuleSet(PseudoClassSelector(ClassSelector(ClassName),":",PseudoClassName,ArgList(ChildSelector(TagSelector(TagName),ChildOp,ClassSelector(ClassName)))),Block)) 131 | -------------------------------------------------------------------------------- /test/statements.txt: -------------------------------------------------------------------------------- 1 | # Import statements 2 | 3 | @import url("fineprint.css") print; 4 | @import url("bluish.css") speech; 5 | @import 'custom.css'; 6 | @import url("chrome://communicator/skin/"); 7 | @import "common.css" screen; 8 | 9 | ==> 10 | 11 | StyleSheet( 12 | ImportStatement(import,CallLiteral(CallTag,StringLiteral),KeywordQuery), 13 | ImportStatement(import,CallLiteral(CallTag,StringLiteral),KeywordQuery), 14 | ImportStatement(import,StringLiteral), 15 | ImportStatement(import,CallLiteral(CallTag,StringLiteral)), 16 | ImportStatement(import,StringLiteral,KeywordQuery)) 17 | 18 | # Namespace statements 19 | 20 | /* Default namespace */ 21 | @namespace url(XML-namespace-URL); 22 | @namespace "XML-namespace-URL"; 23 | @namespace url(http://www.w3.org/1999/xhtml); 24 | @namespace svg url(http://www.w3.org/2000/svg); 25 | 26 | /* Prefixed namespace */ 27 | @namespace prefix url(XML-namespace-URL); 28 | @namespace prefix "XML-namespace-URL"; 29 | 30 | ==> 31 | 32 | StyleSheet( 33 | Comment, 34 | NamespaceStatement(namespace,CallLiteral(CallTag,ParenthesizedContent)), 35 | NamespaceStatement(namespace,StringLiteral), 36 | NamespaceStatement(namespace,CallLiteral(CallTag,ParenthesizedContent)), 37 | NamespaceStatement(namespace,NamespaceName,CallLiteral(CallTag,ParenthesizedContent)), 38 | Comment, 39 | NamespaceStatement(namespace,NamespaceName,CallLiteral(CallTag,ParenthesizedContent)), 40 | NamespaceStatement(namespace,NamespaceName,StringLiteral)) 41 | 42 | # Keyframes statements 43 | 44 | @keyframes important1 { 45 | from { margin-top: 50px; } 46 | 50% { margin-top: 150px !important; } /* ignored */ 47 | to { margin-top: 100px; } 48 | } 49 | 50 | ==> 51 | 52 | StyleSheet(KeyframesStatement(keyframes,KeyframeName,KeyframeList( 53 | from,Block(Declaration(PropertyName,NumberLiteral(Unit))), 54 | NumberLiteral(Unit),Block(Declaration(PropertyName,NumberLiteral(Unit),Important)), 55 | Comment, 56 | to,Block(Declaration(PropertyName,NumberLiteral(Unit)))))) 57 | 58 | # Media statements 59 | 60 | @media screen and (min-width: 30em) and (orientation: landscape) {} 61 | @media (min-height: 680px), screen and (orientation: portrait) {} 62 | @media not all and (monochrome) {} 63 | @media only screen {} 64 | 65 | ==> 66 | 67 | StyleSheet( 68 | MediaStatement(media,BinaryQuery(BinaryQuery(KeywordQuery,LogicOp,FeatureQuery(FeatureName,NumberLiteral(Unit))),LogicOp, 69 | FeatureQuery(FeatureName,ValueName)),Block), 70 | MediaStatement(media,FeatureQuery(FeatureName,NumberLiteral(Unit)),BinaryQuery(KeywordQuery,LogicOp,FeatureQuery(FeatureName,ValueName)),Block), 71 | MediaStatement(media,UnaryQuery(UnaryQueryOp,BinaryQuery(KeywordQuery,LogicOp,ParenthesizedQuery(KeywordQuery))),Block), 72 | MediaStatement(media,UnaryQuery(UnaryQueryOp,KeywordQuery),Block)) 73 | 74 | # Supports statements 75 | 76 | @supports (animation-name: test) { 77 | div { animation-name: test; } 78 | } 79 | @supports (transform-style: preserve) or (-moz-transform-style: preserve) {} 80 | @supports not ((text-align-last: justify) or (-moz-text-align-last: justify)) {} 81 | @supports not selector(:matches(a, b)) {} 82 | 83 | ==> 84 | 85 | StyleSheet( 86 | SupportsStatement(supports,FeatureQuery(FeatureName,ValueName),Block(RuleSet(TagSelector(TagName),Block(Declaration(PropertyName,ValueName))))), 87 | SupportsStatement(supports,BinaryQuery(FeatureQuery(FeatureName,ValueName),LogicOp,FeatureQuery(FeatureName,ValueName)),Block), 88 | SupportsStatement(supports,UnaryQuery(UnaryQueryOp,ParenthesizedQuery( 89 | BinaryQuery(FeatureQuery(FeatureName,ValueName),LogicOp,FeatureQuery(FeatureName,ValueName)))),Block), 90 | SupportsStatement(supports,UnaryQuery(UnaryQueryOp,SelectorQuery(selector,PseudoClassSelector(":",PseudoClassName,ArgList(TagSelector(TagName),TagSelector(TagName))))),Block)) 91 | 92 | # Charset statements 93 | 94 | @charset "utf-8"; 95 | 96 | ==> 97 | 98 | StyleSheet(CharsetStatement(charset,StringLiteral)) 99 | 100 | # Other at-statements 101 | 102 | @font-face { 103 | font-family: "Open Sans"; 104 | src: url("/a") format("woff2"), url("/b/c") format("woff"); 105 | } 106 | 107 | ==> 108 | 109 | StyleSheet(AtRule(AtKeyword,Block( 110 | Declaration(PropertyName,StringLiteral), 111 | Declaration(PropertyName,CallLiteral(CallTag,StringLiteral),CallExpression(Callee,ArgList(StringLiteral)), 112 | CallLiteral(CallTag,StringLiteral),CallExpression(Callee,ArgList(StringLiteral)))))) 113 | -------------------------------------------------------------------------------- /test/declarations.txt: -------------------------------------------------------------------------------- 1 | # Function calls 2 | 3 | a { 4 | color: rgba(0, 255, 0, 0.5); 5 | } 6 | 7 | ==> 8 | 9 | StyleSheet( 10 | RuleSet(TagSelector(TagName),Block( 11 | Declaration(PropertyName,CallExpression(Callee,ArgList(NumberLiteral,NumberLiteral,NumberLiteral,NumberLiteral)))))) 12 | 13 | # Calls where each argument has multiple values 14 | 15 | div { 16 | background: repeating-linear-gradient(red, orange 50px); 17 | clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%) 18 | } 19 | 20 | ==> 21 | 22 | StyleSheet(RuleSet(TagSelector(TagName),Block( 23 | Declaration(PropertyName,CallExpression(Callee,ArgList(ValueName,ValueName,NumberLiteral(Unit)))), 24 | Declaration(PropertyName,CallExpression(Callee,ArgList( 25 | NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit), 26 | NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit), 27 | NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit), 28 | NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit))))))) 29 | 30 | # Color literals 31 | 32 | a { 33 | b: #fafd04; 34 | c: #fafd0401; 35 | } 36 | 37 | ==> 38 | 39 | StyleSheet(RuleSet(TagSelector(TagName),Block( 40 | Declaration(PropertyName,ColorLiteral), 41 | Declaration(PropertyName,ColorLiteral)))) 42 | 43 | # Numbers 44 | 45 | a { 46 | b: 0.5%; 47 | c: 5em; 48 | margin: 10E3px; 49 | margin: -456.8px; 50 | margin: -0.0px; 51 | } 52 | 53 | ==> 54 | 55 | StyleSheet(RuleSet(TagSelector(TagName),Block( 56 | Declaration(PropertyName,NumberLiteral(Unit)), 57 | Declaration(PropertyName,NumberLiteral(Unit)), 58 | Declaration(PropertyName,NumberLiteral(Unit)), 59 | Declaration(PropertyName,NumberLiteral(Unit)), 60 | Declaration(PropertyName,NumberLiteral(Unit))))) 61 | 62 | # Binary arithmetic operators 63 | 64 | a { 65 | width: calc(100% - 80px); 66 | aspect-ratio: 1/2; 67 | font-size: calc(10px + (56 - 10) * ((100vw - 320px) / (1920 - 320))); 68 | } 69 | 70 | ==> 71 | 72 | StyleSheet(RuleSet(TagSelector(TagName),Block( 73 | Declaration(PropertyName,CallExpression(Callee,ArgList(BinaryExpression(NumberLiteral(Unit),BinOp,NumberLiteral(Unit))))), 74 | Declaration(PropertyName,BinaryExpression(NumberLiteral,BinOp,NumberLiteral)), 75 | Declaration(PropertyName,CallExpression(Callee,ArgList( 76 | BinaryExpression(BinaryExpression(NumberLiteral(Unit),BinOp,ParenthesizedValue( 77 | BinaryExpression(NumberLiteral,BinOp,NumberLiteral))),BinOp,ParenthesizedValue( 78 | BinaryExpression(ParenthesizedValue(BinaryExpression(NumberLiteral(Unit),BinOp,NumberLiteral(Unit))),BinOp, 79 | ParenthesizedValue(BinaryExpression(NumberLiteral,BinOp,NumberLiteral))))))))))) 80 | 81 | # Strings 82 | 83 | a { 84 | b: ''; 85 | c: '\'hi\''; 86 | } 87 | 88 | ==> 89 | 90 | StyleSheet(RuleSet(TagSelector(TagName),Block(Declaration(PropertyName,StringLiteral),Declaration(PropertyName,StringLiteral)))) 91 | 92 | # URLs 93 | 94 | a { 95 | b: url(http://something-else?foo=bar); 96 | } 97 | 98 | ==> 99 | 100 | StyleSheet(RuleSet(TagSelector(TagName),Block(Declaration(PropertyName,CallLiteral(CallTag,ParenthesizedContent))))) 101 | 102 | # Important declarations 103 | 104 | a { 105 | b: c !important; 106 | } 107 | 108 | ==> 109 | 110 | StyleSheet(RuleSet(TagSelector(TagName),Block(Declaration(PropertyName,ValueName,Important)))) 111 | 112 | # Comments right after numbers 113 | 114 | a { 115 | shape-outside: circle(20em/*=*/at 50% 50%); 116 | shape-outside: inset(1em, 1em, 1em, 1em); 117 | } 118 | 119 | ==> 120 | 121 | StyleSheet(RuleSet(TagSelector(TagName),Block( 122 | Declaration(PropertyName,CallExpression(Callee,ArgList(NumberLiteral(Unit),Comment,ValueName,NumberLiteral(Unit),NumberLiteral(Unit)))), 123 | Declaration(PropertyName,CallExpression(Callee,ArgList(NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit),NumberLiteral(Unit))))))) 124 | 125 | # Unfinished rule 126 | 127 | a { foo: 2 128 | 129 | ==> 130 | 131 | StyleSheet(RuleSet(TagSelector(TagName),Block(Declaration(PropertyName,NumberLiteral),⚠))) 132 | 133 | # Variable names 134 | 135 | foo { 136 | --my-variable: white; 137 | color: var(--my-variable); 138 | } 139 | 140 | ==> 141 | 142 | StyleSheet(RuleSet(TagSelector(TagName),Block( 143 | Declaration(VariableName,ValueName), 144 | Declaration(PropertyName,CallExpression(Callee,ArgList(VariableName)))))) 145 | -------------------------------------------------------------------------------- /src/css.grammar: -------------------------------------------------------------------------------- 1 | @precedence { 2 | attribute @left, 3 | structure @left, 4 | valueOp @left 5 | } 6 | 7 | @skip { whitespace | Comment } 8 | 9 | @top StyleSheet { item+ | ItemSet } 10 | 11 | item { 12 | RuleSet | 13 | ImportStatement | 14 | MediaStatement | 15 | CharsetStatement | 16 | NamespaceStatement | 17 | KeyframesStatement | 18 | SupportsStatement | 19 | AtRule 20 | } 21 | 22 | RuleSet { 23 | selector ("," selector)* Block 24 | } 25 | 26 | ImportStatement { 27 | @specialize[@name=import] value commaSep ";" 28 | } 29 | 30 | MediaStatement { 31 | @specialize[@name=media] commaSep Block 32 | } 33 | 34 | CharsetStatement { 35 | @specialize[@name=charset] value ";" 36 | } 37 | 38 | NamespaceStatement { 39 | @specialize[@name=namespace] 40 | NamespaceName { identifier }? 41 | (StringLiteral | CallLiteral) ";" 42 | } 43 | 44 | KeyframesStatement { 45 | @specialize[@name=keyframes] 46 | KeyframeName { identifier } 47 | KeyframeList 48 | } 49 | 50 | KeyframeList { 51 | "{" ((kw<"from"> | kw<"to"> | NumberLiteral) Block)* "}" 52 | } 53 | 54 | SupportsStatement { 55 | @specialize[@name=supports] query Block 56 | } 57 | 58 | AtRule { AtKeyword commaSep (";" | Block) } 59 | 60 | ItemSet { item* (Declaration (";" ~item item* Declaration?)*)? } 61 | 62 | Block { "{" ~item item* (Declaration (";" ~item item* Declaration?)*)? "}" } 63 | 64 | selector { 65 | UniversalSelector | 66 | TagSelector { ~item TagName { identifier ~item } } | 67 | NestingSelector | 68 | ClassSelector { selector? !attribute "." ClassName { identifier } } | 69 | PseudoClassSelector { 70 | selector? !attribute (":" | "::") ( 71 | PseudoClassName { identifier } | 72 | pseudoClassWithArg ArgList | 73 | PseudoClassName { callee } ArgList) 74 | } | 75 | IdSelector { selector? !attribute "#" IdName { identifier } } | 76 | AttributeSelector { selector? !attribute "[" AttributeName { identifier } (MatchOp value)? "]" } | 77 | ChildSelector { selector !structure ChildOp selector } | 78 | DescendantSelector { selector !structure descendantOp selector } | 79 | SiblingSelector { selector !structure SiblingOp selector } 80 | } 81 | 82 | pseudoClassWithArg { 83 | @specialize[@name=PseudoClassName] 84 | } 85 | 86 | NumberLiteral { 87 | numberLiteralInner Unit? 88 | } 89 | 90 | ArgList { "(" commaSep ")" } 91 | 92 | Declaration { 93 | (PropertyName { identifier ~item } | VariableName) 94 | ":" value (","? value)* Important? 95 | } 96 | 97 | query { 98 | KeywordQuery { identifier } | 99 | FeatureQuery { "(" FeatureName { identifier } ":" value+ ")" } | 100 | BinaryQuery { query !valueOp LogicOp query } | 101 | UnaryQuery { @specialize[@name=UnaryQueryOp] query } | 102 | ParenthesizedQuery { "(" query ")" } | 103 | SelectorQuery { @specialize[@name=selector] "(" selector ")" } 104 | } 105 | 106 | value { 107 | VariableName | 108 | ValueName { identifier } | 109 | ParenthesizedValue { "(" value ")" } | 110 | ColorLiteral | 111 | NumberLiteral | 112 | StringLiteral | 113 | BinaryExpression { value !valueOp BinOp value } | 114 | CallExpression | 115 | CallLiteral 116 | } 117 | 118 | CallLiteral { 119 | @specialize[@name=CallTag] 120 | "(" (ParenthesizedContent | StringLiteral) ")" 121 | } 122 | 123 | CallExpression { 124 | Callee { callee } ArgList 125 | } 126 | 127 | commaSep { "" | value ("," value)* } 128 | 129 | kw { @specialize[@name={term}] } 130 | 131 | @external tokens descendant from "./tokens" { 132 | descendantOp 133 | } 134 | 135 | @external tokens unitToken from "./tokens" { 136 | Unit 137 | } 138 | 139 | @external tokens identifiers from "./tokens" { 140 | callee, 141 | identifier, 142 | VariableName 143 | } 144 | 145 | @tokens { 146 | UniversalSelector { "*" } 147 | 148 | NestingSelector { "&" } 149 | 150 | AtKeyword { "@" @asciiLetter (@asciiLetter | @digit | "-")* } 151 | 152 | MatchOp { $[~^|*$]? "=" } 153 | 154 | ChildOp { ">" ">"? } 155 | 156 | SiblingOp { "~" | "+" } 157 | 158 | LogicOp { "and" | "or" } 159 | 160 | BinOp { $[+\-*/] } 161 | 162 | Important { "!important" } 163 | 164 | whitespace { @whitespace+ } 165 | 166 | Comment { "/*" commentRest } 167 | 168 | commentRest { ![*] commentRest | "*" commentAfterStar } 169 | 170 | commentAfterStar { "/" | "*" commentAfterStar | ![/*] commentRest } 171 | 172 | @precedence { Comment, BinOp } 173 | 174 | hexDigit { @digit | $[a-fA-F] } 175 | 176 | ParenthesizedContent { !['")] ![)]+ } 177 | 178 | @precedence { whitespace, ParenthesizedContent, Comment } 179 | 180 | ColorLiteral { 181 | "#" hexDigit hexDigit hexDigit (hexDigit (hexDigit hexDigit (hexDigit hexDigit)?)?)? 182 | } 183 | 184 | numberLiteralInner { ("+" | "-")? (@digit+ ("." @digit*)? | "." @digit+) (("e" | "E") ("+" | "-")? @digit+)? } 185 | 186 | @precedence { numberLiteralInner, BinOp, SiblingOp } 187 | 188 | StringLiteral { "\"" (!["\n\\] | "\\" _)* "\"" | "'" (!['\n\\] | "\\" _)* "'" } 189 | 190 | "#" 191 | 192 | ":" "::" ";" "," 193 | 194 | "(" ")" "[" "]" "{" "}" 195 | } 196 | 197 | @external propSource cssHighlighting from "./highlight" 198 | 199 | @detectDelim 200 | -------------------------------------------------------------------------------- /dist/index.es.js: -------------------------------------------------------------------------------- 1 | import { ExternalTokenizer, LRParser } from '@lezer/lr'; 2 | import { NodeProp } from '@lezer/common'; 3 | 4 | // This file was generated by lezer-generator. You probably shouldn't edit it. 5 | const descendantOp = 94, 6 | Unit = 1, 7 | callee = 95, 8 | identifier = 96, 9 | VariableName = 2; 10 | 11 | /* Hand-written tokenizers for CSS tokens that can't be 12 | expressed by Lezer's built-in tokenizer. */ 13 | 14 | const space = [9, 10, 11, 12, 13, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 15 | 8198, 8199, 8200, 8201, 8202, 8232, 8233, 8239, 8287, 12288]; 16 | const colon = 58, parenL = 40, underscore = 95, bracketL = 91, dash = 45, period = 46, 17 | hash = 35, percent = 37; 18 | 19 | function isAlpha(ch) { return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch >= 161 } 20 | 21 | function isDigit(ch) { return ch >= 48 && ch <= 57 } 22 | 23 | const identifiers = new ExternalTokenizer((input, stack) => { 24 | for (let inside = false, dashes = 0, i = 0;; i++) { 25 | let {next} = input; 26 | if (isAlpha(next) || next == dash || next == underscore || (inside && isDigit(next))) { 27 | if (!inside && (next != dash || i > 0)) inside = true; 28 | if (dashes === i && next == dash) dashes++; 29 | input.advance(); 30 | } else { 31 | if (inside) 32 | input.acceptToken(next == parenL ? callee : dashes == 2 && stack.canShift(VariableName) ? VariableName : identifier); 33 | break 34 | } 35 | } 36 | }); 37 | 38 | const descendant = new ExternalTokenizer(input => { 39 | if (space.includes(input.peek(-1))) { 40 | let {next} = input; 41 | if (isAlpha(next) || next == underscore || next == hash || next == period || 42 | next == bracketL || next == colon || next == dash) 43 | input.acceptToken(descendantOp); 44 | } 45 | }); 46 | 47 | const unitToken = new ExternalTokenizer(input => { 48 | if (!space.includes(input.peek(-1))) { 49 | let {next} = input; 50 | if (next == percent) { input.advance(); input.acceptToken(Unit); } 51 | if (isAlpha(next)) { 52 | do { input.advance(); } while (isAlpha(input.next)) 53 | input.acceptToken(Unit); 54 | } 55 | } 56 | }); 57 | 58 | // This file was generated by lezer-generator. You probably shouldn't edit it. 59 | const spec_callee = {__proto__:null,lang:32, "nth-child":32, "nth-last-child":32, "nth-of-type":32, dir:32, url:60, "url-prefix":60, domain:60, regexp:60, selector:134}; 60 | const spec_AtKeyword = {__proto__:null,"@import":114, "@media":138, "@charset":142, "@namespace":146, "@keyframes":152, "@supports":164}; 61 | const spec_identifier = {__proto__:null,not:128, only:128, from:158, to:160}; 62 | const parser = LRParser.deserialize({ 63 | version: 13, 64 | states: "7vO!^Q[OOO!eQXO'#CdOOQP'#Cc'#CcO#YQ[O'#CfO#|QXO'#CaO$TQ[O'#ChO$`Q[O'#DPO$eQ[O'#DTOOQP'#Ed'#EdO$jQWO'#DaO$oQdO'#DeO%ZQ[O'#DrO$oQdO'#DtO%lQ[O'#DvO%wQ[O'#DyO%|Q[O'#EPO&[Q[O'#EROOQS'#Ec'#EcOOQS'#ET'#ETQ&cQ[O'#ESO&jQWO'#ESQOQWOOOOQP'#Cg'#CgOOQP,59Q,59QO#YQ[O,59QO&rQ[O'#EWO'^QWO,58{O'fQ[O,59SO$`Q[O,59kO$eQ[O,59oO&rQ[O,59sO&rQ[O,59uO&rQ[O,59vO'qQ[O'#D`OOQS,58{,58{OOQP'#Ck'#CkOOQO'#C}'#C}OOQP,59S,59SO'xQWO,59SO'}QWO,59SOOQP'#DR'#DROOQP,59k,59kOOQO'#DV'#DVO(SQ`O,59oO$oQdO,59{OOQS'#Cp'#CpO$oQdO'#CqO([QvO'#CsO)iQtO,5:POOQO'#Cx'#CxO'xQWO'#CwO)}QWO'#CyOOQS'#Eg'#EgOOQO'#Dh'#DhO*SQ[O'#DoO*bQWO'#EjO%|Q[O'#DmO*pQWO'#DpOOQO'#Ek'#EkO'aQWO,5:^O*uQpO,5:`OOQS'#Dx'#DxO*}QWO,5:bO+SQ[O,5:bOOQO'#D{'#D{O+[QWO,5:eO+aQWO,5:kO+iQWO,5:mOOQS-E8R-E8RO+qQWO,5:nO+yQ[O'#EYO+qQWO,5:nOOQP1G.l1G.lOOQP'#Cd'#CdO,rQXO,5:rOOQO-E8U-E8UOOQS1G.g1G.gOOQP1G.n1G.nO'xQWO1G.nO'}QWO1G.nOOQP1G/V1G/VO-PQ`O1G/ZO-jQXO1G/_O.QQXO1G/aO.hQXO1G/bOOQS,59z,59zO/OQWO,59zO/WQ[O,59zO/_QdO'#CoO/fQ[O'#DOOOQP1G/Z1G/ZO$oQdO1G/ZO/mQtO1G/gO0TQpO,59]OOQS,59_,59_O$oQdO,59aO0]QWO1G/kOOQS,59c,59cO0bQ!bO,59eO0jQWO'#DhO0uQWO,5:TO0zQWO,5:ZO%|Q[O,5:VO%|Q[O'#EZO1SQWO,5;UO1_QWO,5:XO&rQ[O,5:[OOQS1G/x1G/xOOQS1G/z1G/zOOQS1G/|1G/|O1pQWO1G/|O1uQdO'#D|OOQS1G0P1G0POOQS1G0V1G0VOOQS1G0X1G0XO2TQWO1G0YOOQO,5:t,5:tO2]Q[O,5:tOOQO-E8W-E8WOOQP7+$Y7+$YOOQP7+$u7+$uO$oQdO7+$uO2jQWO1G/fOOQS1G/f1G/fO2jQWO1G/fO2rQtO'#EUO3gQdO'#EfO3qQWO,59ZO3vQXO'#EiO3}QWO,59jO4SQpO7+$uO4[QtO'#EXO$oQdO'#EXO5YQdO7+%ROOQO7+%R7+%ROOQS1G.w1G.wOOQS1G.{1G.{OOQS7+%V7+%VO5mQWO1G/PO$oQdO1G/oOOQO1G/u1G/uOOQO1G/q1G/qO5rQWO,5:uOOQO-E8X-E8XO6QQXO1G/vOOQS7+%h7+%hO6XQYO'#CsO'aQWO'#E[O6aQdO,5:hOOQS,5:h,5:hOOQO1G0`1G0`O6oQpO<O!c!}$w!}#O?[#O#P$w#P#Q?g#Q#R2U#R#T$w#T#U?r#U#c$w#c#d@q#d#o$w#o#pAQ#p#q2U#q#rA]#r#sAh#s#y$w#y#z%]#z$f$w$f$g%]$g#BY$w#BY#BZ%]#BZ$IS$w$IS$I_%]$I_$I|$w$I|$JO%]$JO$JT$w$JT$JU%]$JU$KV$w$KV$KW%]$KW&FU$w&FU&FV%]&FV~$wW$zQOy%Qz~%QW%VQoWOy%Qz~%Q~%bf#U~OX%QX^&v^p%Qpq&vqy%Qz#y%Q#y#z&v#z$f%Q$f$g&v$g#BY%Q#BY#BZ&v#BZ$IS%Q$IS$I_&v$I_$I|%Q$I|$JO&v$JO$JT%Q$JT$JU&v$JU$KV%Q$KV$KW&v$KW&FU%Q&FU&FV&v&FV~%Q~&}f#U~oWOX%QX^&v^p%Qpq&vqy%Qz#y%Q#y#z&v#z$f%Q$f$g&v$g#BY%Q#BY#BZ&v#BZ$IS%Q$IS$I_&v$I_$I|%Q$I|$JO&v$JO$JT%Q$JT$JU&v$JU$KV%Q$KV$KW&v$KW&FU%Q&FU&FV&v&FV~%Q^(fSOy%Qz#]%Q#]#^(r#^~%Q^(wSoWOy%Qz#a%Q#a#b)T#b~%Q^)YSoWOy%Qz#d%Q#d#e)f#e~%Q^)kSoWOy%Qz#c%Q#c#d)w#d~%Q^)|SoWOy%Qz#f%Q#f#g*Y#g~%Q^*_SoWOy%Qz#h%Q#h#i*k#i~%Q^*pSoWOy%Qz#T%Q#T#U*|#U~%Q^+RSoWOy%Qz#b%Q#b#c+_#c~%Q^+dSoWOy%Qz#h%Q#h#i+p#i~%Q^+wQ!VUoWOy%Qz~%Q~,QUOY+}Zr+}rs,ds#O+}#O#P,i#P~+}~,iOh~~,lPO~+}_,tWtPOy%Qz!Q%Q!Q![-^![!c%Q!c!i-^!i#T%Q#T#Z-^#Z~%Q^-cWoWOy%Qz!Q%Q!Q![-{![!c%Q!c!i-{!i#T%Q#T#Z-{#Z~%Q^.QWoWOy%Qz!Q%Q!Q![.j![!c%Q!c!i.j!i#T%Q#T#Z.j#Z~%Q^.qWfUoWOy%Qz!Q%Q!Q![/Z![!c%Q!c!i/Z!i#T%Q#T#Z/Z#Z~%Q^/bWfUoWOy%Qz!Q%Q!Q![/z![!c%Q!c!i/z!i#T%Q#T#Z/z#Z~%Q^0PWoWOy%Qz!Q%Q!Q![0i![!c%Q!c!i0i!i#T%Q#T#Z0i#Z~%Q^0pWfUoWOy%Qz!Q%Q!Q![1Y![!c%Q!c!i1Y!i#T%Q#T#Z1Y#Z~%Q^1_WoWOy%Qz!Q%Q!Q![1w![!c%Q!c!i1w!i#T%Q#T#Z1w#Z~%Q^2OQfUoWOy%Qz~%QY2XSOy%Qz!_%Q!_!`2e!`~%QY2lQzQoWOy%Qz~%QX2wQXPOy%Qz~%Q~3QUOY2}Zw2}wx,dx#O2}#O#P3d#P~2}~3gPO~2}_3oQbVOy%Qz~%Q~3zOa~_4RSUPjSOy%Qz!_%Q!_!`2e!`~%Q_4fUjS!PPOy%Qz!O%Q!O!P4x!P!Q%Q!Q![7_![~%Q^4}SoWOy%Qz!Q%Q!Q![5Z![~%Q^5bWoW#[UOy%Qz!Q%Q!Q![5Z![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%Q^6PWoWOy%Qz{%Q{|6i|}%Q}!O6i!O!Q%Q!Q![6z![~%Q^6nSoWOy%Qz!Q%Q!Q![6z![~%Q^7RSoW#[UOy%Qz!Q%Q!Q![6z![~%Q^7fYoW#[UOy%Qz!O%Q!O!P5Z!P!Q%Q!Q![7_![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%Q_8ZQpVOy%Qz~%Q^8fUjSOy%Qz!O%Q!O!P4x!P!Q%Q!Q![7_![~%Q_8}S#XPOy%Qz!Q%Q!Q![5Z![~%Q~9`RjSOy%Qz{9i{~%Q~9nSoWOy9iyz9zz{:o{~9i~9}ROz9zz{:W{~9z~:ZTOz9zz{:W{!P9z!P!Q:j!Q~9z~:oOR~~:tUoWOy9iyz9zz{:o{!P9i!P!Q;W!Q~9i~;_QoWR~Oy%Qz~%Q^;jY#[UOy%Qz!O%Q!O!P5Z!P!Q%Q!Q![7_![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%QX<_S]POy%Qz![%Q![!]RUOy%Qz!c%Q!c!}>e!}#T%Q#T#o>e#o~%QX>lY!YPoWOy%Qz}%Q}!O>e!O!Q%Q!Q![>e![!c%Q!c!}>e!}#T%Q#T#o>e#o~%QX?aQxPOy%Qz~%Q^?lQvUOy%Qz~%QX?uSOy%Qz#b%Q#b#c@R#c~%QX@WSoWOy%Qz#W%Q#W#X@d#X~%QX@kQ!`PoWOy%Qz~%QX@tSOy%Qz#f%Q#f#g@d#g~%QXAVQ!RPOy%Qz~%Q_AbQ!QVOy%Qz~%QZAmS!PPOy%Qz!_%Q!_!`2e!`~%Q", 76 | tokenizers: [descendant, unitToken, identifiers, 0, 1, 2, 3], 77 | topRules: {"StyleSheet":[0,4]}, 78 | specialized: [{term: 95, get: value => spec_callee[value] || -1},{term: 56, get: value => spec_AtKeyword[value] || -1},{term: 96, get: value => spec_identifier[value] || -1}], 79 | tokenPrec: 1090 80 | }); 81 | 82 | export { parser }; 83 | -------------------------------------------------------------------------------- /dist/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | var lr = require('@lezer/lr'); 6 | var common = require('@lezer/common'); 7 | 8 | // This file was generated by lezer-generator. You probably shouldn't edit it. 9 | const descendantOp = 94, 10 | Unit = 1, 11 | callee = 95, 12 | identifier = 96, 13 | VariableName = 2; 14 | 15 | /* Hand-written tokenizers for CSS tokens that can't be 16 | expressed by Lezer's built-in tokenizer. */ 17 | 18 | const space = [9, 10, 11, 12, 13, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 19 | 8198, 8199, 8200, 8201, 8202, 8232, 8233, 8239, 8287, 12288]; 20 | const colon = 58, parenL = 40, underscore = 95, bracketL = 91, dash = 45, period = 46, 21 | hash = 35, percent = 37; 22 | 23 | function isAlpha(ch) { return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch >= 161 } 24 | 25 | function isDigit(ch) { return ch >= 48 && ch <= 57 } 26 | 27 | const identifiers = new lr.ExternalTokenizer((input, stack) => { 28 | for (let inside = false, dashes = 0, i = 0;; i++) { 29 | let {next} = input; 30 | if (isAlpha(next) || next == dash || next == underscore || (inside && isDigit(next))) { 31 | if (!inside && (next != dash || i > 0)) inside = true; 32 | if (dashes === i && next == dash) dashes++; 33 | input.advance(); 34 | } else { 35 | if (inside) 36 | input.acceptToken(next == parenL ? callee : dashes == 2 && stack.canShift(VariableName) ? VariableName : identifier); 37 | break 38 | } 39 | } 40 | }); 41 | 42 | const descendant = new lr.ExternalTokenizer(input => { 43 | if (space.includes(input.peek(-1))) { 44 | let {next} = input; 45 | if (isAlpha(next) || next == underscore || next == hash || next == period || 46 | next == bracketL || next == colon || next == dash) 47 | input.acceptToken(descendantOp); 48 | } 49 | }); 50 | 51 | const unitToken = new lr.ExternalTokenizer(input => { 52 | if (!space.includes(input.peek(-1))) { 53 | let {next} = input; 54 | if (next == percent) { input.advance(); input.acceptToken(Unit); } 55 | if (isAlpha(next)) { 56 | do { input.advance(); } while (isAlpha(input.next)) 57 | input.acceptToken(Unit); 58 | } 59 | } 60 | }); 61 | 62 | // This file was generated by lezer-generator. You probably shouldn't edit it. 63 | const spec_callee = {__proto__:null,lang:32, "nth-child":32, "nth-last-child":32, "nth-of-type":32, dir:32, url:60, "url-prefix":60, domain:60, regexp:60, selector:134}; 64 | const spec_AtKeyword = {__proto__:null,"@import":114, "@media":138, "@charset":142, "@namespace":146, "@keyframes":152, "@supports":164}; 65 | const spec_identifier = {__proto__:null,not:128, only:128, from:158, to:160}; 66 | const parser = lr.LRParser.deserialize({ 67 | version: 13, 68 | states: "7vO!^Q[OOO!eQXO'#CdOOQP'#Cc'#CcO#YQ[O'#CfO#|QXO'#CaO$TQ[O'#ChO$`Q[O'#DPO$eQ[O'#DTOOQP'#Ed'#EdO$jQWO'#DaO$oQdO'#DeO%ZQ[O'#DrO$oQdO'#DtO%lQ[O'#DvO%wQ[O'#DyO%|Q[O'#EPO&[Q[O'#EROOQS'#Ec'#EcOOQS'#ET'#ETQ&cQ[O'#ESO&jQWO'#ESQOQWOOOOQP'#Cg'#CgOOQP,59Q,59QO#YQ[O,59QO&rQ[O'#EWO'^QWO,58{O'fQ[O,59SO$`Q[O,59kO$eQ[O,59oO&rQ[O,59sO&rQ[O,59uO&rQ[O,59vO'qQ[O'#D`OOQS,58{,58{OOQP'#Ck'#CkOOQO'#C}'#C}OOQP,59S,59SO'xQWO,59SO'}QWO,59SOOQP'#DR'#DROOQP,59k,59kOOQO'#DV'#DVO(SQ`O,59oO$oQdO,59{OOQS'#Cp'#CpO$oQdO'#CqO([QvO'#CsO)iQtO,5:POOQO'#Cx'#CxO'xQWO'#CwO)}QWO'#CyOOQS'#Eg'#EgOOQO'#Dh'#DhO*SQ[O'#DoO*bQWO'#EjO%|Q[O'#DmO*pQWO'#DpOOQO'#Ek'#EkO'aQWO,5:^O*uQpO,5:`OOQS'#Dx'#DxO*}QWO,5:bO+SQ[O,5:bOOQO'#D{'#D{O+[QWO,5:eO+aQWO,5:kO+iQWO,5:mOOQS-E8R-E8RO+qQWO,5:nO+yQ[O'#EYO+qQWO,5:nOOQP1G.l1G.lOOQP'#Cd'#CdO,rQXO,5:rOOQO-E8U-E8UOOQS1G.g1G.gOOQP1G.n1G.nO'xQWO1G.nO'}QWO1G.nOOQP1G/V1G/VO-PQ`O1G/ZO-jQXO1G/_O.QQXO1G/aO.hQXO1G/bOOQS,59z,59zO/OQWO,59zO/WQ[O,59zO/_QdO'#CoO/fQ[O'#DOOOQP1G/Z1G/ZO$oQdO1G/ZO/mQtO1G/gO0TQpO,59]OOQS,59_,59_O$oQdO,59aO0]QWO1G/kOOQS,59c,59cO0bQ!bO,59eO0jQWO'#DhO0uQWO,5:TO0zQWO,5:ZO%|Q[O,5:VO%|Q[O'#EZO1SQWO,5;UO1_QWO,5:XO&rQ[O,5:[OOQS1G/x1G/xOOQS1G/z1G/zOOQS1G/|1G/|O1pQWO1G/|O1uQdO'#D|OOQS1G0P1G0POOQS1G0V1G0VOOQS1G0X1G0XO2TQWO1G0YOOQO,5:t,5:tO2]Q[O,5:tOOQO-E8W-E8WOOQP7+$Y7+$YOOQP7+$u7+$uO$oQdO7+$uO2jQWO1G/fOOQS1G/f1G/fO2jQWO1G/fO2rQtO'#EUO3gQdO'#EfO3qQWO,59ZO3vQXO'#EiO3}QWO,59jO4SQpO7+$uO4[QtO'#EXO$oQdO'#EXO5YQdO7+%ROOQO7+%R7+%ROOQS1G.w1G.wOOQS1G.{1G.{OOQS7+%V7+%VO5mQWO1G/PO$oQdO1G/oOOQO1G/u1G/uOOQO1G/q1G/qO5rQWO,5:uOOQO-E8X-E8XO6QQXO1G/vOOQS7+%h7+%hO6XQYO'#CsO'aQWO'#E[O6aQdO,5:hOOQS,5:h,5:hOOQO1G0`1G0`O6oQpO<O!c!}$w!}#O?[#O#P$w#P#Q?g#Q#R2U#R#T$w#T#U?r#U#c$w#c#d@q#d#o$w#o#pAQ#p#q2U#q#rA]#r#sAh#s#y$w#y#z%]#z$f$w$f$g%]$g#BY$w#BY#BZ%]#BZ$IS$w$IS$I_%]$I_$I|$w$I|$JO%]$JO$JT$w$JT$JU%]$JU$KV$w$KV$KW%]$KW&FU$w&FU&FV%]&FV~$wW$zQOy%Qz~%QW%VQoWOy%Qz~%Q~%bf#U~OX%QX^&v^p%Qpq&vqy%Qz#y%Q#y#z&v#z$f%Q$f$g&v$g#BY%Q#BY#BZ&v#BZ$IS%Q$IS$I_&v$I_$I|%Q$I|$JO&v$JO$JT%Q$JT$JU&v$JU$KV%Q$KV$KW&v$KW&FU%Q&FU&FV&v&FV~%Q~&}f#U~oWOX%QX^&v^p%Qpq&vqy%Qz#y%Q#y#z&v#z$f%Q$f$g&v$g#BY%Q#BY#BZ&v#BZ$IS%Q$IS$I_&v$I_$I|%Q$I|$JO&v$JO$JT%Q$JT$JU&v$JU$KV%Q$KV$KW&v$KW&FU%Q&FU&FV&v&FV~%Q^(fSOy%Qz#]%Q#]#^(r#^~%Q^(wSoWOy%Qz#a%Q#a#b)T#b~%Q^)YSoWOy%Qz#d%Q#d#e)f#e~%Q^)kSoWOy%Qz#c%Q#c#d)w#d~%Q^)|SoWOy%Qz#f%Q#f#g*Y#g~%Q^*_SoWOy%Qz#h%Q#h#i*k#i~%Q^*pSoWOy%Qz#T%Q#T#U*|#U~%Q^+RSoWOy%Qz#b%Q#b#c+_#c~%Q^+dSoWOy%Qz#h%Q#h#i+p#i~%Q^+wQ!VUoWOy%Qz~%Q~,QUOY+}Zr+}rs,ds#O+}#O#P,i#P~+}~,iOh~~,lPO~+}_,tWtPOy%Qz!Q%Q!Q![-^![!c%Q!c!i-^!i#T%Q#T#Z-^#Z~%Q^-cWoWOy%Qz!Q%Q!Q![-{![!c%Q!c!i-{!i#T%Q#T#Z-{#Z~%Q^.QWoWOy%Qz!Q%Q!Q![.j![!c%Q!c!i.j!i#T%Q#T#Z.j#Z~%Q^.qWfUoWOy%Qz!Q%Q!Q![/Z![!c%Q!c!i/Z!i#T%Q#T#Z/Z#Z~%Q^/bWfUoWOy%Qz!Q%Q!Q![/z![!c%Q!c!i/z!i#T%Q#T#Z/z#Z~%Q^0PWoWOy%Qz!Q%Q!Q![0i![!c%Q!c!i0i!i#T%Q#T#Z0i#Z~%Q^0pWfUoWOy%Qz!Q%Q!Q![1Y![!c%Q!c!i1Y!i#T%Q#T#Z1Y#Z~%Q^1_WoWOy%Qz!Q%Q!Q![1w![!c%Q!c!i1w!i#T%Q#T#Z1w#Z~%Q^2OQfUoWOy%Qz~%QY2XSOy%Qz!_%Q!_!`2e!`~%QY2lQzQoWOy%Qz~%QX2wQXPOy%Qz~%Q~3QUOY2}Zw2}wx,dx#O2}#O#P3d#P~2}~3gPO~2}_3oQbVOy%Qz~%Q~3zOa~_4RSUPjSOy%Qz!_%Q!_!`2e!`~%Q_4fUjS!PPOy%Qz!O%Q!O!P4x!P!Q%Q!Q![7_![~%Q^4}SoWOy%Qz!Q%Q!Q![5Z![~%Q^5bWoW#[UOy%Qz!Q%Q!Q![5Z![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%Q^6PWoWOy%Qz{%Q{|6i|}%Q}!O6i!O!Q%Q!Q![6z![~%Q^6nSoWOy%Qz!Q%Q!Q![6z![~%Q^7RSoW#[UOy%Qz!Q%Q!Q![6z![~%Q^7fYoW#[UOy%Qz!O%Q!O!P5Z!P!Q%Q!Q![7_![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%Q_8ZQpVOy%Qz~%Q^8fUjSOy%Qz!O%Q!O!P4x!P!Q%Q!Q![7_![~%Q_8}S#XPOy%Qz!Q%Q!Q![5Z![~%Q~9`RjSOy%Qz{9i{~%Q~9nSoWOy9iyz9zz{:o{~9i~9}ROz9zz{:W{~9z~:ZTOz9zz{:W{!P9z!P!Q:j!Q~9z~:oOR~~:tUoWOy9iyz9zz{:o{!P9i!P!Q;W!Q~9i~;_QoWR~Oy%Qz~%Q^;jY#[UOy%Qz!O%Q!O!P5Z!P!Q%Q!Q![7_![!g%Q!g!h5z!h#X%Q#X#Y5z#Y~%QX<_S]POy%Qz![%Q![!]RUOy%Qz!c%Q!c!}>e!}#T%Q#T#o>e#o~%QX>lY!YPoWOy%Qz}%Q}!O>e!O!Q%Q!Q![>e![!c%Q!c!}>e!}#T%Q#T#o>e#o~%QX?aQxPOy%Qz~%Q^?lQvUOy%Qz~%QX?uSOy%Qz#b%Q#b#c@R#c~%QX@WSoWOy%Qz#W%Q#W#X@d#X~%QX@kQ!`PoWOy%Qz~%QX@tSOy%Qz#f%Q#f#g@d#g~%QXAVQ!RPOy%Qz~%Q_AbQ!QVOy%Qz~%QZAmS!PPOy%Qz!_%Q!_!`2e!`~%Q", 80 | tokenizers: [descendant, unitToken, identifiers, 0, 1, 2, 3], 81 | topRules: {"StyleSheet":[0,4]}, 82 | specialized: [{term: 95, get: value => spec_callee[value] || -1},{term: 56, get: value => spec_AtKeyword[value] || -1},{term: 96, get: value => spec_identifier[value] || -1}], 83 | tokenPrec: 1090 84 | }); 85 | 86 | exports.parser = parser; 87 | --------------------------------------------------------------------------------