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