1 /***************************************************************************
3 * Token-based parser for CSS definitions
4 * Author - Colomban Wendling <colomban@geany.org>
5 **************************************************************************/
16 typedef enum eCssKinds
{
17 K_CLASS
, K_SELECTOR
, K_ID
20 static kindOption CssKinds
[] = {
21 { TRUE
, 'c', "class", "classes" },
22 { TRUE
, 's', "struct", "selectors" },
23 { TRUE
, 'v', "variable", "identities" }
39 static boolean
isSelectorChar (const int c
)
41 /* attribute selectors are handled separately */
42 return (isalnum (c
) ||
43 c
== '_' || // allowed char
44 c
== '-' || // allowed char
45 c
== '+' || // allow all sibling in a single tag
46 c
== '>' || // allow all child in a single tag
47 c
== '|' || // allow namespace separator
48 c
== '(' || // allow pseudo-class arguments
50 c
== '.' || // allow classes and selectors
51 c
== ':' || // allow pseudo classes
52 c
== '*' || // allow globs as P + *
53 c
== '#'); // allow ids
56 static void parseSelector (vString
*const string
, const int firstChar
)
61 vStringPut (string
, (char) c
);
63 } while (isSelectorChar (c
));
65 vStringTerminate (string
);
68 static void readToken (tokenInfo
*const token
)
72 vStringClear (token
->string
);
83 case EOF
: token
->type
= TOKEN_EOF
; break;
88 const int delimiter
= c
;
91 vStringPut (token
->string
, c
);
96 while (c
!= EOF
&& c
!= delimiter
);
98 vStringPut (token
->string
, c
);
99 token
->type
= TOKEN_STRING
;
103 case '/': /* maybe comment start */
109 vStringPut (token
->string
, c
);
120 while (d
!= EOF
&& ! (c
== '*' && d
== '/'));
127 if (! isSelectorChar (c
))
129 vStringPut (token
->string
, c
);
134 parseSelector (token
->string
, c
);
135 token
->type
= TOKEN_SELECTOR
;
141 /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
142 static cssKind
classifySelector (const vString
*const selector
)
146 for (i
= vStringLength (selector
); i
> 0; --i
)
148 char c
= vStringItem (selector
, i
- 1);
157 static void findCssTags (void)
159 boolean readNextToken
= TRUE
;
162 token
.string
= vStringNew ();
169 readNextToken
= TRUE
;
171 if (token
.type
== '@')
172 { /* At-rules, from the "@" to the next block or semicolon */
175 useContents
= (strcmp (vStringValue (token
.string
), "media") == 0 ||
176 strcmp (vStringValue (token
.string
), "supports") == 0);
177 while (token
.type
!= TOKEN_EOF
&&
178 token
.type
!= ';' && token
.type
!= '{')
182 /* HACK: we *eat* the opening '{' for medias and the like so that
183 * the content is parsed as if it was at the root */
184 readNextToken
= useContents
&& token
.type
== '{';
186 else if (token
.type
== TOKEN_SELECTOR
)
187 { /* collect selectors and make a tag */
188 cssKind kind
= K_SELECTOR
;
190 unsigned long lineNumber
;
191 vString
*selector
= vStringNew ();
194 if (vStringLength (selector
) > 0)
195 vStringPut (selector
, ' ');
196 vStringCat (selector
, token
.string
);
198 kind
= classifySelector (token
.string
);
199 lineNumber
= getSourceLineNumber ();
200 filePosition
= getInputFilePosition ();
204 /* handle attribute selectors */
205 if (token
.type
== '[')
208 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
210 vStringCat (selector
, token
.string
);
212 if (token
.type
== '[')
214 else if (token
.type
== ']')
217 if (token
.type
!= TOKEN_EOF
)
218 vStringCat (selector
, token
.string
);
222 while (token
.type
== TOKEN_SELECTOR
);
223 /* we already consumed the next token, don't read it twice */
224 readNextToken
= FALSE
;
226 vStringTerminate (selector
);
227 if (CssKinds
[kind
].enabled
)
230 initTagEntry (&e
, vStringValue (selector
));
232 e
.lineNumber
= lineNumber
;
233 e
.filePosition
= filePosition
;
234 e
.kindName
= CssKinds
[kind
].name
;
235 e
.kind
= (char) CssKinds
[kind
].letter
;
239 vStringDelete (selector
);
241 else if (token
.type
== '{')
242 { /* skip over { ... } */
244 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
247 if (token
.type
== '{')
249 else if (token
.type
== '}')
254 while (token
.type
!= TOKEN_EOF
);
256 vStringDelete (token
.string
);
259 /* parser definition */
260 extern parserDefinition
* CssParser (void)
262 static const char *const extensions
[] = { "css", NULL
};
263 parserDefinition
* def
= parserNew ("CSS");
264 def
->kinds
= CssKinds
;
265 def
->kindCount
= KIND_COUNT (CssKinds
);
266 def
->extensions
= extensions
;
267 def
->parser
= findCssTags
;