/*
 * $Id: SACParserCSSmobileOKBasic1.jj,v 1.1 2008/03/20 01:20:18 sdanig Exp $
 *
 * CSS Parser Project
 *
 * Copyright (C) 1999-2008 David Schweinsberg.  All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * To contact the authors of the library:
 *
 * http://cssparser.sourceforge.net/
 * mailto:davidsch@users.sourceforge.net
 */

options {
	IGNORE_CASE = true;
	STATIC = false;
//    UNICODE_INPUT = false;
    USER_CHAR_STREAM = true;
//    DEBUG_TOKEN_MANAGER = true;
//    DEBUG_PARSER = true;
}

PARSER_BEGIN(SACParserCSSmobileOKBasic1)

package com.steadystate.css.parser;

import java.io.*;
import java.net.*;
import java.text.MessageFormat;
import java.util.*;
import org.w3c.css.sac.*;
import com.steadystate.css.parser.*;
import com.steadystate.css.parser.selectors.*;

/**
 * @author <a href="mailto:davidsch@users.sourceforge.net">David Schweinsberg</a>
 * @version $Id: SACParserCSSmobileOKBasic1.jj,v 1.1 2008/03/20 01:20:18 sdanig Exp $
 */
public class SACParserCSSmobileOKBasic1 extends AbstractSACParser implements Parser {

    private boolean _quiet = true;

    public SACParserCSSmobileOKBasic1() {
        this((CharStream) null);
    }

    public String getParserVersion() {
        return "http://www.w3.org/TR/mobileOK-basic10-tests/#validity";
    }

    protected String getGrammarUri()
    {
        return "CSSgrammarMobileOKBasic1.0.txt";
    }

    protected Token getToken()
    {
        return this.token;
    }

}

PARSER_END(SACParserCSSmobileOKBasic1)

TOKEN_MGR_DECLS :
{
    private boolean _quiet = true;
    private ErrorHandler errorHandler;

    private String trimBy(StringBuffer s, int left, int right) {
        int end = s.length();
        return s.toString().substring(left, end-right);
    }

    private String trimUrl(StringBuffer s) {
        StringBuffer s1 = new StringBuffer( trimBy(s, 4, 1).trim() );
        if (s1.length() == 0)
        {
            return s1.toString();
        }
        int end = s1.length() - 1;
        if ((s1.charAt(0) == '"' && s1.charAt(end) == '"')
            || (s1.charAt(0) == '\'' && s1.charAt(end) == '\''))
            return trimBy(s1, 1, 1);
        else
           return s1.toString();
    }
}

<DEFAULT> TOKEN :
{
    < S: ( " "|"\t"|"\r"|"\n"|"\f" )+ >
}

<DEFAULT> MORE :
{
  < "/*" > : COMMENT
}

<DEFAULT> TOKEN : 
{
	< IDENT: <NMSTART> ( <NMCHAR> )* > 
| < LINK_PSCLASS: ":link" > { matchedToken.image = trimBy(image, 1, 0); }
| < VISITED_PSCLASS: ":visited" > { matchedToken.image = trimBy(image, 1, 0); }
| < ACTIVE_PSCLASS: ":active" > { matchedToken.image = trimBy(image, 1, 0); }
| < FIRST_LINE: ":first-line" > { matchedToken.image = trimBy(image, 1, 0); }
| < FIRST_LETTER: ":first-letter" > { matchedToken.image = trimBy(image, 1, 0); }
| < HASH: "#" <NAME> > 
//| < CLASS: "." <NAME> > 
}

<DEFAULT> TOKEN :
{
  < LBRACE: "{" >
| < RBRACE: "}" >
| < COMMA: "," >
| < DOT: "." >
| < SEMICOLON: ";" >
| < COLON: ":" >
| < ASTERISK: "*" >
| < SLASH: "/" >
| < PLUS: "+" >
| < MINUS: "-" >
| < EQUALS: "=" >
| < GT: ">" >
| < LSQUARE: "[" >
| < RSQUARE: "]" >
}

<DEFAULT> TOKEN :
{
  < STRING: <STRING1> | <STRING2> > { matchedToken.image = trimBy(image, 1, 1); } : DEFAULT
| < RROUND: ")" >
}

