diff options
Diffstat (limited to 'theme-compiler/src/com/vaadin/sass/parser/Parser.jj')
-rw-r--r-- | theme-compiler/src/com/vaadin/sass/parser/Parser.jj | 2463 |
1 files changed, 2463 insertions, 0 deletions
diff --git a/theme-compiler/src/com/vaadin/sass/parser/Parser.jj b/theme-compiler/src/com/vaadin/sass/parser/Parser.jj new file mode 100644 index 0000000000..717094a763 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/parser/Parser.jj @@ -0,0 +1,2463 @@ +/* -*-java-extended-*- + * Copyright (c) 1999 World Wide Web Consortium + * (Massachusetts Institute of Technology, Institut National de Recherche + * en Informatique et en Automatique, Keio University). + * All Rights Reserved. http://www.w3.org/Consortium/Legal/ + * + * $Id: Parser.jj,v 1.15 2000/10/27 21:09:37 plehegar Exp $ + */ + +options { + IGNORE_CASE = true; + STATIC = false; + USER_CHAR_STREAM = true; + /* DEBUG_TOKEN_MANAGER = true; + DEBUG_PARSER = true; */ +} + +PARSER_BEGIN(Parser) + +package com.vaadin.sass.parser; + +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; + +import org.w3c.css.sac.ConditionFactory; +import org.w3c.css.sac.Condition; +import org.w3c.css.sac.SelectorFactory; +import org.w3c.css.sac.SelectorList; +import org.w3c.css.sac.Selector; +import org.w3c.css.sac.SimpleSelector; +import org.w3c.css.sac.DocumentHandler; +import org.w3c.css.sac.InputSource; +import org.w3c.css.sac.ErrorHandler; +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.CSSParseException; +import org.w3c.css.sac.Locator; +import org.w3c.css.sac.LexicalUnit; + +import org.w3c.flute.parser.selectors.SelectorFactoryImpl; +import org.w3c.flute.parser.selectors.ConditionFactoryImpl; + +import org.w3c.flute.util.Encoding; + +import com.vaadin.sass.handler.*; + +import com.vaadin.sass.tree.*; + +/** + * A CSS2 parser + * + * @author Philippe Le H�garet + * @version $Revision: 1.15 $ + */ +public class Parser implements org.w3c.css.sac.Parser { + + // replaces all \t, \n, etc with this StringBuffer. + static final StringBuilder SPACE = new StringBuilder(" "); + + // the document handler for the parser + protected SCSSDocumentHandlerImpl documentHandler; + // the error handler for the parser + protected ErrorHandler errorHandler; + // the input source for the parser + protected InputSource source; + + protected ConditionFactory conditionFactory; + protected SelectorFactory selectorFactory; + + // temporary place holder for pseudo-element ... + private String pseudoElt; + + /** + * Creates a new Parser + */ + public Parser() { + this((CharStream) null); + } + + /** + * @@TODO + * @exception CSSException Not yet implemented + */ + public void setLocale(Locale locale) throws CSSException { + throw new CSSException(CSSException.SAC_NOT_SUPPORTED_ERR); + } + + /** + * Set the document handler for this parser + */ + public void setDocumentHandler(DocumentHandler handler) { + this.documentHandler = (SCSSDocumentHandlerImpl) handler; + } + + public void setSelectorFactory(SelectorFactory selectorFactory) { + this.selectorFactory = selectorFactory; + } + + public void setConditionFactory(ConditionFactory conditionFactory) { + this.conditionFactory = conditionFactory; + } + + /** + * Set the error handler for this parser + */ + public void setErrorHandler(ErrorHandler error) { + this.errorHandler = error; + } + + /** + * Main parse methods + * + * @param source the source of the style sheet. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. + */ + public void parseStyleSheet(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + + parserUnit(); + } + + /** + * Convenient method for URIs. + * + * @param systemId the fully resolved URI of the style sheet. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. + */ + public void parseStyleSheet(String systemId) + throws CSSException, IOException { + parseStyleSheet(new InputSource(systemId)); + } + + /** + * This method parses only one rule (style rule or at-rule, except @charset). + * + * @param source the source of the rule. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. + */ + public void parseRule(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + _parseRule(); + } + + /** + * This method parses a style declaration (including the surrounding curly + * braces). + * + * @param source the source of the style declaration. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. + */ + public void parseStyleDeclaration(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + _parseDeclarationBlock(); + } + + /** + * This methods returns "http://www.w3.org/TR/REC-CSS2". + * @return the string "http://www.w3.org/TR/REC-CSS2". + */ + public String getParserVersion() { + return "http://www.w3.org/TR/REC-CSS2"; + } + + /** + * Parse methods used by DOM Level 2 implementation. + */ + public void parseImportRule(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + _parseImportRule(); + } + + public void parseMediaRule(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + _parseMediaRule(); + } + + public SelectorList parseSelectors(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + if (selectorFactory == null) { + selectorFactory = new SelectorFactoryImpl(); + } + if (conditionFactory == null) { + conditionFactory = new ConditionFactoryImpl(); + } + return _parseSelectors(); + } + + public LexicalUnit parsePropertyValue(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + return expr(); + } + + public boolean parsePriority(InputSource source) + throws CSSException, IOException { + this.source = source; + ReInit(getCharStreamWithLurk(source)); + + return prio(); + } + + /** + * Convert the source into a Reader. Used only by DOM Level 2 parser methods. + */ + private Reader getReader(InputSource source) throws IOException { + if (source.getCharacterStream() != null) { + return source.getCharacterStream(); + } else if (source.getByteStream() != null) { + // My DOM level 2 implementation doesn't use this case. + if (source.getEncoding() == null) { + // unknown encoding, use ASCII as default. + return new InputStreamReader(source.getByteStream(), "ASCII"); + } else { + return new InputStreamReader(source.getByteStream(), + source.getEncoding()); + } + } else { + // systemId + // @@TODO + throw new CSSException("not yet implemented"); + } + } + + /** + * Convert the source into a CharStream with encoding informations. + * The encoding can be found in the InputSource or in the CSS document. + * Since this method marks the reader and make a reset after looking for + * the charset declaration, you'll find the charset declaration into the + * stream. + */ + private CharStream getCharStreamWithLurk(InputSource source) + throws CSSException, IOException { + if (source.getCharacterStream() != null) { + // all encoding are supposed to be resolved by the user + // return the reader + return new Generic_CharStream(source.getCharacterStream(), 1, 1); + } else if (source.getByteStream() == null) { + // @@CONTINUE ME. see also getReader() with systemId + try { + source.setByteStream(new URL(source.getURI()).openStream()); + } catch (Exception e) { + try { + source.setByteStream(new FileInputStream(source.getURI())); + } catch (IOException ex) { + throw new CSSException("invalid url ?"); + } + } + } + String encoding = "ASCII"; + InputStream input = source.getByteStream(); + char c = ' '; + + if (!input.markSupported()) { + input = new BufferedInputStream(input); + source.setByteStream(input); + } + input.mark(100); + c = (char) input.read(); + + if (c == '@') { + // hum, is it a charset ? + int size = 100; + byte[] buf = new byte[size]; + input.read(buf, 0, 7); + String keyword = new String(buf, 0, 7); + if (keyword.equals("charset")) { + // Yes, this is the charset declaration ! + + // here I don't use the right declaration : white space are ' '. + while ((c = (char) input.read()) == ' ') { + // find the first quote + } + char endChar = c; + int i = 0; + + if ((endChar != '"') && (endChar != '\'')) { + // hum this is not a quote. + throw new CSSException("invalid charset declaration"); + } + + while ((c = (char) input.read()) != endChar) { + buf[i++] = (byte) c; + if (i == size) { + byte[] old = buf; + buf = new byte[size + 100]; + System.arraycopy(old, 0, buf, 0, size); + size += 100; + } + } + while ((c = (char) input.read()) == ' ') { + // find the next relevant character + } + if (c != ';') { + // no semi colon at the end ? + throw new CSSException("invalid charset declaration: " + + "missing semi colon"); + } + encoding = new String(buf, 0, i); + if (source.getEncoding() != null) { + // compare the two encoding informations. + // For example, I don't accept to have ASCII and after UTF-8. + // Is it really good ? That is the question. + if (!encoding.equals(source.getEncoding())) { + throw new CSSException("invalid encoding information."); + } + } + } // else no charset declaration available + } + // ok set the real encoding of this source. + source.setEncoding(encoding); + // set the real reader of this source. + source.setCharacterStream(new InputStreamReader(source.getByteStream(), + Encoding.getJavaEncoding(encoding))); + // reset the stream (leave the charset declaration in the stream). + input.reset(); + + return new Generic_CharStream(source.getCharacterStream(), 1, 1); + } + + private LocatorImpl currentLocator; + private Locator getLocator() { + if (currentLocator == null) { + currentLocator = new LocatorImpl(this); + return currentLocator; + } + return currentLocator.reInit(this); + } + private LocatorImpl getLocator(Token save) { + if (currentLocator == null) { + currentLocator = new LocatorImpl(this, save); + return currentLocator; + } + return currentLocator.reInit(this, save); + } + + private void reportError(Locator l, Exception e) { + if (errorHandler != null) { + if (e instanceof ParseException) { + // construct a clean error message. + ParseException pe = (ParseException) e; + if (pe.specialConstructor) { + StringBuffer errorM = new StringBuffer(); + if (pe.currentToken != null) { + errorM.append("encountered \"") + .append(pe.currentToken.next); + } + errorM.append('"'); + if (pe.expectedTokenSequences.length != 0) { + errorM.append(". Was expecting one of: "); + for (int i = 0; i < pe.expectedTokenSequences.length; i++) { + for (int j = 0; j < pe.expectedTokenSequences[i].length; j++) { + int kind = pe.expectedTokenSequences[i][j]; + if (kind != S) { + errorM.append(pe.tokenImage[kind]); + errorM.append(' '); + } + } + } + } + errorHandler.error(new CSSParseException(errorM.toString(), + l, e)); + } else { + errorHandler.error(new CSSParseException(e.getMessage(), + l, e)); + } + } else if (e == null) { + errorHandler.error(new CSSParseException("error", l, null)); + } else { + errorHandler.error(new CSSParseException(e.getMessage(), l, e)); + } + } + } + + private void reportWarningSkipText(Locator l, String text) { + if (errorHandler != null && text != null) { + errorHandler.warning(new CSSParseException("Skipping: " + text, l)); + } + } +} + +PARSER_END(Parser) + +/* + * The tokenizer + */ + +<DEFAULT> +TOKEN : +{ + < S : ( [ " ", "\t" , "\n" , "\r", "\f" ] )+ > + { image = Parser.SPACE; } +} + +<DEFAULT> +MORE : +{ + "//" : IN_SINGLE_LINE_COMMENT +} + +<IN_SINGLE_LINE_COMMENT> +MORE : +{ + < ~["\n","\r"] > : IN_SINGLE_LINE_COMMENT +} + +<IN_SINGLE_LINE_COMMENT> +SKIP : +{ + < "\n"|"\r"|"\r\n" > : DEFAULT +} + +<DEFAULT> +MORE : +{ + <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT +| + "/*" : IN_MULTI_LINE_COMMENT +} +<IN_FORMAL_COMMENT> +SPECIAL_TOKEN : +{ + <FORMAL_COMMENT: "*/" > : DEFAULT +} + +<IN_MULTI_LINE_COMMENT> +SKIP : +{ + <MULTI_LINE_COMMENT: "*/" > : DEFAULT +} + +<IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT> +MORE : +{ + < ~[] > +} + +<DEFAULT> +TOKEN : +{ + < CDO : "<!--" > + | < CDC : "-->" > + | < LBRACE : "{" > + | < RBRACE : "}"> + | < DASHMATCH : "|=" > + | < INCLUDES : "~=" > + | < EQ : "=" > + | < PLUS : "+" > + | < MINUS : "-" > + | < COMMA : "," > + | < SEMICOLON : ";" > + | < PRECEDES : ">" > + | < DIV : "/" > + | < LBRACKET : "[" > + | < RBRACKET : "]" > + | < ANY : "*" > + | < PARENT : "&" > + | < DOT : "." > + | < LPARAN : "(" > + | < RPARAN : ")"> +} + +<DEFAULT> +TOKEN : +{ + < COLON : ":" > +} + +< DEFAULT > +TOKEN : +{ + < EACH_VAR : "#{"< VARIABLE > "}"> +} + +<DEFAULT> +TOKEN : /* basic tokens */ +{ + < NONASCII : ["\200"-"\377"] > + | < #H : ["0"-"9", "a"-"f"] > + | < #UNICODE : "\\" <H> ( <H> )? /* I can't say {1,6} */ + ( <H> )? ( <H> )? + ( <H> )? ( <H> )? + ( [ " ", "\t" , "\n" , "\r", "\f" ] )? > + | < #ESCAPE : <UNICODE> | ( "\\" [ " "-"~","\200"-"\377" ] ) > + | < #NMSTART : ("-")?[ "a"-"z"] | <NONASCII> | <ESCAPE> > + | < #NMCHAR : ["a"-"z", "0"-"9", "-", "_"] | <NONASCII> | <ESCAPE> > + | < #STRINGCHAR : [ "\t"," ","!","#","$","%","&","("-"~" ] + | "\\\n" | "\\\r\n" | "\\\r" | "\\\f" + | <NONASCII> | <ESCAPE> > + | < #D : ["0"-"9"] > + | < #NAME : ( <NMCHAR> )+ > +} + + +<DEFAULT> +TOKEN : +{ + <TO : "to"> + |<THROUGH : "through"> + |<EACH_IN : "in"> +} + +/* DERECTIVES */ +<DEFAULT> +TOKEN : +{ + <MIXIN_SYM : "@mixin"> + | <INCLUDE_SYM : "@include"> + | <FUNCTION_SYM : "@function"> + | <RETURN_SYM : "@return"> + | <DEBUG_SYM : "@debug"> + | <WARN_SYM : "@warn"> + | <FOR_SYM : "@for"> + | <EACH_SYM : "@each"> + | <WHILE_SYM : "@while"> + | <IF_SYM : "@if"> + | <ELSE_SYM : "@else"> + | <EXTEND_SYM : "@extend"> + | <MOZ_DOCUMENT_SYM : "@-moz-document"> + | <SUPPORTS_SYM : "@supports"> +} + +<DEFAULT> +TOKEN: +{ + < GUARDED_SYM : "!" ( <S> )? "default"> +} + +<DEFAULT> +TOKEN : +{ + < STRING : ( "\"" ( <STRINGCHAR> | "'" )* "\"" ) | + ( "'" ( <STRINGCHAR> | "\"" )* "'" ) > + | < IDENT : <NMSTART> ( <NMCHAR> )* > + | < NUMBER : ( <D> )+ | ( <D> )* "." ( <D> )+ > + | < #_URL : [ "!","#","$","%","&","*"-"~" ] | <NONASCII> | <ESCAPE> > + | < URL : "url(" ( <S> )* + ( <STRING> | ( <_URL> )* ) ( <S> )* ")" > +} + +<DEFAULT> +TOKEN: +{ + < VARIABLE : "$" <IDENT>> +} + +<DEFAULT> +TOKEN : +{ + < PERCENTAGE : <NUMBER> "%" > + | < PT : <NUMBER> "pt" > + | < MM : <NUMBER> "mm" > + | < CM : <NUMBER> "cm" > + | < PC : <NUMBER> "pc" > + | < IN : <NUMBER> "in" > + | < PX : <NUMBER> "px" > + | < EMS : <NUMBER> "em" > + | < EXS : <NUMBER> "ex" > + | < DEG : <NUMBER> "deg" > + | < RAD : <NUMBER> "rad" > + | < GRAD : <NUMBER> "grad" > + | < MS : <NUMBER> "ms" > + | < SECOND : <NUMBER> "s" > + | < HZ : <NUMBER> "Hz" > + | < KHZ : <NUMBER> "kHz" > + | < DIMEN : <NUMBER> <IDENT> > +} + +<DEFAULT> +TOKEN : +{ + < HASH : "#" <NAME> > +} + +/* RESERVED ATRULE WORDS */ +<DEFAULT> +TOKEN : +{ + < IMPORT_SYM : "@import"> + | < MEDIA_SYM : "@media" > + | < CHARSET_SYM : "@charset" > + | < PAGE_SYM : "@page" > + | < FONT_FACE_SYM: "@font-face" > + | < ATKEYWORD : "@" <IDENT> > +} + +<DEFAULT> +TOKEN : +{ + < IMPORTANT_SYM : "!" ( <S> )? "important" > +} + +<DEFAULT> +TOKEN : +{ + < #RANGE0 : <H> <H> <H> <H> <H> > + | < #RANGE1 : <H> <H> <H> <H> <H> ( "?" )? > + | < #RANGE2 : <H> <H> <H> <H> ( "?" )? ( "?" )? > + | < #RANGE3 : <H> <H> <H> ( "?" )? ( "?" )? ( "?" )? > + | < #RANGE4 : <H> <H> ( "?" )? ( "?" )? ( "?" )? ( "?" )? > + | < #RANGE5 : <H> ( "?" )? ( "?" )? ( "?" )? ( "?" )? ( "?" )? > + | < #RANGE6 : "?" ( "?" )? ( "?" )? ( "?" )? ( "?" )? ( "?" )? > + | < #RANGE : <RANGE0> | <RANGE1> | <RANGE2> + | <RANGE3> | <RANGE4> | <RANGE5> | <RANGE6> > + | < #UNI : <H> ( <H> )? ( <H> )? ( <H> )? ( <H> )? ( <H> )? > + | < UNICODERANGE : "U+" <RANGE> + | "U+" <UNI> "-" <UNI> > +} + +<DEFAULT> +TOKEN : +{ + < FUNCTION : <IDENT>(< S >)* "(" > +} + +<DEFAULT, IN_MULTI_LINE_COMMENT> +TOKEN : +{ /* avoid token manager error */ + < UNKNOWN : ~[] > +} + +/* + * The grammar of CSS2 + */ + +/** + * The main entry for the parser. + * + * @exception ParseException exception during the parse + */ +void parserUnit() : +{} +{ + try { + { documentHandler.startDocument(source); } + ( charset() )? + ( <S> comments() + | ignoreStatement() )* + ( importDeclaration() ( ignoreStatement() ( <S> )* )* )* + afterImportDeclaration() + <EOF> + } finally { + documentHandler.endDocument(source); + } +} + +void charset() : +{ Token n; } +{ + try { + <CHARSET_SYM> ( <S> )* n=<STRING> ( <S> )* ";" + } catch (ParseException e) { + reportError(getLocator(e.currentToken.next), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } catch (Exception e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } +} + +void afterImportDeclaration() : +{String ret; + Locator l; +} +{ + ( + ( variable() | mixinDirective()|eachDirective() | includeDirective() | styleRule() | media()| page() | fontFace() + | { l = getLocator(); } ret=skipStatement() + { + if ((ret == null) || (ret.length() == 0)) { + return; + } + reportWarningSkipText(l, ret); + if (ret.charAt(0) == '@') { + documentHandler.ignorableAtRule(ret); + } + } + ) + ( ignoreStatement() ( <S> )* )* )* +} + +void ignoreStatement() : +{} +{ + <CDO> | <CDC> | atRuleDeclaration() +} + +/** + * The import statement + * + * @exception ParseException exception during the parse + */ +void importDeclaration() : +{Token n; + String uri; + MediaListImpl ml = new MediaListImpl(); + boolean isURL = false; +} +{ + try { + <IMPORT_SYM> + ( <S> )* ( n=<STRING> { uri = convertStringIndex(n.image, 1, + n.image.length() -1); } + | n=<URL> + { + isURL=true; + uri = n.image.substring(4, n.image.length()-1).trim(); + if ((uri.charAt(0) == '"') + || (uri.charAt(0) == '\'')) { + uri = uri.substring(1, uri.length()-1); + } + } + ) + ( <S> )* ( mediaStatement(ml) )? ";" + ( <S> )* + { + if (ml.getLength() == 0) { + // see section 6.3 of the CSS2 recommandation. + ml.addItem("all"); + } + documentHandler.importStyle(uri, ml, isURL); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } +} + +/** + * @exception ParseException exception during the parse + */ +void media() : +{ + boolean start = false; + String ret; + MediaListImpl ml = new MediaListImpl(); +} +{ + try { + <MEDIA_SYM> ( <S> )* + mediaStatement(ml) + { start = true; documentHandler.startMedia(ml); } + <LBRACE> ( <S> )* ( styleRule() | skipUnknownRule() )* <RBRACE> ( <S> )* + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } finally { + if (start) { + documentHandler.endMedia(ml); + } + } +} + +void mediaStatement(MediaListImpl ml) : +{ + String m; +} +{ + m=medium() ( <COMMA> ( <S> )* { ml.addItem(m); } m=medium() )* + { ml.addItem(m); } +} + +/** + * @exception ParseException exception during the parse + */ +String medium() : /* tv, projection, screen, ... */ +{Token n;} +{ + n=<IDENT> ( <S> )* { return convertIdent(n.image); } +} + +/** + * @exception ParseException exception during the parse + */ +void page() : +{ + boolean start = false; + Token n = null; + String page = null; + String pseudo = null; +} +{ + try { + <PAGE_SYM> ( <S> )* ( n=<IDENT> ( <S> )* )? + ( pseudo=pseudo_page() )? + { + if (n != null) { + page = convertIdent(n.image); + } + } + <LBRACE> (<S>)* + { + start = true; + documentHandler.startPage(page, pseudo); + } + ( declaration() )? ( ";" ( <S> )* ( declaration() )? )* + <RBRACE> (<S>)* + } catch (ParseException e) { + if (errorHandler != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + skipStatement(); + // reportWarningSkipText(li, skipStatement()); + } else { + skipStatement(); + } + } finally { + if (start) { + documentHandler.endPage(page, pseudo); + } + } +} + +String pseudo_page() : +{ Token n; } +{ + ":" n=<IDENT> ( <S> )* { return convertIdent(n.image); } +} + +void fontFace() : +{ + boolean start = false; +} +{ + try { + <FONT_FACE_SYM> ( <S> )* + <LBRACE> (<S>)* + { start = true; documentHandler.startFontFace(); } + ( declaration() )? ( ";" ( <S> )* ( declaration() )? )* + <RBRACE> (<S>)* + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } finally { + if (start) { + documentHandler.endFontFace(); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +void atRuleDeclaration() : +{Token n; + String ret; +} +{ + n=<ATKEYWORD> + { + ret=skipStatement(); + reportWarningSkipText(getLocator(), ret); + if ((ret != null) && (ret.charAt(0) == '@')) { + documentHandler.ignorableAtRule(ret); + } + } +} + +void skipUnknownRule() : +{ Token n;} +{ + ( n=<ATKEYWORD> +| n=<CDO> +| n=<CHARSET_SYM> +| n=<COMMA> +| n=<DASHMATCH> +| n=<FONT_FACE_SYM> +| n=<FUNCTION> +| n=<IMPORTANT_SYM> +| n=<IMPORT_SYM> +| n=<INCLUDES> +| n=<LBRACE> +| n=<MEDIA_SYM> +| n=<NONASCII> +| n=<NUMBER> +| n=<PAGE_SYM> +| n=<PERCENTAGE> +| n=<STRING> +| n=<UNICODERANGE> +| n=<URL> +| n=";" +| n="+" +| n=">" +| n="-" +| n=<UNKNOWN> + ) { + String ret; + Locator loc = getLocator(); + ret=skipStatement(); + reportWarningSkipText(loc, ret); + if ((ret != null) && (n.image.charAt(0) == '@')) { + documentHandler.ignorableAtRule(ret); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +char combinator() : +{ +char connector = ' '; +} +{ + "+" ( <S> )* { return '+'; } + | ">" ( <S> )* { return '>'; } +| <S> ( ( "+" { connector = '+'; } + | ">" { connector = '>'; } ) + ( <S> )* )? { return connector; } +} + +/** + * @exception ParseException exception during the parse + */ +String property() : +{Token n; +} +{ + n=<IDENT> ( <S> )* { return convertIdent(n.image); } +} + +String variableName() : +{Token n;} +{ + n=<VARIABLE> (<S>)* {return convertIdent(n.image.substring(1));} +} + +String functionName() : +{Token n;} +{ + n=<FUNCTION> ( <S> )* {return convertIdent(n.image.substring(0, n.image.length()-1));} +} +/** + * @exception ParseException exception during the parse + */ +void styleRule() : +{ + boolean start = false; + SelectorList l = null; + Token save; + Locator loc; +} +{ + try { + l=selectorList() { save = token; } <LBRACE> (<S>)* + { + start = true; + documentHandler.startSelector(l); + } + ( includeDirective() | media() | extendDirective()| eachDirective() | variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())* + <RBRACE> (<S>)* + } catch (ThrowedParseException e) { + if (errorHandler != null) { + LocatorImpl li = new LocatorImpl(this, + e.e.currentToken.next.beginLine, + e.e.currentToken.next.beginColumn-1); + reportError(li, e.e); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + } catch (TokenMgrError e) { + reportWarningSkipText(getLocator(), skipStatement()); + } finally { + if (start) { + documentHandler.endSelector(l); + } + } +} + +SelectorList selectorList() : +{ + SelectorListImpl selectors = new SelectorListImpl(); + Selector selector; +} +{ + selector=selector() ( <COMMA> (<S>)* { selectors.addSelector(selector); } + selector=selector() )* + { selectors.addSelector(selector); + return selectors; + } +} + +/** + * @exception ParseException exception during the parse + */ +Selector selector() : +{ + Selector selector; + char comb; +} +{ + try { + selector=simple_selector(null, ' ') + ( LOOKAHEAD(2) comb=combinator() + selector=simple_selector(selector, comb) )* (<S>)* + { + return selector; + } + } catch (ParseException e) { + /* + Token t = getToken(1); + StringBuffer s = new StringBuffer(); + s.append(getToken(0).image); + while ((t.kind != COMMA) && (t.kind != SEMICOLON) + && (t.kind != LBRACE) && (t.kind != EOF)) { + s.append(t.image); + getNextToken(); + t = getToken(1); + } + reportWarningSkipText(getLocator(), s.toString()); + */ + Token t = getToken(1); + while ((t.kind != COMMA) && (t.kind != SEMICOLON) + && (t.kind != LBRACE) && (t.kind != EOF)) { + getNextToken(); + t = getToken(1); + } + + throw new ThrowedParseException(e); + } +} + +/** + * @exception ParseException exception during the parse + */ +Selector simple_selector(Selector selector, char comb) : +{ + SimpleSelector simple_current = null; + Condition cond = null; + + pseudoElt = null; +} +{ + ( simple_current=element_name() + ( cond=hash(cond) | cond=_class(cond) + | cond=attrib(cond) | cond=pseudo(cond) )* + | cond=hash(cond) ( cond=_class(cond) + | cond=attrib(cond) | cond=pseudo(cond) )* + | cond=_class(cond) ( cond=hash(cond) | cond=_class(cond) + | cond=attrib(cond) | cond=pseudo(cond) )* + | cond=pseudo(cond) ( cond=hash(cond) | cond=_class(cond) + | cond=attrib(cond) | cond=pseudo(cond) )* + | cond=attrib(cond) ( cond=hash(cond) | cond=_class(cond) + | cond=attrib(cond) | cond=pseudo(cond) )* + ) + { + if (simple_current == null) { + simple_current = selectorFactory.createElementSelector(null, null); + } + if (cond != null) { + simple_current = selectorFactory.createConditionalSelector(simple_current, + cond); + } + if (selector != null) { + switch (comb) { + case ' ': + selector = selectorFactory.createDescendantSelector(selector, + simple_current); + break; + case '+': + selector = + selectorFactory.createDirectAdjacentSelector((short) 1, + selector, + simple_current); + break; + case '>': + selector = selectorFactory.createChildSelector(selector, + simple_current); + break; + default: + throw new ParseException("invalid state. send a bug report"); + } + } else { + selector= simple_current; + } + if (pseudoElt != null) { + selector = selectorFactory.createChildSelector(selector, + selectorFactory.createPseudoElementSelector(null, pseudoElt)); + } + return selector; + } +} + +/** + * @exception ParseException exception during the parse + */ +Condition _class(Condition pred) : +{Token t; +String s = ""; +Condition c; +} +{ + "." (t = <IDENT>{s += t.image; }|t = < EACH_VAR >{ s += t.image; })+ + { + c = conditionFactory.createClassCondition(null, s); + if (pred == null) { + return c; + } else { + return conditionFactory.createAndCondition(pred, c); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +SimpleSelector element_name() : +{Token t; String s = "";} +{ + (t = <IDENT>{s += t.image; }|t = < EACH_VAR >{ s += t.image; })+ + { + return selectorFactory.createElementSelector(null, s); + } + | "*" + { return selectorFactory.createElementSelector(null, "*"); } + | "&" + { return selectorFactory.createElementSelector(null, "&"); } +} + +/** + * @exception ParseException exception during the parse + */ +Condition attrib(Condition pred) : +{ + int cases = 0; + Token att = null; + Token val = null; + String attValue = null; +} +{ + "[" ( <S> )* att=<IDENT> ( <S> )* + ( ( "=" { cases = 1; } + | <INCLUDES> { cases = 2; } + | <DASHMATCH> { cases = 3; } ) ( <S> )* + ( val=<IDENT> { attValue = val.image; } + | val=<STRING> { attValue = convertStringIndex(val.image, 1, + val.image.length() -1);} + ) + ( <S> )* )? + "]" + { + String name = convertIdent(att.image); + Condition c; + switch (cases) { + case 0: + c = conditionFactory.createAttributeCondition(name, null, false, null); + break; + case 1: + c = conditionFactory.createAttributeCondition(name, null, false, + attValue); + break; + case 2: + c = conditionFactory.createOneOfAttributeCondition(name, null, false, + attValue); + break; + case 3: + c = conditionFactory.createBeginHyphenAttributeCondition(name, null, + false, + attValue); + break; + default: + // never reached. + c = null; + } + if (pred == null) { + return c; + } else { + return conditionFactory.createAndCondition(pred, c); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +Condition pseudo(Condition pred) : +{Token n; +Token language; +boolean isPseudoElement = false; +} +{ + ":" (":"{isPseudoElement=true;})?( n=<IDENT> + { + String s = convertIdent(n.image); + if (isPseudoElement) { + if (pseudoElt != null) { + throw new CSSParseException("duplicate pseudo element definition " + + s, getLocator()); + } else { + pseudoElt = s; + return pred; + } + } else { + Condition c = + conditionFactory.createPseudoClassCondition(null, s); + if (pred == null) { + return c; + } else { + return conditionFactory.createAndCondition(pred, c); + } + } + } + | ( n=<FUNCTION> ( <S> )* language=<IDENT> ( <S> )* ")" + { + String f = convertIdent(n.image); + if (f.equals("lang(")) { + Condition d = + conditionFactory.createLangCondition(convertIdent(language.image)); + if (pred == null) { + return d; + } else { + return conditionFactory.createAndCondition(pred, d); + } + } else { + throw new CSSParseException("invalid pseudo function name " + + f, getLocator()); + } + } + ) + ) +} + +/** + * @exception ParseException exception during the parse + */ +Condition hash(Condition pred) : +{Token n; } +{ + n=<HASH> + { + Condition d = + conditionFactory.createIdCondition(n.image.substring(1)); + if (pred == null) { + return d; + } else { + return conditionFactory.createAndCondition(pred, d); + } + } +} + +void variable() : +{ + String name; + LexicalUnit exp = null; + boolean guarded = false; + String raw; +} +{ + try{ + name = variableName() + ":" ( <S> )* exp=expr() ( guarded=guarded() )?(";"(<S>)*)+ + //raw=skipStatementUntilSemiColon() + { + documentHandler.variable(name, exp, guarded); + } + }catch (JumpException e) { + skipAfterExpression(); + } catch (NumberFormatException e) { + if (errorHandler != null) { + errorHandler.error(new CSSParseException("Invalid number " + + e.getMessage(), + getLocator(), + e)); + } + reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + } else { + skipAfterExpression(); + } + } +} + +void eachDirective() : +{ + Token var; + ArrayList<String> list; +} +{ + < EACH_SYM > + (< S >)* + var = < VARIABLE > (< S >)* < EACH_IN > (< S >)* + list = stringList() + < LBRACE >(< S >)* + { documentHandler.startEachDirective(var.image, list);} + ( includeDirective() | media() | extendDirective()| variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())* + < RBRACE >(< S >)* + { documentHandler.endEachDirective();} +} + +ArrayList<String > stringList(): +{ + ArrayList<String > strings = new ArrayList<String >(); + Token input; +} +{ + (input = < IDENT > (< S >)*) + { strings.add(input.image); } + + (< COMMA >(< S >)* input = < IDENT > { strings.add(input.image); } (< S >)*)* + { return strings; } + +} + +void mixinDirective() : +{ + String name; + ArrayList<VariableNode> args = null; + String body; +} +{ + <MIXIN_SYM> + (<S>)* + (name = property() + |(name = functionName() + args = arglist()) <RPARAN> (<S>)*) <LBRACE> (<S>)* + {documentHandler.startMixinDirective(name, args);} + ( includeDirective() | media()| extendDirective()| variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())* + //(includeDirective() | media() | LOOKAHEAD(declaration()) declaration()";"(<S>)* | styleRule())* + <RBRACE>(<S>)* + {documentHandler.endMixinDirective(name, args);} +} + +ArrayList<VariableNode> arglist() : +{ + ArrayList<VariableNode> args = new ArrayList<VariableNode>(); + VariableNode arg; +} +{ + arg=mixinArg() ( <COMMA> (<S>)* { args.add(arg); } + arg=mixinArg() )* + { args.add(arg); + return args; + } +} + +VariableNode mixinArg() : +{ + String name; + LexicalUnit value = null; +} +{ + name=variableName() (":" (<S>)* value=term(null) )? + { + VariableNode arg = new VariableNode(name, value, false); + return arg; + } +} + +ArrayList<LexicalUnit> argValuelist() : +{ + ArrayList<LexicalUnit> args = new ArrayList<LexicalUnit>(); + LexicalUnit argValue; +} +{ + argValue= term(null) { args.add(argValue); } + ( <COMMA> (<S>)* argValue = term(null) + {args.add(argValue);} + )* + {return args;} +} + +void includeDirective() : +{ + String name; + ArrayList<LexicalUnit> args=null; +} +{ + <INCLUDE_SYM> + (<S>)* + (name = property() + |(name = functionName() + args = argValuelist()) <RPARAN>)(";"(<S>)*)+ + {documentHandler.includeDirective(name, args);} +} + +Node functionDirective() : +{ + String name; + String args = null; + String body; + int[] stops = {RPARAN}; +} +{ + (name=functionName() args = skipStatementUntilRightParan() <RPARAN>) + (<S>)* + body = skipStatement() + { + return null; + } +} + +Node returnDirective() : +{ + String raw; +} +{ + raw = skipStatement() + {return null;} +} + +JAVACODE +void debugDirective(){ +} + +JAVACODE +void warnDirective(){ +} + +Node forDirective() : +{ + String var; + String from; + String to; + boolean exclusive; + String body; + Token tok; +} +{ + var = variableName() + { + int[] toThrough = {TO, THROUGH}; + from = skipStatementUntil(toThrough); + } + (tok = <TO> {exclusive = true;} + | tok = <THROUGH> {exclusive = false;}) + to = skipStatementUntilLeftBrace() + (<S>)* + body = skipStatement() + {return documentHandler.forDirective(var, from, to, exclusive, body);} +} + +Node whileDirective() : +{ + String condition; + String body; +} +{ + condition = skipStatementUntilLeftBrace() + body = skipStatement() + { return documentHandler.whileDirective(condition, body);} +} + +JAVACODE +Node ifDirective(){ + return documentHandler.ifDirective(); +} + +JAVACODE +void elseDirective(){ + // throw new ParseException("Invalid CSS: @else must come after @if"); +} + +void extendDirective() : +{SelectorList list;} +{ + <EXTEND_SYM> + (<S>)* + list = selectorList() + (";"(<S>)*)+ + {documentHandler.extendDirective(list);} +} + +JAVACODE +Node importDirective(){ + return null; +} + +JAVACODE +Node charsetDirective(){ + return null; +} + +JAVACODE +Node mozDocumentDirective(){ + return null; +} + +JAVACODE +Node supportsDirective(){ + return null; +} + + +void nestedProperties(): +{String name; +LexicalUnit exp;} +{ + name=property() + ":" ( <S> )* + <LBRACE> (<S>)* + { + documentHandler.startNestedProperties(name); + } + ( declaration() )? ( ";" ( <S> )* ( declaration() )? )* + <RBRACE> + { + documentHandler.endNestedProperties(name); + } + (<S>)* +} +/** + * @exception ParseException exception during the parse + */ +void declarationOrNestedProperties() : +{ boolean important = false; + String name; + LexicalUnit exp; + Token save; + String comment = null; +} +{ + try { + name=property() + { save = token; } + ":" ( <S> )* + (exp=expr() ( important=prio() )? + { + Token next = getToken(1); + if(next.kind == SEMICOLON || next.kind == RBRACE){ + while(next.kind == SEMICOLON){ + skipStatement(); + next = getToken(1); + } + if(token.specialToken!=null){ + documentHandler.property(name, exp, important, token.specialToken.image); + }else{ + documentHandler.property(name, exp, important, null); + } + } + } + |<LBRACE> (<S>)* + { + documentHandler.startNestedProperties(name); + } + ( declaration() )? ( ";" ( <S> )* ( declaration() )? )* + <RBRACE>(<S>)* + { + documentHandler.endNestedProperties(name); + } + ) + + } catch (JumpException e) { + skipAfterExpression(); + // reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (NumberFormatException e) { + if (errorHandler != null) { + errorHandler.error(new CSSParseException("Invalid number " + + e.getMessage(), + getLocator(), + e)); + } + reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + /* + LocatorImpl loc = (LocatorImpl) getLocator(); + loc.column--; + reportWarningSkipText(loc, skipAfterExpression()); + */ + } else { + skipAfterExpression(); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +void declaration() : +{ boolean important = false; + String name; + LexicalUnit exp; + Token save; +} +{ + try { + name=property() + { save = token; } + ":" ( <S> )* exp=expr() ( important=prio() )? + { + documentHandler.property(name, exp, important); + } + } catch (JumpException e) { + skipAfterExpression(); + // reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (NumberFormatException e) { + if (errorHandler != null) { + errorHandler.error(new CSSParseException("Invalid number " + + e.getMessage(), + getLocator(), + e)); + } + reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + /* + LocatorImpl loc = (LocatorImpl) getLocator(); + loc.column--; + reportWarningSkipText(loc, skipAfterExpression()); + */ + } else { + skipAfterExpression(); + } + } +} + +/** + * @exception ParseException exception during the parse + */ +boolean prio() : +{} +{ + <IMPORTANT_SYM> ( <S> )* { return true; } +} + +boolean guarded() : +{} +{ + <GUARDED_SYM> (<S>)* {return true;} +} + +/** + * @exception ParseException exception during the parse + */ +LexicalUnitImpl operator(LexicalUnitImpl prev) : +{Token n;} +{ +n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine, + n.beginColumn, + prev); } +| n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine, + n.beginColumn, + prev); } +} + +/** + * @exception ParseException exception during the parse + */ +LexicalUnit expr() : +{ + LexicalUnitImpl first, res; + char op; +} +{ + first=term(null){ res = first; } + ( LOOKAHEAD(2) ( res=operator(res) )? res=term(res))* + { return first; } +} + +/** + * @exception ParseException exception during the parse + */ +char unaryOperator() : +{} +{ + "-" { return '-'; } +| "+" { return '+'; } +} + +/** + * @exception ParseException exception during the parse + */ +LexicalUnitImpl term(LexicalUnitImpl prev) : +{ LexicalUnitImpl result = null; + Token n = null; + char op = ' '; + String varName; +} +{ + (( ( ( op=unaryOperator() )? + ( + n=<NUMBER> + { result = LexicalUnitImpl.createNumber(n.beginLine, n.beginColumn, + prev, number(op, n, 0)); } + | n=<PERCENTAGE> + { result = LexicalUnitImpl.createPercentage(n.beginLine, n.beginColumn, + prev, number(op, n, 1)); } + | n=<PT> + { result = LexicalUnitImpl.createPT(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<CM> + { result = LexicalUnitImpl.createCM(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<MM> + { result = LexicalUnitImpl.createMM(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<PC> + { result = LexicalUnitImpl.createPC(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<IN> + { result = LexicalUnitImpl.createIN(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<PX> + { result = LexicalUnitImpl.createPX(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<EMS> + { result = LexicalUnitImpl.createEMS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<EXS> + { result = LexicalUnitImpl.createEXS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<DEG> + { result = LexicalUnitImpl.createDEG(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); } + | n=<RAD> + { result = LexicalUnitImpl.createRAD(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); } + | n=<GRAD> + { result = LexicalUnitImpl.createGRAD(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); } + | n=<SECOND> + { result = LexicalUnitImpl.createS(n.beginLine, n.beginColumn, + prev, number(op, n, 1)); } + | n=<MS> + { result = LexicalUnitImpl.createMS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<HZ> + { result = LexicalUnitImpl.createHZ(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); } + | n=<KHZ> + { result = LexicalUnitImpl.createKHZ(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); } + | n=<DIMEN> + { + String s = n.image; + int i = 0; + while (i < s.length() + && (Character.isDigit(s.charAt(i)) || (s.charAt(i) == '.'))) { + i++; + } + result = LexicalUnitImpl.createDimen(n.beginLine, n.beginColumn, prev, + Float.valueOf(s.substring(0, i)).floatValue(), + s.substring(i)); + } + | result=function(op, prev) ) ) + | ( n=<STRING> + { result = + LexicalUnitImpl.createString(n.beginLine, n.beginColumn, prev, + convertStringIndex(n.image, 1, + n.image.length() -1));} + | n=<IDENT> + { String s = convertIdent(n.image); + if ("inherit".equals(s)) { + result = LexicalUnitImpl.createInherit(n.beginLine, n.beginColumn, + prev); + } else { + result = LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + prev, convertIdent(n.image)); + } + + /* / + Auto correction code used in the CSS Validator but must not + be used by a conformant CSS2 parser. + * Common error : + * H1 { + * color : black + * background : white + * } + * + Token t = getToken(1); + Token semicolon = new Token(); + semicolon.kind = SEMICOLON; + semicolon.image = ";"; + if (t.kind == COLON) { + // @@SEEME. (generate a warning?) + // @@SEEME if expression is a single ident, + generate an error ? + rejectToken(semicolon); + + result = prev; + } + / */ + } + | result=hexcolor(prev) + | result=url(prev) + | result=unicode(prev) + ) ) ( <S> )* + | varName = variableName() + {result = LexicalUnitImpl.createVariable(token.beginLine, token.beginColumn, + prev, varName);}) + { + return result; + } +} + +/** + * Handle all CSS2 functions. + * @exception ParseException exception during the parse + */ +LexicalUnitImpl function(char operator, LexicalUnitImpl prev) : +{Token n; + LexicalUnit params = null; +} +{ + n=<FUNCTION> ( <S> )* + { + String fname = convertIdent(n.image); + if("alpha(".equals(fname)){ + String body = skipStatementUntilSemiColon(); + return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + null, "alpha("+body); + } + } + ( params=expr() )? ")" + { + if (operator != ' ') { + throw new CSSParseException("invalid operator before a function.", + getLocator()); + } + String f = convertIdent(n.image); + LexicalUnitImpl l = (LexicalUnitImpl) params; + boolean loop = true; + if ("rgb(".equals(f)) { + // this is a RGB declaration (e.g. rgb(255, 50%, 0) ) + int i = 0; + while (loop && l != null && i < 5) { + switch (i) { + case 0: + case 2: + case 4: + if ((l.getLexicalUnitType() != LexicalUnit.SAC_INTEGER) + && (l.getLexicalUnitType() != LexicalUnit.SAC_PERCENTAGE)) { + loop = false; + } + break; + case 1: + case 3: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + throw new ParseException("implementation error"); + } + if (loop) { + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; + } + } + if ((i == 5) && loop && (l == null)) { + return LexicalUnitImpl.createRGBColor(n.beginLine, + n.beginColumn, + prev, params); + } else { + if (errorHandler != null) { + String errorText; + Locator loc; + if (i < 5) { + if (params == null) { + loc = new LocatorImpl(this, n.beginLine, + n.beginColumn-1); + errorText = "not enough parameters."; + } else if (l == null) { + loc = new LocatorImpl(this, n.beginLine, + n.beginColumn-1); + errorText = "not enough parameters: " + + params.toString(); + } else { + loc = new LocatorImpl(this, l.getLineNumber(), + l.getColumnNumber()); + errorText = "invalid parameter: " + + l.toString(); + } + } else { + loc = new LocatorImpl(this, l.getLineNumber(), + l.getColumnNumber()); + errorText = "too many parameters: " + + l.toString(); + } + errorHandler.error(new CSSParseException(errorText, loc)); + } + + throw new JumpException(); + } + } else if ("counter".equals(f)) { + int i = 0; + while (loop && l != null && i < 3) { + switch (i) { + case 0: + case 2: + if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { + loop = false; + } + break; + case 1: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + throw new ParseException("implementation error"); + } + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; + } + if (((i == 1) || (i == 3)) && loop && (l == null)) { + return LexicalUnitImpl.createCounter(n.beginLine, n.beginColumn, + prev, params); + } + + } else if ("counters(".equals(f)) { + + int i = 0; + while (loop && l != null && i < 5) { + switch (i) { + case 0: + case 4: + if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { + loop = false; + } + break; + case 2: + if (l.getLexicalUnitType() != LexicalUnit.SAC_STRING_VALUE) { + loop = false; + } + break; + case 1: + case 3: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + throw new ParseException("implementation error"); + } + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; + } + if (((i == 3) || (i == 5)) && loop && (l == null)) { + return LexicalUnitImpl.createCounters(n.beginLine, n.beginColumn, + prev, params); + } + } else if ("attr(".equals(f)) { + if ((l != null) + && (l.getNextLexicalUnit() == null) + && (l.getLexicalUnitType() == LexicalUnit.SAC_IDENT)) { + return LexicalUnitImpl.createAttr(l.getLineNumber(), + l.getColumnNumber(), + prev, l.getStringValue()); + } + } else if ("rect(".equals(f)) { + int i = 0; + while (loop && l != null && i < 7) { + switch (i) { + case 0: + case 2: + case 4: + case 6: + switch (l.getLexicalUnitType()) { + case LexicalUnit.SAC_INTEGER: + if (l.getIntegerValue() != 0) { + loop = false; + } + break; + case LexicalUnit.SAC_IDENT: + if (!l.getStringValue().equals("auto")) { + loop = false; + } + break; + case LexicalUnit.SAC_EM: + case LexicalUnit.SAC_EX: + case LexicalUnit.SAC_PIXEL: + case LexicalUnit.SAC_CENTIMETER: + case LexicalUnit.SAC_MILLIMETER: + case LexicalUnit.SAC_INCH: + case LexicalUnit.SAC_POINT: + case LexicalUnit.SAC_PICA: + // nothing + break; + default: + loop = false; + } + break; + case 1: + case 3: + case 5: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + throw new ParseException("implementation error"); + } + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; + } + if ((i == 7) && loop && (l == null)) { + return LexicalUnitImpl.createRect(n.beginLine, n.beginColumn, + prev, params); + } + } + return LexicalUnitImpl.createFunction(n.beginLine, n.beginColumn, prev, + f.substring(0, + f.length() -1), + params); + } +} + +LexicalUnitImpl unicode(LexicalUnitImpl prev) : +{ Token n; +} +{ + n=<UNICODERANGE> + { + LexicalUnitImpl params = null; + String s = n.image.substring(2); + int index = s.indexOf('-'); + if (index == -1) { + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s, 16)); + } else { + String s1 = s.substring(0, index); + String s2 = s.substring(index); + + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s1, 16)); + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s2, 16)); + } + + return LexicalUnitImpl.createUnicodeRange(n.beginLine, n.beginColumn, + prev, params); + } +} + +LexicalUnitImpl url(LexicalUnitImpl prev) : +{ Token n; +} +{ + n=<URL> + { + String urlname = n.image.substring(4, n.image.length()-1).trim(); + if (urlname.charAt(0) == '"' + || urlname.charAt(0) == '\'') { + urlname = urlname.substring(1, urlname.length()-1); + } + return LexicalUnitImpl.createURL(n.beginLine, n.beginColumn, prev, urlname); + } +} + +/** + * @exception ParseException exception during the parse + */ +LexicalUnitImpl hexcolor(LexicalUnitImpl prev) : +{Token n; +} +{ + n=<HASH> + { + int r; + LexicalUnitImpl first, params = null; + String s = n.image.substring(1); + + if(s.length()!=3 && s.length()!=6) { + first = null; + throw new CSSParseException("invalid hexadecimal notation for RGB: " + s, + getLocator()); + } + return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + prev, n.image); + } +} + +JAVACODE +float number(char operator, Token n, int lengthUnit) { + String image = n.image; + float f = 0; + + if (lengthUnit != 0) { + image = image.substring(0, image.length() - lengthUnit); + } + f = Float.valueOf(image).floatValue(); + return (operator == '-')? -f: f; +} + +JAVACODE +String skipStatementUntilSemiColon(){ + int[] semicolon = {SEMICOLON}; + return skipStatementUntil(semicolon); +} + +JAVACODE +String skipStatementUntilLeftBrace(){ + int[] lBrace = {LBRACE}; + return skipStatementUntil(lBrace); +} + +JAVACODE +String skipStatementUntilRightParan(){ + int[] rParan = {RPARAN}; + return skipStatementUntil(rParan); +} + +JAVACODE +String skipStatementUntil(int[] symbols){ + StringBuffer s = new StringBuffer(); + boolean stop = false; + Token tok; + while(!stop){ + tok = getToken(1); + if(tok.kind == EOF) { + return null; + } + for(int sym : symbols){ + if(tok.kind == sym){ + stop = true; + break; + } + } + if(!stop){ + if (tok.image != null) { + s.append(tok.image); + } + getNextToken(); + } + } + return s.toString().trim(); +} + + +JAVACODE +String skipStatement() { + StringBuffer s = new StringBuffer(); + Token tok = getToken(0); + if (tok.image != null) { + s.append(tok.image); + } + while (true) { + tok = getToken(1); + if (tok.kind == EOF) { + return null; + } + s.append(tok.image); + if (tok.kind == LBRACE) { + getNextToken(); + s.append(skip_to_matching_brace()); + getNextToken(); + tok = getToken(1); + break; + } else if (tok.kind == RBRACE) { + getNextToken(); + tok = getToken(1); + break; + } else if (tok.kind == SEMICOLON) { + getNextToken(); + tok = getToken(1); + break; + } + getNextToken(); + } + + // skip white space + while (true) { + if (tok.kind != S) { + break; + } + tok = getNextToken(); + tok = getToken(1); + } + + return s.toString().trim(); +} + +JAVACODE +String skip_to_matching_brace() { + StringBuffer s = new StringBuffer(); + Token tok; + int nesting = 1; + while (true) { + tok = getToken(1); + if (tok.kind == EOF) { + break; + } + s.append(tok.image); + if (tok.kind == LBRACE) { + nesting++; + } else if (tok.kind == RBRACE) { + nesting--; + if (nesting == 0) { + break; + } + } + getNextToken(); + } + return s.toString(); +} + +/* + * Here I handle all CSS2 unicode character stuffs. + * I convert all \XXXXXX character into a single character. + * Don't forget that the parser has recognize the token before. + * (So IDENT won't contain newline and stuffs like this). + */ +JAVACODE +String convertStringIndex(String s, int start, int len) { + StringBuffer buf = new StringBuffer(len); + int index = start; + + while (index < len) { + char c = s.charAt(index); + if (c == '\\') { + if (++index < len) { + c = s.charAt(index); + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + int numValue = Character.digit(c, 16); + int count = 0; + int p = 16; + + while (index + 1 < len && count < 6) { + c = s.charAt(index+1); + + if (Character.digit(c, 16) != -1) { + numValue = (numValue * 16) + Character.digit(c, 16); + p *= 16; + index++; + } else { + if (c == ' ') { + // skip the latest white space + index++; + } + break; + } + } + buf.append((char) numValue); + break; + case '\n': + case '\f': + break; + case '\r': + if (index + 1 < len) { + if (s.charAt(index + 1) == '\n') { + index ++; + } + } + break; + default: + buf.append(c); + } + } else { + throw new CSSParseException("invalid string " + s, getLocator()); + } + } else { + buf.append(c); + } + index++; + } + + return buf.toString(); +} + +JAVACODE +String convertIdent(String s) { + return convertStringIndex(s, 0, s.length()); +} + +JAVACODE +String convertString(String s) { + return convertStringIndex(s, 0, s.length()); +} + +JAVACODE +void comments(){ + if (token.specialToken != null){ + Token tmp_t = token.specialToken; + while (tmp_t.specialToken != null) tmp_t = tmp_t.specialToken; + while (tmp_t != null) { + documentHandler.comment(tmp_t.image); + tmp_t = tmp_t.next; + } + } +} + +/* + * @@HACK + * I can't insert a token into the tokens flow. + * It's jj_consume_token implementation dependant! :-( + */ +JAVACODE +void rejectToken(Token t) { + Token fakeToken = new Token(); + t.next = token; + fakeToken.next = t; + token = fakeToken; +} + +/** + * skip after an expression + */ +JAVACODE +String skipAfterExpression() { + Token t = getToken(1); + StringBuffer s = new StringBuffer(); + s.append(getToken(0).image); + + while ((t.kind != RBRACE) && (t.kind != SEMICOLON) && (t.kind != EOF)) { + s.append(t.image); + getNextToken(); + t = getToken(1); + } + + return s.toString(); +} + +/** + * The following functions are useful for a DOM CSS implementation only and are + * not part of the general CSS2 parser. + */ + +void _parseRule() : +{String ret = null; +} +{ + ( <S> )* + ( importDeclaration() | styleRule() | media() | page() + | fontFace() | ret=skipStatement() + { + if ((ret == null) || (ret.length() == 0)) { + return; + } + if (ret.charAt(0) == '@') { + documentHandler.ignorableAtRule(ret); + } else { + throw new CSSParseException("unrecognize rule: " + ret, + getLocator()); + } + } + ) +} + +void _parseImportRule() : +{ +} +{ + ( <S> )* importDeclaration() +} + +void _parseMediaRule() : +{ +} +{ + ( <S> )* media() +} + +void _parseDeclarationBlock() : +{ +} +{ + ( <S> )* + ( declaration() )? ( ";" ( <S> )* ( declaration() )? )* + } + +SelectorList _parseSelectors() : +{ SelectorList p = null; +} +{ + try { + ( <S> )* p = selectorList() + { return p; } + } catch (ThrowedParseException e) { + throw (ParseException) e.e.fillInStackTrace(); + } +} + +/* + * Local Variables: + * compile-command: javacc Parser.jj & javac Parser.java + * End: + */ |