xref: /Universal-ctags/parsers/powershell.c (revision e852ee0e939802331dc3117fd85a31b243c3d04f)
1b689382aSJiří Techet /*
2b689382aSJiří Techet *   Copyright (c) 2015, Enrico Tröger <enrico.troeger@uvena.de>
3b689382aSJiří Techet *
4b689382aSJiří Techet *   Loosely based on the PHP tags parser since the syntax is somewhat similar
5b689382aSJiří Techet *   regarding variable and function definitions.
6b689382aSJiří Techet *
7b689382aSJiří Techet *   This source code is released for free distribution under the terms of the
83b275cf8SMasatake YAMATO *   GNU General Public License version 2 or (at your option) any later version.
9b689382aSJiří Techet *
10b689382aSJiří Techet *   This module contains code for generating tags for Windows PowerShell scripts
11b689382aSJiří Techet *   (https://en.wikipedia.org/wiki/PowerShell).
12b689382aSJiří Techet */
13b689382aSJiří Techet 
14b689382aSJiří Techet /*
15b689382aSJiří Techet *   INCLUDE FILES
16b689382aSJiří Techet */
17b689382aSJiří Techet #include "general.h"  /* must always come first */
18b689382aSJiří Techet #include "debug.h"
19b689382aSJiří Techet #include "parse.h"
20b689382aSJiří Techet #include "read.h"
21b689382aSJiří Techet #include "vstring.h"
22b689382aSJiří Techet #include "keyword.h"
23b689382aSJiří Techet #include "entry.h"
24b689382aSJiří Techet #include "routines.h"
25b689382aSJiří Techet #include <string.h>
26b689382aSJiří Techet 
27b689382aSJiří Techet #define SCOPE_SEPARATOR "::"
28b689382aSJiří Techet 
29b689382aSJiří Techet 
30b689382aSJiří Techet #define ACCESS_UNDEFINED NULL
31b689382aSJiří Techet static const char *const accessTypes[] = {
32b689382aSJiří Techet 	ACCESS_UNDEFINED,
33b689382aSJiří Techet 	"global",
34b689382aSJiří Techet 	"local",
35b689382aSJiří Techet 	"script",
36b689382aSJiří Techet 	"private"
37b689382aSJiří Techet };
38b689382aSJiří Techet 
39b689382aSJiří Techet typedef enum {
40b689382aSJiří Techet 	K_FUNCTION,
41b689382aSJiří Techet 	K_VARIABLE,
42b689382aSJiří Techet 	COUNT_KIND
43b689382aSJiří Techet } powerShellKind;
44b689382aSJiří Techet 
45b689382aSJiří Techet static kindDefinition PowerShellKinds[COUNT_KIND] = {
46b689382aSJiří Techet 	{ true, 'f', "function",	"functions" },
47b689382aSJiří Techet 	{ true, 'v', "variable",	"variables" }
48b689382aSJiří Techet };
49b689382aSJiří Techet 
50b689382aSJiří Techet 
51b689382aSJiří Techet typedef enum eTokenType {
52b689382aSJiří Techet 	TOKEN_UNDEFINED,
53b689382aSJiří Techet 	TOKEN_EOF,
54b689382aSJiří Techet 	TOKEN_CLOSE_PAREN,
55b689382aSJiří Techet 	TOKEN_SEMICOLON,
56b689382aSJiří Techet 	TOKEN_COLON,
57b689382aSJiří Techet 	TOKEN_COMMA,
58b689382aSJiří Techet 	TOKEN_KEYWORD,
59b689382aSJiří Techet 	TOKEN_OPEN_PAREN,
60b689382aSJiří Techet 	TOKEN_OPERATOR,
61b689382aSJiří Techet 	TOKEN_IDENTIFIER,
62b689382aSJiří Techet 	TOKEN_STRING,
63b689382aSJiří Techet 	TOKEN_PERIOD,
64b689382aSJiří Techet 	TOKEN_OPEN_CURLY,
65b689382aSJiří Techet 	TOKEN_CLOSE_CURLY,
66b689382aSJiří Techet 	TOKEN_EQUAL_SIGN,
67b689382aSJiří Techet 	TOKEN_OPEN_SQUARE,
68b689382aSJiří Techet 	TOKEN_CLOSE_SQUARE,
69b689382aSJiří Techet 	TOKEN_VARIABLE
70b689382aSJiří Techet } tokenType;
71b689382aSJiří Techet 
72b689382aSJiří Techet typedef struct {
73b689382aSJiří Techet 	tokenType		type;
74b689382aSJiří Techet 	vString *		string;
75b689382aSJiří Techet 	vString *		scope;
76b689382aSJiří Techet 	unsigned long	lineNumber;
77b689382aSJiří Techet 	MIOPos			filePosition;
78b689382aSJiří Techet 	int 			parentKind; /* KIND_GHOST_INDEX if none */
79b689382aSJiří Techet } tokenInfo;
80b689382aSJiří Techet 
81b689382aSJiří Techet 
findValidAccessType(const char * const access)82b689382aSJiří Techet static const char *findValidAccessType (const char *const access)
83b689382aSJiří Techet {
84b689382aSJiří Techet 	unsigned int i;
85b689382aSJiří Techet 	if (access == ACCESS_UNDEFINED)
86b689382aSJiří Techet 		return ACCESS_UNDEFINED; /* early out to save the for-loop if possible */
87b689382aSJiří Techet 	for (i = 0; i < ARRAY_SIZE(accessTypes); i++)
88b689382aSJiří Techet 	{
89b689382aSJiří Techet 		if (accessTypes[i] == ACCESS_UNDEFINED)
90b689382aSJiří Techet 			continue;
91b689382aSJiří Techet 		if (strcasecmp (access, accessTypes[i]) == 0)
92b689382aSJiří Techet 			return accessTypes[i];
93b689382aSJiří Techet 		i++;
94b689382aSJiří Techet 	}
95b689382aSJiří Techet 	return ACCESS_UNDEFINED;
96b689382aSJiří Techet }
97b689382aSJiří Techet 
initPowerShellEntry(tagEntryInfo * const e,const tokenInfo * const token,const powerShellKind kind,const char * const access)98b689382aSJiří Techet static void initPowerShellEntry (tagEntryInfo *const e, const tokenInfo *const token,
99b689382aSJiří Techet 								 const powerShellKind kind, const char *const access)
100b689382aSJiří Techet {
101b689382aSJiří Techet 	initTagEntry (e, vStringValue (token->string), kind);
102b689382aSJiří Techet 
103b689382aSJiří Techet 	e->lineNumber	= token->lineNumber;
104b689382aSJiří Techet 	e->filePosition	= token->filePosition;
105b689382aSJiří Techet 
106b689382aSJiří Techet 	if (access != NULL)
107b689382aSJiří Techet 		e->extensionFields.access = access;
108b689382aSJiří Techet 	if (vStringLength (token->scope) > 0)
109b689382aSJiří Techet 	{
110b689382aSJiří Techet 		int parentKind = token->parentKind;
111b689382aSJiří Techet 		Assert (parentKind >= 0);
112b689382aSJiří Techet 
113b689382aSJiří Techet 		e->extensionFields.scopeKindIndex = parentKind;
114b689382aSJiří Techet 		e->extensionFields.scopeName = vStringValue (token->scope);
115b689382aSJiří Techet 	}
116b689382aSJiří Techet }
117b689382aSJiří Techet 
makeSimplePowerShellTag(const tokenInfo * const token,const powerShellKind kind,const char * const access)118b689382aSJiří Techet static void makeSimplePowerShellTag (const tokenInfo *const token, const powerShellKind kind,
119b689382aSJiří Techet 									 const char *const access)
120b689382aSJiří Techet {
121b689382aSJiří Techet 	if (PowerShellKinds[kind].enabled)
122b689382aSJiří Techet 	{
123b689382aSJiří Techet 		tagEntryInfo e;
124b689382aSJiří Techet 
125b689382aSJiří Techet 		initPowerShellEntry (&e, token, kind, access);
126b689382aSJiří Techet 		makeTagEntry (&e);
127b689382aSJiří Techet 	}
128b689382aSJiří Techet }
129b689382aSJiří Techet 
makeFunctionTag(const tokenInfo * const token,const vString * const arglist,const char * const access)130b689382aSJiří Techet static void makeFunctionTag (const tokenInfo *const token, const vString *const arglist,
131b689382aSJiří Techet 							 const char *const access)
132b689382aSJiří Techet {
133b689382aSJiří Techet 	if (PowerShellKinds[K_FUNCTION].enabled)
134b689382aSJiří Techet 	{
135b689382aSJiří Techet 		tagEntryInfo e;
136b689382aSJiří Techet 
137b689382aSJiří Techet 		initPowerShellEntry (&e, token, K_FUNCTION, access);
138b689382aSJiří Techet 
139b689382aSJiří Techet 		if (arglist)
140b689382aSJiří Techet 			e.extensionFields.signature = vStringValue (arglist);
141b689382aSJiří Techet 
142b689382aSJiří Techet 		makeTagEntry (&e);
143b689382aSJiří Techet 	}
144b689382aSJiří Techet }
145b689382aSJiří Techet 
newToken(void)146b689382aSJiří Techet static tokenInfo *newToken (void)
147b689382aSJiří Techet {
148b689382aSJiří Techet 	tokenInfo *const token = xMalloc (1, tokenInfo);
149b689382aSJiří Techet 
150b689382aSJiří Techet 	token->type			= TOKEN_UNDEFINED;
151b689382aSJiří Techet 	token->string		= vStringNew ();
152b689382aSJiří Techet 	token->scope		= vStringNew ();
153b689382aSJiří Techet 	token->lineNumber   = getInputLineNumber ();
154b689382aSJiří Techet 	token->filePosition = getInputFilePosition ();
155b689382aSJiří Techet 	token->parentKind	= KIND_GHOST_INDEX;
156b689382aSJiří Techet 
157b689382aSJiří Techet 	return token;
158b689382aSJiří Techet }
159b689382aSJiří Techet 
deleteToken(tokenInfo * const token)160b689382aSJiří Techet static void deleteToken (tokenInfo *const token)
161b689382aSJiří Techet {
162b689382aSJiří Techet 	vStringDelete (token->string);
163b689382aSJiří Techet 	vStringDelete (token->scope);
164b689382aSJiří Techet 	eFree (token);
165b689382aSJiří Techet }
166b689382aSJiří Techet 
copyToken(tokenInfo * const dest,const tokenInfo * const src,bool scope)167b689382aSJiří Techet static void copyToken (tokenInfo *const dest, const tokenInfo *const src,
168b689382aSJiří Techet 					   bool scope)
169b689382aSJiří Techet {
170b689382aSJiří Techet 	dest->lineNumber = src->lineNumber;
171b689382aSJiří Techet 	dest->filePosition = src->filePosition;
172b689382aSJiří Techet 	dest->type = src->type;
173b689382aSJiří Techet 	vStringCopy (dest->string, src->string);
174b689382aSJiří Techet 	dest->parentKind = src->parentKind;
175b689382aSJiří Techet 	if (scope)
176b689382aSJiří Techet 		vStringCopy (dest->scope, src->scope);
177b689382aSJiří Techet }
178b689382aSJiří Techet 
addToScope(tokenInfo * const token,const vString * const extra)179b689382aSJiří Techet static void addToScope (tokenInfo *const token, const vString *const extra)
180b689382aSJiří Techet {
181b689382aSJiří Techet 	if (vStringLength (token->scope) > 0)
182b689382aSJiří Techet 		vStringCatS (token->scope, SCOPE_SEPARATOR);
183b689382aSJiří Techet 	vStringCatS (token->scope, vStringValue (extra));
184b689382aSJiří Techet }
185b689382aSJiří Techet 
isIdentChar(const int c)186b689382aSJiří Techet static bool isIdentChar (const int c)
187b689382aSJiří Techet {
188b689382aSJiří Techet 	return (isalnum (c) || c == ':' || c == '_' || c == '-' || c >= 0x80);
189b689382aSJiří Techet }
190b689382aSJiří Techet 
parseString(vString * const string,const int delimiter)191b689382aSJiří Techet static void parseString (vString *const string, const int delimiter)
192b689382aSJiří Techet {
193b689382aSJiří Techet 	while (true)
194b689382aSJiří Techet 	{
195b689382aSJiří Techet 		int c = getcFromInputFile ();
196b689382aSJiří Techet 
197b689382aSJiří Techet 		if (c == '\\' && (c = getcFromInputFile ()) != EOF)
198b689382aSJiří Techet 			vStringPut (string, (char) c);
199b689382aSJiří Techet 		else if (c == EOF || c == delimiter)
200b689382aSJiří Techet 			break;
201b689382aSJiří Techet 		else
202b689382aSJiří Techet 			vStringPut (string, (char) c);
203b689382aSJiří Techet 	}
204b689382aSJiří Techet }
205b689382aSJiří Techet 
parseIdentifier(vString * const string,const int firstChar)206b689382aSJiří Techet static void parseIdentifier (vString *const string, const int firstChar)
207b689382aSJiří Techet {
208b689382aSJiří Techet 	int c = firstChar;
209b689382aSJiří Techet 	do
210b689382aSJiří Techet 	{
211b689382aSJiří Techet 		vStringPut (string, (char) c);
212b689382aSJiří Techet 		c = getcFromInputFile ();
213b689382aSJiří Techet 	} while (isIdentChar (c));
214b689382aSJiří Techet 	ungetcToInputFile (c);
215b689382aSJiří Techet }
216b689382aSJiří Techet 
isTokenFunction(vString * const name)217b689382aSJiří Techet static bool isTokenFunction (vString *const name)
218b689382aSJiří Techet {
219b689382aSJiří Techet 	return (strcasecmp (vStringValue (name), "function") == 0 ||
220b689382aSJiří Techet 			strcasecmp (vStringValue (name), "filter") == 0);
221b689382aSJiří Techet }
222b689382aSJiří Techet 
isSpace(int c)223b689382aSJiří Techet static bool isSpace (int c)
224b689382aSJiří Techet {
225b689382aSJiří Techet 	return (c == '\t' || c == ' ' || c == '\v' ||
226b689382aSJiří Techet 			c == '\n' || c == '\r' || c == '\f');
227b689382aSJiří Techet }
228b689382aSJiří Techet 
skipWhitespaces(int c)229b689382aSJiří Techet static int skipWhitespaces (int c)
230b689382aSJiří Techet {
231b689382aSJiří Techet 	while (isSpace (c))
232b689382aSJiří Techet 		c = getcFromInputFile ();
233b689382aSJiří Techet 	return c;
234b689382aSJiří Techet }
235b689382aSJiří Techet 
skipSingleComment(void)236b689382aSJiří Techet static int skipSingleComment (void)
237b689382aSJiří Techet {
238b689382aSJiří Techet 	int c;
239b689382aSJiří Techet 	do
240b689382aSJiří Techet 	{
241b689382aSJiří Techet 		c = getcFromInputFile ();
242b689382aSJiří Techet 		if (c == '\r')
243b689382aSJiří Techet 		{
244b689382aSJiří Techet 			int next = getcFromInputFile ();
245b689382aSJiří Techet 			if (next != '\n')
246b689382aSJiří Techet 				ungetcToInputFile (next);
247b689382aSJiří Techet 			else
248b689382aSJiří Techet 				c = next;
249b689382aSJiří Techet 		}
250b689382aSJiří Techet 	} while (c != EOF && c != '\n' && c != '\r');
251b689382aSJiří Techet 	return c;
252b689382aSJiří Techet }
253b689382aSJiří Techet 
readToken(tokenInfo * const token)254b689382aSJiří Techet static void readToken (tokenInfo *const token)
255b689382aSJiří Techet {
256b689382aSJiří Techet 	int c;
257b689382aSJiří Techet 
258b689382aSJiří Techet 	token->type		= TOKEN_UNDEFINED;
259b689382aSJiří Techet 	vStringClear (token->string);
260b689382aSJiří Techet 
261b689382aSJiří Techet getNextChar:
262b689382aSJiří Techet 
263b689382aSJiří Techet 	c = getcFromInputFile ();
264b689382aSJiří Techet 	c = skipWhitespaces (c);
265b689382aSJiří Techet 
266b689382aSJiří Techet 	token->lineNumber   = getInputLineNumber ();
267b689382aSJiří Techet 	token->filePosition = getInputFilePosition ();
268b689382aSJiří Techet 
269b689382aSJiří Techet 	switch (c)
270b689382aSJiří Techet 	{
271b689382aSJiří Techet 		case EOF: token->type = TOKEN_EOF;					break;
272b689382aSJiří Techet 		case '(': token->type = TOKEN_OPEN_PAREN;			break;
273b689382aSJiří Techet 		case ')': token->type = TOKEN_CLOSE_PAREN;			break;
274b689382aSJiří Techet 		case ';': token->type = TOKEN_SEMICOLON;			break;
275b689382aSJiří Techet 		case ',': token->type = TOKEN_COMMA;				break;
276b689382aSJiří Techet 		case '.': token->type = TOKEN_PERIOD;				break;
277b689382aSJiří Techet 		case ':': token->type = TOKEN_COLON;				break;
278b689382aSJiří Techet 		case '{': token->type = TOKEN_OPEN_CURLY;			break;
279b689382aSJiří Techet 		case '}': token->type = TOKEN_CLOSE_CURLY;			break;
280b689382aSJiří Techet 		case '[': token->type = TOKEN_OPEN_SQUARE;			break;
281b689382aSJiří Techet 		case ']': token->type = TOKEN_CLOSE_SQUARE;			break;
282b689382aSJiří Techet 		case '=': token->type = TOKEN_EQUAL_SIGN;			break;
283b689382aSJiří Techet 
284b689382aSJiří Techet 		case '\'':
285b689382aSJiří Techet 		case '"':
286b689382aSJiří Techet 			token->type = TOKEN_STRING;
287b689382aSJiří Techet 			parseString (token->string, c);
288b689382aSJiří Techet 			token->lineNumber = getInputLineNumber ();
289b689382aSJiří Techet 			token->filePosition = getInputFilePosition ();
290b689382aSJiří Techet 			break;
291b689382aSJiří Techet 
292b689382aSJiří Techet 		case '<':
293b689382aSJiří Techet 		{
294b689382aSJiří Techet 			int d = getcFromInputFile ();
295b689382aSJiří Techet 			if (d == '#')
296b689382aSJiří Techet 			{
297b689382aSJiří Techet 				/* <# ... #> multiline comment */
298b689382aSJiří Techet 				do
299b689382aSJiří Techet 				{
300b689382aSJiří Techet 					c = skipToCharacterInInputFile ('#');
301b689382aSJiří Techet 					if (c != EOF)
302b689382aSJiří Techet 					{
303b689382aSJiří Techet 						c = getcFromInputFile ();
304b689382aSJiří Techet 						if (c == '>')
305b689382aSJiří Techet 							break;
306b689382aSJiří Techet 						else
307b689382aSJiří Techet 							ungetcToInputFile (c);
308b689382aSJiří Techet 					}
309b689382aSJiří Techet 				} while (c != EOF);
310b689382aSJiří Techet 				goto getNextChar;
311b689382aSJiří Techet 			}
312b689382aSJiří Techet 			else
313b689382aSJiří Techet 			{
314b689382aSJiří Techet 				ungetcToInputFile (d);
315b689382aSJiří Techet 				token->type = TOKEN_UNDEFINED;
316b689382aSJiří Techet 			}
317b689382aSJiří Techet 			break;
318b689382aSJiří Techet 		}
319b689382aSJiří Techet 
320b689382aSJiří Techet 		case '#': /* comment */
321b689382aSJiří Techet 			skipSingleComment ();
322b689382aSJiří Techet 			goto getNextChar;
323b689382aSJiří Techet 			break;
324b689382aSJiří Techet 
325b689382aSJiří Techet 		case '+':
326b689382aSJiří Techet 		case '-':
327b689382aSJiří Techet 		case '*':
328b689382aSJiří Techet 		case '/':
329b689382aSJiří Techet 		case '%':
330b689382aSJiří Techet 		{
331b689382aSJiří Techet 			int d = getcFromInputFile ();
332b689382aSJiří Techet 			if (d != '=')
333b689382aSJiří Techet 				ungetcToInputFile (d);
334b689382aSJiří Techet 			token->type = TOKEN_OPERATOR;
335b689382aSJiří Techet 			break;
336b689382aSJiří Techet 		}
337b689382aSJiří Techet 
338b689382aSJiří Techet 		case '$': /* variable start */
339b689382aSJiří Techet 		{
340b689382aSJiří Techet 			int d = getcFromInputFile ();
341b689382aSJiří Techet 			if (! isIdentChar (d))
342b689382aSJiří Techet 			{
343b689382aSJiří Techet 				ungetcToInputFile (d);
344b689382aSJiří Techet 				token->type = TOKEN_UNDEFINED;
345b689382aSJiří Techet 			}
346b689382aSJiří Techet 			else
347b689382aSJiří Techet 			{
348b689382aSJiří Techet 				parseIdentifier (token->string, d);
349b689382aSJiří Techet 				token->type = TOKEN_VARIABLE;
350b689382aSJiří Techet 			}
351b689382aSJiří Techet 			break;
352b689382aSJiří Techet 		}
353b689382aSJiří Techet 
354b689382aSJiří Techet 		default:
355b689382aSJiří Techet 			if (! isIdentChar (c))
356b689382aSJiří Techet 				token->type = TOKEN_UNDEFINED;
357b689382aSJiří Techet 			else
358b689382aSJiří Techet 			{
359b689382aSJiří Techet 				parseIdentifier (token->string, c);
360b689382aSJiří Techet 				if (isTokenFunction (token->string))
361b689382aSJiří Techet 					token->type = TOKEN_KEYWORD;
362b689382aSJiří Techet 				else
363b689382aSJiří Techet 					token->type = TOKEN_IDENTIFIER;
364b689382aSJiří Techet 			}
365b689382aSJiří Techet 			break;
366b689382aSJiří Techet 	}
367b689382aSJiří Techet }
368b689382aSJiří Techet 
369b689382aSJiří Techet static void enterScope (tokenInfo *const parentToken,
370b689382aSJiří Techet 						const vString *const extraScope,
371b689382aSJiří Techet 						const int parentKind);
372b689382aSJiří Techet 
373b689382aSJiří Techet /* strip a possible PowerShell scope specification and convert it to accessType */
parsePowerShellScope(tokenInfo * const token)374b689382aSJiří Techet static const char *parsePowerShellScope (tokenInfo *const token)
375b689382aSJiří Techet {
376b689382aSJiří Techet 	const char *access = ACCESS_UNDEFINED;
377b689382aSJiří Techet 	const char *const tokenName = vStringValue (token->string);
378b689382aSJiří Techet 	const char *powershellScopeEnd;
379b689382aSJiří Techet 
380b689382aSJiří Techet 	powershellScopeEnd = strchr (tokenName, ':');
381b689382aSJiří Techet 	if (powershellScopeEnd)
382b689382aSJiří Techet 	{
383b689382aSJiří Techet 		size_t powershellScopeLen;
384b689382aSJiří Techet 		vString * powershellScope = vStringNew ();
385b689382aSJiří Techet 
386b689382aSJiří Techet 		powershellScopeLen = (size_t)(powershellScopeEnd - tokenName);
387b689382aSJiří Techet 		/* extract the scope */
388b689382aSJiří Techet 		vStringNCopyS (powershellScope, tokenName, powershellScopeLen);
389b689382aSJiří Techet 		/* cut the resulting scope string from the identifier */
390*e852ee0eSMasatake YAMATO 		memmove (vStringValue (token->string),
391b689382aSJiří Techet 				 /* +1 to skip the leading colon */
392*e852ee0eSMasatake YAMATO 				 vStringValue (token->string) + powershellScopeLen + 1,
393b689382aSJiří Techet 				 /* +1 for the skipped leading colon and - 1 to include the trailing \0 byte */
394b689382aSJiří Techet 				 token->string->length + 1 - powershellScopeLen - 1);
395b689382aSJiří Techet 		token->string->length -= powershellScopeLen + 1;
396b689382aSJiří Techet 
397b689382aSJiří Techet 		access = findValidAccessType (vStringValue (powershellScope));
398b689382aSJiří Techet 
399b689382aSJiří Techet 		vStringDelete (powershellScope);
400b689382aSJiří Techet 	}
401b689382aSJiří Techet 	return access;
402b689382aSJiří Techet }
403b689382aSJiří Techet 
404b689382aSJiří Techet 
405b689382aSJiří Techet /* parse a function
406b689382aSJiří Techet  *
407b689382aSJiří Techet  * 	function myfunc($foo, $bar) {}
408b689382aSJiří Techet  */
parseFunction(tokenInfo * const token)409b689382aSJiří Techet static bool parseFunction (tokenInfo *const token)
410b689382aSJiří Techet {
411b689382aSJiří Techet 	bool readNext = true;
412b689382aSJiří Techet 	tokenInfo *nameFree = NULL;
413b689382aSJiří Techet 	const char *access;
414b689382aSJiří Techet 
415b689382aSJiří Techet 	readToken (token);
416b689382aSJiří Techet 
417b689382aSJiří Techet 	if (token->type != TOKEN_IDENTIFIER)
418b689382aSJiří Techet 		return false;
419b689382aSJiří Techet 
420b689382aSJiří Techet 	access = parsePowerShellScope (token);
421b689382aSJiří Techet 
422b689382aSJiří Techet 	nameFree = newToken ();
423b689382aSJiří Techet 	copyToken (nameFree, token, true);
424b689382aSJiří Techet 	readToken (token);
425b689382aSJiří Techet 
426b689382aSJiří Techet 	if (token->type == TOKEN_OPEN_PAREN)
427b689382aSJiří Techet 	{
428b689382aSJiří Techet 		vString *arglist = vStringNew ();
429b689382aSJiří Techet 		int depth = 1;
430b689382aSJiří Techet 
431b689382aSJiří Techet 		vStringPut (arglist, '(');
432b689382aSJiří Techet 		do
433b689382aSJiří Techet 		{
434b689382aSJiří Techet 			readToken (token);
435b689382aSJiří Techet 
436b689382aSJiří Techet 			switch (token->type)
437b689382aSJiří Techet 			{
438b689382aSJiří Techet 				case TOKEN_OPEN_PAREN:  depth++; break;
439b689382aSJiří Techet 				case TOKEN_CLOSE_PAREN: depth--; break;
440b689382aSJiří Techet 				default: break;
441b689382aSJiří Techet 			}
442b689382aSJiří Techet 			/* display part */
443b689382aSJiří Techet 			switch (token->type)
444b689382aSJiří Techet 			{
445b689382aSJiří Techet 				case TOKEN_CLOSE_CURLY:		vStringPut (arglist, '}');		break;
446b689382aSJiří Techet 				case TOKEN_CLOSE_PAREN:		vStringPut (arglist, ')');		break;
447b689382aSJiří Techet 				case TOKEN_CLOSE_SQUARE:	vStringPut (arglist, ']');		break;
448b689382aSJiří Techet 				case TOKEN_COLON:			vStringPut (arglist, ':');		break;
449b689382aSJiří Techet 				case TOKEN_COMMA:			vStringCatS (arglist, ", ");	break;
450b689382aSJiří Techet 				case TOKEN_EQUAL_SIGN:		vStringCatS (arglist, " = ");	break;
451b689382aSJiří Techet 				case TOKEN_OPEN_CURLY:		vStringPut (arglist, '{');		break;
452b689382aSJiří Techet 				case TOKEN_OPEN_PAREN:		vStringPut (arglist, '(');		break;
453b689382aSJiří Techet 				case TOKEN_OPEN_SQUARE:		vStringPut (arglist, '[');		break;
454b689382aSJiří Techet 				case TOKEN_PERIOD:			vStringPut (arglist, '.');		break;
455b689382aSJiří Techet 				case TOKEN_SEMICOLON:		vStringPut (arglist, ';');		break;
456b689382aSJiří Techet 				case TOKEN_STRING:			vStringCatS (arglist, "'...'");	break;
457b689382aSJiří Techet 
458b689382aSJiří Techet 				case TOKEN_IDENTIFIER:
459b689382aSJiří Techet 				case TOKEN_KEYWORD:
460b689382aSJiří Techet 				case TOKEN_VARIABLE:
461b689382aSJiří Techet 				{
462b689382aSJiří Techet 					switch (vStringLast (arglist))
463b689382aSJiří Techet 					{
464b689382aSJiří Techet 						case 0:
465b689382aSJiří Techet 						case ' ':
466b689382aSJiří Techet 						case '{':
467b689382aSJiří Techet 						case '(':
468b689382aSJiří Techet 						case '[':
469b689382aSJiří Techet 						case '.':
470b689382aSJiří Techet 							/* no need for a space between those and the identifier */
471b689382aSJiří Techet 							break;
472b689382aSJiří Techet 
473b689382aSJiří Techet 						default:
474b689382aSJiří Techet 							vStringPut (arglist, ' ');
475b689382aSJiří Techet 							break;
476b689382aSJiří Techet 					}
477b689382aSJiří Techet 					if (token->type == TOKEN_VARIABLE)
478b689382aSJiří Techet 						vStringPut (arglist, '$');
479b689382aSJiří Techet 					vStringCat (arglist, token->string);
480b689382aSJiří Techet 					break;
481b689382aSJiří Techet 				}
482b689382aSJiří Techet 
483b689382aSJiří Techet 				default: break;
484b689382aSJiří Techet 			}
485b689382aSJiří Techet 		}
486b689382aSJiří Techet 		while (token->type != TOKEN_EOF && depth > 0);
487b689382aSJiří Techet 
488b689382aSJiří Techet 		makeFunctionTag (nameFree, arglist, access);
489b689382aSJiří Techet 		vStringDelete (arglist);
490b689382aSJiří Techet 
491b689382aSJiří Techet 		readToken (token);
492b689382aSJiří Techet 	}
493b689382aSJiří Techet 	else if (token->type == TOKEN_OPEN_CURLY)
494b689382aSJiří Techet 	{	/* filters doesn't need to have an arglist */
495b689382aSJiří Techet 		makeFunctionTag (nameFree, NULL, access);
496b689382aSJiří Techet 	}
497b689382aSJiří Techet 
498b689382aSJiří Techet 	if (token->type == TOKEN_OPEN_CURLY)
499b689382aSJiří Techet 		enterScope (token, nameFree->string, K_FUNCTION);
500b689382aSJiří Techet 	else
501b689382aSJiří Techet 		readNext = false;
502b689382aSJiří Techet 
503b689382aSJiří Techet 	if (nameFree)
504b689382aSJiří Techet 		deleteToken (nameFree);
505b689382aSJiří Techet 
506b689382aSJiří Techet 	return readNext;
507b689382aSJiří Techet }
508b689382aSJiří Techet 
509b689382aSJiří Techet /* parses declarations of the form
510b689382aSJiří Techet  * 	$var = VALUE
511b689382aSJiří Techet  */
parseVariable(tokenInfo * const token)512b689382aSJiří Techet static bool parseVariable (tokenInfo *const token)
513b689382aSJiří Techet {
514b689382aSJiří Techet 	tokenInfo *name;
515b689382aSJiří Techet 	bool readNext = true;
516b689382aSJiří Techet 	const char *access;
517b689382aSJiří Techet 
518b689382aSJiří Techet 	name = newToken ();
519b689382aSJiří Techet 	copyToken (name, token, true);
520b689382aSJiří Techet 
521b689382aSJiří Techet 	readToken (token);
522b689382aSJiří Techet 	if (token->type == TOKEN_EQUAL_SIGN)
523b689382aSJiří Techet 	{
524b689382aSJiří Techet 		if (token->parentKind != K_FUNCTION)
525b689382aSJiří Techet 		{	/* ignore local variables (i.e. within a function) */
526b689382aSJiří Techet 			access = parsePowerShellScope (name);
527b689382aSJiří Techet 			makeSimplePowerShellTag (name, K_VARIABLE, access);
528b689382aSJiří Techet 			readNext = true;
529b689382aSJiří Techet 		}
530b689382aSJiří Techet 	}
531b689382aSJiří Techet 	else
532b689382aSJiří Techet 		readNext = false;
533b689382aSJiří Techet 
534b689382aSJiří Techet 	deleteToken (name);
535b689382aSJiří Techet 
536b689382aSJiří Techet 	return readNext;
537b689382aSJiří Techet }
538b689382aSJiří Techet 
enterScope(tokenInfo * const parentToken,const vString * const extraScope,const int parentKind)539b689382aSJiří Techet static void enterScope (tokenInfo *const parentToken,
540b689382aSJiří Techet 						const vString *const extraScope,
541b689382aSJiří Techet 						const int parentKind)
542b689382aSJiří Techet {
543b689382aSJiří Techet 	tokenInfo *token = newToken ();
544b689382aSJiří Techet 	int origParentKind = parentToken->parentKind;
545b689382aSJiří Techet 
546b689382aSJiří Techet 	copyToken (token, parentToken, true);
547b689382aSJiří Techet 
548b689382aSJiří Techet 	if (extraScope)
549b689382aSJiří Techet 	{
550b689382aSJiří Techet 		addToScope (token, extraScope);
551b689382aSJiří Techet 		token->parentKind = parentKind;
552b689382aSJiří Techet 	}
553b689382aSJiří Techet 
554b689382aSJiří Techet 	readToken (token);
555b689382aSJiří Techet 	while (token->type != TOKEN_EOF &&
556b689382aSJiří Techet 		   token->type != TOKEN_CLOSE_CURLY)
557b689382aSJiří Techet 	{
558b689382aSJiří Techet 		bool readNext = true;
559b689382aSJiří Techet 
560b689382aSJiří Techet 		switch (token->type)
561b689382aSJiří Techet 		{
562b689382aSJiří Techet 			case TOKEN_OPEN_CURLY:
563b689382aSJiří Techet 				enterScope (token, NULL, KIND_GHOST_INDEX);
564b689382aSJiří Techet 				break;
565b689382aSJiří Techet 
566b689382aSJiří Techet 			case TOKEN_KEYWORD:
567b689382aSJiří Techet 				readNext = parseFunction (token);
568b689382aSJiří Techet 				break;
569b689382aSJiří Techet 
570b689382aSJiří Techet 			case TOKEN_VARIABLE:
571b689382aSJiří Techet 				readNext = parseVariable (token);
572b689382aSJiří Techet 				break;
573b689382aSJiří Techet 
574b689382aSJiří Techet 			default: break;
575b689382aSJiří Techet 		}
576b689382aSJiří Techet 
577b689382aSJiří Techet 		if (readNext)
578b689382aSJiří Techet 			readToken (token);
579b689382aSJiří Techet 	}
580b689382aSJiří Techet 
581b689382aSJiří Techet 	copyToken (parentToken, token, false);
582b689382aSJiří Techet 	parentToken->parentKind = origParentKind;
583b689382aSJiří Techet 	deleteToken (token);
584b689382aSJiří Techet }
585b689382aSJiří Techet 
findPowerShellTags(void)586b689382aSJiří Techet static void findPowerShellTags (void)
587b689382aSJiří Techet {
588b689382aSJiří Techet 	tokenInfo *const token = newToken ();
589b689382aSJiří Techet 
590b689382aSJiří Techet 	do
591b689382aSJiří Techet 	{
592b689382aSJiří Techet 		enterScope (token, NULL, KIND_GHOST_INDEX);
593b689382aSJiří Techet 	}
594b689382aSJiří Techet 	while (token->type != TOKEN_EOF); /* keep going even with unmatched braces */
595b689382aSJiří Techet 
596b689382aSJiří Techet 	deleteToken (token);
597b689382aSJiří Techet }
598b689382aSJiří Techet 
PowerShellParser(void)599b689382aSJiří Techet extern parserDefinition* PowerShellParser (void)
600b689382aSJiří Techet {
601b689382aSJiří Techet 	static const char *const extensions [] = { "ps1", "psm1", NULL };
602b689382aSJiří Techet 	parserDefinition* def = parserNew ("PowerShell");
603b689382aSJiří Techet 	def->kindTable  = PowerShellKinds;
604b689382aSJiří Techet 	def->kindCount  = ARRAY_SIZE (PowerShellKinds);
605b689382aSJiří Techet 	def->extensions = extensions;
606b689382aSJiří Techet 	def->parser     = findPowerShellTags;
607b689382aSJiří Techet 	return def;
608b689382aSJiří Techet }
609