// TODO: check url
<DEFAULT> TOKEN :
{
//  < #URL: ["!","#","$","%","&","*"-"~"] | <NONASCII> | <ESCAPE> >
//  < #URL: ["!","#","$","%","&","*"-"[","]"-"~"] | <NONASCII> | <ESCAPE> > 
 < URL: "url(" ( <S> )* ( <STRING> | ( ["!","#","$","%","&","*"-"[","]"-"~"] | <NONASCII> | <ESCAPE> )* ) ( <S> )* <RROUND> > { matchedToken.image = trimUrl(image); } : DEFAULT
}

<DEFAULT> TOKEN :
{
  < CDO: "<!--" > : DEFAULT
| < CDC: "-->" > : DEFAULT
| < IMPORT_SYM: "@import" > : DEFAULT
| < MEDIA_SYM: "@media" >
| < IMPORTANT_SYM: "!" ( <S> )? "important" > : DEFAULT
| < ATKEYWORD: "@" <IDENT> >
| < EMS: <NUM> "em" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < EXS: <NUM> "ex" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_PX: <NUM> "px" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_CM: <NUM> "cm" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_MM: <NUM> "mm" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_IN: <NUM> "in" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_PT: <NUM> "pt" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < LENGTH_PC: <NUM> "pc" > { matchedToken.image = trimBy(image, 0, 2); } : DEFAULT
| < PERCENTAGE: <NUM> "%" > { matchedToken.image = trimBy(image, 0, 1); } : DEFAULT
| < NUMBER: <NUM> > : DEFAULT
// TODO: check rgb
| < RGB: "rgb(" > : DEFAULT
| < #NAME: ( <NMCHAR> )+ > 
| < #D: ["0"-"9"] > 
| < NUM: ( <D> )+ | ( <D> )* "." ( <D> )+ > 
// TODO: check unicoderange
| < UNICODERANGE: "U+" ( <RANGE> | ( <HNUM> "-" <HNUM> ) ) > 
| < #RANGE: <Q16> | <H> ( (<Q15>)? | <H> ( (<Q14>)? | <H> ( (<Q13>)? | <H> ( (<Q12>)? | <H> ( (<Q11>)? | <H> ) ) ) ) ) >
| < #Q16: "?" | "??" | "???" | "????" | "?????" | "??????" >
| < #Q15: "?" | "??" | "???" | "????" | "?????" >
| < #Q14: "?" | "??" | "???" | "????" >
| < #Q13: "?" | "??" | "???" >
| < #Q12: "?" | "??" >
| < #Q11: "?" >
// TODO: check latin1
// < LATIN1 : "\u0080"-"\uFFFF" >
// < STRINGCHAR : <ESCAPE> | <LATIN1> | [" ","!","#","$","%","&","(", "-", "~"] >
// < NMCHAR: ["a"-"z","0"-"9","-"] | <LATIN1> | <ESCAPE>
// < NMSTRT: ["a"-"z"] | <LATIN1> | <ESCAPE> >
// < NOTNM: ["^", "-", "a-z", "0-9", "\"] | <LATIN1>
| < #NMSTART: ["a"-"z"] | <NONASCII> | <ESCAPE> >
| < #NMCHAR: ["a"-"z","0"-"9","-"] | <NONASCII> | <ESCAPE> >
// TODO: check strings
//| < #STRING1: "\"" ( ["\t"," ","!","#","$","%","&","("-"~"] | "\\" <NL> | "\'" | <NONASCII> | <ESCAPE> )* "\"" >
//| < #STRING2: "\'" ( ["\t"," ","!","#","$","%","&","("-"~"] | "\\" <NL> | "\"" | <NONASCII> | <ESCAPE> )* "\'" >
| < #STRING1: "\"" ( ["\t"," ","!","#","$","%","&","("-"[","]"-"~"] | "\\" <NL> | "\'" | <NONASCII> | <ESCAPE> )* "\"" >
| < #STRING2: "\'" ( ["\t"," ","!","#","$","%","&","("-"[","]"-"~"] | "\\" <NL> | "\"" | <NONASCII> | <ESCAPE> )* "\'" >
| < #NONASCII: ["\u0080"-"\uFFFF"] >
// TODO: check escape
| < #ESCAPE: <UNICODE> | ( "\\" [" "-"~","\u0080"-"\uFFFF"] ) >
| < #NL: "\n" | "\r\n" | "\r" | "\f" >
| < #UNICODE: "\\" <HNUM> ( " " | "\t" | "\r" | "\n" | "\f" )? >
| < #HNUM: <H> | <H><H> | <H><H><H> | <H><H><H><H> | <H><H><H><H><H> | <H><H><H><H><H><H> >
| < #H: ["0"-"9","a"-"f"] >
}

