xref: /Universal-ctags/parsers/pascal.c (revision 3024e8b99f0f8de6e23f0a27bc35eb5aada6c0c3)
13ae02089SMasatake YAMATO /*
23ae02089SMasatake YAMATO *   Copyright (c) 2001-2002, Darren Hiebert
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 functions for generating tags for the Pascal language,
83ae02089SMasatake YAMATO *   including some extensions for Object Pascal.
93ae02089SMasatake YAMATO */
103ae02089SMasatake YAMATO 
113ae02089SMasatake YAMATO /*
123ae02089SMasatake YAMATO *   INCLUDE FILES
133ae02089SMasatake YAMATO */
143ae02089SMasatake YAMATO #include "general.h"  /* must always come first */
153ae02089SMasatake YAMATO 
163ae02089SMasatake YAMATO #include <string.h>
173ae02089SMasatake YAMATO 
183ae02089SMasatake YAMATO #include "entry.h"
193ae02089SMasatake YAMATO #include "parse.h"
203ae02089SMasatake YAMATO #include "read.h"
213db72c21SMasatake YAMATO #include "routines.h"
223ae02089SMasatake YAMATO #include "vstring.h"
233ae02089SMasatake YAMATO 
243ae02089SMasatake YAMATO /*
253ae02089SMasatake YAMATO *   DATA DEFINITIONS
263ae02089SMasatake YAMATO */
273ae02089SMasatake YAMATO typedef enum {
283ae02089SMasatake YAMATO 	K_FUNCTION, K_PROCEDURE
293ae02089SMasatake YAMATO } pascalKind;
303ae02089SMasatake YAMATO 
31e112e8abSMasatake YAMATO static kindDefinition PascalKinds [] = {
32ce990805SThomas Braun 	{ true, 'f', "function",  "functions"},
33ce990805SThomas Braun 	{ true, 'p', "procedure", "procedures"}
343ae02089SMasatake YAMATO };
353ae02089SMasatake YAMATO 
363ae02089SMasatake YAMATO /*
373ae02089SMasatake YAMATO *   FUNCTION DEFINITIONS
383ae02089SMasatake YAMATO */
393ae02089SMasatake YAMATO 
createPascalTag(tagEntryInfo * const tag,const vString * const name,const int kind,const vString * arglist,const vString * vartype)403ae02089SMasatake YAMATO static void createPascalTag (
41bd605841SEnrico Tröger 		tagEntryInfo* const tag, const vString* const name, const int kind,
42bd605841SEnrico Tröger 		const vString *arglist, const vString *vartype)
433ae02089SMasatake YAMATO {
443ae02089SMasatake YAMATO 	if (PascalKinds [kind].enabled  &&  name != NULL  &&  vStringLength (name) > 0)
45bd605841SEnrico Tröger 	{
4616a2541cSMasatake YAMATO 		initTagEntry (tag, vStringValue (name), kind);
4786795b57SMasatake YAMATO 		if (arglist && !vStringIsEmpty (arglist))
48bd605841SEnrico Tröger 		{
49bd605841SEnrico Tröger 			tag->extensionFields.signature = vStringValue (arglist);
50bd605841SEnrico Tröger 		}
51bd605841SEnrico Tröger 		if (vartype && !vStringIsEmpty (vartype))
52bd605841SEnrico Tröger 		{
53bd605841SEnrico Tröger 			tag->extensionFields.typeRef[0] = "typename";
54bd605841SEnrico Tröger 			tag->extensionFields.typeRef[1] = vStringValue (vartype);
55bd605841SEnrico Tröger 		}
56bd605841SEnrico Tröger 	}
573ae02089SMasatake YAMATO 	else
5898392b01SMasatake YAMATO 		/* TODO: Passing NULL as name makes an assertion behind initTagEntry failure */
5916a2541cSMasatake YAMATO 		initTagEntry (tag, NULL, KIND_GHOST_INDEX);
603ae02089SMasatake YAMATO }
613ae02089SMasatake YAMATO 
makePascalTag(const tagEntryInfo * const tag)623ae02089SMasatake YAMATO static void makePascalTag (const tagEntryInfo* const tag)
633ae02089SMasatake YAMATO {
643ae02089SMasatake YAMATO 	if (tag->name != NULL)
653ae02089SMasatake YAMATO 		makeTagEntry (tag);
663ae02089SMasatake YAMATO }
673ae02089SMasatake YAMATO 
683ae02089SMasatake YAMATO static const unsigned char* dbp;
693ae02089SMasatake YAMATO 
703ae02089SMasatake YAMATO #define starttoken(c) (isalpha ((int) c) || (int) c == '_')
713ae02089SMasatake YAMATO #define intoken(c)    (isalnum ((int) c) || (int) c == '_' || (int) c == '.')
723ae02089SMasatake YAMATO #define endtoken(c)   (! intoken (c)  &&  ! isdigit ((int) c))
733ae02089SMasatake YAMATO 
tail(const char * cp)74ce990805SThomas Braun static bool tail (const char *cp)
753ae02089SMasatake YAMATO {
76ce990805SThomas Braun 	bool result = false;
773ae02089SMasatake YAMATO 	register int len = 0;
783ae02089SMasatake YAMATO 
793ae02089SMasatake YAMATO 	while (*cp != '\0' && tolower ((int) *cp) == tolower ((int) dbp [len]))
803ae02089SMasatake YAMATO 		cp++, len++;
813ae02089SMasatake YAMATO 	if (*cp == '\0' && !intoken (dbp [len]))
823ae02089SMasatake YAMATO 	{
833ae02089SMasatake YAMATO 		dbp += len;
84ce990805SThomas Braun 		result = true;
853ae02089SMasatake YAMATO 	}
863ae02089SMasatake YAMATO 	return result;
873ae02089SMasatake YAMATO }
883ae02089SMasatake YAMATO 
parseArglist(const char * buf,vString * arglist,vString * vartype)89bd605841SEnrico Tröger static void parseArglist (const char *buf, vString *arglist, vString *vartype)
90bd605841SEnrico Tröger {
91bd605841SEnrico Tröger 	const char *start, *end;
92bd605841SEnrico Tröger 	int level;
93bd605841SEnrico Tröger 
94bd605841SEnrico Tröger 	if (NULL == buf || arglist == NULL)
95bd605841SEnrico Tröger 		return;
96bd605841SEnrico Tröger 
97bd605841SEnrico Tröger 	/* parse argument list which can be missing like in "function ginit:integer;" */
98bd605841SEnrico Tröger 	if (NULL != (start = strchr (buf, '(')))
99bd605841SEnrico Tröger 	{
100bd605841SEnrico Tröger 		for (level = 1, end = start + 1; level > 0; ++end)
101bd605841SEnrico Tröger 		{
102bd605841SEnrico Tröger 			if ('\0' == *end)
103bd605841SEnrico Tröger 				break;
104bd605841SEnrico Tröger 			else if ('(' == *end)
105bd605841SEnrico Tröger 				++ level;
106bd605841SEnrico Tröger 			else if (')' == *end)
107bd605841SEnrico Tröger 				-- level;
108bd605841SEnrico Tröger 		}
109bd605841SEnrico Tröger 	}
110bd605841SEnrico Tröger 	else /* if no argument list was found, continue looking for a return value */
111bd605841SEnrico Tröger 	{
112bd605841SEnrico Tröger 		start = NULL;
113bd605841SEnrico Tröger 		end = buf;
114bd605841SEnrico Tröger 	}
115bd605841SEnrico Tröger 
116bd605841SEnrico Tröger 	/* parse return type if requested by passing a non-NULL vartype argument */
117bd605841SEnrico Tröger 	if (NULL != vartype)
118bd605841SEnrico Tröger 	{
119bd605841SEnrico Tröger 		char *var, *var_start;
120bd605841SEnrico Tröger 
121bd605841SEnrico Tröger 		if (NULL != (var = strchr (end, ':')))
122bd605841SEnrico Tröger 		{
123bd605841SEnrico Tröger 			var++; /* skip ':' */
124bd605841SEnrico Tröger 			while (isspace ((int) *var))
125bd605841SEnrico Tröger 				++var;
126bd605841SEnrico Tröger 
127bd605841SEnrico Tröger 			if (starttoken (*var))
128bd605841SEnrico Tröger 			{
129bd605841SEnrico Tröger 				var_start = var;
130bd605841SEnrico Tröger 				var++;
131bd605841SEnrico Tröger 				while (intoken (*var))
132bd605841SEnrico Tröger 					var++;
133bd605841SEnrico Tröger 				if (endtoken (*var))
134bd605841SEnrico Tröger 				{
135bd605841SEnrico Tröger 					vStringNCatS (vartype, var_start, var - var_start);
136bd605841SEnrico Tröger 				}
137bd605841SEnrico Tröger 			}
138bd605841SEnrico Tröger 		}
139bd605841SEnrico Tröger 	}
140bd605841SEnrico Tröger 
141bd605841SEnrico Tröger 	if (NULL == start) /* no argument list */
142bd605841SEnrico Tröger 		vStringCatS (arglist, "()");
143bd605841SEnrico Tröger 	else
144bd605841SEnrico Tröger 		vStringNCatS (arglist, start, end - start);
145bd605841SEnrico Tröger }
146bd605841SEnrico Tröger 
1473ae02089SMasatake YAMATO /* Algorithm adapted from from GNU etags.
1483ae02089SMasatake YAMATO  * Locates tags for procedures & functions.  Doesn't do any type- or
1493ae02089SMasatake YAMATO  * var-definitions.  It does look for the keyword "extern" or "forward"
1503ae02089SMasatake YAMATO  * immediately following the procedure statement; if found, the tag is
1513ae02089SMasatake YAMATO  * skipped.
1523ae02089SMasatake YAMATO  */
findPascalTags(void)1533ae02089SMasatake YAMATO static void findPascalTags (void)
1543ae02089SMasatake YAMATO {
1553ae02089SMasatake YAMATO 	vString *name = vStringNew ();
156bd605841SEnrico Tröger 	vString *arglist = vStringNew ();
157bd605841SEnrico Tröger 	vString *vartype = vStringNew ();
1583ae02089SMasatake YAMATO 	tagEntryInfo tag;
1593ae02089SMasatake YAMATO 	pascalKind kind = K_FUNCTION;
160ce990805SThomas Braun 		/* each of these flags is true iff: */
161ce990805SThomas Braun 	bool incomment = false;  /* point is inside a comment */
1623ae02089SMasatake YAMATO 	int comment_char = '\0';    /* type of current comment */
163ce990805SThomas Braun 	bool inquote = false;    /* point is inside '..' string */
164ce990805SThomas Braun 	bool get_tagname = false;/* point is after PROCEDURE/FUNCTION
1653ae02089SMasatake YAMATO 		keyword, so next item = potential tag */
166ce990805SThomas Braun 	bool found_tag = false;  /* point is after a potential tag */
167ce990805SThomas Braun 	bool inparms = false;    /* point is within parameter-list */
168ce990805SThomas Braun 	bool verify_tag = false;
1693ae02089SMasatake YAMATO 		/* point has passed the parm-list, so the next token will determine
1703ae02089SMasatake YAMATO 		 * whether this is a FORWARD/EXTERN to be ignored, or whether it is a
1713ae02089SMasatake YAMATO 		 * real tag
1723ae02089SMasatake YAMATO 		 */
1733ae02089SMasatake YAMATO 
1741b312fe7SMasatake YAMATO 	dbp = readLineFromInputFile ();
1753ae02089SMasatake YAMATO 	while (dbp != NULL)
1763ae02089SMasatake YAMATO 	{
1773ae02089SMasatake YAMATO 		int c = *dbp++;
1783ae02089SMasatake YAMATO 
1793ae02089SMasatake YAMATO 		if (c == '\0')  /* if end of line */
1803ae02089SMasatake YAMATO 		{
1811b312fe7SMasatake YAMATO 			dbp = readLineFromInputFile ();
1823ae02089SMasatake YAMATO 			if (dbp == NULL  ||  *dbp == '\0')
1833ae02089SMasatake YAMATO 				continue;
1843ae02089SMasatake YAMATO 			if (!((found_tag && verify_tag) || get_tagname))
1853ae02089SMasatake YAMATO 				c = *dbp++;
1863ae02089SMasatake YAMATO 					/* only if don't need *dbp pointing to the beginning of
1873ae02089SMasatake YAMATO 					 * the name of the procedure or function
1883ae02089SMasatake YAMATO 					 */
1893ae02089SMasatake YAMATO 		}
1903ae02089SMasatake YAMATO 		if (incomment)
1913ae02089SMasatake YAMATO 		{
1923ae02089SMasatake YAMATO 			if (comment_char == '{' && c == '}')
193ce990805SThomas Braun 				incomment = false;
1943ae02089SMasatake YAMATO 			else if (comment_char == '(' && c == '*' && *dbp == ')')
1953ae02089SMasatake YAMATO 			{
1963ae02089SMasatake YAMATO 				dbp++;
197ce990805SThomas Braun 				incomment = false;
1983ae02089SMasatake YAMATO 			}
1993ae02089SMasatake YAMATO 			continue;
2003ae02089SMasatake YAMATO 		}
2013ae02089SMasatake YAMATO 		else if (inquote)
2023ae02089SMasatake YAMATO 		{
2033ae02089SMasatake YAMATO 			if (c == '\'')
204ce990805SThomas Braun 				inquote = false;
2053ae02089SMasatake YAMATO 			continue;
2063ae02089SMasatake YAMATO 		}
2073ae02089SMasatake YAMATO 		else switch (c)
2083ae02089SMasatake YAMATO 		{
2093ae02089SMasatake YAMATO 			case '\'':
210ce990805SThomas Braun 				inquote = true;  /* found first quote */
2113ae02089SMasatake YAMATO 				continue;
2123ae02089SMasatake YAMATO 			case '{':  /* found open { comment */
213ce990805SThomas Braun 				incomment = true;
2143ae02089SMasatake YAMATO 				comment_char = c;
2153ae02089SMasatake YAMATO 				continue;
2163ae02089SMasatake YAMATO 			case '(':
2173ae02089SMasatake YAMATO 				if (*dbp == '*')  /* found open (* comment */
2183ae02089SMasatake YAMATO 				{
219ce990805SThomas Braun 					incomment = true;
2203ae02089SMasatake YAMATO 					comment_char = c;
2213ae02089SMasatake YAMATO 					dbp++;
2223ae02089SMasatake YAMATO 				}
2233ae02089SMasatake YAMATO 				else if (found_tag)  /* found '(' after tag, i.e., parm-list */
224ce990805SThomas Braun 					inparms = true;
2253ae02089SMasatake YAMATO 				continue;
2263ae02089SMasatake YAMATO 			case ')':  /* end of parms list */
2273ae02089SMasatake YAMATO 				if (inparms)
228ce990805SThomas Braun 					inparms = false;
2293ae02089SMasatake YAMATO 				continue;
2303ae02089SMasatake YAMATO 			case ';':
2313ae02089SMasatake YAMATO 				if (found_tag && !inparms)  /* end of proc or fn stmt */
2323ae02089SMasatake YAMATO 				{
233ce990805SThomas Braun 					verify_tag = true;
2343ae02089SMasatake YAMATO 					break;
2353ae02089SMasatake YAMATO 				}
2363ae02089SMasatake YAMATO 				continue;
2373ae02089SMasatake YAMATO 		}
2383ae02089SMasatake YAMATO 		if (found_tag && verify_tag && *dbp != ' ')
2393ae02089SMasatake YAMATO 		{
2403ae02089SMasatake YAMATO 			/* check if this is an "extern" declaration */
2413ae02089SMasatake YAMATO 			if (*dbp == '\0')
2423ae02089SMasatake YAMATO 				continue;
2433ae02089SMasatake YAMATO 			if (tolower ((int) *dbp == 'e'))
2443ae02089SMasatake YAMATO 			{
2453ae02089SMasatake YAMATO 				if (tail ("extern"))  /* superfluous, really! */
2463ae02089SMasatake YAMATO 				{
247ce990805SThomas Braun 					found_tag = false;
248ce990805SThomas Braun 					verify_tag = false;
2493ae02089SMasatake YAMATO 				}
2503ae02089SMasatake YAMATO 			}
2513ae02089SMasatake YAMATO 			else if (tolower ((int) *dbp) == 'f')
2523ae02089SMasatake YAMATO 			{
2533ae02089SMasatake YAMATO 				if (tail ("forward"))  /*  check for forward reference */
2543ae02089SMasatake YAMATO 				{
255ce990805SThomas Braun 					found_tag = false;
256ce990805SThomas Braun 					verify_tag = false;
2573ae02089SMasatake YAMATO 				}
2583ae02089SMasatake YAMATO 			}
2593ae02089SMasatake YAMATO 			if (found_tag && verify_tag)  /* not external proc, so make tag */
2603ae02089SMasatake YAMATO 			{
261ce990805SThomas Braun 				found_tag = false;
262ce990805SThomas Braun 				verify_tag = false;
2633ae02089SMasatake YAMATO 				makePascalTag (&tag);
2643ae02089SMasatake YAMATO 				continue;
2653ae02089SMasatake YAMATO 			}
2663ae02089SMasatake YAMATO 		}
2673ae02089SMasatake YAMATO 		if (get_tagname)  /* grab name of proc or fn */
2683ae02089SMasatake YAMATO 		{
2693ae02089SMasatake YAMATO 			const unsigned char *cp;
2703ae02089SMasatake YAMATO 
2713ae02089SMasatake YAMATO 			if (*dbp == '\0')
2723ae02089SMasatake YAMATO 				continue;
2733ae02089SMasatake YAMATO 
2743ae02089SMasatake YAMATO 			/* grab block name */
2753ae02089SMasatake YAMATO 			while (isspace ((int) *dbp))
2763ae02089SMasatake YAMATO 				++dbp;
277*781ef2d9SNick Treleaven 			if (!starttoken(*dbp))
278*781ef2d9SNick Treleaven 				continue;
2793ae02089SMasatake YAMATO 			for (cp = dbp  ;  *cp != '\0' && !endtoken (*cp)  ;  cp++)
2803ae02089SMasatake YAMATO 				continue;
2813ae02089SMasatake YAMATO 			vStringNCopyS (name, (const char*) dbp,  cp - dbp);
282bd605841SEnrico Tröger 
283bd605841SEnrico Tröger 			vStringClear (arglist);
284bd605841SEnrico Tröger 			vStringClear (vartype);
285bd605841SEnrico Tröger 			parseArglist ((const char*) cp, arglist, (kind == K_FUNCTION) ? vartype : NULL);
286bd605841SEnrico Tröger 
287bd605841SEnrico Tröger 			createPascalTag (&tag, name, kind, arglist, (kind == K_FUNCTION) ? vartype : NULL);
2883ae02089SMasatake YAMATO 			dbp = cp;  /* set dbp to e-o-token */
289ce990805SThomas Braun 			get_tagname = false;
290ce990805SThomas Braun 			found_tag = true;
2913ae02089SMasatake YAMATO 			/* and proceed to check for "extern" */
2923ae02089SMasatake YAMATO 		}
2933ae02089SMasatake YAMATO 		else if (!incomment && !inquote && !found_tag)
2943ae02089SMasatake YAMATO 		{
2953ae02089SMasatake YAMATO 			switch (tolower ((int) c))
2963ae02089SMasatake YAMATO 			{
2973ae02089SMasatake YAMATO 				case 'c':
2983ae02089SMasatake YAMATO 					if (tail ("onstructor"))
2993ae02089SMasatake YAMATO 					{
300ce990805SThomas Braun 						get_tagname = true;
3013ae02089SMasatake YAMATO 						kind = K_PROCEDURE;
3023ae02089SMasatake YAMATO 					}
3033ae02089SMasatake YAMATO 					break;
3043ae02089SMasatake YAMATO 				case 'd':
3053ae02089SMasatake YAMATO 					if (tail ("estructor"))
3063ae02089SMasatake YAMATO 					{
307ce990805SThomas Braun 						get_tagname = true;
3083ae02089SMasatake YAMATO 						kind = K_PROCEDURE;
3093ae02089SMasatake YAMATO 					}
3103ae02089SMasatake YAMATO 					break;
3113ae02089SMasatake YAMATO 				case 'p':
3123ae02089SMasatake YAMATO 					if (tail ("rocedure"))
3133ae02089SMasatake YAMATO 					{
314ce990805SThomas Braun 						get_tagname = true;
3153ae02089SMasatake YAMATO 						kind = K_PROCEDURE;
3163ae02089SMasatake YAMATO 					}
3173ae02089SMasatake YAMATO 					break;
3183ae02089SMasatake YAMATO 				case 'f':
3193ae02089SMasatake YAMATO 					if (tail ("unction"))
3203ae02089SMasatake YAMATO 					{
321ce990805SThomas Braun 						get_tagname = true;
3223ae02089SMasatake YAMATO 						kind = K_FUNCTION;
3233ae02089SMasatake YAMATO 					}
3243ae02089SMasatake YAMATO 					break;
3253ae02089SMasatake YAMATO 			}
3263ae02089SMasatake YAMATO 		}  /* while not eof */
3273ae02089SMasatake YAMATO 	}
328bd605841SEnrico Tröger 	vStringDelete (arglist);
329bd605841SEnrico Tröger 	vStringDelete (vartype);
3303ae02089SMasatake YAMATO 	vStringDelete (name);
3313ae02089SMasatake YAMATO }
3323ae02089SMasatake YAMATO 
PascalParser(void)3333ae02089SMasatake YAMATO extern parserDefinition* PascalParser (void)
3343ae02089SMasatake YAMATO {
3353ae02089SMasatake YAMATO 	static const char *const extensions [] = { "p", "pas", NULL };
3363ae02089SMasatake YAMATO 	parserDefinition* def = parserNew ("Pascal");
3373ae02089SMasatake YAMATO 	def->extensions = extensions;
33809ae690fSMasatake YAMATO 	def->kindTable      = PascalKinds;
3393db72c21SMasatake YAMATO 	def->kindCount  = ARRAY_SIZE (PascalKinds);
3403ae02089SMasatake YAMATO 	def->parser     = findPascalTags;
3413ae02089SMasatake YAMATO 	return def;
3423ae02089SMasatake YAMATO }
343