xref: /Universal-ctags/parsers/tcl.c (revision 1c3778f39cb6b22f33631b8570724d48eefd2335)
13ae02089SMasatake YAMATO /*
23ae02089SMasatake YAMATO *   Copyright (c) 2000-2003, Darren Hiebert
323da13bdSMasatake YAMATO *   Copyright (c) 2017, Masatake YAMATO
423da13bdSMasatake YAMATO *   Copyright (c) 2017, Red Hat, Inc.
53ae02089SMasatake YAMATO *
63ae02089SMasatake YAMATO *   This source code is released for free distribution under the terms of the
70ce38835Sviccuad *   GNU General Public License version 2 or (at your option) any later version.
83ae02089SMasatake YAMATO *
93ae02089SMasatake YAMATO *   This module contains functions for generating tags for TCL scripts.
103ae02089SMasatake YAMATO */
113ae02089SMasatake YAMATO 
123ae02089SMasatake YAMATO /*
133ae02089SMasatake YAMATO *   INCLUDE FILES
143ae02089SMasatake YAMATO */
153ae02089SMasatake YAMATO #include "general.h"  /* must always come first */
1623da13bdSMasatake YAMATO #include "tokeninfo.h"
1723da13bdSMasatake YAMATO #include "parse.h"
1823da13bdSMasatake YAMATO #include "read.h"
1923da13bdSMasatake YAMATO #include "vstring.h"
2023da13bdSMasatake YAMATO #include "keyword.h"
2123da13bdSMasatake YAMATO #include "entry.h"
2223da13bdSMasatake YAMATO #include "routines.h"
2323da13bdSMasatake YAMATO #include "ptrarray.h"
246ae73029SMasatake YAMATO #include "tcl.h"
253ae02089SMasatake YAMATO 
263ae02089SMasatake YAMATO #include <string.h>
273ae02089SMasatake YAMATO 
2823da13bdSMasatake YAMATO 
293ae02089SMasatake YAMATO 
303ae02089SMasatake YAMATO /*
313ae02089SMasatake YAMATO *   DATA DEFINITIONS
323ae02089SMasatake YAMATO */
333ae02089SMasatake YAMATO typedef enum {
3471f9a680SMasatake YAMATO 	K_PROCEDURE, K_NAMESPACE, K_PARAMETER,
353ae02089SMasatake YAMATO } tclKind;
363ae02089SMasatake YAMATO 
3771f9a680SMasatake YAMATO static scopeSeparator TclParameterSeparators [] = {
3871f9a680SMasatake YAMATO 	{ K_PROCEDURE        , "{" },
3971f9a680SMasatake YAMATO };
4071f9a680SMasatake YAMATO 
41e112e8abSMasatake YAMATO static kindDefinition TclKinds [] = {
425535211dSMasatake YAMATO 	{ true, 'p', "procedure", "procedures", },
435535211dSMasatake YAMATO 	{ true, 'n', "namespace", "namespaces", },
4471f9a680SMasatake YAMATO 	{ false, 'z', "parameter", "procedure parameters",
4571f9a680SMasatake YAMATO 	  ATTACH_SEPARATORS(TclParameterSeparators)},
4623da13bdSMasatake YAMATO };
4723da13bdSMasatake YAMATO 
4823da13bdSMasatake YAMATO enum {
4923da13bdSMasatake YAMATO 	KEYWORD_PROC,
5023da13bdSMasatake YAMATO 	KEYWORD_NAMESPACE,
5123da13bdSMasatake YAMATO 	KEYWORD_EVAL,
52c499a4edSMasatake YAMATO 	KEYWORD_PACKAGE,
5323da13bdSMasatake YAMATO };
5423da13bdSMasatake YAMATO 
5523da13bdSMasatake YAMATO typedef int keywordId; /* to allow KEYWORD_NONE */
5623da13bdSMasatake YAMATO 
5723da13bdSMasatake YAMATO 
5823da13bdSMasatake YAMATO static const keywordTable TclKeywordTable[] = {
5923da13bdSMasatake YAMATO 	/* keyword			keyword ID */
6023da13bdSMasatake YAMATO 	{ "proc",			KEYWORD_PROC		},
6123da13bdSMasatake YAMATO 	{ "namespace",		KEYWORD_NAMESPACE	},
6223da13bdSMasatake YAMATO 	{ "eval",			KEYWORD_EVAL		},
63c499a4edSMasatake YAMATO 	{ "package",        KEYWORD_PACKAGE     },
6423da13bdSMasatake YAMATO };
6523da13bdSMasatake YAMATO 
6671f9a680SMasatake YAMATO typedef struct sCollector collector;
6771f9a680SMasatake YAMATO struct sCollector {
6871f9a680SMasatake YAMATO 	void (* proc) (const tokenInfo *const, collector *);
6971f9a680SMasatake YAMATO 	vString *str;
7071f9a680SMasatake YAMATO 	int depth;
7171f9a680SMasatake YAMATO 	int scopeIndex;
72*50e0c3e8SMasatake YAMATO 	int nth;
7371f9a680SMasatake YAMATO };
7471f9a680SMasatake YAMATO 
753ae02089SMasatake YAMATO /*
763ae02089SMasatake YAMATO *   FUNCTION DEFINITIONS
773ae02089SMasatake YAMATO */
783ae02089SMasatake YAMATO 
7971f9a680SMasatake YAMATO static bool tokenIsEOL (const tokenInfo *const token);
8023da13bdSMasatake YAMATO 
815d9d66cfSMasatake YAMATO static void initToken (tokenInfo *token, void *data);
825d9d66cfSMasatake YAMATO static void readToken (tokenInfo *const token, void *data);
8323da13bdSMasatake YAMATO static void clearToken (tokenInfo *token);
8423da13bdSMasatake YAMATO static void copyToken (tokenInfo *dest, tokenInfo *src, void *data CTAGS_ATTR_UNUSED);
8523da13bdSMasatake YAMATO 
865d9d66cfSMasatake YAMATO struct sTclParserState {
875d9d66cfSMasatake YAMATO 	enum TclTokenType lastTokenType;
885d9d66cfSMasatake YAMATO };
895d9d66cfSMasatake YAMATO 
90002dd246SMasatake YAMATO typedef struct sTclToken {
91002dd246SMasatake YAMATO 	tokenInfo base;
9223da13bdSMasatake YAMATO 	int scopeIndex;
935d9d66cfSMasatake YAMATO 	struct sTclParserState *pstate;
94002dd246SMasatake YAMATO } tclToken;
95002dd246SMasatake YAMATO 
96002dd246SMasatake YAMATO #define TCL(TOKEN) ((tclToken *)TOKEN)
97002dd246SMasatake YAMATO #define TCL_PSTATE(TOKEN) (TCL(TOKEN)->pstate)
9823da13bdSMasatake YAMATO 
991082869bSMasatake YAMATO static struct tokenTypePair typePairs [] = {
10023da13bdSMasatake YAMATO 	{ '{', '}' },
10123da13bdSMasatake YAMATO 	{ '[', ']' },
10223da13bdSMasatake YAMATO };
10323da13bdSMasatake YAMATO 
10423da13bdSMasatake YAMATO 
10523da13bdSMasatake YAMATO static struct tokenInfoClass tclTokenInfoClass = {
10623da13bdSMasatake YAMATO 	.nPreAlloc = 4,
1076ae73029SMasatake YAMATO 	.typeForUndefined = TOKEN_TCL_UNDEFINED,
10823da13bdSMasatake YAMATO 	.keywordNone      = KEYWORD_NONE,
1096ae73029SMasatake YAMATO 	.typeForKeyword   = TOKEN_TCL_KEYWORD,
1106ae73029SMasatake YAMATO 	.typeForEOF       = TOKEN_TCL_EOF,
111002dd246SMasatake YAMATO 	.extraSpace       = sizeof (tclToken) - sizeof (tokenInfo),
11223da13bdSMasatake YAMATO 	.pairs            = typePairs,
11323da13bdSMasatake YAMATO 	.pairCount        = ARRAY_SIZE (typePairs),
1145d9d66cfSMasatake YAMATO 	.init             = initToken,
11523da13bdSMasatake YAMATO 	.read             = readToken,
11623da13bdSMasatake YAMATO 	.clear            = clearToken,
11723da13bdSMasatake YAMATO 	.copy             = copyToken,
11823da13bdSMasatake YAMATO };
11923da13bdSMasatake YAMATO 
newTclToken(void * pstate)1205d9d66cfSMasatake YAMATO extern tokenInfo *newTclToken (void *pstate)
1213ae02089SMasatake YAMATO {
1225d9d66cfSMasatake YAMATO 	return newTokenFull (&tclTokenInfoClass, pstate);
1233ae02089SMasatake YAMATO }
1243ae02089SMasatake YAMATO 
clearToken(tokenInfo * token)12523da13bdSMasatake YAMATO static void clearToken (tokenInfo *token)
1263ae02089SMasatake YAMATO {
127002dd246SMasatake YAMATO 	TCL (token)->scopeIndex = CORK_NIL;
128002dd246SMasatake YAMATO 	TCL (token)->pstate = NULL;
12923da13bdSMasatake YAMATO }
13023da13bdSMasatake YAMATO 
copyToken(tokenInfo * dest,tokenInfo * src,void * data CTAGS_ATTR_UNUSED)13123da13bdSMasatake YAMATO static void copyToken (tokenInfo *dest, tokenInfo *src, void *data CTAGS_ATTR_UNUSED)
13223da13bdSMasatake YAMATO {
133002dd246SMasatake YAMATO 	TCL (dest)->scopeIndex =
134002dd246SMasatake YAMATO 		TCL (src)->scopeIndex;
135002dd246SMasatake YAMATO 	TCL (dest)->pstate =
136002dd246SMasatake YAMATO 		TCL (src)->pstate;
13723da13bdSMasatake YAMATO }
13823da13bdSMasatake YAMATO 
readString(vString * string)13923da13bdSMasatake YAMATO static void readString (vString *string)
14023da13bdSMasatake YAMATO {
14123da13bdSMasatake YAMATO 	int c;
14223da13bdSMasatake YAMATO 	bool escaped = false;
14323da13bdSMasatake YAMATO 
14423da13bdSMasatake YAMATO 	while (1)
14523da13bdSMasatake YAMATO 	{
14623da13bdSMasatake YAMATO 		c = getcFromInputFile ();
14723da13bdSMasatake YAMATO 		switch (c)
14823da13bdSMasatake YAMATO 		{
14923da13bdSMasatake YAMATO 		case EOF:
1504a31d2a5SMasatake YAMATO 			return;
15123da13bdSMasatake YAMATO 		case '\\':
152964a762cSMasatake YAMATO 			if (escaped)
153964a762cSMasatake YAMATO 			{
15423da13bdSMasatake YAMATO 				vStringPut (string, c);
155964a762cSMasatake YAMATO 				escaped = false;
156964a762cSMasatake YAMATO 			}
157964a762cSMasatake YAMATO 			else
15823da13bdSMasatake YAMATO 				escaped = true;
1594a31d2a5SMasatake YAMATO 			break;
16023da13bdSMasatake YAMATO 		case '"':
16123da13bdSMasatake YAMATO 			vStringPut (string, c);
16223da13bdSMasatake YAMATO 			if (escaped)
16323da13bdSMasatake YAMATO 				escaped = false;
16423da13bdSMasatake YAMATO 			else
16523da13bdSMasatake YAMATO 				return;
16623da13bdSMasatake YAMATO 			break;
16723da13bdSMasatake YAMATO 		default:
1684a31d2a5SMasatake YAMATO 			escaped = false;
16923da13bdSMasatake YAMATO 			vStringPut (string, c);
17023da13bdSMasatake YAMATO 			break;
17123da13bdSMasatake YAMATO 		}
17223da13bdSMasatake YAMATO 	}
17323da13bdSMasatake YAMATO }
17423da13bdSMasatake YAMATO 
readIdentifier(vString * string)17523da13bdSMasatake YAMATO static void readIdentifier (vString *string)
17623da13bdSMasatake YAMATO {
17723da13bdSMasatake YAMATO 	while (1)
17823da13bdSMasatake YAMATO 	{
17923da13bdSMasatake YAMATO 		int c = getcFromInputFile ();
18023da13bdSMasatake YAMATO 		if (isgraph (c) && (!strchr ("{}[]", c)))
18123da13bdSMasatake YAMATO 			vStringPut (string, c);
18223da13bdSMasatake YAMATO 		else
18323da13bdSMasatake YAMATO 		{
18423da13bdSMasatake YAMATO 			ungetcToInputFile (c);
18523da13bdSMasatake YAMATO 			break;
18623da13bdSMasatake YAMATO 		}
18723da13bdSMasatake YAMATO 	}
18823da13bdSMasatake YAMATO }
18923da13bdSMasatake YAMATO 
resolveKeyword(vString * string)19023da13bdSMasatake YAMATO static keywordId resolveKeyword (vString *string)
19123da13bdSMasatake YAMATO {
19223da13bdSMasatake YAMATO 	char *s = vStringValue (string);
19323da13bdSMasatake YAMATO 	static langType lang = LANG_AUTO;
19423da13bdSMasatake YAMATO 
19523da13bdSMasatake YAMATO 	if (lang == LANG_AUTO)
19623da13bdSMasatake YAMATO 		lang = getInputLanguage ();
19723da13bdSMasatake YAMATO 
19823da13bdSMasatake YAMATO 	return lookupKeyword (s, lang);
19923da13bdSMasatake YAMATO }
20023da13bdSMasatake YAMATO 
initToken(tokenInfo * token,void * data)2015d9d66cfSMasatake YAMATO static void initToken (tokenInfo *token, void *data)
20223da13bdSMasatake YAMATO {
203002dd246SMasatake YAMATO 	TCL (token)->pstate = data;
2045d9d66cfSMasatake YAMATO }
20523da13bdSMasatake YAMATO 
readToken0(tokenInfo * const token,struct sTclParserState * pstate)2065d9d66cfSMasatake YAMATO static void readToken0 (tokenInfo *const token, struct sTclParserState *pstate)
2075d9d66cfSMasatake YAMATO {
2085d9d66cfSMasatake YAMATO 	int c = EOF;
2095d9d66cfSMasatake YAMATO 	bool escaped;
2105d9d66cfSMasatake YAMATO 	bool bol = (pstate->lastTokenType == TOKEN_TCL_EOL
2115d9d66cfSMasatake YAMATO 				|| pstate->lastTokenType == ';'
2125d9d66cfSMasatake YAMATO 				|| pstate->lastTokenType == TOKEN_TCL_UNDEFINED);
2136ae73029SMasatake YAMATO 	token->type		= TOKEN_TCL_UNDEFINED;
21423da13bdSMasatake YAMATO 	token->keyword	= KEYWORD_NONE;
21523da13bdSMasatake YAMATO 	vStringClear (token->string);
21623da13bdSMasatake YAMATO 
21723da13bdSMasatake YAMATO  getNextChar:
21823da13bdSMasatake YAMATO 	escaped = false;
21923da13bdSMasatake YAMATO 
22023da13bdSMasatake YAMATO 	do {
22123da13bdSMasatake YAMATO 		c = getcFromInputFile ();
22223da13bdSMasatake YAMATO 	} while (c == ' ' || c== '\t' || c == '\f');
22323da13bdSMasatake YAMATO 
22423da13bdSMasatake YAMATO 	if (c == '\\')
22523da13bdSMasatake YAMATO 	{
2265d9d66cfSMasatake YAMATO 		bol = false;
22723da13bdSMasatake YAMATO 		int c0 = getcFromInputFile ();
22823da13bdSMasatake YAMATO 		switch (c0)
22923da13bdSMasatake YAMATO 		{
23023da13bdSMasatake YAMATO 		case '\n':
23123da13bdSMasatake YAMATO 		case '\r':
23223da13bdSMasatake YAMATO 			goto getNextChar;
23323da13bdSMasatake YAMATO 		default:
23423da13bdSMasatake YAMATO 			escaped = true;
23523da13bdSMasatake YAMATO 			c = c0;
23623da13bdSMasatake YAMATO 			break;
23723da13bdSMasatake YAMATO 		}
23823da13bdSMasatake YAMATO 	}
23923da13bdSMasatake YAMATO 
24023da13bdSMasatake YAMATO 	token->lineNumber   = getInputLineNumber ();
24123da13bdSMasatake YAMATO 	token->filePosition = getInputFilePosition ();
24223da13bdSMasatake YAMATO 
24323da13bdSMasatake YAMATO 	switch (c)
24423da13bdSMasatake YAMATO 	{
24523da13bdSMasatake YAMATO 	case EOF:
2466ae73029SMasatake YAMATO 		token->type = TOKEN_TCL_EOF;
24723da13bdSMasatake YAMATO 		break;
24823da13bdSMasatake YAMATO 	case '\n':
24923da13bdSMasatake YAMATO 	case '\r':
2506ae73029SMasatake YAMATO 		token->type = TOKEN_TCL_EOL;
25123da13bdSMasatake YAMATO 		break;
25223da13bdSMasatake YAMATO 	case '#':
25323da13bdSMasatake YAMATO 		if (!escaped)
25423da13bdSMasatake YAMATO 		{
2555d9d66cfSMasatake YAMATO 			if (bol)
2565d9d66cfSMasatake YAMATO 			{
25723da13bdSMasatake YAMATO 				do
25823da13bdSMasatake YAMATO 					c = getcFromInputFile ();
25923da13bdSMasatake YAMATO 				while (c != EOF && c != '\r' && c != '\n');
2605d9d66cfSMasatake YAMATO 			}
26123da13bdSMasatake YAMATO 			goto getNextChar;
26223da13bdSMasatake YAMATO 		}
26323da13bdSMasatake YAMATO 	case '"':
26423da13bdSMasatake YAMATO 		if (!escaped)
26523da13bdSMasatake YAMATO 		{
2666ae73029SMasatake YAMATO 			token->type = TOKEN_TCL_STRING;
2675c2563d5SMasatake YAMATO 			tokenPutc (token, c);
26823da13bdSMasatake YAMATO 			readString (token->string);
26923da13bdSMasatake YAMATO 			break;
27023da13bdSMasatake YAMATO 		}
271ca83e3bfSMasatake YAMATO 	case ';':
27223da13bdSMasatake YAMATO 	case '{':
27323da13bdSMasatake YAMATO 	case '}':
27423da13bdSMasatake YAMATO 	case '[':
27523da13bdSMasatake YAMATO 	case ']':
27623da13bdSMasatake YAMATO 		if (!escaped)
27723da13bdSMasatake YAMATO 		{
27871f9a680SMasatake YAMATO 			tokenPutc (token, c);
27923da13bdSMasatake YAMATO 			token->type = c;
28023da13bdSMasatake YAMATO 			break;
28123da13bdSMasatake YAMATO 		}
28223da13bdSMasatake YAMATO 	case '$':
28323da13bdSMasatake YAMATO 		if (!escaped)
28423da13bdSMasatake YAMATO 		{
2855c2563d5SMasatake YAMATO 			tokenPutc (token, c);
2866ae73029SMasatake YAMATO 			token->type = TOKEN_TCL_VARIABLE;
28723da13bdSMasatake YAMATO 
28823da13bdSMasatake YAMATO 			int c0 = getcFromInputFile ();
28923da13bdSMasatake YAMATO 			if (c0 == EOF)
29023da13bdSMasatake YAMATO 				break;
29123da13bdSMasatake YAMATO 
29223da13bdSMasatake YAMATO 			if (c0 == '{')
29323da13bdSMasatake YAMATO 			{
2946d139d9bSMasatake YAMATO 				tokenPutc (token, c0);
29523da13bdSMasatake YAMATO 				while ((c0 = getcFromInputFile ()) != EOF)
29623da13bdSMasatake YAMATO 				{
2975c2563d5SMasatake YAMATO 					tokenPutc (token, c0);
29823da13bdSMasatake YAMATO 					if (c0 == '}')
29923da13bdSMasatake YAMATO 						break;
30023da13bdSMasatake YAMATO 				}
30123da13bdSMasatake YAMATO 			}
3026d139d9bSMasatake YAMATO 			else if (isalnum (c0))
3036d139d9bSMasatake YAMATO 			{
3046d139d9bSMasatake YAMATO 				tokenPutc (token, c0);
30523da13bdSMasatake YAMATO 				readIdentifier (token->string);
3066d139d9bSMasatake YAMATO 			}
3076d139d9bSMasatake YAMATO 			else
3086d139d9bSMasatake YAMATO 				ungetcToInputFile (c0);
30923da13bdSMasatake YAMATO 			break;
31023da13bdSMasatake YAMATO 		}
31123da13bdSMasatake YAMATO 	default:
3125c2563d5SMasatake YAMATO 			tokenPutc (token, c);
31323da13bdSMasatake YAMATO 			readIdentifier (token->string);
31423da13bdSMasatake YAMATO 
31523da13bdSMasatake YAMATO 			token->keyword = resolveKeyword (token->string);
31623da13bdSMasatake YAMATO 			if (token->keyword == KEYWORD_NONE)
3176ae73029SMasatake YAMATO 				token->type = TOKEN_TCL_IDENTIFIER;
31823da13bdSMasatake YAMATO 			else
3196ae73029SMasatake YAMATO 				token->type = TOKEN_TCL_KEYWORD;
32023da13bdSMasatake YAMATO 			break;
32123da13bdSMasatake YAMATO 	}
32223da13bdSMasatake YAMATO }
32323da13bdSMasatake YAMATO 
readToken(tokenInfo * const token,void * data)32471f9a680SMasatake YAMATO static void readToken (tokenInfo *const token, void *data)
3255d9d66cfSMasatake YAMATO {
326002dd246SMasatake YAMATO 	struct sTclParserState *pstate = TCL_PSTATE(token);
3275d9d66cfSMasatake YAMATO 
3285d9d66cfSMasatake YAMATO 	readToken0 (token, pstate);
3295d9d66cfSMasatake YAMATO 
3305d9d66cfSMasatake YAMATO 	pstate->lastTokenType = token->type;
33171f9a680SMasatake YAMATO 
33271f9a680SMasatake YAMATO 	if (data)
33371f9a680SMasatake YAMATO 	{
33471f9a680SMasatake YAMATO 		collector *col = data;
33571f9a680SMasatake YAMATO 		col->proc (token, col);
33671f9a680SMasatake YAMATO 	}
3375d9d66cfSMasatake YAMATO }
33823da13bdSMasatake YAMATO 
tokenIsEOL(const tokenInfo * const token)33971f9a680SMasatake YAMATO static bool tokenIsEOL (const tokenInfo *const token)
3402b7fcc1eSMasatake YAMATO {
3412b7fcc1eSMasatake YAMATO 	if (token->type == ';'
3422b7fcc1eSMasatake YAMATO 		|| tokenIsType (token, TCL_EOL)
3432b7fcc1eSMasatake YAMATO 		|| tokenIsEOF (token))
3442b7fcc1eSMasatake YAMATO 		return true;
3452b7fcc1eSMasatake YAMATO 	return false;
3462b7fcc1eSMasatake YAMATO }
34723da13bdSMasatake YAMATO 
skipToEndOfCmdline(tokenInfo * const token)34823da13bdSMasatake YAMATO static void skipToEndOfCmdline (tokenInfo *const token)
34923da13bdSMasatake YAMATO {
3502b7fcc1eSMasatake YAMATO 	while (!tokenIsEOL (token))
3512b7fcc1eSMasatake YAMATO 	{
3522b7fcc1eSMasatake YAMATO 		if ((token->type == '{')
35323da13bdSMasatake YAMATO 			|| (token->type == '['))
35423da13bdSMasatake YAMATO 			tokenSkipOverPair(token);
35523da13bdSMasatake YAMATO 		tokenRead (token);
3562b7fcc1eSMasatake YAMATO 	}
35723da13bdSMasatake YAMATO }
35823da13bdSMasatake YAMATO 
skipToEndOfTclCmdline(tokenInfo * const token)35994e60171SMasatake YAMATO extern void skipToEndOfTclCmdline (tokenInfo *const token)
36094e60171SMasatake YAMATO {
36194e60171SMasatake YAMATO 	skipToEndOfCmdline (token);
36294e60171SMasatake YAMATO }
36394e60171SMasatake YAMATO 
isAbsoluteIdentifier(tokenInfo * const token)36423da13bdSMasatake YAMATO static bool isAbsoluteIdentifier(tokenInfo *const token)
36523da13bdSMasatake YAMATO {
3665c2563d5SMasatake YAMATO 	return !strncmp (tokenString (token), "::", 2);
36723da13bdSMasatake YAMATO }
36823da13bdSMasatake YAMATO 
getLastComponentInIdentifier(tokenInfo * const token)36923da13bdSMasatake YAMATO static const char* getLastComponentInIdentifier(tokenInfo *const token)
37023da13bdSMasatake YAMATO {
3715c2563d5SMasatake YAMATO 	const char* s = tokenString (token);
37223da13bdSMasatake YAMATO 	char *last = strrstr(s, "::");
37323da13bdSMasatake YAMATO 
37423da13bdSMasatake YAMATO 	if (last)
37523da13bdSMasatake YAMATO 		return last + 2;
37623da13bdSMasatake YAMATO 	else
37723da13bdSMasatake YAMATO 		return NULL;
37823da13bdSMasatake YAMATO }
37923da13bdSMasatake YAMATO 
380c499a4edSMasatake YAMATO 
notifyNamespaceImport(tokenInfo * const token)381bf898f4dSMasatake YAMATO static void notifyNamespaceImport (tokenInfo *const token)
382bf898f4dSMasatake YAMATO {
383bf898f4dSMasatake YAMATO 	subparser *sub;
384bf898f4dSMasatake YAMATO 
385bf898f4dSMasatake YAMATO 	foreachSubparser (sub, false)
386bf898f4dSMasatake YAMATO 	{
387bf898f4dSMasatake YAMATO 		tclSubparser *tclsub = (tclSubparser *)sub;
388bf898f4dSMasatake YAMATO 
389bf898f4dSMasatake YAMATO 		if (tclsub->namespaceImportNotify)
390bf898f4dSMasatake YAMATO 		{
391bf898f4dSMasatake YAMATO 			enterSubparser(sub);
3925c2563d5SMasatake YAMATO 			tclsub->namespaceImportNotify (tclsub, tokenString (token),
393002dd246SMasatake YAMATO 										   TCL_PSTATE(token));
394bf898f4dSMasatake YAMATO 			leaveSubparser();
395bf898f4dSMasatake YAMATO 		}
396bf898f4dSMasatake YAMATO 	}
397bf898f4dSMasatake YAMATO }
398bf898f4dSMasatake YAMATO 
notifyCommand(tokenInfo * const token,int parent)3993afb5475SMasatake YAMATO static int notifyCommand (tokenInfo *const token, int parent)
4006ae73029SMasatake YAMATO {
4016ae73029SMasatake YAMATO 	subparser *sub;
402c2c434adSjannick0 	int r = CORK_NIL;
4036ae73029SMasatake YAMATO 
4046ae73029SMasatake YAMATO 	foreachSubparser (sub, false)
4056ae73029SMasatake YAMATO 	{
4066ae73029SMasatake YAMATO 		tclSubparser *tclsub = (tclSubparser *)sub;
4074e7f9b4dSMasatake YAMATO 
4084e7f9b4dSMasatake YAMATO 		if (tclsub->commandNotify)
4094e7f9b4dSMasatake YAMATO 		{
4106ae73029SMasatake YAMATO 			enterSubparser(sub);
4115c2563d5SMasatake YAMATO 			r = tclsub->commandNotify (tclsub, tokenString (token), parent,
412002dd246SMasatake YAMATO 									   TCL_PSTATE(token));
4136ae73029SMasatake YAMATO 			leaveSubparser();
4146ae73029SMasatake YAMATO 			if (r != CORK_NIL)
4156ae73029SMasatake YAMATO 				break;
4166ae73029SMasatake YAMATO 		}
4174e7f9b4dSMasatake YAMATO 	}
4186ae73029SMasatake YAMATO 	return r;
4196ae73029SMasatake YAMATO }
4206ae73029SMasatake YAMATO 
collectSignature(const tokenInfo * const token,collector * col)42171f9a680SMasatake YAMATO static void collectSignature (const tokenInfo *const token, collector * col)
42271f9a680SMasatake YAMATO {
42371f9a680SMasatake YAMATO 	if (tokenIsEOL (token))
42471f9a680SMasatake YAMATO 		return;
42571f9a680SMasatake YAMATO 
42671f9a680SMasatake YAMATO 	if (tokenIsType (token, TCL_IDENTIFIER) &&
42771f9a680SMasatake YAMATO 		(col->depth == 1
42871f9a680SMasatake YAMATO 		 || (col->depth == 2 && tokenIsType (token, TCL_IDENTIFIER)
42971f9a680SMasatake YAMATO 			 && vStringLast (col->str) == '{')))
43071f9a680SMasatake YAMATO 	{
43171f9a680SMasatake YAMATO 		tagEntryInfo e;
43271f9a680SMasatake YAMATO 		initTagEntry (&e, tokenString (token), K_PARAMETER);
43371f9a680SMasatake YAMATO 		e.extensionFields.scopeIndex = col->scopeIndex;
434*50e0c3e8SMasatake YAMATO 		e.extensionFields.nth = col->nth++;
43571f9a680SMasatake YAMATO 		makeTagEntry (&e);
43671f9a680SMasatake YAMATO 	}
43771f9a680SMasatake YAMATO 	else if (tokenIsTypeVal (token, '{'))
43871f9a680SMasatake YAMATO 		col->depth++;
43971f9a680SMasatake YAMATO 	else if (tokenIsTypeVal (token, '}'))
44071f9a680SMasatake YAMATO 		col->depth--;
44171f9a680SMasatake YAMATO 
44271f9a680SMasatake YAMATO 	if ((vStringLength (col->str) > 0
44371f9a680SMasatake YAMATO 		 && vStringLast (col->str) != '{'
44471f9a680SMasatake YAMATO 		 && vStringLast (col->str) != '['
44571f9a680SMasatake YAMATO 		 && vStringLast (col->str) != '(')
44671f9a680SMasatake YAMATO 		&& (!tokenIsTypeVal (token, '}'))
44771f9a680SMasatake YAMATO 		&& (!tokenIsTypeVal (token, ']'))
44871f9a680SMasatake YAMATO 		&& (!tokenIsTypeVal (token, ')')))
44971f9a680SMasatake YAMATO 		vStringPut (col->str, ' ');
45071f9a680SMasatake YAMATO 	vStringCat (col->str, token->string);
45171f9a680SMasatake YAMATO }
45271f9a680SMasatake YAMATO 
parseProc(tokenInfo * const token,int parent)45323da13bdSMasatake YAMATO static void parseProc (tokenInfo *const token,
4543afb5475SMasatake YAMATO 					   int parent)
45523da13bdSMasatake YAMATO {
45623da13bdSMasatake YAMATO 	int index = CORK_NIL;
45723da13bdSMasatake YAMATO 	int index_fq = CORK_NIL;
45823da13bdSMasatake YAMATO 
45923da13bdSMasatake YAMATO 	tokenRead (token);
46023da13bdSMasatake YAMATO 
4616ae73029SMasatake YAMATO 	if (tokenIsType(token, TCL_IDENTIFIER))
46223da13bdSMasatake YAMATO 	{
46323da13bdSMasatake YAMATO 		const char *last = getLastComponentInIdentifier (token);
46423da13bdSMasatake YAMATO 		if (last)
46523da13bdSMasatake YAMATO 		{
46623da13bdSMasatake YAMATO 			tagEntryInfo e;
46723da13bdSMasatake YAMATO 
46816a2541cSMasatake YAMATO 			initTagEntry (&e, last, K_PROCEDURE);
46923da13bdSMasatake YAMATO 			e.lineNumber = token->lineNumber;
47023da13bdSMasatake YAMATO 			e.filePosition = token->filePosition;
47123da13bdSMasatake YAMATO 
4725c2563d5SMasatake YAMATO 			int len  = (last - tokenString (token));
47323da13bdSMasatake YAMATO 			vString *ns = vStringNew();
4743671ad72SMasatake YAMATO 			tagEntryInfo *e_parent = getEntryInCorkQueue (parent);
47523da13bdSMasatake YAMATO 			if (isAbsoluteIdentifier (token))
47623da13bdSMasatake YAMATO 			{
47723da13bdSMasatake YAMATO 				if (len > 2)
47823da13bdSMasatake YAMATO 					vStringNCopy (ns, token->string, len - 2);
47923da13bdSMasatake YAMATO 			}
4803671ad72SMasatake YAMATO 			else if (e_parent)
48123da13bdSMasatake YAMATO 			{
482c22925e9SMasatake YAMATO 				const char * sep = scopeSeparatorFor (getInputLanguage(),
483c22925e9SMasatake YAMATO 													  K_PROCEDURE,
484c22925e9SMasatake YAMATO 													  e_parent->kindIndex);
48523da13bdSMasatake YAMATO 				vStringCatS(ns, e_parent->name);
486c22925e9SMasatake YAMATO 				vStringCatS(ns, sep);
48723da13bdSMasatake YAMATO 				vStringNCopy(ns, token->string, len - 2);
48823da13bdSMasatake YAMATO 			}
4893671ad72SMasatake YAMATO 			else
490ff499819SMasatake YAMATO 				vStringNCopy (ns, token->string, len - 2);
49123da13bdSMasatake YAMATO 
49223da13bdSMasatake YAMATO 			if (vStringLength(ns) > 0)
49323da13bdSMasatake YAMATO 			{
494f92e6bf2SMasatake YAMATO 				e.extensionFields.scopeKindIndex = K_NAMESPACE;
49523da13bdSMasatake YAMATO 				e.extensionFields.scopeName = vStringValue (ns);
49623da13bdSMasatake YAMATO 			}
49723da13bdSMasatake YAMATO 
4988ee65243SMasatake YAMATO 			e.skipAutoFQEmission = 1;
49923da13bdSMasatake YAMATO 			index = makeTagEntry (&e);
50023da13bdSMasatake YAMATO 
50123da13bdSMasatake YAMATO 			if (isXtagEnabled(XTAG_QUALIFIED_TAGS))
50223da13bdSMasatake YAMATO 			{
503c22925e9SMasatake YAMATO 				const char * sep = scopeSeparatorFor (getInputLanguage(),
504c22925e9SMasatake YAMATO 													  K_PROCEDURE,
505a3f7eeb8SMasatake YAMATO 													  vStringIsEmpty (ns)
506a3f7eeb8SMasatake YAMATO 													  ? KIND_GHOST_INDEX
507a3f7eeb8SMasatake YAMATO 													  : K_NAMESPACE);
508c22925e9SMasatake YAMATO 
509c22925e9SMasatake YAMATO 				vStringCatS (ns, sep);
51023da13bdSMasatake YAMATO 				vStringCatS (ns, last);
51123da13bdSMasatake YAMATO 
51216a2541cSMasatake YAMATO 				index_fq = makeSimpleTag (ns, K_PROCEDURE);
51323da13bdSMasatake YAMATO 				tagEntryInfo *e_fq = getEntryInCorkQueue (index_fq);
5143671ad72SMasatake YAMATO 				if (e_fq)
51523da13bdSMasatake YAMATO 					markTagExtraBit (e_fq, XTAG_QUALIFIED_TAGS);
51623da13bdSMasatake YAMATO 			}
51723da13bdSMasatake YAMATO 			vStringDelete (ns);
51823da13bdSMasatake YAMATO 		}
51923da13bdSMasatake YAMATO 		else
52023da13bdSMasatake YAMATO 		{
52123da13bdSMasatake YAMATO 			tagEntryInfo *ep;
52216a2541cSMasatake YAMATO 			index = makeSimpleTag (token->string, K_PROCEDURE);
52323da13bdSMasatake YAMATO 			ep = getEntryInCorkQueue (index);
5243671ad72SMasatake YAMATO 			if (ep)
52523da13bdSMasatake YAMATO 				ep->extensionFields.scopeIndex = parent;
52623da13bdSMasatake YAMATO 		}
52723da13bdSMasatake YAMATO 	}
52823da13bdSMasatake YAMATO 
52971f9a680SMasatake YAMATO 	vString *signature = NULL;
5302b7fcc1eSMasatake YAMATO 	if (!tokenIsEOL (token))
53123da13bdSMasatake YAMATO 	{
5322b7fcc1eSMasatake YAMATO 		tokenRead (token);
53371f9a680SMasatake YAMATO 		if (tokenIsType (token, TCL_IDENTIFIER))
53471f9a680SMasatake YAMATO 		{
53571f9a680SMasatake YAMATO 			tagEntryInfo e;
53671f9a680SMasatake YAMATO 			initTagEntry (&e, tokenString (token), K_PARAMETER);
53771f9a680SMasatake YAMATO 			e.extensionFields.scopeIndex = index;
53871f9a680SMasatake YAMATO 			makeTagEntry (&e);
53971f9a680SMasatake YAMATO 			signature = vStringNewCopy (token->string);
54071f9a680SMasatake YAMATO 		}
54171f9a680SMasatake YAMATO 		else if (token->type == '{')
54271f9a680SMasatake YAMATO 		{
54371f9a680SMasatake YAMATO 			signature = vStringNewInit ("{");
54471f9a680SMasatake YAMATO 			collector col = {
54571f9a680SMasatake YAMATO 				.proc = collectSignature,
54671f9a680SMasatake YAMATO 				.str = signature,
54771f9a680SMasatake YAMATO 				.depth = 1,
54871f9a680SMasatake YAMATO 				.scopeIndex = index,
549*50e0c3e8SMasatake YAMATO 				.nth = 0,
55071f9a680SMasatake YAMATO 			};
55171f9a680SMasatake YAMATO 			tokenSkipOverPairFull (token, &col);
55271f9a680SMasatake YAMATO 		}
55371f9a680SMasatake YAMATO 
55423da13bdSMasatake YAMATO 		skipToEndOfCmdline(token);
55523da13bdSMasatake YAMATO 	}
55623da13bdSMasatake YAMATO 
5573671ad72SMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (index);
5583671ad72SMasatake YAMATO 	if (e)
55923da13bdSMasatake YAMATO 	{
56023da13bdSMasatake YAMATO 		e->extensionFields.endLine = token->lineNumber;
56123da13bdSMasatake YAMATO 
56271f9a680SMasatake YAMATO 		if (signature)
56371f9a680SMasatake YAMATO 		{
56471f9a680SMasatake YAMATO 			e->extensionFields.signature = vStringDeleteUnwrap (signature);
56571f9a680SMasatake YAMATO 			signature = NULL;
56671f9a680SMasatake YAMATO 		}
56771f9a680SMasatake YAMATO 
5683671ad72SMasatake YAMATO 		tagEntryInfo *e_fq = getEntryInCorkQueue (index_fq);
5693671ad72SMasatake YAMATO 		if (e_fq)
57023da13bdSMasatake YAMATO 		{
57171f9a680SMasatake YAMATO 			const char *sig = e->extensionFields.signature;
5723671ad72SMasatake YAMATO 			e_fq->extensionFields.endLine = token->lineNumber;
57371f9a680SMasatake YAMATO 			if (sig)
5743671ad72SMasatake YAMATO 				e_fq->extensionFields.signature = eStrdup (sig);
57523da13bdSMasatake YAMATO 		}
57623da13bdSMasatake YAMATO 	}
57771f9a680SMasatake YAMATO 	vStringDelete (signature);	/* NULL is acceptable */
57823da13bdSMasatake YAMATO }
57923da13bdSMasatake YAMATO 
parseNamespace(tokenInfo * const token,int parent)58023da13bdSMasatake YAMATO static void parseNamespace (tokenInfo *const token,
5813afb5475SMasatake YAMATO 							int parent)
58223da13bdSMasatake YAMATO {
58323da13bdSMasatake YAMATO 	tokenRead (token);
58423da13bdSMasatake YAMATO 	if (tokenIsEOF(token))
58523da13bdSMasatake YAMATO 		return;
58623da13bdSMasatake YAMATO 
587bf898f4dSMasatake YAMATO 	if (tokenIsType (token, TCL_IDENTIFIER) &&
588bf898f4dSMasatake YAMATO 		(strcmp(tokenString(token), "import") == 0))
589bf898f4dSMasatake YAMATO 	{
590bf898f4dSMasatake YAMATO 		while (1)
591bf898f4dSMasatake YAMATO 		{
592bf898f4dSMasatake YAMATO 			tokenRead (token);
593bf898f4dSMasatake YAMATO 
594bf898f4dSMasatake YAMATO 			if (!tokenIsType (token, TCL_IDENTIFIER))
595bf898f4dSMasatake YAMATO 				break;
596bf898f4dSMasatake YAMATO 
5975c2563d5SMasatake YAMATO 			if (tokenString(token)[0] == '-')
598bf898f4dSMasatake YAMATO 				continue;
599bf898f4dSMasatake YAMATO 
600bf898f4dSMasatake YAMATO 			notifyNamespaceImport (token);
601bf898f4dSMasatake YAMATO 		}
602bf898f4dSMasatake YAMATO 		skipToEndOfCmdline(token);
603bf898f4dSMasatake YAMATO 		return;
604bf898f4dSMasatake YAMATO 	}
605bf898f4dSMasatake YAMATO 	else if (!tokenIsKeyword (token, EVAL))
60623da13bdSMasatake YAMATO 		return;
60723da13bdSMasatake YAMATO 
60823da13bdSMasatake YAMATO 	tokenRead (token);
6096ae73029SMasatake YAMATO 	if (!tokenIsType (token, TCL_IDENTIFIER))
61023da13bdSMasatake YAMATO 	{
61123da13bdSMasatake YAMATO 		skipToEndOfCmdline(token);
61223da13bdSMasatake YAMATO 		return;
61323da13bdSMasatake YAMATO 	}
61423da13bdSMasatake YAMATO 
61516a2541cSMasatake YAMATO 	int index = makeSimpleTag (token->string, K_NAMESPACE);
61623da13bdSMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (index);
6173671ad72SMasatake YAMATO 	if (e && parent != CORK_NIL && !isAbsoluteIdentifier(token))
61823da13bdSMasatake YAMATO 		e->extensionFields.scopeIndex = parent;
61923da13bdSMasatake YAMATO 
62023da13bdSMasatake YAMATO 	tokenRead (token);
62123da13bdSMasatake YAMATO 	if (token->type != '{')
62223da13bdSMasatake YAMATO 	{
62323da13bdSMasatake YAMATO 		skipToEndOfCmdline(token);
62423da13bdSMasatake YAMATO 		return;
62523da13bdSMasatake YAMATO 	}
62623da13bdSMasatake YAMATO 
62723da13bdSMasatake YAMATO 	do {
62823da13bdSMasatake YAMATO 		tokenRead (token);
62923da13bdSMasatake YAMATO 		if (tokenIsKeyword (token, NAMESPACE))
63023da13bdSMasatake YAMATO 			parseNamespace (token, index);
63123da13bdSMasatake YAMATO 		else if (tokenIsKeyword (token, PROC))
63223da13bdSMasatake YAMATO 			parseProc (token, index);
6336ae73029SMasatake YAMATO 		else if (tokenIsType (token, TCL_IDENTIFIER))
6346ae73029SMasatake YAMATO 		{
6356ae73029SMasatake YAMATO 			notifyCommand (token, index);
6366ae73029SMasatake YAMATO 			skipToEndOfCmdline(token); /* ??? */
6376ae73029SMasatake YAMATO 		}
63823da13bdSMasatake YAMATO 		else if (token->type == '}')
63923da13bdSMasatake YAMATO 		{
6403671ad72SMasatake YAMATO 			if (e)
64123da13bdSMasatake YAMATO 				e->extensionFields.endLine = token->lineNumber;
64223da13bdSMasatake YAMATO 			break;
64323da13bdSMasatake YAMATO 		}
64423da13bdSMasatake YAMATO 		else
64523da13bdSMasatake YAMATO 			skipToEndOfCmdline(token);
64623da13bdSMasatake YAMATO 	} while (!tokenIsEOF(token));
6473ae02089SMasatake YAMATO }
6483ae02089SMasatake YAMATO 
parsePackage(tokenInfo * const token)649c499a4edSMasatake YAMATO static void parsePackage (tokenInfo *const token)
650c499a4edSMasatake YAMATO {
651c499a4edSMasatake YAMATO 	tokenRead (token);
652c499a4edSMasatake YAMATO 	if (tokenIsType (token, TCL_IDENTIFIER)
6535c2563d5SMasatake YAMATO 		&& (strcmp (tokenString (token), "require") == 0))
654c499a4edSMasatake YAMATO 	{
655c499a4edSMasatake YAMATO 	next:
656c499a4edSMasatake YAMATO 		tokenRead (token);
657c499a4edSMasatake YAMATO 		if (tokenIsType (token, TCL_IDENTIFIER)
658c499a4edSMasatake YAMATO 			&& (vStringLength (token->string) > 0))
659c499a4edSMasatake YAMATO 		{
6605c2563d5SMasatake YAMATO 			if (tokenString(token)[0] == '-')
661c499a4edSMasatake YAMATO 				goto next;
662c499a4edSMasatake YAMATO 		}
663c499a4edSMasatake YAMATO 	}
664c499a4edSMasatake YAMATO 	skipToEndOfCmdline(token);
665c499a4edSMasatake YAMATO }
666c499a4edSMasatake YAMATO 
667c499a4edSMasatake YAMATO 
findTclTags(void)6683ae02089SMasatake YAMATO static void findTclTags (void)
6693ae02089SMasatake YAMATO {
6705d9d66cfSMasatake YAMATO 	struct sTclParserState pstate = {
6715d9d66cfSMasatake YAMATO 		.lastTokenType = TOKEN_TCL_UNDEFINED,
6725d9d66cfSMasatake YAMATO 	};
6735d9d66cfSMasatake YAMATO 	tokenInfo *const token = newTclToken (&pstate);
6743ae02089SMasatake YAMATO 
67523da13bdSMasatake YAMATO 	do {
67623da13bdSMasatake YAMATO 		tokenRead (token);
67723da13bdSMasatake YAMATO 		if (tokenIsKeyword (token, NAMESPACE))
67823da13bdSMasatake YAMATO 			parseNamespace (token, CORK_NIL);
67923da13bdSMasatake YAMATO 		else if (tokenIsKeyword (token, PROC))
68023da13bdSMasatake YAMATO 			parseProc (token, CORK_NIL);
681c499a4edSMasatake YAMATO 		else if (tokenIsKeyword (token, PACKAGE))
682c499a4edSMasatake YAMATO 			parsePackage (token);
6836ae73029SMasatake YAMATO 		else if (tokenIsType (token, TCL_IDENTIFIER))
6846ae73029SMasatake YAMATO 		{
6856ae73029SMasatake YAMATO 			notifyCommand (token, CORK_NIL);
6866ae73029SMasatake YAMATO 			skipToEndOfCmdline(token); /* ??? */
6876ae73029SMasatake YAMATO 		}
68823da13bdSMasatake YAMATO 		else
68923da13bdSMasatake YAMATO 			skipToEndOfCmdline(token);
69023da13bdSMasatake YAMATO 	} while (!tokenIsEOF(token));
6913ae02089SMasatake YAMATO 
692a203ce0eSMasatake YAMATO 	tokenDelete (token);
69323da13bdSMasatake YAMATO 	flashTokenBacklog (&tclTokenInfoClass);
6943ae02089SMasatake YAMATO }
6953ae02089SMasatake YAMATO 
TclParser(void)6963ae02089SMasatake YAMATO extern parserDefinition* TclParser (void)
6973ae02089SMasatake YAMATO {
698ec223a9aSMasatake YAMATO 	static const char *const extensions [] = { "tcl", "tk", "wish", "exp", NULL };
699137271eeSMasatake YAMATO 	static const char *const aliases [] = {"expect", "tclsh", NULL };
70098d31190SMasatake YAMATO 
7013ae02089SMasatake YAMATO 	parserDefinition* def = parserNew ("Tcl");
70209ae690fSMasatake YAMATO 	def->kindTable      = TclKinds;
7033db72c21SMasatake YAMATO 	def->kindCount  = ARRAY_SIZE (TclKinds);
7043ae02089SMasatake YAMATO 	def->extensions = extensions;
70598d31190SMasatake YAMATO 	def->aliases = aliases;
70623da13bdSMasatake YAMATO 	def->keywordTable = TclKeywordTable;
70723da13bdSMasatake YAMATO 	def->keywordCount = ARRAY_SIZE (TclKeywordTable);
70823da13bdSMasatake YAMATO 
7093ae02089SMasatake YAMATO 	def->parser     = findTclTags;
7106b1a862eSMasatake YAMATO 	def->useCork    = CORK_QUEUE;
71123da13bdSMasatake YAMATO 	def->requestAutomaticFQTag = true;
7125535211dSMasatake YAMATO 	def->defaultScopeSeparator = "::";
7137de1c18bSMasatake YAMATO 	def->defaultRootScopeSeparator = "::";
7145535211dSMasatake YAMATO 
7153ae02089SMasatake YAMATO 	return def;
7163ae02089SMasatake YAMATO }
717