<COMMENT> SKIP :
{
  < "*/" > : DEFAULT
}

<COMMENT> MORE :
{
  < ~[] > : COMMENT
}

<*> TOKEN:
{
    < UNKNOWN: ~[] >
    {
        if (!_quiet) {
            System.err.println("Illegal character : " + image.toString());
        }
    }
}

//
// stylesheet
//  : [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
//      [ [ ruleset | media ] [S|CDO|CDC]* ]*
//  ;
//
void styleSheet() :
{
}
{
    try
    {
        { this.handleStartDocument(); }
        styleSheetRuleList()
        <EOF>
    }
    finally
    {
    	this.handleEndDocument();
    }
}

// Although the grammar does not include [S|CDO|CDC] but [CDO|CDC], white space
// should be allowed
void styleSheetRuleList() :
{
}
{
    ( <S> | <CDO> | <CDC> )*
    ( importRule() ( <S> | <CDO> | <CDC> )* )*
    (
     ( styleRule() | mediaRule() | unknownRule() )
     ( <S> | <CDO> | <CDC> )*
    )*
    (
     ( styleRule() | mediaRule() | importRuleIgnored() | unknownRule() )
     ( <S> | <CDO> | <CDC> )*
    )*
}

//
// This is used by ASTStyleSheet.insertRule to parse a single rule
//
void styleSheetRuleSingle() :
{
}
{
  ( importRule() | styleRule() | mediaRule() | unknownRule() )
}

void unknownRule() :
{
    Token t;
    String s;
}
{
    try
    {
        t = <ATKEYWORD>
        {
            s = skip();
            this.handleIgnorableAtRule(s);
        }
    }
    catch (ParseException e)
    {
        this.getErrorHandler().error(
            this.toCSSParseException("invalidUnknownRule", e));
    }
}

//
// import
//   : IMPORT_SYM S*
//     [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
//   ;
//
void importRule() :
{
    Token t;
    SACMediaListImpl ml = new SACMediaListImpl();
}
{
    try
    {
        <IMPORT_SYM> ( <S> )*
        ( t = <STRING>
        | t = <URL> ) ( <S> )*
        ( mediaList(ml) )?
        <SEMICOLON>
        {
            this.handleImportStyle(unescape(t.image), new SACMediaListImpl(), null);
        }
    }
    catch (CSSParseException e)
    {
        this.getErrorHandler().error(e);
        this.error_skipAtRule();
    }
    catch (ParseException e)
    {
        this.getErrorHandler().error(
            this.toCSSParseException("invalidImportRule", e));
        this.error_skipAtRule();
    }
}

void importRuleIgnored() :
{
    Token t;
    SACMediaListImpl ml = new SACMediaListImpl();
}
{
    { ParseException e = generateParseException(); }
    try
    {
        <IMPORT_SYM>
        ( <S> )*
        ( t = <STRING>
        | t = <URL> ) ( <S> )*
        ( mediaList(ml) )?
        <SEMICOLON>
    }
    finally
    {
        this.getErrorHandler().error(this.toCSSParseException("invalidImportRuleIgnored", e));
    }
}

//
// media
//   : MEDIA_SYM S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
//   ;
//
void mediaRule() :
{
    boolean start = false;
    SACMediaListImpl ml = new SACMediaListImpl();
}
{
    try
    {
        <MEDIA_SYM> ( <S> )*
        mediaList(ml)
        {
            start = true;
            this.handleStartMedia(ml);
        }
        <LBRACE> ( <S> )*
        ( mediaRuleList() )?
        <RBRACE>
    }
    catch (CSSParseException e)
    {
        this.getErrorHandler().error(e);
        this.error_skipblock();
    }
    catch(ParseException e)
    {
        CSSParseException cpe = this.toCSSParseException("invalidMediaRule", e);
        this.getErrorHandler().error(cpe);
        this.getErrorHandler().warning(this.createSkipWarning("ignoringRule", cpe));
        this.error_skipblock();
    }
    finally
    {
        if (start) {
            this.handleEndMedia(ml);
        }
    }
}

