SSL Css3.g
Sprache: unbekannt
|
|
// A complete lexer and grammar for CSS 2.1 as defined by the
// W3 specification.
//
// This grammar is free to use providing you retain everyhting in this header comment
// section.
//
// Author : Jim Idle, Temporal Wave LLC.
// Contact : jimi@temporal-wave.com
// Website : http://www.temporal-wave.com
// License : ANTLR Free BSD License
//
// Please visit our Web site at http://www.temporal-wave.com and try our commercial
// parsers for SQL, C#, VB.Net and more.
//
// This grammar is free to use providing you retain everything in this header comment
// section.
//
//Modifications to the original css21 source file by Jim Idle have been done to fulfill the
//css3 parsing rules and making the parser more error prone.
//1) incorporated the grammar changes from selectors module: http://www.w3.org/TR/css3-selectors/#grammar
// a. There's no 'universal' selector node, 'typeSelector' is used instead where instead of th e identifier there's the star token.
// This solves the too long (==3) lookahead problem in the simpleSelectorSequence rule
//2) implemented custom error recovery
//3) removed whitespaces from the alpha token fragments
//
//Please be aware that the grammar doesn't properly and fully reflect the whole css3 specification!!!
// The original CSS 2.1 grammar was downloaded from the antlr 3 grammars:
// https://raw.githubusercontent.com/antlr/grammars-v3/62555b5befd6f89af05bf918b79fa98f5391e12f/css21/css21.g
// Rebuild the lexer and parser:
// 1. Update Css3.g
// 2. Update the lexer/parser sources by running
// ant generate-antlr-parser
// from the module directory (ide/css.lib)
// 3. Rerun unittests
// 4. Commit Css3.g together with generated Css3Lexer.java and Css3Parser.java
//
// INFO: It is known, that the grammar does not compile without warnings
//
grammar Css3;
//options {
// k='*';
//}
@header {
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// DO NOT EDIT THIS FILE MANUALLY!
// SEE Css3.g FOR INSTRUCTIONS
package org.netbeans.modules.css.lib;
}
@lexer::members {
protected boolean isLessSource() {
return false;
}
protected boolean isScssSource() {
return false;
}
private boolean isCssPreprocessorSource() {
return isLessSource() || isScssSource();
}
}
@members {
protected boolean isLessSource() {
return false;
}
protected boolean isScssSource() {
return false;
}
private boolean isCssPreprocessorSource() {
return isLessSource() || isScssSource();
}
private boolean tokenNameEquals(String tokenImage) {
return tokenImage.equalsIgnoreCase(input.LT(1).getText());
}
private boolean tokenNameEquals2(String tokenImage) {
return tokenImage.equalsIgnoreCase(input.LT(2).getText());
}
private boolean tokenNameIs(String[] tokens) {
for(String tokenImage : tokens) {
if(tokenImage.equalsIgnoreCase(input.LT(1).getText())) {
return true;
}
}
return false;
}
private boolean tokenNameStartsWith(String prefix) {
return input.LT(1).getText() != null
&& input.LT(1).getText().startsWith(prefix);
}
/**
* Use the current stacked followset to work out the valid tokens that
* can follow on from the current point in the parse, then recover by
* eating tokens that are not a member of the follow set we compute.
*
* This method is used whenever we wish to force a sync, even though
* the parser has not yet checked LA(1) for alt selection. This is useful
* in situations where only a subset of tokens can begin a new construct
* (such as the start of a new statement in a block) and we want to
* proactively detect garbage so that the current rule does not exit on
* on an exception.
*
* We could override recover() to make this the default behavior but that
* is too much like using a sledge hammer to crack a nut. We want finer
* grained control of the recovery and error mechanisms.
*/
protected void syncToSet()
{
// Compute the followset that is in context wherever we are in the
// rule chain/stack
//
BitSet follow = state.following[state._fsp]; //computeContextSensitiveRuleFOLLOW();
syncToSet(follow);
}
protected void syncToSet(BitSet follow)
{
int mark = -1;
//create error-recovery node
dbg.enterRule(getGrammarFileName(), "recovery");
try {
mark = input.mark();
// Consume all tokens in the stream until we find a member of the follow
// set, which means the next production should be guaranteed to be happy.
//
while (! follow.member(input.LA(1)) ) {
if (input.LA(1) == Token.EOF) {
// Looks like we didn't find anything at all that can help us here
// so we need to rewind to where we were and let normal error handling
// bail out.
//
input.rewind();
mark = -1;
return;
}
input.consume();
// Now here, because you are consuming some tokens, yu will probably want
// to raise an error message such as "Spurious elements after the class member were discarded"
// using whatever your override of displayRecognitionError() routine does to record
// error messages. The exact error my depend on context etc.
//
}
} catch (Exception e) {
// Just ignore any errors here, we will just let the recognizer
// try to resync as normal - something must be very screwed.
//
}
finally {
dbg.exitRule(getGrammarFileName(), "recovery");
// Always release the mark we took
//
if (mark != -1) {
input.release(mark);
}
}
}
/**
* synces to next RBRACE "}" taking nesting into account
*/
protected void syncToRBRACE(int nest)
{
int mark = -1;
//create error-recovery node
//dbg.enterRule(getGrammarFileName(), "recovery");
try {
mark = input.mark();
for(;;) {
//read char
int c = input.LA(1);
switch(c) {
case Token.EOF:
input.rewind();
mark = -1;
return ;
case Css3Lexer.LBRACE:
nest++;
break;
case Css3Lexer.RBRACE:
nest--;
if(nest == 0) {
//do not eat the final RBRACE
return ;
}
}
input.consume();
}
} catch (Exception e) {
// Just ignore any errors here, we will just let the recognizer
// try to resync as normal - something must be very screwed.
//
}
finally {
if (mark != -1) {
input.release(mark);
}
//dbg.exitRule(getGrammarFileName(), "recovery");
}
}
}
@lexer::header {
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.css.lib;
}
// -------------
// Main rule. This is the main entry rule for the parser, the top level
// grammar rule.
//
// A style sheet consists of an optional character set specification, an optional series
// of imports, and then the main body of style rules.
//
styleSheet
:
ws?
( charSet ws? )?
( layerStatement ws? )?
imports?
namespaces?
body?
EOF
;
namespaces
:
( namespace ws? )+
;
namespace
: NAMESPACE_SYM ws? (namespacePrefixName ws?)? resourceIdentifier ws? SEMI
;
namespacePrefixName
: IDENT
;
resourceIdentifier
: STRING | URI
;
charSet
: CHARSET_SYM ws? charSetValue ws? SEMI
;
charSetValue
: STRING
;
imports
:
(
( importItem ws? SEMI ws? )
|
{isScssSource()}? ( sass_use ws? SEMI ws? )
|
{isScssSource()}? ( sass_forward ws? SEMI ws? )
)+
;
importItem
:
IMPORT_SYM ws? resourceIdentifier ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
|
//multiple imports in one directive
{isScssSource()}? IMPORT_SYM ws? resourceIdentifier (ws? COMMA ws? resourceIdentifier)* ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
|
{isLessSource()}? IMPORT_SYM ws? (LPAREN less_import_types RPAREN ws?)? resourceIdentifier ws? importLayer? ((ws? mediaQueryList)=>ws? mediaQueryList)?
;
importLayer
:
{tokenNameEquals("layer")}? IDENT (LPAREN ws? layerName ws? RPAREN)?
;
sass_use
:
SASS_USE ws resourceIdentifier (ws sass_use_as)? (ws sass_use_with)?
;
sass_use_as
:
{tokenNameEquals("as")}? IDENT ws IDENT
;
sass_use_with
:
{tokenNameEquals("with")}? IDENT ws? LPAREN ws? sass_use_with_declaration (ws? COMMA ws? sass_use_with_declaration)* ws? RPAREN
;
sass_use_with_declaration
:
cp_variable ws? COLON ws? cp_expression
;
sass_forward
:
SASS_FORWARD ws resourceIdentifier ( ws ( sass_forward_hide | sass_forward_show))? ({tokenNameEquals2("as")}? ws sass_forward_as)? ({tokenNameEquals2("with")}? ws sass_forward_with)?
;
sass_forward_as
:
{tokenNameEquals("as")}? IDENT ws IDENT
;
sass_forward_with
:
{tokenNameEquals("with")}? IDENT ws? LPAREN ws? sass_forward_with_declaration (ws? COMMA ws? sass_forward_with_declaration)* ws? RPAREN
;
sass_forward_with_declaration
:
cp_variable ws? COLON ws? cp_expression
;
sass_forward_hide
:
{tokenNameEquals("hide")}? IDENT ws IDENT (ws? COMMA ws? IDENT)*
;
sass_forward_show
:
{tokenNameEquals("show")}? IDENT ws IDENT (ws? COMMA ws? IDENT)*
;
media
: MEDIA_SYM ws?
(
mediaQueryList
) ws?
LBRACE ws? syncToFollow
mediaBody?
RBRACE
;
mediaBody
:
(
( mediaBodyItem ((ws? SEMI)=>ws? SEMI)? ws? )
|
( SEMI ws? )
| ({isScssSource()}? sass_extend (ws | (SEMI)))
)+
;
mediaBodyItem
:
(SASS_MIXIN | (((DOT IDENT) | HASH) ws? LPAREN (~RPAREN)* RPAREN ~(LBRACE|SEMI)* LBRACE))=>cp_mixin_declaration
//https://netbeans.org/bugzilla/show_bug.cgi?id=227510#c12 -- class selector in selector group recognized as mixin call -- workarounded by adding the ws? SEMI to the predicate
| (cp_mixin_call (ws? IMPORTANT_SYM)? ws? SEMI)=> {isLessSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?
| (cp_mixin_call)=> {isScssSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?
| (((SASS_AT_ROOT (ws selectorsGroup)? ) | (SASS_AT_ROOT ws LPAREN ws? IDENT ws? COLON ws? IDENT ws? RPAREN) | selectorsGroup) ws? LBRACE)=>rule
| (propertyDeclaration)=>propertyDeclaration
| {isScssSource()}? sass_debug
| {isScssSource()}? sass_control
| {isScssSource()}? sass_content
| {isCssPreprocessorSource()}? importItem
| rule
| page
| fontFace
| vendorAtRule
//Just a partial hotfix for nested MQ: complete grammar is defined in: http://www.w3.org/TR/css3-conditional/#processing
| media
| supportsAtRule
;
mediaQueryList
: mediaQuery ( (ws? COMMA)=> ws? COMMA ws? mediaQuery )*
;
mediaQuery
:
mediaCondition
| (mediaQueryOperator ws? )? mediaType ((ws? key_and) => ws? key_and ws? mediaConditionWithoutOr)?
| {isLessSource()}? cp_variable
;
mediaQueryOperator
:
key_only | NOT
;
mediaCondition
:
(NOT ws?) mediaInParens //media-not
| mediaInParens (ws? (
ws? key_and ws? mediaInParens //media-and
| ws? key_or ws? mediaInParens //media-or
)
)*
| LPAREN ws* RPAREN
;
mediaConditionWithoutOr
:
(NOT ws?) mediaInParens //media-not
| mediaInParens (ws? key_and ws? mediaInParens)* //media-and
| (HASH) => {isCssPreprocessorSource()}? sass_interpolation_expression_var
;
mediaInParens:
LPAREN ws? (mediaCondition | mediaExpression) ws? RPAREN
;
mediaType
: IDENT | GEN | {isCssPreprocessorSource()}? sass_interpolation_expression_var
;
mediaExpression
:
mediaFeature (ws? (COLON | mediaComparisonOperator) ws? mediaFeatureValue)?
| mediaFeatureRangeContext
| (HASH) => {isCssPreprocessorSource()}? sass_interpolation_expression_var
;
mediaComparisonOperator
:
OPEQ | LESS | LESS_OR_EQ | GREATER | GREATER_OR_EQ
;
mediaRangeExplicitValue
: LENGTH | EMS | REM | RESOLUTION | EXS | DIMENSION
;
mediaFeatureValue
:
mediaRangeExplicitValue
| {isCssPreprocessorSource()}? cp_expression
| expression
;
mediaFeatureRangeContext
:
(mediaFeatureValue ws? (LESS | LESS_OR_EQ) ) => mediaFeatureValue ws? (LESS | LESS_OR_EQ) ws? mediaFeature (ws? (LESS | LESS_OR_EQ) ws? mediaFeatureValue)?
| (mediaFeatureValue ws? (GREATER | GREATER_OR_EQ) ) => mediaFeatureValue ws? (GREATER | GREATER_OR_EQ) ws? mediaFeature (ws? (GREATER | GREATER_OR_EQ) ws? mediaFeatureValue)?
| mediaFeatureValue ws? OPEQ ws? mediaFeature
;
mediaFeature
: IDENT | GEN | {isCssPreprocessorSource()}? ( cp_variable | sass_interpolation_expression_var )
;
body
:
(
( bodyItem ((ws? SEMI)=>ws? SEMI)? ws? )
|
( SEMI ws? )
)+
;
bodyItem
:
(SASS_MIXIN | (((DOT IDENT) | HASH) ws? LPAREN (~RPAREN)* RPAREN ~(LBRACE|RBRACE|SEMI)* LBRACE))=>cp_mixin_declaration
//https://netbeans.org/bugzilla/show_bug.cgi?id=227510#c12 -- class selector in selector group recognized as mixin call -- workarounded by adding the ws? SEMI to the predicate
| (cp_mixin_call ws? SEMI)=> {isLessSource()}? cp_mixin_call
| (cp_mixin_call)=> {isScssSource()}? cp_mixin_call
| rule
| (cp_variable ws? COLON)=> cp_variable_declaration
| (sass_map)=> sass_map
| at_rule
//not exactly acc. to the spec, since just CP stuff can preceede, but is IMO satisfactory
| {isCssPreprocessorSource()}? importItem
| {isScssSource()}? sass_debug
| {isScssSource()}? sass_control
| {isScssSource()}? sass_function_declaration
; catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(NL));
}
supportsAtRule
:
SUPPORTS_SYM ws? supportsCondition ws? LBRACE ws? syncToFollow mediaBody? RBRACE
;
supportsCondition
:
NOT ws supportsInParens
| supportsInParens (ws supportsWithOperator)?
;
supportsWithOperator
:
supportsConjunction (ws supportsConjunction)*
| supportsDisjunction (ws supportsDisjunction)*
;
supportsConjunction
: (key_and ws supportsInParens)
;
supportsDisjunction
: (key_or ws supportsInParens)
;
supportsInParens options {backtrack=true;}
:
LPAREN ws? supportsCondition ws? RPAREN
| supportsFeature
| function
// this is still lacking ( <any-value>?) - lets see whether this becomes
// a problem or not
;
supportsFeature
:
supportsDecl
;
supportsDecl
:
LPAREN ws? declaration ws? RPAREN
;
containerAtRule options {backtrack=true;}
:
(CONTAINER_SYM ws containerCondition ws? LBRACE) => CONTAINER_SYM ws containerCondition ws? LBRACE ws? syncToFollow body? RBRACE
| CONTAINER_SYM ws containerName ws containerCondition ws? LBRACE ws? syncToFollow body? RBRACE
;
containerCondition
:
NOT ws containerQueryInParens
| containerQueryInParens (ws containerQueryWithOperator)?
;
containerQueryWithOperator
:
containerQueryConjunction (ws containerQueryConjunction)*
| containerQueryDisjunction (ws containerQueryDisjunction)*
;
containerQueryConjunction
: (key_and ws containerQueryInParens)
;
containerQueryDisjunction
: (key_or ws containerQueryInParens)
;
containerQueryInParens options {backtrack=true;}
:
LPAREN ws? containerCondition ws? RPAREN
| sizeFeature
| {tokenNameEquals("style")}? IDENT ws? LPAREN ws? styleQuery ws? RPAREN
| function
// this is still lacking ( <any-value>?) - lets see whether this becomes
// a problem or not
;
containerName
: IDENT
;
styleQuery:
styleCondition
| styleFeature
;
styleCondition:
NOT ws styleInParens
| styleInParens (ws styleConditionWithOperator)
;
styleConditionWithOperator
:
styleQueryConjunction (ws styleQueryConjunction)*
| styleQueryDisjunction (ws styleQueryDisjunction)*
;
styleQueryConjunction
: (key_and ws styleInParens)
;
styleQueryDisjunction
: (key_or ws styleInParens)
;
styleInParens options {backtrack=true;}
:
LPAREN ws? styleCondition ws? RPAREN
| LPAREN ws? styleFeature ws? RPAREN
| function
// this is still lacking ( <any-value>?) - lets see whether this becomes
// a problem or not
;
sizeFeature options {backtrack=true;}
:
LPAREN ws? sizeFeatureFixedValue ws? RPAREN
| LPAREN ws? sizeFeatureRangeSingle ws? RPAREN
| LPAREN ws? sizeFeatureRangeBetweenLt ws? RPAREN
| LPAREN ws? sizeFeatureRangeBetweenGt ws? RPAREN
;
sizeFeatureFixedValue
:
sizeFeatureName ( ws? COLON ws? sizeFeatureValue)?
;
sizeFeatureRangeSingle
:
(sizeFeatureName | sizeFeatureValue) ws? (OPEQ | LESS | LESS_OR_EQ | GREATER | GREATER_OR_EQ) ws? (sizeFeatureName | sizeFeatureValue)
;
sizeFeatureRangeBetweenLt
:
sizeFeatureValue ws? (LESS | LESS_OR_EQ) ws? sizeFeatureName ws? (LESS | LESS_OR_EQ) ws? sizeFeatureValue
;
sizeFeatureRangeBetweenGt
:
sizeFeatureValue ws? (GREATER | GREATER_OR_EQ) ws? sizeFeatureName ws? (GREATER | GREATER_OR_EQ) ws? sizeFeatureValue
;
sizeFeatureName
:
IDENT
| VARIABLE
;
sizeFeatureValue
:
term
;
styleFeature
:
declaration
;
layerAtRule
:
layerBlock
|
layerStatement
;
layerBlock
:
(LAYER_SYM ws layerName? ws? layerBody)
;
layerStatement
:
(LAYER_SYM ws layerName ( ws? COMMA ws? layerName)* SEMI)
;
layerName
:
IDENT (DOT IDENT)*
;
layerBody
:
LBRACE ws? body? ws? RBRACE
;
at_rule
:
media
| page
| counterStyle
| fontFace
| supportsAtRule
| vendorAtRule
| layerAtRule
| containerAtRule
;
vendorAtRule
: moz_document | webkitKeyframes | generic_at_rule;
atRuleId
:
IDENT | STRING | {isCssPreprocessorSource()}? ( cp_variable | sass_interpolation_expression_var )
;
generic_at_rule
: {! tokenNameEquals("@charset")}? AT_IDENT (( ~ (SEMI | LBRACE)) => componentValue )* ((LBRACE) => braceBlock2 | SEMI);
moz_document
:
MOZ_DOCUMENT_SYM ws? ( moz_document_function ws?) ( COMMA ws? moz_document_function ws? )*
LBRACE ws?
body? //can be empty
RBRACE
;
moz_document_function
:
URI | MOZ_URL_PREFIX | MOZ_DOMAIN | MOZ_REGEXP
;
//http://developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariCSSRef/Articles/OtherStandardCSS3Features.html#//apple_ref/doc/uid/TP40007601-SW1
webkitKeyframes
:
( WEBKIT_KEYFRAMES_SYM | KEYFRAMES_SYM | {tokenNameEquals("@-moz-keyframes")}? AT_IDENT | {tokenNameEquals("@-o-keyframes")}? AT_IDENT ) ws? atRuleId ws?
LBRACE ws?
( webkitKeyframesBlock ws? )*
RBRACE
;
webkitKeyframesBlock
:
webkitKeyframeSelectors ws?
LBRACE ws? syncToFollow
declarations?
RBRACE
| {isScssSource()}? {isScssSource()}? sass_content SEMI?
;
webkitKeyframeSelectors
:
( {tokenNameEquals("from")}? IDENT | {tokenNameEquals("to")}? IDENT | PERCENTAGE ) ( ws? COMMA ws? ( {tokenNameEquals("from")}? IDENT | {tokenNameEquals("to")}? IDENT | PERCENTAGE ) )*
;
page
@init {
boolean semiRequired = false;
}
: PAGE_SYM ws? ( IDENT ws? )? (pseudoPage ws?)?
LBRACE
//the grammar in the http://www.w3.org/TR/css3-page/ says the declaration/margins should be delimited by the semicolon,
//but there's no such char in the examples => making it arbitrary
( ws? ({semiRequired}? (SEMI ws?) | (SEMI ws?)?) (propertyDeclaration{semiRequired=true;}|margin{semiRequired=false;}))*
SEMI?
ws?
RBRACE
;
counterStyle
: COUNTER_STYLE_SYM ws? IDENT ws?
LBRACE ws? syncToDeclarationsRule
declarations?
RBRACE
;
fontFace
: FONT_FACE_SYM ws?
LBRACE ws? syncToDeclarationsRule
declarations?
RBRACE
;
margin
: margin_sym ws? LBRACE ws? syncToDeclarationsRule declarations? RBRACE
;
margin_sym
:
TOPLEFTCORNER_SYM |
TOPLEFT_SYM |
TOPCENTER_SYM |
TOPRIGHT_SYM |
TOPRIGHTCORNER_SYM |
BOTTOMLEFTCORNER_SYM |
BOTTOMLEFT_SYM |
BOTTOMCENTER_SYM |
BOTTOMRIGHT_SYM |
BOTTOMRIGHTCORNER_SYM |
LEFTTOP_SYM |
LEFTMIDDLE_SYM |
LEFTBOTTOM_SYM |
RIGHTTOP_SYM |
RIGHTMIDDLE_SYM |
RIGHTBOTTOM_SYM
;
pseudoPage
: COLON IDENT
;
operator
: SOLIDUS
| COMMA
;
unaryOperator
: MINUS
| PLUS
;
property
:
//parse as scss_declaration_interpolation_expression only if it really contains some #{} content
//(the IE allows also just ident as its content)
{isScssSource()}? sass_selector_interpolation_exp
| {isLessSource()}? less_selector_interpolation_exp
| VARIABLE
| IDENT
| GEN
| {isCssPreprocessorSource()}? cp_variable
; catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(COLON));
}
sass_map
:
sass_map_name COLON ws? LPAREN ws? syncToFollow
//what can be in the map? --- just properties?
sass_map_pairs?
RPAREN ((ws? SASS_DEFAULT) | (ws? SASS_GLOBAL))*
;
sass_map_name
:
cp_variable
;
sass_map_pairs
:
(
( sass_map_pair ((ws? COMMA)=>ws? COMMA)? ws? )
|
( COMMA ws? )
)+
;
sass_map_pair
:
(NUMBER|(STRING (ws? STRING)*)|((function)=>function)|property|sass_map) ws? COLON ws? cp_expression (ws? prio)?
;
rule
:
(
(SASS_AT_ROOT (ws selectorsGroup)?)
| (SASS_AT_ROOT ws LPAREN ws? {tokenNameEquals("without") || tokenNameEquals("with")}? IDENT /* with || without */ ws? COLON ws? IDENT ws? RPAREN)
| selectorsGroup
) ws?
LBRACE ws? syncToFollow
declarations?
RBRACE
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(RBRACE));
input.consume(); //consume the RBRACE as well
}
declarations
:
(SEMI ws?)* declaration (((ws? (SEMI ws?)+)|ws) declaration)* ((ws? (SEMI ws?)+)|ws)?
| (SEMI ws?)+
;
declaration
:
(cp_variable_declaration)=>cp_variable_declaration
| (sass_map)=> sass_map
| (sass_nested_properties)=>sass_nested_properties
| (((SASS_AT_ROOT (ws selectorsGroup)? ) | (SASS_AT_ROOT ws LPAREN ws? IDENT ws? COLON ws? IDENT ws? RPAREN) | selectorsGroup) ws? LBRACE)=>rule
| (propertyDeclaration)=>propertyDeclaration
//for the error recovery - if the previous synt. predicate fails (an error in the declaration we'll still able to recover INSIDE the declaration
| (property ws? COLON ~(LBRACE|SEMI|RBRACE)* (RBRACE|SEMI) )=>propertyDeclaration
| (cp_mixin_declaration)=>cp_mixin_declaration
| (cp_mixin_call)=> cp_mixin_call (ws? IMPORTANT_SYM)?
| (cp_mixin_call)=> {isScssSource()}? cp_mixin_call (ws? IMPORTANT_SYM)?
| at_rule
| {isScssSource()}? sass_control
| {isScssSource()}? sass_extend
| {isScssSource()}? sass_debug
| {isScssSource()}? sass_content
| {isScssSource()}? sass_function_return
| {isScssSource()}? sass_error
| {isScssSource()}? importItem
| GEN
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(SEMI));
}
selectorsGroup
:
selector (ws? COMMA ws? selector)* ({isCssPreprocessorSource()}? COMMA)?
;
selector
: (combinator ws?)? simpleSelectorSequence ( ((ws? combinator ws?)|ws) simpleSelectorSequence)*
| {isScssSource()}? combinator
;
combinator
:
PLUS | GREATER | TILDE
;
simpleSelectorSequence
:
(elementSubsequent | {isScssSource()}? sass_selector_interpolation_exp
| {isLessSource()}? less_selector_interpolation_exp ) ((ws? esPred)=>((ws? elementSubsequent) |(ws ({isScssSource()}? sass_selector_interpolation_exp | {isLessSource()}? less_selector_interpolation_exp))))*
| (typeSelector)=>typeSelector ((ws? esPred)=>((ws? elementSubsequent) | {isScssSource()}? ws sass_selector_interpolation_exp))*
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(LBRACE));
}
//predicate
esPred
: HASH_SYMBOL | HASH | DOT | LBRACKET | COLON | DCOLON | SASS_EXTEND_ONLY_SELECTOR | {isCssPreprocessorSource()}? LESS_AND
;
typeSelector
options { k = 2; }
: (((IDENT | STAR)? PIPE)=>namespacePrefix)? elementName
;
namespacePrefix
: ( namespacePrefixName | STAR)? PIPE
;
elementSubsequent
:
(
{isScssSource()}? sass_extend_only_selector
| {isCssPreprocessorSource()}? LESS_AND (IDENT | NUMBER | {isScssSource()}? sass_interpolation_expression_var)*
| {isLessSource()}? LESS_AND less_selector_interpolation_exp
| cssId
| cssClass
| slAttribute
| pseudo
)
;
//Error Recovery: Allow the parser to enter the cssId rule even if there's just hash char.
cssId
: HASH ({isScssSource()}? sass_selector_interpolation_exp)?
|
( HASH_SYMBOL
( NAME
| {isLessSource()}? less_selector_interpolation_exp // #@{var} { ... }
)
)
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(WS, IDENT, LBRACE));
}
cssClass
: (DOT
(
{isScssSource()}? sass_selector_interpolation_exp
| {isLessSource()}? less_selector_interpolation_exp
| IDENT
| NOT
| GEN
)
) | {tokenNameStartsWith(".")}? DIMENSION
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(WS, IDENT, LBRACE));
}
//using typeSelector even for the universal selector since the lookahead would have to be 3 (IDENT PIPE (IDENT|STAR) :-(
elementName
: IDENT | GEN | LESS_AND | STAR
;
slAttribute
: LBRACKET
namespacePrefix? ws?
slAttributeName ws?
(
(
OPEQ
| INCLUDES
| DASHMATCH
| BEGINS
| ENDS
| CONTAINS
)
ws?
slAttributeValue
ws?
)?
RBRACKET
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(IDENT, LBRACE));
}
//bit similar naming to attrvalue, attrname - but these are different - for functions
slAttributeName
: IDENT
;
slAttributeValue
:
(
IDENT
| STRING
)
;
pseudo
: ( COLON | DCOLON )
(
(
( IDENT | GEN )
( // Function
ws? LPAREN ws? ( (expression ws?) | STAR )? RPAREN
)?
)
| {isScssSource()}? sass_interpolation_expression_var
| ( NOT ws? LPAREN ws? ( selectorsGroup ws?)? RPAREN )
| {tokenNameEquals("is") || tokenNameEquals("where") || tokenNameEquals("has")}? ( IDENT ws? LPAREN ws? ( selectorsGroup ws?)? RPAREN )
| ({isLessSource()}? {tokenNameEquals("extend")}? IDENT ws? LPAREN ws? selectorsGroup? RPAREN)
)
;
propertyDeclaration
:
{isCssPreprocessorSource() && !tokenNameStartsWith("--")}? STAR? property ws? COLON ws? cp_propertyValue //cp_expression may contain the IMPORT_SYM
| {tokenNameStartsWith("--")}? property ws? COLON ws? componentValueOuter?
| STAR? property ws? COLON ws? propertyValue (ws? prio)?
;
catch[ RecognitionException rce] {
reportError(rce);
//recovery: if an mismatched token occures inside a declaration is found,
//then skip all tokens until an end of the rule is found represented by right curly brace
consumeUntil(input, BitSet.of(SEMI, RBRACE));
}
//XXX this is a hack for the IMPORT_SYM inside cp_expression
cp_propertyValue
:
{isCssPreprocessorSource()}? cp_expression_list
| propertyValue
;
propertyValue
:
expression
;
//an expression wich doesn't contain cp expression operators
expressionPredicate
options { k = 1; }
:
( ~ (AT_IDENT | STAR | SOLIDUS | LBRACE | SEMI | RBRACE | SASS_VAR) )+ ( SEMI | RBRACE )
;
preservedToken: ~ (LPAREN | LBRACE | LBRACKET | RPAREN | RBRACE | RBRACKET);
preservedTokenTopLevel: ~ (LPAREN | LBRACE | LBRACKET | RPAREN | RBRACE | RBRACKET | SEMI );
// {} block
braceBlock2:
LBRACE ws?
declarations?
RBRACE
;
// simple brace block
braceBlock: LBRACE componentValue* RBRACE;
bracketBlock: LBRACKET componentValue+ RBRACKET;
parenBlock: LPAREN componentValue+ RPAREN;
componentValue: parenBlock | braceBlock | bracketBlock | (functionName ws? LPAREN) => function | preservedToken;
componentValueOuter: (parenBlock | braceBlock | bracketBlock | (functionName ws? LPAREN) => function | preservedTokenTopLevel) componentValueOuter*;
//recovery: syncs the parser to the first identifier in the token input stream or the closing curly bracket
//since the rule matches epsilon it will always be entered
syncToDeclarationsRule
@init {
//why sync to DOT? - LESS allows class rules nested
syncToSet(BitSet.of(IDENT, RBRACE, STAR, DOT));
}
:
;
syncTo_RBRACE
@init {
syncToRBRACE(1); //initial nest == 1
}
:
;
syncTo_SEMI
@init {
syncToSet(BitSet.of(SEMI));
}
:
SEMI
;
//synct to computed follow set in the rule
syncToFollow
@init {
syncToSet();
}
:
;
prio
: IMPORTANT_SYM
;
expression
: term ( (( ws | (ws? operator ws?) | /* nothing */) term)=> ( ws | (ws? operator ws?) | /* nothing */) term)*
;
term
:
( unaryOperator ws? )?
(
(functionName ws? LPAREN)=>function //"myfunction(" as predicate
| VARIABLE
| {! (isScssSource() && tokenNameEquals2("."))}? IDENT
| (LBRACKET WS? IDENT (WS IDENT)* WS? RBRACKET)
| NUMBER
| URANGE
| PERCENTAGE
| LENGTH
| EMS
| REM
| EXS
| ANGLE
| TIME
| FREQ
| RESOLUTION
| DIMENSION //so we can match expression like a:nth-child(3n+1) -- the "3n" is lexed as dimension
| STRING
| TILDE ( STRING | LESS_JS_STRING ) //less escaped string/js string
| LESS_JS_STRING //less js string
| GEN
| URI
| hexColor
| {isCssPreprocessorSource()}? cp_variable
| {isScssSource()}? LESS_AND
| {isScssSource()}? sass_interpolation_expression_var
| {isLessSource()}? less_selector_interpolation
| {isCssPreprocessorSource()}? cp_term_symbol //accept any garbage in preprocessors
)
;
//SASS/LESS expressions workaround
//Bug 233359 - false error in SASS editor
//https://netbeans.org/bugzilla/show_bug.cgi?id=233359
cp_term_symbol
: PERCENTAGE_SYMBOL //what else?
;
function
: functionName
LPAREN ws?
(
fnAttributes
| //empty
)
RPAREN
;
catch[ RecognitionException rce] {
reportError(rce);
consumeUntil(input, BitSet.of(RPAREN, SEMI, RBRACE));
}
functionName
//css spec allows? here just IDENT,
//but due to some nonstandart MS extension like progid:DXImageTransform.Microsoft.gradien
//the function name can be a bit more complicated
:
(IDENT COLON)? IDENT (DOT IDENT)*
;
fnAttributes
:
fnAttribute (ws? (COMMA | {isLessSource()}? SEMI) ws? fnAttribute)* ws?
;
fnAttribute
:
(fnAttributeName ws? (OPEQ|COLON) )=>fnAttributeName ws? (OPEQ|COLON) ws? fnAttributeValue
| (cp_expression)=> cp_expression
| expression
;
fnAttributeName
:
IDENT (DOT IDENT)*
| {isCssPreprocessorSource()}? cp_variable
;
fnAttributeValue
:
term ( (( ws | (ws? SOLIDUS ws?) | /* nothing */) term)=> ( ws | (ws? SOLIDUS ws?) | /* nothing */) term)* //== expression w/o COMMAs
| {isCssPreprocessorSource()}? cp_math_expression
;
hexColor
: HASH
;
ws
: ( WS | NL | COMMENT )+
;
//*** LESS SYNTAX ***
//Some additional modifications to the standard syntax rules has also been done.
//ENTRY POINT FROM CSS GRAMMAR
cp_variable_declaration
:
{isLessSource()}? cp_variable ws? COLON ws? cp_expression_list
|
{isScssSource()}? cp_variable ws? COLON ws? cp_expression_list ((ws? SASS_DEFAULT) | (ws? SASS_GLOBAL))*
;
//ENTRY POINT FROM CSS GRAMMAR
cp_variable
:
//every token which might possibly begin with the at sign
{isLessSource()}? ( AT_IDENT | IMPORT_SYM | PAGE_SYM | MEDIA_SYM | NAMESPACE_SYM | CHARSET_SYM | COUNTER_STYLE_SYM | FONT_FACE_SYM | TOPLEFTCORNER_SYM | TOPLEFT_SYM | TOPCENTER_SYM | TOPRIGHT_SYM | TOPRIGHTCORNER_SYM | BOTTOMLEFTCORNER_SYM | BOTTOMLEFT_SYM | BOTTOMCENTER_SYM | BOTTOMRIGHT_SYM | BOTTOMRIGHTCORNER_SYM | LEFTTOP_SYM | LEFTMIDDLE_SYM | LEFTBOTTOM_SYM | RIGHTTOP_SYM | RIGHTMIDDLE_SYM | RIGHTBOTTOM_SYM | MOZ_DOCUMENT_SYM | WEBKIT_KEYFRAMES_SYM | SASS_CONTENT | SASS_MIXIN | SASS_INCLUDE | SASS_EXTEND | SASS_DEBUG | SASS_WARN | SASS_IF | SASS_ELSE | SASS_FOR | SASS_FUNCTION | SASS_RETURN | SASS_EACH | SASS_WHILE | SASS_AT_ROOT | SASS_USE | SASS_FORWARD | KEYFRAMES_SYM )
|
{isScssSource()}? ( SASS_VAR | IDENT DOT SASS_VAR )
;
//comma separated list of cp_expression-s
cp_expression_list
:
(cp_expression) => cp_expression
((ws? COMMA ws? cp_expression)=>ws? COMMA ws? cp_expression)*
;
//expression:
//-----------
//
//allowed content:
//- boolean expression binary operators: and, or, <, >, <=, ==, ...
//- boolean expression unary operator: not
//- mathematical expression as term: cp_math_expression
//- whitespace separated list of expression-s
//- comma separted list of expressions-s in parenthesis
//
cp_expression
:
{isLessSource()}? (LBRACE ws? syncToFollow declarations? RBRACE)
| (cp_expression_atom) => (cp_expression_atom
(
(ws? cp_expression_operator)=>(ws? cp_expression_operator ws?) cp_expression_atom
| (ws? cp_expression_atom)=>ws? cp_expression_atom
)*)
| {isScssSource()}? LPAREN ws? syncToFollow sass_map_pairs? RPAREN
;
cp_expression_operator
:
key_or | key_and | CP_EQ | CP_NOT_EQ | LESS | LESS_OR_EQ | GREATER | GREATER_OR_EQ
;
cp_expression_atom
:
(NOT ws?)?
(
(cp_math_expression)=>cp_math_expression
| LPAREN ws? (cp_expression_list ws?)? RPAREN
)
;
//WS separated list of cp_math_expression-s
cp_math_expressions
:
cp_math_expression
(ws cp_math_expression)*
;
//mathematical expression:
//-------------------------
//allowed content:
//- parens: ()
//- binary oparators: +,-,*
//- unary operators: +,-
//- terms
//- SASS interpolation expression where the term is allowed
//
//NOT ALLOWED:
//- COMMAS
//- terms separated just by whitespace - e.g. "one two"
//
cp_math_expression
: cp_math_expression_atom
(
(ws? (PLUS|MINUS|STAR|SOLIDUS) )=> ws? (PLUS|MINUS|STAR|SOLIDUS) ws? cp_math_expression_atom
)*
;
cp_math_expression_atom
:
term
| IMPORTANT_SYM //cp property value may contain any gargabe - TODO - possibly add other garbage tokens
| ( unaryOperator ws? )? LPAREN ws? cp_math_expression ws? RPAREN
;
//parametric mixins:
// .border-radius (@radius)
// .box-shadow (@x: 0, @y: 0, @blur: 1px, @color: #000)
//
//normal mixin has common css syntax: .mixin so cannot be distinguished from a css class
//ENTRY POINT FROM CSS GRAMMAR
cp_mixin_declaration
:
(
{isLessSource()}? (LESS_AND | (((DOT cp_mixin_name) | HASH) ws? LPAREN ws? cp_args_list? RPAREN)) (ws? less_mixin_guarded)?
|
{isScssSource()}? SASS_MIXIN ws cp_mixin_name (ws? LPAREN ws? cp_args_list? RPAREN)?
)
ws? cp_mixin_block
;
//allow: .mixin; .mixin(); .mixin(@param, #77aa00);
//ENTRY POINT FROM CSS GRAMMAR
cp_mixin_call
:
(
{isLessSource()}? (DOT cp_mixin_name | HASH | AT_IDENT | LESS_AND) ((ws? combinator ws?) => ws? combinator ws? (DOT cp_mixin_name | HASH | AT_IDENT | LESS_AND))* ((pseudo)=>pseudo | (ws? LPAREN)=>(ws? LPAREN ws? cp_mixin_call_args? RPAREN))?
|
{isScssSource()}? SASS_INCLUDE ws cp_mixin_name (ws? LPAREN ws? cp_mixin_call_args? RPAREN)? (ws? cp_mixin_block)?
)
;
cp_mixin_block
:
LBRACE ws? syncToFollow
(declarations | (webkitKeyframeSelectors) =>
( webkitKeyframesBlock ws? )*)?
RBRACE
;
cp_mixin_name
:
IDENT
;
cp_mixin_call_args
:
//the term separatos is supposed to be just COMMA, but in some weird old? samples
//I found semicolon used as a delimiter between arguments
cp_mixin_call_arg ( (COMMA | SEMI) ws? cp_mixin_call_arg)* (CP_DOTS ws?)? SEMI?
;
cp_mixin_call_arg
:
(
cp_variable ws? COLON ws? cp_expression
| cp_expression
) ws?
;
//.box-shadow ("@x: 0, @y: 0, @blur: 1px, @color: #000")
cp_args_list
:
//the term separatos is supposed to be just COMMA, but in some weird old? samples
//I found semicolon used as a delimiter between arguments
//sass varargs:
//@mixin box-shadow($shadows...) {} -- note that now also LESS parser allows this incorrectly (minor issue)
( cp_arg ( ( COMMA | SEMI ) ws? cp_arg)* ( (COMMA | SEMI) ws? )? ( (CP_DOTS | LESS_REST) ws? )?)
|
(CP_DOTS | LESS_REST) ws?
;
//.box-shadow ("@x: 0", @y: 0, @blur: 1px, @color: #000)
cp_arg
:
cp_variable ws? ( COLON ws? cp_expression ws?)?
| {isLessSource()}? IDENT
;
//.mixin (@a) "when (lightness(@a) >= 50%)" {
//.mixin (@a) "when (@a > 10), (@a < -10)" { ... }
less_mixin_guarded
:
less_when ws? less_condition (ws? (COMMA | key_and) ws? less_condition)*
;
//.truth (@a) when (@a) { ... }
//.truth (@a) when (@a = true) { ... }
less_condition
:
(NOT ws?)?
LPAREN ws?
(
(cp_variable | less_function_in_condition) ws? (less_condition_operator ws? cp_math_expression)?
)
RPAREN
;
//.mixin (@a, @b: 0) when ("isnumber(@b)") { ... }
less_function_in_condition
:
less_fn_name ws? LPAREN ws? cp_variable ws? RPAREN
;
//.mixin (@a, @b: 0) when ("isnumber"(@b)) { ... }
less_fn_name
:
IDENT
;
less_condition_operator
:
GREATER | GREATER_OR_EQ | OPEQ | LESS | LESS_OR_EQ
;
less_selector_interpolation_exp :
(IDENT | MINUS)? less_selector_interpolation (less_selector_interpolation_exp | ( IDENT | MINUS | DIMENSION | LENGTH)+)?
;
less_selector_interpolation
:
AT_SIGN LBRACE ws? IDENT ws? RBRACE
;
//SCSS interpolation expression
sass_selector_interpolation_exp :
(IDENT | MINUS)? sass_interpolation_expression_var (sass_selector_interpolation_exp | ( IDENT | MINUS | DIMENSION | LENGTH)+)?
;
sass_interpolation_expression_var
:
HASH_SYMBOL LBRACE WS? cp_expression WS? RBRACE //XXX possibly allow cp_expression inside
;
//SASS nested properties:
//.funky {
// font: 2px/3px {
// family: fantasy;
// size: 30em;
// weight: bold;
// }
//}
//
//or just:
//
//.funky {
// font: {
// family: fantasy;
// size: 30em;
// weight: bold;
// }
//}
sass_nested_properties
:
property ws? COLON ws? (propertyValue ws?)? LBRACE ws? syncToFollow declarations? RBRACE
;
sass_extend
:
SASS_EXTEND ws simpleSelectorSequence (ws? COMMA ws? simpleSelectorSequence)* (ws SASS_OPTIONAL)?
;
sass_extend_only_selector
:
SASS_EXTEND_ONLY_SELECTOR sass_selector_interpolation_exp?
;
sass_debug
:
( SASS_DEBUG | SASS_WARN ) ws cp_expression
;
sass_error
:
SASS_ERROR ws STRING
;
sass_control
:
sass_if | sass_for | sass_each | sass_while
;
sass_if
:
SASS_IF ws? sass_control_expression ws? sass_control_block (ws? sass_else)?
;
sass_else
:
SASS_ELSE ws? sass_control_block
|
((SASS_ELSE ws? {tokenNameEquals("if")}? IDENT /* if */) | SASS_ELSEIF) ws? sass_control_expression ws? sass_control_block (ws? sass_else)?
;
sass_control_expression
:
cp_expression
;
sass_for
:
SASS_FOR ws cp_variable ws {tokenNameEquals("from")}? IDENT /*from*/ ws cp_math_expression ws {tokenNameEquals("to")|tokenNameEquals("through")}? IDENT /*to, through*/ ws cp_math_expression ws? sass_control_block
;
sass_each
:
SASS_EACH ws sass_each_variables ws {tokenNameEquals("in")}? IDENT /*in*/ ws (cp_expression_list (ws? COMMA)? ws?)+ sass_control_block
;
sass_each_variables
:
cp_variable ( (ws? COMMA)=> ws? COMMA ws? cp_variable )*
;
sass_while
:
SASS_WHILE ws sass_control_expression ws? sass_control_block
;
sass_control_block
:
LBRACE ws? declarations? RBRACE //likely not enough!
;
sass_function_declaration
:
//I assume there can be not only the return statement in the function block,
//but so far haven't found any such example so I put the declarations rule inside
//and added the sass_function_return into the declarations rule itself (not fully correct)
//as the return should be allowed only from the sass function declaration
SASS_FUNCTION ws sass_function_name ws? LPAREN ws? cp_args_list? RPAREN ws? LBRACE ws? declarations? RBRACE
;
sass_function_name
:
IDENT
;
sass_function_return
:
SASS_RETURN ws cp_expression
;
sass_content
:
SASS_CONTENT
;
less_import_types:
{tokenNameIs(new String[]{"LESS", "CSS", "REFERENCE", "INLINE", "ONCE", "MULTIPLE", "OPTIONAL"})}? IDENT
; catch[ RecognitionException rce] {
reportError(rce);
input.consume();
}
less_when:
{tokenNameEquals("when")}? IDENT
;
key_and:
{tokenNameEquals("and")}? IDENT
;
key_or:
{tokenNameEquals("or")}? IDENT
;
key_only:
{tokenNameEquals("only")}? IDENT
;
//*** END OF LESS SYNTAX ***
// ==============================================================
// LEXER
//
// The lexer follows the normative section of WWW standard as closely
// as it can. For instance, where the ANTLR lexer returns a token that
// is unambiguous for both ANTLR and lex (the standard defines tokens
// in lex notation), then the token names are equivalent.
//
// Note however that lex has a match order defined as top to bottom
// with longest match first. This results in a fairly inefficent, match,
// REJECT, match REJECT set of operations. ANTLR lexer grammars are actaully
// LL grammars (and hence LL recognizers), which means that we must
// specifically disambiguate longest matches and so on, when the lex
// like normative grammar results in ambiguities as far as ANTLR is concerned.
//
// This means that some tokens will either be combined compared to the
// normative spec, and the paresr will recognize them for what they are.
// In this case, the token will named as XXX_YYY where XXX and YYY are the
// token names used in the specification.
//
// Lex style macro names used in the spec may sometimes be used (in upper case
// version) as fragment rules in this grammar. However ANTLR fragment rules
// are not quite the same as lex macros, in that they generate actual
// methods in the recognizer class, and so may not be as effecient. In
// some cases then, the macro contents are embedded. Annotation indicate when
// this is the case.
//
// See comments in the rules for specific details.
// --------------------------------------------------------------
//
// N.B. CSS 2.1 is defined as case insensitive, but because each character
// is allowed to be written as in escaped form we basically define each
// character as a fragment and reuse it in all other rules.
// ==============================================================
// --------------------------------------------------------------
// Define all the fragments of the lexer. These rules neither recognize
// nor create tokens, but must be called from non-fragment rules, which
// do create tokens, using these fragments to either purely define the
// token number, or by calling them to match a certain portion of
// the token string.
//
GEN : '@@@';
fragment HEXCHAR : ('a'..'f'|'A'..'F'|'0'..'9') ;
fragment NONASCII : '\u0080'..'\uFFFF' ; // NB: Upper bound should be \u4177777
fragment UNICODE : '\\' HEXCHAR
(HEXCHAR
(HEXCHAR
(HEXCHAR
(HEXCHAR HEXCHAR?)?
)?
)?
)?
('\r'|'\n'|'\t'|'\f'|' ')* ;
fragment ESCAPE : UNICODE | '\\' ~('\r'|'\n'|'\f'|HEXCHAR) ;
fragment NMSTART : '_'
| 'a'..'z'
| 'A'..'Z'
| NONASCII
| ESCAPE
;
fragment NMCHAR : '_'
| 'a'..'z'
| 'A'..'Z'
| '0'..'9'
| '-'
| NONASCII
| ESCAPE
;
fragment NAME : NMCHAR+ ;
fragment URL : ((
'['|'!'|'#'|'$'|'%'|'&'|'*'|'~'|'.'|':'|'/'|'?'|'='|';'|','|'+'|'@'|'|' | '{' | '}'
| NMCHAR
)
(
'['|'!'|'#'|'$'|'%'|'&'|'*'|'~'|'.'|':'|'/'|'?'|'='|';'|','|'+'|'@'|'|' | WS | '\"' | '{' | '}'
| NMCHAR
)*)?
;
// Basic Alpha characters in upper, lower and escaped form.
fragment A : ('a'|'A')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'1'
;
fragment B : ('b'|'B')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'2'
;
fragment C : ('c'|'C')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'3'
;
fragment D : ('d'|'D')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'4'
;
fragment E : ('e'|'E')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'5'
;
fragment F : ('f'|'F')
| '\\' ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'6'
;
fragment G : ('g'|'G')
| '\\'
(
'g'
| 'G'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'7'
)
;
fragment H : ('h'|'H')
| '\\'
(
'h'
| 'H'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'8'
)
;
fragment I : ('i'|'I')
| '\\'
(
'i'
| 'I'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')'9'
)
;
fragment J : ('j'|'J')
| '\\'
(
'j'
| 'J'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('A'|'a')
)
;
fragment K : ('k'|'K')
| '\\'
(
'k'
| 'K'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('B'|'b')
)
;
fragment L : ('l'|'L')
| '\\'
(
'l'
| 'L'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('C'|'c')
)
;
fragment M : ('m'|'M')
| '\\'
(
'm'
| 'M'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('D'|'d')
)
;
fragment N : ('n'|'N')
| '\\'
(
'n'
| 'N'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('E'|'e')
)
;
fragment O : ('o'|'O')
| '\\'
(
'o'
| 'O'
| ('0' ('0' ('0' '0'?)?)?)? ('4'|'6')('F'|'f')
)
;
fragment P : ('p'|'P')
| '\\'
(
'p'
| 'P'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('0')
)
;
fragment Q : ('q'|'Q')
| '\\'
(
'q'
| 'Q'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('1')
)
;
fragment R : ('r'|'R')
| '\\'
(
'r'
| 'R'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('2')
)
;
fragment S : ('s'|'S')
| '\\'
(
's'
| 'S'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('3')
)
;
fragment T : ('t'|'T')
| '\\'
(
't'
| 'T'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('4')
)
;
fragment U : ('u'|'U')
| '\\'
(
'u'
| 'U'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('5')
)
;
fragment V : ('v'|'V')
| '\\'
( 'v'
| 'V'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('6')
)
;
fragment W : ('w'|'W')
| '\\'
(
'w'
| 'W'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('7')
)
;
fragment X : ('x'|'X')
| '\\'
(
'x'
| 'X'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('8')
)
;
fragment Y : ('y'|'Y')
| '\\'
(
'y'
| 'Y'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('9')
)
;
fragment Z : ('z'|'Z')
| '\\'
(
'z'
| 'Z'
| ('0' ('0' ('0' '0'?)?)?)? ('5'|'7')('A'|'a')
)
;
// ---------------------
// HTML comment open. HTML/XML comments may be placed around style sheets so that they
// are hidden from higher scope parsing engines such as HTML parsers.
// They comment open is therfore ignored by the CSS parser and we hide
// it from the ANLTR parser.
//
CDO : '<!--'
{
$channel = 3; // CDO on channel 3 in case we want it later
}
;
// ---------------------
// HTML comment close. HTML/XML comments may be placed around style sheets so that they
// are hidden from higher scope parsing engines such as HTML parsers.
// They comment close is therfore ignored by the CSS parser and we hide
// it from the ANLTR parser.
//
CDC : '-->'
{
$channel = 4; // CDC on channel 4 in case we want it later
}
;
INCLUDES : '~=' ;
DASHMATCH : '|=' ;
BEGINS : '^=' ;
ENDS : '$=' ;
CONTAINS : '*=' ;
GREATER : '>' ;
LBRACE : '{' ;
RBRACE : '}' ;
LBRACKET : '[' ;
RBRACKET : ']' ;
OPEQ : '=' ;
SEMI : ';' ;
COLON : ':' ;
DCOLON : '::' ;
SOLIDUS : '/' ;
MINUS : '-' ;
PLUS : '+' ;
STAR : '*' ;
LPAREN : '(' ;
RPAREN : ')' ;
COMMA : ',' ;
DOT : '.' ;
TILDE : '~' ;
PIPE : '|' ;
PERCENTAGE_SYMBOL
: '%' ;
EXCLAMATION_MARK: '!' ;
CP_EQ : '==' ;
CP_NOT_EQ : '!=' ;
LESS : '<' ;
GREATER_OR_EQ : '>=' | '=>'; //a weird operator variant supported by SASS
LESS_OR_EQ : '=<' | '<='; //a weird operator variant supported by SASS
LESS_AND : '&' '-'* ;
CP_DOTS : '...';
LESS_REST : '@rest...';
// -----------------
// Literal strings. Delimited by either ' or "
//
fragment INVALID :;
STRING : '\'' ( ~('\r'|'\f'|'\'') )*
(
'\''
| { $type = INVALID; }
)
| '"' ( ('\\\"') => '\\\"' | ('\\\\') => '\\\\' | ~ ('\r'|'\f'|'"') )*
(
'"'
| { $type = INVALID; }
)
;
LESS_JS_STRING : '`' ( ~('\r'|'\f'|'`') )*
(
'`'
| { $type = INVALID; }
)
;
NOT : 'NOT';
// Variable. Used to define properties, that can be used in the grammar by the
// var function (https://www.w3.org/TR/css-variables-1/)
VARIABLE : '--' NMSTART NMCHAR* ;
// -------------
// Identifier. Identifier tokens pick up properties names and values
//
IDENT : '-'? NMSTART NMCHAR* ;
// -------------
// Reference. Reference to an element in the body we are styling, such as <XXXX id="reference">
//
HASH_SYMBOL : '#';
HASH : HASH_SYMBOL NAME;
IMPORTANT_SYM : EXCLAMATION_MARK (WS|COMMENT)* 'IMPORTANT' ;
IMPORT_SYM : '@IMPORT';
PAGE_SYM : '@PAGE';
MEDIA_SYM : '@MEDIA';
NAMESPACE_SYM : '@NAMESPACE' ;
CHARSET_SYM : '@CHARSET';
COUNTER_STYLE_SYM : '@COUNTER-STYLE';
FONT_FACE_SYM : '@FONT-FACE';
SUPPORTS_SYM : '@SUPPORTS';
LAYER_SYM : '@LAYER';
CONTAINER_SYM : '@CONTAINER';
KEYFRAMES_SYM : '@KEYFRAMES';
TOPLEFTCORNER_SYM :'@TOP-LEFT-CORNER';
TOPLEFT_SYM :'@TOP-LEFT';
TOPCENTER_SYM :'@TOP-CENTER';
TOPRIGHT_SYM :'@TOP-RIGHT';
TOPRIGHTCORNER_SYM :'@TOP-RIGHT-CORNER';
BOTTOMLEFTCORNER_SYM :'@BOTTOM-LEFT-CORNER';
BOTTOMLEFT_SYM :'@BOTTOM-LEFT';
BOTTOMCENTER_SYM :'@BOTTOM-CENTER';
BOTTOMRIGHT_SYM :'@BOTTOM-RIGHT';
BOTTOMRIGHTCORNER_SYM :'@BOTTOM-RIGHT-CORNER';
LEFTTOP_SYM :'@LEFT-TOP';
LEFTMIDDLE_SYM :'@LEFT-MIDDLE';
LEFTBOTTOM_SYM :'@LEFT-BOTTOM';
RIGHTTOP_SYM :'@RIGHT-TOP';
RIGHTMIDDLE_SYM :'@RIGHT-MIDDLE';
RIGHTBOTTOM_SYM :'@RIGHT-BOTTOM';
MOZ_DOCUMENT_SYM : '@-MOZ-DOCUMENT';
WEBKIT_KEYFRAMES_SYM : '@-WEBKIT-KEYFRAMES';
//this generic at rule must be after the last of the specific at rule tokens
SASS_CONTENT : '@CONTENT';
SASS_MIXIN : '@MIXIN';
SASS_INCLUDE : '@INCLUDE';
SASS_EXTEND : '@EXTEND';
SASS_DEBUG : '@DEBUG';
SASS_ERROR : '@ERROR';
SASS_WARN : '@WARN';
SASS_IF : '@IF';
SASS_ELSE : '@ELSE';
SASS_ELSEIF : '@ELSEIF'; //@elseif
SASS_FOR : '@FOR';
SASS_FUNCTION : '@FUNCTION';
SASS_RETURN : '@RETURN';
SASS_USE : '@USE';
SASS_FORWARD : '@FORWARD';
SASS_EACH : '@EACH';
SASS_WHILE : '@WHILE';
SASS_AT_ROOT : '@AT-ROOT';
AT_SIGN : '@';
AT_IDENT : (AT_SIGN | (AT_SIGN AT_SIGN)) NMCHAR+;
SASS_VAR : '$' NMCHAR+;
SASS_DEFAULT : '!DEFAULT';
SASS_OPTIONAL : '!OPTIONAL';
SASS_GLOBAL : '!GLOBAL';
SASS_EXTEND_ONLY_SELECTOR
: PERCENTAGE_SYMBOL NMCHAR+;
// ---------
// Numbers. Numbers can be followed by pre-known units or unknown units
// as well as '%' it is a precentage. Whitespace cannot be between
// the numebr and teh unit or percent. Hence we scan any numeric, then
// if we detect one of the lexical sequences for unit tokens, we change
// the lexical type dynamically.
//
// Here we first define the various tokens, then we implement the
// number parsing rule.
//
fragment EMS :; // 'em'
fragment EXS :; // 'ex'
fragment LENGTH :; // 'px'. 'cm', 'mm', 'in'. 'pt', 'pc'
fragment REM :; // 'rem'
fragment ANGLE :; // 'deg', 'rad', 'grad'
fragment TIME :; // 'ms', 's'
fragment FREQ :; // 'khz', 'hz'
fragment DIMENSION :; // nnn'Somethingnotyetinvented'
fragment PERCENTAGE :; // '%'
fragment RESOLUTION :; //dpi,dpcm
NUMBER
: (
'0'..'9'+ ('.' '0'..'9'+)?
| '.' '0'..'9'+
)
(
(D P (I|C))=>
D P
(
I | C M
)
{ $type = RESOLUTION; }
| (E (M|X))=>
E
(
M { $type = EMS; }
| X { $type = EXS; }
)
| (P(X|T|C))=>
P
(
X
| T
| C
)
{ $type = LENGTH; }
| (C M)=>
C M { $type = LENGTH; }
| (M (M|S))=>
M
(
M { $type = LENGTH; }
| S { $type = TIME; }
)
| (I N)=>
I N { $type = LENGTH; }
| (D E G)=>
D E G { $type = ANGLE; }
// | (R A D)=>
// R A D { $type = ANGLE; }
| (R (A|E))=>
R
(
A D {$type = ANGLE; }
| E M {$type = REM; }
)
| (S)=>S { $type = TIME; }
| (K? H Z)=>
K? H Z { $type = FREQ; }
| IDENT { $type = DIMENSION; }
| PERCENTAGE_SYMBOL { $type = PERCENTAGE; }
| // Just a number
)
;
// ------------
// url and uri.
//
URI : U R L
'('
((WS)=>WS)? (URL|STRING) WS?
')'
;
fragment HEXCHAR_WILDCARD: '?' | HEXCHAR;
URANGE: ('u'|'U') PLUS HEXCHAR_WILDCARD+ (MINUS HEXCHAR_WILDCARD+)?;
MOZ_URL_PREFIX
:
'URL-PREFIX('
((WS)=>WS)? (URL|STRING) WS?
')'
;
MOZ_DOMAIN
:
'DOMAIN('
((WS)=>WS)? (URL|STRING) WS?
')'
;
MOZ_REGEXP
:
'REGEXP('
((WS)=>WS)? STRING WS?
')'
;
// -------------
// Whitespace. Though the W3 standard shows a Yacc/Lex style parser and lexer
// that process the whitespace within the parser, ANTLR does not
// need to deal with the whitespace directly in the parser.
//
WS
:
(' '|'\t')+
;
NL
:
// ('\r' '\n'? | '\n')
('\r' | '\n')+
;
// Comments. Comments may not be nested, may be multilined and are delimited
// like C comments: /* ..... */
COMMENT
:
'/*' ( options { greedy=false; } : .*) '*/'
;
LINE_COMMENT
:
'//'( options { greedy=false; } : ~('\r' | '\n')* ) {
if (isCssPreprocessorSource()) {$channel = HIDDEN;}
}
;
// -------------
// Illegal. Any other character shoudl not be allowed.
//
[ Verzeichnis aufwärts0.64unsichere Verbindung
Übersetzung europäischer Sprachen durch Browser
]
|
2026-04-02
|