1 /***************************************************************************
3 * Token-based parser for CSS definitions
4 * Author - Colomban Wendling <colomban@geany.org>
6 **************************************************************************/
17 typedef enum eCssKinds
{
18 K_CLASS
, K_SELECTOR
, K_ID
21 static kindOption CssKinds
[] = {
22 { TRUE
, 'c', "class", "classes" },
23 { TRUE
, 's', "selector", "selectors" },
24 { TRUE
, 'i', "id", "identities" }
40 static boolean
isSelectorChar (const int c
)
42 /* attribute selectors are handled separately */
43 return (isalnum (c
) ||
44 c
== '_' || // allowed char
45 c
== '-' || // allowed char
46 c
== '+' || // allow all sibling in a single tag
47 c
== '>' || // allow all child in a single tag
48 c
== '|' || // allow namespace separator
49 c
== '(' || // allow pseudo-class arguments
51 c
== '.' || // allow classes and selectors
52 c
== ':' || // allow pseudo classes
53 c
== '*' || // allow globs as P + *
54 c
== '#'); // allow ids
57 static void parseSelector (vString
*const string
, const int firstChar
)
62 vStringPut (string
, (char) c
);
63 c
= getcFromInputFile ();
64 } while (isSelectorChar (c
));
65 ungetcToInputFile (c
);
66 vStringTerminate (string
);
69 static void readToken (tokenInfo
*const token
)
73 vStringClear (token
->string
);
77 c
= getcFromInputFile ();
79 c
= getcFromInputFile ();
84 case EOF
: token
->type
= TOKEN_EOF
; break;
89 const int delimiter
= c
;
92 vStringPut (token
->string
, c
);
93 c
= getcFromInputFile ();
95 c
= getcFromInputFile ();
97 while (c
!= EOF
&& c
!= delimiter
);
99 vStringPut (token
->string
, c
);
100 token
->type
= TOKEN_STRING
;
104 case '/': /* maybe comment start */
106 int d
= getcFromInputFile ();
109 ungetcToInputFile (d
);
110 vStringPut (token
->string
, c
);
115 d
= getcFromInputFile ();
119 d
= getcFromInputFile ();
121 while (d
!= EOF
&& ! (c
== '*' && d
== '/'));
128 if (! isSelectorChar (c
))
130 vStringPut (token
->string
, c
);
135 parseSelector (token
->string
, c
);
136 token
->type
= TOKEN_SELECTOR
;
142 /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
143 static cssKind
classifySelector (const vString
*const selector
)
147 for (i
= vStringLength (selector
); i
> 0; --i
)
149 char c
= vStringItem (selector
, i
- 1);
158 static void findCssTags (void)
160 boolean readNextToken
= TRUE
;
163 token
.string
= vStringNew ();
170 readNextToken
= TRUE
;
172 if (token
.type
== '@')
173 { /* At-rules, from the "@" to the next block or semicolon */
176 useContents
= (strcmp (vStringValue (token
.string
), "media") == 0 ||
177 strcmp (vStringValue (token
.string
), "supports") == 0);
178 while (token
.type
!= TOKEN_EOF
&&
179 token
.type
!= ';' && token
.type
!= '{')
183 /* HACK: we *eat* the opening '{' for medias and the like so that
184 * the content is parsed as if it was at the root */
185 readNextToken
= useContents
&& token
.type
== '{';
187 else if (token
.type
== TOKEN_SELECTOR
)
188 { /* collect selectors and make a tag */
189 cssKind kind
= K_SELECTOR
;
191 unsigned long lineNumber
;
192 vString
*selector
= vStringNew ();
195 if (vStringLength (selector
) > 0)
196 vStringPut (selector
, ' ');
197 vStringCat (selector
, token
.string
);
199 kind
= classifySelector (token
.string
);
200 lineNumber
= getSourceLineNumber ();
201 filePosition
= getInputFilePosition ();
205 /* handle attribute selectors */
206 if (token
.type
== '[')
209 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
211 vStringCat (selector
, token
.string
);
213 if (token
.type
== '[')
215 else if (token
.type
== ']')
218 if (token
.type
!= TOKEN_EOF
)
219 vStringCat (selector
, token
.string
);
223 while (token
.type
== TOKEN_SELECTOR
);
224 /* we already consumed the next token, don't read it twice */
225 readNextToken
= FALSE
;
227 vStringTerminate (selector
);
228 if (CssKinds
[kind
].enabled
)
231 initTagEntry (&e
, vStringValue (selector
));
233 e
.lineNumber
= lineNumber
;
234 e
.filePosition
= filePosition
;
235 e
.kindName
= CssKinds
[kind
].name
;
236 e
.kind
= (char) CssKinds
[kind
].letter
;
240 vStringDelete (selector
);
242 else if (token
.type
== '{')
243 { /* skip over { ... } */
245 while (depth
> 0 && token
.type
!= TOKEN_EOF
)
248 if (token
.type
== '{')
250 else if (token
.type
== '}')
255 while (token
.type
!= TOKEN_EOF
);
257 vStringDelete (token
.string
);
260 /* parser definition */
261 extern parserDefinition
* CssParser (void)
263 static const char *const extensions
[] = { "css", NULL
};
264 parserDefinition
* def
= parserNew ("CSS");
265 def
->kinds
= CssKinds
;
266 def
->kindCount
= ARRAY_SIZE (CssKinds
);
267 def
->extensions
= extensions
;
268 def
->parser
= findCssTags
;