void mediaList(SACMediaListImpl ml) :
{
    String s;
}
{
    try
    {
        s = medium()
        ( <COMMA> ( <S> )* { ml.add(s); } s = medium() )*
        { ml.add(s); }
    }
    catch(ParseException e)
    {
        throw this.toCSSParseException("invalidMediaList", e);
    }
}

void mediaRuleList() :
{
}
{
  ( ( styleRule() | unknownRule() ) ( <S> )* )+ 
}

void mediaRuleSingle() :
{
}
{
  ( styleRule() | unknownRule() )
}

//
// medium
//   : IDENT S*
//   ;
//
String medium() :
{
  Token t;
}
{
    t = <IDENT> ( <S> )*
    {
        this.handleMedium(t.image);
        return t.image;
    }
}

//
// operator
//  : '/' | ',' | /* empty */
//  ;
//
LexicalUnit operator(LexicalUnit prev) :
{
  Token t;
}
{
  t = <SLASH> ( <S> )*   { return new LexicalUnitImpl(prev, LexicalUnit.SAC_OPERATOR_SLASH); }
  | t = <COMMA> ( <S> )* { return new LexicalUnitImpl(prev, LexicalUnit.SAC_OPERATOR_COMMA); }
}

//
// unary_operator
//   : '-' | '+'
//   ;
//
char unaryOperator() :
{
}
{
  ( <MINUS> { return '-'; } | <PLUS> { return '+'; } )
}

//
// property
//   : IDENT S*
//   ;
//
String property() :
{
    Token t;
}
{
    t = <IDENT> ( <S> )*
    { return unescape(t.image); }
}

//
// ruleset
//   : selector [ ',' S* selector ]*
//     '{' S* declaration [ ';' S* declaration ]* '}' S*
//   ;
//
void styleRule() :
{
    SelectorList selList = null;
    boolean start = false;
    boolean noError = true;
}
{
    try {
        selList = selectorList()
        <LBRACE> ( <S> )*
        {
            start = true;
            this.handleStartSelector(selList);
        }
        // TODO: css 2 must have at least one declaration too
        ( declaration() )
        ( <SEMICOLON> ( <S> )* ( declaration() )? )*
        <RBRACE>
    }
    catch(CSSParseException e)
    {
        this.getErrorHandler().error(e);
        noError = false;
        this.error_skipblock();
    }
    catch(ParseException e)
    {
        this.getErrorHandler().error(this.toCSSParseException("invalidStyleRule", e));
        noError = false;
        this.error_skipblock();
    }
    finally {
        if (start) {
            this.handleEndSelector(selList);
        }
    }
}

SelectorList selectorList() :
{
    SelectorListImpl selList = new SelectorListImpl();
    Selector sel;
}
{
    sel = selector() 
    ( <COMMA> ( <S> )* { selList.add(sel); } sel = selector() )* ( <S> )*
    {
        selList.add(sel);
        return selList;
    }
}

//
// selector
//   : simple_selector+ [ pseudo_element ]? 
//   ;
//
Selector selector() :
{
    Selector sel;
}
{
    try {
        sel = simpleSelector(null, ' ') 
        ( LOOKAHEAD(2) <S> sel = simpleSelector(sel, ' ') )*
        {
            this.handleSelector(sel);
            return sel;
        }
    } catch (ParseException e) {
        throw this.toCSSParseException("invalidSelector", e);
        //skipSelector();
    }
}

