13ae02089SMasatake YAMATO /***************************************************************************
23ae02089SMasatake YAMATO * css.c
33ae02089SMasatake YAMATO * Token-based parser for CSS definitions
43ae02089SMasatake YAMATO * Author - Colomban Wendling <colomban@geany.org>
593c4b724Sviccuad * License GPL-2
63ae02089SMasatake YAMATO **************************************************************************/
73ae02089SMasatake YAMATO #include "general.h"
83ae02089SMasatake YAMATO
93ae02089SMasatake YAMATO #include <string.h>
103ae02089SMasatake YAMATO #include <ctype.h>
113ae02089SMasatake YAMATO
123ae02089SMasatake YAMATO #include "entry.h"
133ae02089SMasatake YAMATO #include "parse.h"
143ae02089SMasatake YAMATO #include "read.h"
153db72c21SMasatake YAMATO #include "routines.h"
163ae02089SMasatake YAMATO
172b28d0e0SJiří Techet #define isSelectorChar(c) \
182b28d0e0SJiří Techet /* attribute selectors are handled separately */ \
192b28d0e0SJiří Techet (isalnum (c) || \
202b28d0e0SJiří Techet (c) == '_' || /* allowed char */ \
212b28d0e0SJiří Techet (c) == '-' || /* allowed char */ \
222b28d0e0SJiří Techet (c) == '+' || /* allow all sibling in a single tag */ \
232b28d0e0SJiří Techet (c) == '>' || /* allow all child in a single tag */ \
240cfba698SMasatake YAMATO (c) == '~' || /* allow general sibling combinator */ \
252b28d0e0SJiří Techet (c) == '|' || /* allow namespace separator */ \
262b28d0e0SJiří Techet (c) == '(' || /* allow pseudo-class arguments */ \
272b28d0e0SJiří Techet (c) == ')' || \
282b28d0e0SJiří Techet (c) == '.' || /* allow classes and selectors */ \
292b28d0e0SJiří Techet (c) == ':' || /* allow pseudo classes */ \
302b28d0e0SJiří Techet (c) == '*' || /* allow globs as P + * */ \
312b28d0e0SJiří Techet (c) == '#') /* allow ids */
322b28d0e0SJiří Techet
333ae02089SMasatake YAMATO typedef enum eCssKinds {
343ae02089SMasatake YAMATO K_CLASS, K_SELECTOR, K_ID
353ae02089SMasatake YAMATO } cssKind;
363ae02089SMasatake YAMATO
37e112e8abSMasatake YAMATO static kindDefinition CssKinds [] = {
38ce990805SThomas Braun { true, 'c', "class", "classes" },
39ce990805SThomas Braun { true, 's', "selector", "selectors" },
40ce990805SThomas Braun { true, 'i', "id", "identities" }
413ae02089SMasatake YAMATO };
423ae02089SMasatake YAMATO
433ae02089SMasatake YAMATO typedef enum {
443ae02089SMasatake YAMATO /* any ASCII */
453ae02089SMasatake YAMATO TOKEN_EOF = 257,
463ae02089SMasatake YAMATO TOKEN_SELECTOR,
473ae02089SMasatake YAMATO TOKEN_STRING
483ae02089SMasatake YAMATO } tokenType;
493ae02089SMasatake YAMATO
503ae02089SMasatake YAMATO typedef struct {
513ae02089SMasatake YAMATO tokenType type;
523ae02089SMasatake YAMATO vString *string;
533ae02089SMasatake YAMATO } tokenInfo;
543ae02089SMasatake YAMATO
553ae02089SMasatake YAMATO
parseSelector(vString * const string,const int firstChar)563ae02089SMasatake YAMATO static void parseSelector (vString *const string, const int firstChar)
573ae02089SMasatake YAMATO {
583ae02089SMasatake YAMATO int c = firstChar;
593ae02089SMasatake YAMATO do
603ae02089SMasatake YAMATO {
613ae02089SMasatake YAMATO vStringPut (string, (char) c);
62018bce0bSMasatake YAMATO c = getcFromInputFile ();
633ae02089SMasatake YAMATO } while (isSelectorChar (c));
6461f14fa5SMasatake YAMATO ungetcToInputFile (c);
653ae02089SMasatake YAMATO }
663ae02089SMasatake YAMATO
readToken(tokenInfo * const token)673ae02089SMasatake YAMATO static void readToken (tokenInfo *const token)
683ae02089SMasatake YAMATO {
693ae02089SMasatake YAMATO int c;
703ae02089SMasatake YAMATO
713ae02089SMasatake YAMATO vStringClear (token->string);
723ae02089SMasatake YAMATO
733ae02089SMasatake YAMATO getNextChar:
743ae02089SMasatake YAMATO
75018bce0bSMasatake YAMATO c = getcFromInputFile ();
763ae02089SMasatake YAMATO while (isspace (c))
77018bce0bSMasatake YAMATO c = getcFromInputFile ();
783ae02089SMasatake YAMATO
793ae02089SMasatake YAMATO token->type = c;
803ae02089SMasatake YAMATO switch (c)
813ae02089SMasatake YAMATO {
823ae02089SMasatake YAMATO case EOF: token->type = TOKEN_EOF; break;
833ae02089SMasatake YAMATO
843ae02089SMasatake YAMATO case '\'':
853ae02089SMasatake YAMATO case '"':
863ae02089SMasatake YAMATO {
873ae02089SMasatake YAMATO const int delimiter = c;
883ae02089SMasatake YAMATO do
893ae02089SMasatake YAMATO {
903ae02089SMasatake YAMATO vStringPut (token->string, c);
91018bce0bSMasatake YAMATO c = getcFromInputFile ();
923ae02089SMasatake YAMATO if (c == '\\')
93018bce0bSMasatake YAMATO c = getcFromInputFile ();
943ae02089SMasatake YAMATO }
953ae02089SMasatake YAMATO while (c != EOF && c != delimiter);
963ae02089SMasatake YAMATO if (c != EOF)
973ae02089SMasatake YAMATO vStringPut (token->string, c);
983ae02089SMasatake YAMATO token->type = TOKEN_STRING;
993ae02089SMasatake YAMATO break;
1003ae02089SMasatake YAMATO }
1013ae02089SMasatake YAMATO
1023ae02089SMasatake YAMATO case '/': /* maybe comment start */
1033ae02089SMasatake YAMATO {
104018bce0bSMasatake YAMATO int d = getcFromInputFile ();
1053ae02089SMasatake YAMATO if (d != '*')
1063ae02089SMasatake YAMATO {
10761f14fa5SMasatake YAMATO ungetcToInputFile (d);
1083ae02089SMasatake YAMATO vStringPut (token->string, c);
1093ae02089SMasatake YAMATO token->type = c;
1103ae02089SMasatake YAMATO }
1113ae02089SMasatake YAMATO else
1123ae02089SMasatake YAMATO {
113018bce0bSMasatake YAMATO d = getcFromInputFile ();
1143ae02089SMasatake YAMATO do
1153ae02089SMasatake YAMATO {
1163ae02089SMasatake YAMATO c = d;
117018bce0bSMasatake YAMATO d = getcFromInputFile ();
1183ae02089SMasatake YAMATO }
1193ae02089SMasatake YAMATO while (d != EOF && ! (c == '*' && d == '/'));
1203ae02089SMasatake YAMATO goto getNextChar;
1213ae02089SMasatake YAMATO }
1223ae02089SMasatake YAMATO break;
1233ae02089SMasatake YAMATO }
1243ae02089SMasatake YAMATO
1253ae02089SMasatake YAMATO default:
1263ae02089SMasatake YAMATO if (! isSelectorChar (c))
1273ae02089SMasatake YAMATO {
1283ae02089SMasatake YAMATO vStringPut (token->string, c);
1293ae02089SMasatake YAMATO token->type = c;
1303ae02089SMasatake YAMATO }
1313ae02089SMasatake YAMATO else
1323ae02089SMasatake YAMATO {
1333ae02089SMasatake YAMATO parseSelector (token->string, c);
1343ae02089SMasatake YAMATO token->type = TOKEN_SELECTOR;
1353ae02089SMasatake YAMATO }
1363ae02089SMasatake YAMATO break;
1373ae02089SMasatake YAMATO }
1383ae02089SMasatake YAMATO }
1393ae02089SMasatake YAMATO
1403ae02089SMasatake YAMATO /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
classifySelector(const vString * const selector)1413ae02089SMasatake YAMATO static cssKind classifySelector (const vString *const selector)
1423ae02089SMasatake YAMATO {
1433ae02089SMasatake YAMATO size_t i;
1443ae02089SMasatake YAMATO
1453ae02089SMasatake YAMATO for (i = vStringLength (selector); i > 0; --i)
1463ae02089SMasatake YAMATO {
147*7171591bSMasatake YAMATO char c = vStringChar (selector, i - 1);
1483ae02089SMasatake YAMATO if (c == '.')
1493ae02089SMasatake YAMATO return K_CLASS;
1503ae02089SMasatake YAMATO else if (c == '#')
1513ae02089SMasatake YAMATO return K_ID;
1523ae02089SMasatake YAMATO }
1533ae02089SMasatake YAMATO return K_SELECTOR;
1543ae02089SMasatake YAMATO }
1553ae02089SMasatake YAMATO
findCssTags(void)1563ae02089SMasatake YAMATO static void findCssTags (void)
1573ae02089SMasatake YAMATO {
158ce990805SThomas Braun bool readNextToken = true;
1593ae02089SMasatake YAMATO tokenInfo token;
1603ae02089SMasatake YAMATO
1613ae02089SMasatake YAMATO token.string = vStringNew ();
1623ae02089SMasatake YAMATO
1633ae02089SMasatake YAMATO do
1643ae02089SMasatake YAMATO {
1653ae02089SMasatake YAMATO if (readNextToken)
1663ae02089SMasatake YAMATO readToken (&token);
1673ae02089SMasatake YAMATO
168ce990805SThomas Braun readNextToken = true;
1693ae02089SMasatake YAMATO
1703ae02089SMasatake YAMATO if (token.type == '@')
1713ae02089SMasatake YAMATO { /* At-rules, from the "@" to the next block or semicolon */
172ce990805SThomas Braun bool useContents;
1733ae02089SMasatake YAMATO readToken (&token);
1743ae02089SMasatake YAMATO useContents = (strcmp (vStringValue (token.string), "media") == 0 ||
1753ae02089SMasatake YAMATO strcmp (vStringValue (token.string), "supports") == 0);
1763ae02089SMasatake YAMATO while (token.type != TOKEN_EOF &&
1773ae02089SMasatake YAMATO token.type != ';' && token.type != '{')
1783ae02089SMasatake YAMATO {
1793ae02089SMasatake YAMATO readToken (&token);
1803ae02089SMasatake YAMATO }
1813ae02089SMasatake YAMATO /* HACK: we *eat* the opening '{' for medias and the like so that
1823ae02089SMasatake YAMATO * the content is parsed as if it was at the root */
1833ae02089SMasatake YAMATO readNextToken = useContents && token.type == '{';
1843ae02089SMasatake YAMATO }
1853ae02089SMasatake YAMATO else if (token.type == TOKEN_SELECTOR)
1863ae02089SMasatake YAMATO { /* collect selectors and make a tag */
1873ae02089SMasatake YAMATO cssKind kind = K_SELECTOR;
188509a47dbSJiří Techet MIOPos filePosition;
1893ae02089SMasatake YAMATO unsigned long lineNumber;
1903ae02089SMasatake YAMATO vString *selector = vStringNew ();
1913ae02089SMasatake YAMATO do
1923ae02089SMasatake YAMATO {
1933ae02089SMasatake YAMATO if (vStringLength (selector) > 0)
1943ae02089SMasatake YAMATO vStringPut (selector, ' ');
1953ae02089SMasatake YAMATO vStringCat (selector, token.string);
1963ae02089SMasatake YAMATO
1973ae02089SMasatake YAMATO kind = classifySelector (token.string);
198a31b37dcSMasatake YAMATO lineNumber = getInputLineNumber ();
1993ae02089SMasatake YAMATO filePosition = getInputFilePosition ();
2003ae02089SMasatake YAMATO
2013ae02089SMasatake YAMATO readToken (&token);
2023ae02089SMasatake YAMATO
2033ae02089SMasatake YAMATO /* handle attribute selectors */
2043ae02089SMasatake YAMATO if (token.type == '[')
2053ae02089SMasatake YAMATO {
2063ae02089SMasatake YAMATO int depth = 1;
2073ae02089SMasatake YAMATO while (depth > 0 && token.type != TOKEN_EOF)
2083ae02089SMasatake YAMATO {
2093ae02089SMasatake YAMATO vStringCat (selector, token.string);
2103ae02089SMasatake YAMATO readToken (&token);
2113ae02089SMasatake YAMATO if (token.type == '[')
2123ae02089SMasatake YAMATO depth++;
2133ae02089SMasatake YAMATO else if (token.type == ']')
2143ae02089SMasatake YAMATO depth--;
2153ae02089SMasatake YAMATO }
2163ae02089SMasatake YAMATO if (token.type != TOKEN_EOF)
2173ae02089SMasatake YAMATO vStringCat (selector, token.string);
2183ae02089SMasatake YAMATO readToken (&token);
2193ae02089SMasatake YAMATO }
2203ae02089SMasatake YAMATO }
2213ae02089SMasatake YAMATO while (token.type == TOKEN_SELECTOR);
2223ae02089SMasatake YAMATO /* we already consumed the next token, don't read it twice */
223ce990805SThomas Braun readNextToken = false;
2243ae02089SMasatake YAMATO
2253ae02089SMasatake YAMATO if (CssKinds[kind].enabled)
2263ae02089SMasatake YAMATO {
2273ae02089SMasatake YAMATO tagEntryInfo e;
22816a2541cSMasatake YAMATO initTagEntry (&e, vStringValue (selector), kind);
2293ae02089SMasatake YAMATO
2303ae02089SMasatake YAMATO e.lineNumber = lineNumber;
2313ae02089SMasatake YAMATO e.filePosition = filePosition;
2323ae02089SMasatake YAMATO
2333ae02089SMasatake YAMATO makeTagEntry (&e);
2343ae02089SMasatake YAMATO }
2353ae02089SMasatake YAMATO vStringDelete (selector);
2363ae02089SMasatake YAMATO }
2373ae02089SMasatake YAMATO else if (token.type == '{')
2383ae02089SMasatake YAMATO { /* skip over { ... } */
2393ae02089SMasatake YAMATO int depth = 1;
2403ae02089SMasatake YAMATO while (depth > 0 && token.type != TOKEN_EOF)
2413ae02089SMasatake YAMATO {
2423ae02089SMasatake YAMATO readToken (&token);
2433ae02089SMasatake YAMATO if (token.type == '{')
2443ae02089SMasatake YAMATO depth++;
2453ae02089SMasatake YAMATO else if (token.type == '}')
2463ae02089SMasatake YAMATO depth--;
2473ae02089SMasatake YAMATO }
2483ae02089SMasatake YAMATO }
2493ae02089SMasatake YAMATO }
2503ae02089SMasatake YAMATO while (token.type != TOKEN_EOF);
2513ae02089SMasatake YAMATO
2523ae02089SMasatake YAMATO vStringDelete (token.string);
2533ae02089SMasatake YAMATO }
2543ae02089SMasatake YAMATO
2553ae02089SMasatake YAMATO /* parser definition */
CssParser(void)2563ae02089SMasatake YAMATO extern parserDefinition* CssParser (void)
2573ae02089SMasatake YAMATO {
2583ae02089SMasatake YAMATO static const char *const extensions [] = { "css", NULL };
2593ae02089SMasatake YAMATO parserDefinition* def = parserNew ("CSS");
26009ae690fSMasatake YAMATO def->kindTable = CssKinds;
2613db72c21SMasatake YAMATO def->kindCount = ARRAY_SIZE (CssKinds);
2623ae02089SMasatake YAMATO def->extensions = extensions;
2633ae02089SMasatake YAMATO def->parser = findCssTags;
2643ae02089SMasatake YAMATO return def;
2653ae02089SMasatake YAMATO }
266