xref: /Universal-ctags/parsers/css.c (revision 7171591b6943df46a75e52a3995afc39a193a8ee)
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