//
// simple_selector
//  : element_name id? class? pseudo_class?	/* eg: H1.subject */
//  | class? pseudo_class?		/* eg: #xyz33 */
//  | pseudo_class?			/* eg: .author */
//  ;
//
Selector simpleSelector(Selector sel, char comb) :
{
    SimpleSelector simpleSel = null;
    Condition c = null;
}
{
    try
    {
        (
            ( simpleSel = elementName()
                ( c = hash(c) )? 
                ( c = _class(c) )? 
                ( c = pseudoClass(c) )? 
                ( c = pseudoElement(c) )? 
            )
            | 
            ( { simpleSel = this.getSelectorFactory().createElementSelector(null, null); }
                ( 
                	( c = hash(c) ) 
                	( c = _class(c) )? 
                	( c = pseudoClass(c) )? 
                	( c = pseudoElement(c) )? 
                )
            ) 
            | 
            ( { simpleSel = this.getSelectorFactory().createElementSelector(null, null); }
                ( 
                	( c = _class(c) ) 
                	( c = pseudoClass(c) )? 
                	( c = pseudoElement(c) )? 
                )
            ) 
            | 
            ( { simpleSel = this.getSelectorFactory().createElementSelector(null, null); }
                ( 
                	( c = pseudoClass(c) ) 
                	( c = pseudoElement(c) )? 
                )
            ) 
            | 
            ( { simpleSel = this.getSelectorFactory().createElementSelector(null, null); }
                ( 
                	( c = pseudoElement(c) ) 
                )
            )
        )

        {
            if (c != null) {
                simpleSel = this.getSelectorFactory().createConditionalSelector(simpleSel, c);
            }

            if (sel != null) {
                switch (comb) {
                case ' ':
                    sel = this.getSelectorFactory().createDescendantSelector(sel, simpleSel);
                    break;
                }
            } else {
                sel = simpleSel;
            }

            return sel;
        }
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidSimpleSelector", e);
    }
}

//
// class
//   : '.' IDENT
//   ;
//
Condition _class(Condition pred) :
{
    Token t;
}
{
    try
    {
        <DOT> t = <IDENT>
        {
            Condition c = this.getConditionFactory().createClassCondition(null, t.image);
            return (pred == null) ? c : this.getConditionFactory().createAndCondition(pred, c);
        }
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidClassSelector", e);
    }
}

//
// element_name
//   : IDENT 
//   ;
//
SimpleSelector elementName() :
{
    Token t;
}
{
    try
    {
        t=<IDENT>
        { return this.getSelectorFactory().createElementSelector(null, unescape(t.image)); }
        | <ASTERISK>
        { return this.getSelectorFactory().createElementSelector(null, null); }
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidElementName", e);
    }
}

//
// pseudo
//   : ':' [ IDENT | S* ')' ]
//   ;
//
Condition pseudoClass(Condition pred) :
{
    Condition c;
    Token t;
}
{
    try
    {
        (
            (t = <LINK_PSCLASS> 
            | t = <VISITED_PSCLASS> 
            | t = <ACTIVE_PSCLASS> )
            {
                // There appears to be an issue here regarding "first-letter" & "first-line"
                //String s = unescape(t.image);
                String s = t.image;
    		    c = this.getConditionFactory().createPseudoClassCondition(null, s);
                return (pred == null)
                    ? c
                    : this.getConditionFactory().createAndCondition(pred, c);
            }
        )
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidPseudoClass", e);
    }
}

//
// pseudo
//   : ':' [ IDENT | S* ')' ]
//   ;
//
Condition pseudoElement(Condition pred) :
{
    Condition c;
    Token t;
}
{
    try
    {
        (
            t = <FIRST_LETTER> 
            | t = <FIRST_LINE> 
            {
                //String s = unescape(t.image);
                String s = t.image;
                // TODO: createPseudoElementCondition
    		    c = this.getConditionFactory().createPseudoClassCondition(null, s);
                return (pred == null)
                    ? c
                    : this.getConditionFactory().createAndCondition(pred, c);
            }
        )
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidPseudoElement", e);
    }
}

Condition hash(Condition pred) :
{
    Token t;
}
{
    try
    {
        t = <HASH>
        {
            Condition c = this.getConditionFactory().createIdCondition(t.image.substring(1));
            return (pred == null) ? c : this.getConditionFactory().createAndCondition(pred, c);
        }
    }
    catch (ParseException e)
    {
        throw this.toCSSParseException("invalidHash", e);
    }
}


void styleDeclaration()  :
{
}
{
	//<LBRACE> ( <S> )*
	( declaration() )?
	( <SEMICOLON> ( <S> )* ( declaration() )? )*
	//<RBRACE>
}

