1 /***************************************************************************
3 * Token-based parser for CSS definitions
4 * Author - Colomban Wendling <colomban@geany.org>
6 **************************************************************************/
17 #define isSelectorChar(c) \
18 /* attribute selectors are handled separately */ \
20 (c) == '_' || /* allowed char */ \
21 (c) == '-' || /* allowed char */ \
22 (c) == '+' || /* allow all sibling in a single tag */ \
23 (c) == '>' || /* allow all child in a single tag */ \
24 (c) == '~' || /* allow general sibling combinator */ \
25 (c) == '|' || /* allow namespace separator */ \
26 (c) == '(' || /* allow pseudo-class arguments */ \
28 (c) == '.' || /* allow classes and selectors */ \
29 (c) == ':' || /* allow pseudo classes */ \
30 (c) == '*' || /* allow globs as P + * */ \
31 (c) == '#') /* allow ids */
33 typedef enum eCssKinds
{
34 K_CLASS
, K_SELECTOR
, K_ID
37 static kindDefinition CssKinds
[] = {
38 { true, 'c', "class", "classes" },
39 { true, 's', "selector", "selectors" },
40 { true, 'i', "id", "identities" }
56 static void parseSelector (vString
*const string
, const int firstChar
)
61 vStringPut (string
, (char) c
);
62 c
= getcFromInputFile ();
63 } while (isSelectorChar (c
));
64 ungetcToInputFile (c
);
67 static void readToken (tokenInfo
*const token
)
71 vStringClear (token
->string
);
75 c
= getcFromInputFile ();
77 c
= getcFromInputFile ();
82 case EOF
: token
->type
= TOKEN_EOF
; break;
87 const int delimiter
= c
;
90 vStringPut (token
->string
, c
);
91 c
= getcFromInputFile ();
93 c
= getcFromInputFile ();
95 while (c
!= EOF
&& c
!= delimiter
);
97 vStringPut (token
->string
, c
);
98 token
->type
= TOKEN_STRING
;
102 case '/': /* maybe comment start */
104 int d
= getcFromInputFile ();
107 ungetcToInputFile (d
);
108 vStringPut (token
->string
, c
);
113 d
= getcFromInputFile ();
117 d
= getcFromInputFile ();
119 while (d
!= EOF
&& ! (c
== '*' && d
== '/'));
126 if (! isSelectorChar (c
))
128 vStringPut (token
->string
, c
);
133 parseSelector (token
->string
, c
);
134 token
->type
= TOKEN_SELECTOR
;
140 /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
141 static cssKind
classifySelector (const vString
*const selector
)
145 for (i
= vStringLength (selector
); i
> 0; --i
)
147 char c
= vStringChar (selector
, i
- 1);
156 static void findCssTags (void)
158 bool readNextToken
= true;
161 token
.string
= vStringNew ();
168 readNextToken
= true;
170 if (token
.type
== '@')
171 { /* At-rules, from the "@" to the next block or semicolon */
174 useContents
= (strcmp (vStringValue (token
.string
), "media") == 0 ||
175 strcmp (vStringValue (token
.string
), "supports") == 0);
176 while (token
.type
!= TOKEN_EOF
&&
177 token
.type
!= ';' && token
.type
!= '{')
181 /* HACK: we *eat* the opening '{' for medias and the like so that
182 * the content is parsed as if it was at the root */
183 readNextToken
= useContents
&& token
.type
== '{';
185 else if (token
.type
== TOKEN_SELECTOR
)
186 { /* collect selectors and make a tag */
187 cssKind kind
= K_SELECTOR
;
189 unsigned long lineNumber
;
190 vString
*selector
= vStringNew ();
193 if (vStringLength (selector
) > 0)
194 vStringPut (selector
, ' ');
195 vStringCat (selector
, token
.string
);
197 kind
= classifySelector (token
.string
);
198 lineNumber
= getInputLineNumber ();
199 filePosition
= getInputFilePosition ();
203 /* handle attribute selectors */
204 if (token
.type
== '[')
207 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
209 vStringCat (selector
, token
.string
);
211 if (token
.type
== '[')
213 else if (token
.type
== ']')
216 if (token
.type
!= TOKEN_EOF
)
217 vStringCat (selector
, token
.string
);
221 while (token
.type
== TOKEN_SELECTOR
);
222 /* we already consumed the next token, don't read it twice */
223 readNextToken
= false;
225 if (CssKinds
[kind
].enabled
)
228 initTagEntry (&e
, vStringValue (selector
), kind
);
230 e
.lineNumber
= lineNumber
;
231 e
.filePosition
= filePosition
;
235 vStringDelete (selector
);
237 else if (token
.type
== '{')
238 { /* skip over { ... } */
240 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
243 if (token
.type
== '{')
245 else if (token
.type
== '}')
250 while (token
.type
!= TOKEN_EOF
);
252 vStringDelete (token
.string
);
255 /* parser definition */
256 extern parserDefinition
* CssParser (void)
258 static const char *const extensions
[] = { "css", NULL
};
259 parserDefinition
* def
= parserNew ("CSS");
260 def
->kindTable
= CssKinds
;
261 def
->kindCount
= ARRAY_SIZE (CssKinds
);
262 def
->extensions
= extensions
;
263 def
->parser
= findCssTags
;