xref: /Universal-ctags/parsers/clojure.c (revision c18ddfad91670a09f3c9b4891ad7a49730cd9c1b)
13ae02089SMasatake YAMATO /**
23ae02089SMasatake YAMATO  *   Copyright (c) 2015, Miloslav Nenadál <nenadalm@gmail.com>
33ae02089SMasatake YAMATO  *
43ae02089SMasatake YAMATO  *   This source code is released for free distribution under the terms of the
50ce38835Sviccuad  *   GNU General Public License version 2 or (at your option) any later version.
63ae02089SMasatake YAMATO  *
73ae02089SMasatake YAMATO  *   This module contains code for generating tags for the Clojure language.
83ae02089SMasatake YAMATO  */
93ae02089SMasatake YAMATO 
103ae02089SMasatake YAMATO #include "general.h"
113ae02089SMasatake YAMATO 
12f1d950b8SMasatake YAMATO #include <string.h>
13f1d950b8SMasatake YAMATO 
143ae02089SMasatake YAMATO #include "parse.h"
153ae02089SMasatake YAMATO #include "read.h"
163db72c21SMasatake YAMATO #include "routines.h"
173ae02089SMasatake YAMATO #include "vstring.h"
183ae02089SMasatake YAMATO #include "entry.h"
193ae02089SMasatake YAMATO 
203ae02089SMasatake YAMATO typedef enum {
213ae02089SMasatake YAMATO 	K_FUNCTION,
223ae02089SMasatake YAMATO 	K_NAMESPACE
233ae02089SMasatake YAMATO } clojureKind;
243ae02089SMasatake YAMATO 
25e112e8abSMasatake YAMATO static kindDefinition ClojureKinds[] = {
26ce990805SThomas Braun 	{true, 'f', "function", "functions"},
27ce990805SThomas Braun 	{true, 'n', "namespace", "namespaces"}
283ae02089SMasatake YAMATO };
293ae02089SMasatake YAMATO 
isNamespace(const char * strp)303ae02089SMasatake YAMATO static int isNamespace (const char *strp)
313ae02089SMasatake YAMATO {
323ae02089SMasatake YAMATO 	return strncmp (++strp, "ns", 2) == 0 && isspace (strp[2]);
333ae02089SMasatake YAMATO }
343ae02089SMasatake YAMATO 
isCoreNamespace(const char * strp)359f5a3ddaSVlad Bokov static int isCoreNamespace (const char *strp)
369f5a3ddaSVlad Bokov {
379f5a3ddaSVlad Bokov 	return strncmp (++strp, "clojure.core/ns", 15) == 0 && isspace (strp[15]);
389f5a3ddaSVlad Bokov }
399f5a3ddaSVlad Bokov 
isFunction(const char * strp)403ae02089SMasatake YAMATO static int isFunction (const char *strp)
413ae02089SMasatake YAMATO {
429f5a3ddaSVlad Bokov 	return (strncmp (++strp, "defn", 4) == 0 && isspace (strp[4]));
43a761eaf7SVlad Bokov }
44a761eaf7SVlad Bokov 
isCoreFunction(const char * strp)45a761eaf7SVlad Bokov static int isCoreFunction (const char *strp)
46a761eaf7SVlad Bokov {
47a761eaf7SVlad Bokov 	return (strncmp (++strp, "clojure.core/defn", 17) == 0 && isspace (strp[17]));
483ae02089SMasatake YAMATO }
493ae02089SMasatake YAMATO 
isQuote(const char * strp)503ae02089SMasatake YAMATO static int isQuote (const char *strp)
513ae02089SMasatake YAMATO {
523ae02089SMasatake YAMATO 	return strncmp (++strp, "quote", 5) == 0 && isspace (strp[5]);
533ae02089SMasatake YAMATO }
543ae02089SMasatake YAMATO 
functionName(vString * const name,const char * dbp)553ae02089SMasatake YAMATO static void functionName (vString * const name, const char *dbp)
563ae02089SMasatake YAMATO {
573ae02089SMasatake YAMATO 	const char *p;
583ae02089SMasatake YAMATO 
593ae02089SMasatake YAMATO 	if (*dbp == '\'')
603ae02089SMasatake YAMATO 		dbp++;
613ae02089SMasatake YAMATO 	else if (*dbp == '(' && isQuote (dbp))
623ae02089SMasatake YAMATO 	{
633ae02089SMasatake YAMATO 		dbp += 7;
643ae02089SMasatake YAMATO 		while (isspace (*dbp))
653ae02089SMasatake YAMATO 			dbp++;
663ae02089SMasatake YAMATO 	}
673ae02089SMasatake YAMATO 
683ae02089SMasatake YAMATO 	for (p = dbp; *p != '\0' && *p != '(' && !isspace ((int) *p) && *p != ')';
693ae02089SMasatake YAMATO 		p++)
703ae02089SMasatake YAMATO 		vStringPut (name, *p);
713ae02089SMasatake YAMATO }
723ae02089SMasatake YAMATO 
skipMetadata(const char * dbp)7393423845SMasatake YAMATO const char* skipMetadata (const char *dbp)
7493423845SMasatake YAMATO {
7593423845SMasatake YAMATO 	while (1)
7693423845SMasatake YAMATO 	{
7793423845SMasatake YAMATO 		if (*dbp == '^')
7893423845SMasatake YAMATO 		{
7993423845SMasatake YAMATO 			dbp++;
8093423845SMasatake YAMATO 			if (*dbp == '{')
8193423845SMasatake YAMATO 			{
8293423845SMasatake YAMATO 				/* skipping an arraymap */
8393423845SMasatake YAMATO 				for (; *dbp != '\0' && *dbp != '}'; dbp++)
8493423845SMasatake YAMATO 					;
8593423845SMasatake YAMATO 			}
8693423845SMasatake YAMATO 			else
8793423845SMasatake YAMATO 			{
8893423845SMasatake YAMATO 				/* skip a keyword or a symbol */
8993423845SMasatake YAMATO 				for (; *dbp != '\0' && !isspace((unsigned char)*dbp); dbp++)
9093423845SMasatake YAMATO 					;
9193423845SMasatake YAMATO 			}
9293423845SMasatake YAMATO 
9393423845SMasatake YAMATO 			if (*dbp == '\0')
9493423845SMasatake YAMATO 				break;
9593423845SMasatake YAMATO 
9693423845SMasatake YAMATO 			dbp++;
9793423845SMasatake YAMATO 			while (isspace ((unsigned char)*dbp))
9893423845SMasatake YAMATO 				dbp++;
9993423845SMasatake YAMATO 		}
10093423845SMasatake YAMATO 		else
10193423845SMasatake YAMATO 			break;
10293423845SMasatake YAMATO 	}
10393423845SMasatake YAMATO 
10493423845SMasatake YAMATO 	return dbp;
10593423845SMasatake YAMATO }
10693423845SMasatake YAMATO 
makeNamespaceTag(vString * const name,const char * dbp)10718599907SMasatake YAMATO static int makeNamespaceTag (vString * const name, const char *dbp)
1083ae02089SMasatake YAMATO {
10993423845SMasatake YAMATO 	dbp = skipMetadata (dbp);
1103ae02089SMasatake YAMATO 	functionName (name, dbp);
1114a95e4a5SColomban Wendling 	if (vStringLength (name) > 0 && ClojureKinds[K_NAMESPACE].enabled)
1123ae02089SMasatake YAMATO 	{
1133ae02089SMasatake YAMATO 		tagEntryInfo e;
11416a2541cSMasatake YAMATO 		initTagEntry (&e, vStringValue (name), K_NAMESPACE);
115a31b37dcSMasatake YAMATO 		e.lineNumber = getInputLineNumber ();
1163ae02089SMasatake YAMATO 		e.filePosition = getInputFilePosition ();
1173ae02089SMasatake YAMATO 
11818599907SMasatake YAMATO 		return makeTagEntry (&e);
1193ae02089SMasatake YAMATO 	}
12018599907SMasatake YAMATO 	else
121f6027918SMasatake YAMATO 		return CORK_NIL;
1223ae02089SMasatake YAMATO }
1233ae02089SMasatake YAMATO 
makeFunctionTag(vString * const name,const char * dbp,int scope_index)12418599907SMasatake YAMATO static void makeFunctionTag (vString * const name, const char *dbp, int scope_index)
1253ae02089SMasatake YAMATO {
126*c18ddfadSAlex Chen 	dbp = skipMetadata (dbp);
1273ae02089SMasatake YAMATO 	functionName (name, dbp);
1284a95e4a5SColomban Wendling 	if (vStringLength (name) > 0 && ClojureKinds[K_FUNCTION].enabled)
1293ae02089SMasatake YAMATO 	{
1303ae02089SMasatake YAMATO 		tagEntryInfo e;
13116a2541cSMasatake YAMATO 		initTagEntry (&e, vStringValue (name), K_FUNCTION);
132a31b37dcSMasatake YAMATO 		e.lineNumber = getInputLineNumber ();
1333ae02089SMasatake YAMATO 		e.filePosition = getInputFilePosition ();
1343ae02089SMasatake YAMATO 
1353b5d9090SMasatake YAMATO 		e.extensionFields.scopeIndex =  scope_index;
1363ae02089SMasatake YAMATO 		makeTagEntry (&e);
1373ae02089SMasatake YAMATO 	}
1383ae02089SMasatake YAMATO }
1393ae02089SMasatake YAMATO 
skipToSymbol(const char ** p)1403ae02089SMasatake YAMATO static void skipToSymbol (const char **p)
1413ae02089SMasatake YAMATO {
1423ae02089SMasatake YAMATO 	while (**p != '\0' && !isspace ((int) **p))
1433ae02089SMasatake YAMATO 		*p = *p + 1;
1443ae02089SMasatake YAMATO 	while (isspace ((int) **p))
1453ae02089SMasatake YAMATO 		*p = *p + 1;
1463ae02089SMasatake YAMATO }
1473ae02089SMasatake YAMATO 
findClojureTags(void)1483ae02089SMasatake YAMATO static void findClojureTags (void)
1493ae02089SMasatake YAMATO {
1503ae02089SMasatake YAMATO 	vString *name = vStringNew ();
1513ae02089SMasatake YAMATO 	const char *p;
152f6027918SMasatake YAMATO 	int scope_index = CORK_NIL;
1533ae02089SMasatake YAMATO 
1541b312fe7SMasatake YAMATO 	while ((p = (char *)readLineFromInputFile ()) != NULL)
1553ae02089SMasatake YAMATO 	{
1563ae02089SMasatake YAMATO 		vStringClear (name);
1573ae02089SMasatake YAMATO 
1583ae02089SMasatake YAMATO 		while (isspace (*p))
1593ae02089SMasatake YAMATO 			p++;
1603ae02089SMasatake YAMATO 
1613ae02089SMasatake YAMATO 		if (*p == '(')
1623ae02089SMasatake YAMATO 		{
1639f5a3ddaSVlad Bokov 			if (isNamespace (p) || isCoreNamespace (p))
1643ae02089SMasatake YAMATO 			{
1653ae02089SMasatake YAMATO 				skipToSymbol (&p);
16618599907SMasatake YAMATO 				scope_index = makeNamespaceTag (name, p);
1673ae02089SMasatake YAMATO 			}
168a761eaf7SVlad Bokov 			else if (isFunction (p) || isCoreFunction (p))
1693ae02089SMasatake YAMATO 			{
1703ae02089SMasatake YAMATO 				skipToSymbol (&p);
17118599907SMasatake YAMATO 				makeFunctionTag (name, p, scope_index);
1723ae02089SMasatake YAMATO 			}
1733ae02089SMasatake YAMATO 		}
1743ae02089SMasatake YAMATO 	}
1753ae02089SMasatake YAMATO 	vStringDelete (name);
1763ae02089SMasatake YAMATO }
1773ae02089SMasatake YAMATO 
ClojureParser(void)1783ae02089SMasatake YAMATO extern parserDefinition *ClojureParser (void)
1793ae02089SMasatake YAMATO {
1803ae02089SMasatake YAMATO 	static const char *const extensions[] = {
18175145629SFöldi Tamás 		"clj", "cljs", "cljc", NULL
1823ae02089SMasatake YAMATO 	};
1833ae02089SMasatake YAMATO 	static const char *const aliases[] = {
1843ae02089SMasatake YAMATO 		NULL
1853ae02089SMasatake YAMATO 	};
1863ae02089SMasatake YAMATO 
1873ae02089SMasatake YAMATO 	parserDefinition *def = parserNew ("Clojure");
18809ae690fSMasatake YAMATO 	def->kindTable = ClojureKinds;
1893db72c21SMasatake YAMATO 	def->kindCount = ARRAY_SIZE (ClojureKinds);
1903ae02089SMasatake YAMATO 	def->extensions = extensions;
1913ae02089SMasatake YAMATO 	def->aliases = aliases;
1923ae02089SMasatake YAMATO 	def->parser = findClojureTags;
1936b1a862eSMasatake YAMATO 	def->useCork = CORK_QUEUE;
1943ae02089SMasatake YAMATO 	return def;
1953ae02089SMasatake YAMATO }
196