//
// declaration
//   : property ':' S* expr prio?
//   |
//   ;
//
void declaration() :
{
  String p;
  LexicalUnit e;
  boolean priority = false;
}
{
    try
    {
        p = property()
        <COLON> ( <S> )*
        e = expr()
        ( priority = prio() )?
        {
            this.handleProperty(p, e, priority);
        }
    }
    catch (CSSParseException ex)
    {
        this.getErrorHandler().error(ex);
        error_skipdecl();
    }
    catch (ParseException ex)
    {
        this.getErrorHandler().error(this.toCSSParseException("invalidDeclaration", ex));
        error_skipdecl();
    }
}

//
// prio
//   : IMPORTANT_SYM S*
//   ;
//
boolean prio() :
{
}
{
  <IMPORTANT_SYM> ( <S> )*
  { return true; }
}

//
// expr
//   : term [ operator term ]*
//   ;
//
// TODO: Deal with the operator
//
LexicalUnit expr() :
{
    LexicalUnit head;
    LexicalUnit body;
}
{
    try
    {
        head = term(null) { body = head; }
        ( ( body = operator(body) )? body = term(body) )*
        { return head; }
    }
    catch (ParseException ex)
    {
        throw this.toCSSParseException("invalidExpr", ex);
    }
}

//
// term
//   : unary_operator?
//     [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
//		STRING S* | URI S* | RGB S* | hexcolor ]
//   ;
//
LexicalUnit term(LexicalUnit prev) :
{
  Token t;
  char op = ' ';
  String s;
  LexicalUnit value = null;
//  RGBColor color;
//  HexColor h;
//  CSSValueImpl value = null;
}
{
/*
  ( ( op = unaryOperator() )?
  ( ( t = <NUMBER>    { value = new CSSValueImpl( CSSPrimitiveValue.CSS_NUMBER, new Float( op + t.image ) ); }
    | t = <PERCENTAGE>  { value = new CSSValueImpl( CSSPrimitiveValue.CSS_PERCENTAGE, new Float( op + t.image ) ); }
    | t = <LENGTH_PX>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_PX, new Float( op + t.image ) ); }
    | t = <LENGTH_CM>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_CM, new Float( op + t.image ) ); }
    | t = <LENGTH_MM>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_MM, new Float( op + t.image ) ); }
    | t = <LENGTH_IN>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_IN, new Float( op + t.image ) ); }
    | t = <LENGTH_PT>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_PT, new Float( op + t.image ) ); }
    | t = <LENGTH_PC>   { value = new CSSValueImpl( CSSPrimitiveValue.CSS_PC, new Float( op + t.image ) ); }
    | t = <EMS>     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_EMS, new Float( op + t.image ) ); }
    | t = <EXS>     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_EXS, new Float( op + t.image ) ); }
  | t = <STRING>      { value = new CSSValueImpl( CSSPrimitiveValue.CSS_STRING, op + t.image ); }
  | t = <IDENT>     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_IDENT, op + t.image ); }
  | t = <URL>       { value = new CSSValueImpl( CSSPrimitiveValue.CSS_URI, op + t.image ); }
  | t = <UNICODERANGE>  { value = new CSSValueImpl( CSSPrimitiveValue.CSS_IDENT, op + t.image ); }
  | color = rgb()     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_RGBCOLOR, color ); }
  | color = hexcolor()  { value = new CSSValueImpl( CSSPrimitiveValue.CSS_RGBCOLOR, color ); }
  | t = <DIMEN>     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_DIMENSION, new Float( op + t.image ) ); }
  | t = <INHERIT>     { value = new CSSValueImpl( CSSPrimitiveValue.CSS_INHERIT, op + t.image ); } )
  ( <S> )* )
  { return value; }
*/
    ( op = unaryOperator() )?
    (
        ( t = <NUMBER>
        {
            try
            {
                value = LexicalUnitImpl.createNumber(prev, intValue(op, t.image));
            }
            catch (NumberFormatException e)
            {
                value = LexicalUnitImpl.createNumber(prev, floatValue(op, t.image));
            }
        }
        | t = <PERCENTAGE>  { value = LexicalUnitImpl.createPercentage(prev, floatValue(op, t.image)); }
        | t = <LENGTH_PX>   { value = LexicalUnitImpl.createPixel(prev, floatValue(op, t.image)); }
        | t = <LENGTH_CM>   { value = LexicalUnitImpl.createCentimeter(prev, floatValue(op, t.image)); }
        | t = <LENGTH_MM>   { value = LexicalUnitImpl.createMillimeter(prev, floatValue(op, t.image)); }
        | t = <LENGTH_IN>   { value = LexicalUnitImpl.createInch(prev, floatValue(op, t.image)); }
        | t = <LENGTH_PT>   { value = LexicalUnitImpl.createPoint(prev, floatValue(op, t.image)); }
        | t = <LENGTH_PC>   { value = LexicalUnitImpl.createPica(prev, floatValue(op, t.image)); }
        | t = <EMS>         { value = LexicalUnitImpl.createEm(prev, floatValue(op, t.image)); }
        | t = <EXS>         { value = LexicalUnitImpl.createEx(prev, floatValue(op, t.image)); }
        )
    | t = <STRING>          { value = new LexicalUnitImpl(prev, LexicalUnit.SAC_STRING_VALUE, t.image); }
    | t = <IDENT>           { value = new LexicalUnitImpl(prev, LexicalUnit.SAC_IDENT, t.image); }
    | t = <URL>             { value = new LexicalUnitImpl(prev, LexicalUnit.SAC_URI, t.image); }
    | t = <UNICODERANGE>    { value = new LexicalUnitImpl(prev, LexicalUnit.SAC_UNICODERANGE, t.image); }
    | value = rgb(prev)
    | value = hexcolor(prev)
    )
    ( <S> )*
    {
        if (value instanceof LexicalUnitImpl)
        {
            ((LexicalUnitImpl) value).setLocator(this.getLocator());
        }
        return value;
    }
}


//
// rgb
//   : RGB S* expr ')' S*
//   ;
//
LexicalUnit rgb(LexicalUnit prev) :
{
    Token t;
    LexicalUnit params;
}
{
    t = <RGB> ( <S> )*
    params = expr()
    <RROUND>
    {
        return LexicalUnitImpl.createRgbColor(prev, params);
    }
}

//
// hexcolor
//   : HASH
//   ;
//
LexicalUnit hexcolor(LexicalUnit prev) :
{
    Token t;
}
{
    t = <HASH>
    {
        return this.hexcolorInternal(prev, t);
    }
}

JAVACODE
void skipSelector() {
    Token t = getToken(1);
    while (t.kind != COMMA && t.kind != SEMICOLON && t.kind != LBRACE && t.kind != EOF ) {
        getNextToken();
        t = getToken(1);
    }
}

JAVACODE
String skip() {
  StringBuffer sb = new StringBuffer();
  int nesting = 0;
  Token t = getToken(0);
  if (t.image != null) {
    sb.append(t.image);    
  }
  do {
    t = getNextToken();
    if (t.kind == EOF)
      break;
    sb.append(t.image);
    if (t.kind == LBRACE)
      nesting++;
    else if (t.kind == RBRACE)
      nesting--;
    else if (t.kind == SEMICOLON && nesting <= 0)
      break;
  }
  while ((t.kind != RBRACE) || (nesting > 0));

  return sb.toString();
}

JAVACODE
void error_skipblock()
{
    Token t;
    int nesting = 0;
    do
    {
        t = getNextToken();
        if (t.kind == LBRACE)
        {
            nesting++;
        }
        else if (t.kind == RBRACE)
        {
            nesting--;
        }
        else if (t.kind == EOF)
        {
            break;
        }
    }
    while ((t.kind != RBRACE) || (nesting > 0));
}

JAVACODE
void error_skipdecl()
{
    int nesting = 0;
    Token t = getToken(1);
    if (t.kind == LBRACE)
    {
        error_skipblock();
    }
    else
    {
        Token oldToken = t;
        while (t.kind != SEMICOLON && t.kind != RBRACE && t.kind != EOF )
        {
            oldToken = t;
            t = getNextToken();
        }
        if (t.kind == RBRACE)
        {
            token = oldToken;
        }
    }
}

JAVACODE
void error_skipAtRule()
{
    Token t = null;
    do
    {
        t = getNextToken();
    }
    while (t.kind != SEMICOLON);
}
