Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  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 the 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.
//

[ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge