Merge pull request #404 from ntrel/enum-base
[geany-mirror.git] / tagmanager / ctags / css.c
blobadb6695ef1290151fc3cbe92b3e5e9ea14268a24
1 /***************************************************************************
2 * css.c
3 * Token-based parser for CSS definitions
4 * Author - Colomban Wendling <colomban@geany.org>
5 **************************************************************************/
6 #include "general.h"
8 #include <string.h>
9 #include <ctype.h>
11 #include "entry.h"
12 #include "parse.h"
13 #include "read.h"
16 typedef enum eCssKinds {
17 K_CLASS, K_SELECTOR, K_ID
18 } cssKind;
20 static kindOption CssKinds [] = {
21 { TRUE, 'c', "class", "classes" },
22 { TRUE, 's', "struct", "selectors" },
23 { TRUE, 'v', "variable", "identities" }
26 typedef enum {
27 /* any ASCII */
28 TOKEN_EOF = 257,
29 TOKEN_SELECTOR,
30 TOKEN_STRING
31 } tokenType;
33 typedef struct {
34 tokenType type;
35 vString *string;
36 } tokenInfo;
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
49 c == ')' ||
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)
58 int c = firstChar;
61 vStringPut (string, (char) c);
62 c = fileGetc ();
63 } while (isSelectorChar (c));
64 fileUngetc (c);
65 vStringTerminate (string);
68 static void readToken (tokenInfo *const token)
70 int c;
72 vStringClear (token->string);
74 getNextChar:
76 c = fileGetc ();
77 while (isspace (c))
78 c = fileGetc ();
80 token->type = c;
81 switch (c)
83 case EOF: token->type = TOKEN_EOF; break;
85 case '\'':
86 case '"':
88 const int delimiter = c;
91 vStringPut (token->string, c);
92 c = fileGetc ();
93 if (c == '\\')
94 c = fileGetc ();
96 while (c != EOF && c != delimiter);
97 if (c != EOF)
98 vStringPut (token->string, c);
99 token->type = TOKEN_STRING;
100 break;
103 case '/': /* maybe comment start */
105 int d = fileGetc ();
106 if (d != '*')
108 fileUngetc (d);
109 vStringPut (token->string, c);
110 token->type = c;
112 else
114 d = fileGetc ();
117 c = d;
118 d = fileGetc ();
120 while (d != EOF && ! (c == '*' && d == '/'));
121 goto getNextChar;
123 break;
126 default:
127 if (! isSelectorChar (c))
129 vStringPut (token->string, c);
130 token->type = c;
132 else
134 parseSelector (token->string, c);
135 token->type = TOKEN_SELECTOR;
137 break;
141 /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
142 static cssKind classifySelector (const vString *const selector)
144 size_t i;
146 for (i = vStringLength (selector); i > 0; --i)
148 char c = vStringItem (selector, i - 1);
149 if (c == '.')
150 return K_CLASS;
151 else if (c == '#')
152 return K_ID;
154 return K_SELECTOR;
157 static void findCssTags (void)
159 boolean readNextToken = TRUE;
160 tokenInfo token;
162 token.string = vStringNew ();
166 if (readNextToken)
167 readToken (&token);
169 readNextToken = TRUE;
171 if (token.type == '@')
172 { /* At-rules, from the "@" to the next block or semicolon */
173 boolean useContents;
174 readToken (&token);
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 != '{')
180 readToken (&token);
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;
189 MIOPos filePosition;
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 ();
202 readToken (&token);
204 /* handle attribute selectors */
205 if (token.type == '[')
207 int depth = 1;
208 while (depth > 0 && token.type != TOKEN_EOF)
210 vStringCat (selector, token.string);
211 readToken (&token);
212 if (token.type == '[')
213 depth++;
214 else if (token.type == ']')
215 depth--;
217 if (token.type != TOKEN_EOF)
218 vStringCat (selector, token.string);
219 readToken (&token);
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)
229 tagEntryInfo e;
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;
237 makeTagEntry (&e);
239 vStringDelete (selector);
241 else if (token.type == '{')
242 { /* skip over { ... } */
243 int depth = 1;
244 while (depth > 0 && token.type != TOKEN_EOF)
246 readToken (&token);
247 if (token.type == '{')
248 depth++;
249 else if (token.type == '}')
250 depth--;
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;
268 return def;