VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/VDScript.cpp@ 44855

Last change on this file since 44855 was 44855, checked in by vboxsync, 12 years ago

Storage/testcase: More scripting updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 92.8 KB
Line 
1/** @file
2 *
3 * VBox HDD container test utility - scripting engine.
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_vd_script VDScript - Simple scripting language for VD I/O testing.
19 *
20 * This component implements a very simple scripting language to make testing the VD
21 * library more flexible and testcases faster to implement without the need to recompile
22 * everything after it changed.
23 * The language is a small subset of the C language. It doesn't support unions, structs,
24 * global variables, typedefed types or pointers (yet). It also adds a boolean and a string type.
25 * Strings are immutable and only to print messages from the script.
26 * There are also not the default types like int or unsigned because theire ranges are architecture
27 * dependent. Instead VDScript uses uint8_t, int8_t, ... as primitive types.
28 *
29 * Why inventing a completely new language?
30 *
31 * Well it is not a completely new language to start with, it is a subset of C and the
32 * language can be extended later on to reach the full C language later on.
33 * Second, there is no static typed scripting language I like which could be implemented
34 * and finally because I can ;)
35 * The code implementing the scripting engine is designed to be easily incorporated into other
36 * code. Could be used as a scripting language for the VBox debugger for example or in the scm
37 * tool to automatically rewrite C code using the AST VDSCript generates...
38 *
39 * The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken
40 * from: http://www.csci.csusb.edu/dick/samples/c.syntax.html
41 * and: http://slps.github.com/zoo/c/iso-9899-tc3.html
42 * and: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
43 */
44#define LOGGROUP LOGGROUP_DEFAULT
45#include <iprt/string.h>
46#include <iprt/list.h>
47#include <iprt/mem.h>
48#include <iprt/ctype.h>
49#include <iprt/stream.h>
50
51#include <VBox/log.h>
52
53#include "VDScriptAst.h"
54#include "VDScriptInternal.h"
55
56/**
57 * VD script token class.
58 */
59typedef enum VDTOKENCLASS
60{
61 /** Invalid. */
62 VDTOKENCLASS_INVALID = 0,
63 /** Identifier class. */
64 VDTOKENCLASS_IDENTIFIER,
65 /** Numerical constant. */
66 VDTOKENCLASS_NUMCONST,
67 /** String constant. */
68 VDTOKENCLASS_STRINGCONST,
69 /** Operators */
70 VDTOKENCLASS_OPERATORS,
71 /** Reserved keyword */
72 VDTOKENCLASS_KEYWORD,
73 /** Punctuator */
74 VDTOKENCLASS_PUNCTUATOR,
75 /** End of stream */
76 VDTOKENCLASS_EOS,
77 /** 32bit hack. */
78 VDTOKENCLASS_32BIT_HACK = 0x7fffffff
79} VDTOKENCLASS;
80/** Pointer to a token class. */
81typedef VDTOKENCLASS *PVDTOKENCLASS;
82
83/**
84 * Keyword types.
85 */
86typedef enum VDSCRIPTTOKENKEYWORD
87{
88 VDSCRIPTTOKENKEYWORD_INVALID = 0,
89 VDSCRIPTTOKENKEYWORD_CONTINUE,
90 VDSCRIPTTOKENKEYWORD_DEFAULT,
91 VDSCRIPTTOKENKEYWORD_RETURN,
92 VDSCRIPTTOKENKEYWORD_SWITCH,
93 VDSCRIPTTOKENKEYWORD_WHILE,
94 VDSCRIPTTOKENKEYWORD_BREAK,
95 VDSCRIPTTOKENKEYWORD_FALSE,
96 VDSCRIPTTOKENKEYWORD_TRUE,
97 VDSCRIPTTOKENKEYWORD_ELSE,
98 VDSCRIPTTOKENKEYWORD_CASE,
99 VDSCRIPTTOKENKEYWORD_FOR,
100 VDSCRIPTTOKENKEYWORD_IF,
101 VDSCRIPTTOKENKEYWORD_DO,
102 VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff
103} VDSCRIPTTOKENKEYWORD;
104/** Pointer to a keyword type. */
105typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD;
106
107/**
108 * VD script token.
109 */
110typedef struct VDSCRIPTTOKEN
111{
112 /** Token class. */
113 VDTOKENCLASS enmClass;
114 /** Token position in the source buffer. */
115 VDSRCPOS Pos;
116 /** Data based on the token class. */
117 union
118 {
119 /** Identifier. */
120 struct
121 {
122 /** Pointer to the start of the identifier. */
123 const char *pszIde;
124 /** Number of characters for the identifier excluding the null terminator. */
125 size_t cchIde;
126 } Ide;
127 /** Numerical constant. */
128 struct
129 {
130 uint64_t u64;
131 } NumConst;
132 /** String constant */
133 struct
134 {
135 /** Pointer to the start of the string constant. */
136 const char *pszString;
137 /** Number of characters of the string, including the null terminator. */
138 size_t cchString;
139 } StringConst;
140 /** Operator */
141 struct
142 {
143 /** The operator string. */
144 char aszOp[4]; /** Maximum of 3 for >>= + null terminator. */
145 } Operator;
146 /** Keyword. */
147 struct
148 {
149 /** The keyword type. */
150 VDSCRIPTTOKENKEYWORD enmKeyword;
151 } Keyword;
152 /** Punctuator. */
153 struct
154 {
155 /** The punctuator in question. */
156 char chPunctuator;
157 } Punctuator;
158 } Class;
159} VDSCRIPTTOKEN;
160/** Pointer to a script token. */
161typedef VDSCRIPTTOKEN *PVDSCRIPTTOKEN;
162/** Pointer to a const script token. */
163typedef const VDSCRIPTTOKEN *PCVDSCRIPTTOKEN;
164
165/**
166 * Tokenizer state.
167 */
168typedef struct VDTOKENIZER
169{
170 /** Char buffer to read from. */
171 const char *pszInput;
172 /** Current position ininput buffer. */
173 VDSRCPOS Pos;
174 /** Token 1. */
175 VDSCRIPTTOKEN Token1;
176 /** Token 2. */
177 VDSCRIPTTOKEN Token2;
178 /** Pointer to the current active token. */
179 PVDSCRIPTTOKEN pTokenCurr;
180 /** The next token in the input stream (used for peeking). */
181 PVDSCRIPTTOKEN pTokenNext;
182} VDTOKENIZER;
183
184/**
185 * Script function which can be called.
186 */
187typedef struct VDSCRIPTFN
188{
189 /** String space core. */
190 RTSTRSPACECORE Core;
191 /** Flag whether function is defined in the source or was
192 * registered from the outside. */
193 bool fExternal;
194 /** Flag dependent data. */
195 union
196 {
197 /** Data for functions defined in the source. */
198 struct
199 {
200 /** Pointer to the AST defining the function. */
201 PVDSCRIPTASTFN pAstFn;
202 } Internal;
203 /** Data for external defined functions. */
204 struct
205 {
206 /** Callback function. */
207 PFNVDSCRIPTCALLBACK pfnCallback;
208 /** Opaque user data. */
209 void *pvUser;
210 } External;
211 } Type;
212 /** Return type of the function. */
213 VDSCRIPTTYPE enmTypeRetn;
214 /** Number of arguments the function takes. */
215 unsigned cArgs;
216 /** Variable sized array of argument types. */
217 VDSCRIPTTYPE aArgTypes[1];
218} VDSCRIPTFN;
219typedef VDSCRIPTFN *PVDSCRIPTFN;
220
221/**
222 * Operators entry.
223 */
224typedef struct VDSCRIPTOP
225{
226 /** Operator string. */
227 const char *pszOp;
228 /** Size of the operator in characters without zero terminator. */
229 size_t cchOp;
230} VDSCRIPTOP;
231/** Pointer to a script operator. */
232typedef VDSCRIPTOP *PVDSCRIPTOP;
233
234/**
235 * Known operators array, sort from higest character count to lowest.
236 */
237static VDSCRIPTOP g_aScriptOps[] =
238{
239 {">>=", 3},
240 {"<<=", 3},
241 {"+=", 2},
242 {"-=", 2},
243 {"/=", 2},
244 {"%=", 2},
245 {"&=", 2},
246 {"|=", 2},
247 {"^=", 2},
248 {"&&", 2},
249 {"||", 2},
250 {"<<", 2},
251 {">>", 2},
252 {"++", 2},
253 {"--", 2},
254 {"==", 2},
255 {"!=", 2},
256 {">=", 2},
257 {"<=", 2},
258 {"=", 1},
259 {"+", 1},
260 {"-", 1},
261 {"*", 1},
262 {"/", 1},
263 {"%", 1},
264 {"|", 1},
265 {"&", 1},
266 {"^", 1},
267 {"<", 1},
268 {">", 1},
269 {"!", 1},
270 {"~", 1}
271};
272
273/**
274 * Known punctuators.
275 */
276static VDSCRIPTOP g_aScriptPunctuators[] =
277{
278 {"(", 1},
279 {")", 1},
280 {"{", 1},
281 {"}", 1},
282 {",", 1},
283 {";", 1},
284};
285
286/**
287 * Keyword entry.
288 */
289typedef struct VDSCRIPTKEYWORD
290{
291 /** Keyword string. */
292 const char *pszKeyword;
293 /** Size of the string in characters without zero terminator. */
294 size_t cchKeyword;
295 /** Keyword type. */
296 VDSCRIPTTOKENKEYWORD enmKeyword;
297} VDSCRIPTKEYWORD;
298/** */
299typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD;
300
301/**
302 * Known keywords.
303 */
304static VDSCRIPTKEYWORD g_aKeywords[] =
305{
306 {"continue", 8, VDSCRIPTTOKENKEYWORD_CONTINUE},
307 {"default", 7, VDSCRIPTTOKENKEYWORD_DEFAULT},
308 {"return", 6, VDSCRIPTTOKENKEYWORD_RETURN},
309 {"switch", 6, VDSCRIPTTOKENKEYWORD_SWITCH},
310 {"while", 5, VDSCRIPTTOKENKEYWORD_WHILE},
311 {"break", 5, VDSCRIPTTOKENKEYWORD_BREAK},
312 {"false", 5, VDSCRIPTTOKENKEYWORD_FALSE},
313 {"true", 4, VDSCRIPTTOKENKEYWORD_TRUE},
314 {"else", 4, VDSCRIPTTOKENKEYWORD_ELSE},
315 {"case", 4, VDSCRIPTTOKENKEYWORD_CASE},
316 {"for", 3, VDSCRIPTTOKENKEYWORD_FOR},
317 {"if", 2, VDSCRIPTTOKENKEYWORD_IF},
318 {"do", 2, VDSCRIPTTOKENKEYWORD_DO}
319};
320
321static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound);
322static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt);
323static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
324static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
325
326/**
327 * Returns whether the tokenizer reached the end of the stream.
328 *
329 * @returns true if the tokenizer reached the end of stream marker
330 * false otherwise.
331 * @param pTokenizer The tokenizer state.
332 */
333DECLINLINE(bool) vdScriptTokenizerIsEos(PVDTOKENIZER pTokenizer)
334{
335 return *pTokenizer->pszInput == '\0';
336}
337
338/**
339 * Skip one character in the input stream.
340 *
341 * @returns nothing.
342 * @param pTokenizer The tokenizer state.
343 */
344DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer)
345{
346 pTokenizer->pszInput++;
347 pTokenizer->Pos.iChStart++;
348 pTokenizer->Pos.iChEnd++;
349}
350
351/**
352 * Returns the next char in the input buffer without advancing it.
353 *
354 * @returns Next character in the input buffer.
355 * @param pTokenizer The tokenizer state.
356 */
357DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer)
358{
359 return vdScriptTokenizerIsEos(pTokenizer)
360 ? '\0'
361 : *(pTokenizer->pszInput + 1);
362}
363
364/**
365 * Returns the next character in the input buffer advancing the internal
366 * position.
367 *
368 * @returns Next character in the stream.
369 * @param pTokenizer The tokenizer state.
370 */
371DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer)
372{
373 char ch;
374
375 if (vdScriptTokenizerIsEos(pTokenizer))
376 ch = '\0';
377 else
378 ch = *pTokenizer->pszInput;
379
380 return ch;
381}
382
383/**
384 * Sets a new line for the tokenizer.
385 *
386 * @returns nothing.
387 * @param pTokenizer The tokenizer state.
388 */
389DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip)
390{
391 pTokenizer->pszInput += cSkip;
392 pTokenizer->Pos.iLine++;
393 pTokenizer->Pos.iChStart = 1;
394 pTokenizer->Pos.iChEnd = 1;
395}
396
397/**
398 * Checks whether the current position in the input stream is a new line
399 * and skips it.
400 *
401 * @returns Flag whether there was a new line at the current position
402 * in the input buffer.
403 * @param pTokenizer The tokenizer state.
404 */
405DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer)
406{
407 bool fNewline = true;
408
409 if ( *pTokenizer->pszInput == '\r'
410 && vdScriptTokenizerPeekCh(pTokenizer) == '\n')
411 vdScriptTokenizerNewLine(pTokenizer, 2);
412 else if (*pTokenizer->pszInput == '\n')
413 vdScriptTokenizerNewLine(pTokenizer, 1);
414 else
415 fNewline = false;
416
417 return fNewline;
418}
419
420/**
421 * Skips a multi line comment.
422 *
423 * @returns nothing.
424 * @param pTokenizer The tokenizer state.
425 */
426DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer)
427{
428 while ( !vdScriptTokenizerIsEos(pTokenizer)
429 && *pTokenizer->pszInput != '*'
430 && vdScriptTokenizerPeekCh(pTokenizer) != '/')
431 {
432 if (!vdScriptTokenizerIsSkipNewLine(pTokenizer))
433 vdScriptTokenizerSkipCh(pTokenizer);
434 }
435
436 if (!vdScriptTokenizerIsEos(pTokenizer))
437 vdScriptTokenizerSkipCh(pTokenizer);
438 if (!vdScriptTokenizerIsEos(pTokenizer))
439 vdScriptTokenizerSkipCh(pTokenizer);
440}
441
442/**
443 * Skip all whitespace starting from the current input buffer position.
444 * Skips all present comments too.
445 *
446 * @returns nothing.
447 * @param pTokenizer The tokenizer state.
448 */
449DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer)
450{
451 while (!vdScriptTokenizerIsEos(pTokenizer))
452 {
453 while ( vdScriptTokenizerGetCh(pTokenizer) == ' '
454 || vdScriptTokenizerGetCh(pTokenizer) == '\t')
455 vdScriptTokenizerSkipCh(pTokenizer);
456
457 if ( !vdScriptTokenizerIsEos(pTokenizer)
458 && !vdScriptTokenizerIsSkipNewLine(pTokenizer))
459 {
460 if ( vdScriptTokenizerGetCh(pTokenizer) == '/'
461 && vdScriptTokenizerPeekCh(pTokenizer) == '*')
462 {
463 vdScriptTokenizerSkipCh(pTokenizer);
464 vdScriptTokenizerSkipCh(pTokenizer);
465 vdScriptTokenizerSkipComment(pTokenizer);
466 }
467 else
468 break; /* Skipped everything, next is some real content. */
469 }
470 }
471}
472
473/**
474 * Get an identifier token from the tokenizer.
475 *
476 * @returns nothing.
477 * @param pTokenizer The tokenizer state.
478 * @param pToken The uninitialized token.
479 */
480static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
481{
482 char ch;
483 size_t cchIde = 0;
484 bool fIsKeyword = false;
485 const char *pszIde = pTokenizer->pszInput;
486
487 pToken->Pos = pTokenizer->Pos;
488
489 Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' );
490
491 do
492 {
493 cchIde++;
494 vdScriptTokenizerSkipCh(pTokenizer);
495 ch = vdScriptTokenizerGetCh(pTokenizer);
496 }
497 while (RT_C_IS_ALNUM(ch) || ch == '_');
498
499 /* Check whether we got an identifier or an reserved keyword. */
500 for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++)
501 {
502 if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword))
503 {
504 fIsKeyword = true;
505 pToken->enmClass = VDTOKENCLASS_KEYWORD;
506 pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword;
507 break;
508 }
509 }
510
511 if (!fIsKeyword)
512 {
513 pToken->enmClass = VDTOKENCLASS_IDENTIFIER;
514 pToken->Class.Ide.pszIde = pszIde;
515 pToken->Class.Ide.cchIde = cchIde;
516 }
517 pToken->Pos.iChEnd += cchIde;
518}
519
520/**
521 * Get a numerical constant from the tokenizer.
522 *
523 * @returns nothing.
524 * @param pTokenizer The tokenizer state.
525 * @param pToken The uninitialized token.
526 */
527static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
528{
529 unsigned uBase = 10;
530 char *pszNext = NULL;
531
532 Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer)));
533
534 /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */
535 pToken->enmClass = VDTOKENCLASS_NUMCONST;
536 int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64);
537 Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES);
538 /** @todo: Handle number to big, throw a warning */
539
540 unsigned cchNumber = pszNext - pTokenizer->pszInput;
541 for (unsigned i = 0; i < cchNumber; i++)
542 vdScriptTokenizerSkipCh(pTokenizer);
543}
544
545/**
546 * Parses a string constant.
547 *
548 * @returns nothing.
549 * @param pTokenizer The tokenizer state.
550 * @param pToken The uninitialized token.
551 *
552 * @remarks: No escape sequences allowed at this time.
553 */
554static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
555{
556 size_t cchStr = 0;
557
558 Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"');
559 vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */
560
561 pToken->enmClass = VDTOKENCLASS_STRINGCONST;
562 pToken->Pos = pTokenizer->Pos;
563 pToken->Class.StringConst.pszString = pTokenizer->pszInput;
564
565 while (vdScriptTokenizerGetCh(pTokenizer) != '\"')
566 {
567 cchStr++;
568 vdScriptTokenizerSkipCh(pTokenizer);
569 }
570
571 vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */
572
573 pToken->Class.StringConst.cchString = cchStr;
574 pToken->Pos.iChEnd += cchStr;
575}
576
577/**
578 * Get the end of stream token.
579 *
580 * @returns nothing.
581 * @param pTokenizer The tokenizer state.
582 * @param pToken The uninitialized token.
583 */
584static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
585{
586 Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0');
587
588 pToken->enmClass = VDTOKENCLASS_EOS;
589 pToken->Pos = pTokenizer->Pos;
590}
591
592/**
593 * Get operator or punctuator token.
594 *
595 * @returns nothing.
596 * @param pTokenizer The tokenizer state.
597 * @param pToken The uninitialized token.
598 */
599static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
600{
601 bool fOpFound = false;
602
603 pToken->enmClass = VDTOKENCLASS_INVALID;
604 pToken->Pos = pTokenizer->Pos;
605
606 /*
607 * Use table based approach here, not the fastest solution but enough for our purpose
608 * for now.
609 */
610 for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++)
611 {
612 if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp))
613 {
614 memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp));
615
616 int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp);
617 AssertRC(rc);
618
619 pToken->enmClass = VDTOKENCLASS_OPERATORS;
620 pToken->Pos.iChEnd += g_aScriptOps[i].cchOp;
621
622 /** @todo: Make this prettier. */
623 for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++)
624 vdScriptTokenizerSkipCh(pTokenizer);
625 fOpFound = true;
626 break;
627 }
628 }
629
630 if (!fOpFound)
631 {
632 for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++)
633 {
634 if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp))
635 {
636 pToken->Pos.iChEnd += g_aScriptPunctuators[i].cchOp;
637 pToken->enmClass = VDTOKENCLASS_PUNCTUATOR;
638 pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp;
639
640 vdScriptTokenizerSkipCh(pTokenizer);
641 fOpFound = true;
642 break;
643 }
644 }
645 }
646}
647
648/**
649 * Read the next token from the tokenizer stream.
650 *
651 * @returns nothing.
652 * @param pTokenizer The tokenizer to read from.
653 * @param pToken Uninitialized token to fill the token data into.
654 */
655static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
656{
657 /* Skip all eventually existing whitespace, newlines and comments first. */
658 vdScriptTokenizerSkipWhitespace(pTokenizer);
659
660 char ch = vdScriptTokenizerGetCh(pTokenizer);
661 if (RT_C_IS_ALPHA(ch) || ch == '_')
662 vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken);
663 else if (RT_C_IS_DIGIT(ch))
664 vdScriptTokenizerGetNumberConst(pTokenizer, pToken);
665 else if (ch == '\"')
666 vdScriptTokenizerGetStringConst(pTokenizer, pToken);
667 else if (ch == '\0')
668 vdScriptTokenizerGetEos(pTokenizer, pToken);
669 else
670 vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken);
671}
672
673/**
674 * Create a new tokenizer.
675 *
676 * @returns Pointer to the new tokenizer state on success.
677 * NULL if out of memory.
678 * @param pszInput The input to create the tokenizer for.
679 */
680static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput)
681{
682 PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER));
683 if (pTokenizer)
684 {
685 pTokenizer->pszInput = pszInput;
686 pTokenizer->Pos.iLine = 1;
687 pTokenizer->Pos.iChStart = 1;
688 pTokenizer->Pos.iChEnd = 1;
689 pTokenizer->pTokenCurr = &pTokenizer->Token1;
690 pTokenizer->pTokenNext = &pTokenizer->Token2;
691 /* Fill the tokenizer with two first tokens. */
692 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
693 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
694 }
695
696 return pTokenizer;
697}
698
699/**
700 * Destroys a given tokenizer state.
701 *
702 * @returns nothing.
703 * @param pTokenizer The tokenizer to destroy.
704 */
705static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer)
706{
707 RTMemFree(pTokenizer);
708}
709
710/**
711 * Get the current token in the input stream.
712 *
713 * @returns Pointer to the next token in the stream.
714 * @param pTokenizer The tokenizer to destroy.
715 */
716DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer)
717{
718 return pTokenizer->pTokenCurr;
719}
720
721/**
722 * Get the class of the current token.
723 *
724 * @returns Class of the current token.
725 * @param pTokenizer The tokenizer state.
726 */
727DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer)
728{
729 return pTokenizer->pTokenCurr->enmClass;
730}
731
732/**
733 * Returns the token class of the next token in the stream.
734 *
735 * @returns Token class of the next token.
736 * @param pTokenizer The tokenizer state.
737 */
738DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer)
739{
740 return pTokenizer->pTokenNext->enmClass;
741}
742
743/**
744 * Consume the current token advancing to the next in the stream.
745 *
746 * @returns nothing.
747 * @param pTokenizer The tokenizer state.
748 */
749static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer)
750{
751 PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr;
752
753 /* Switch next token to current token and read in the next token. */
754 pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
755 pTokenizer->pTokenNext = pTokenTmp;
756 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
757}
758
759/**
760 * Check whether the next token in the input stream is a punctuator and matches the given
761 * character.
762 *
763 * @returns true if the token matched.
764 * false otherwise.
765 * @param pTokenizer The tokenizer state.
766 * @param chCheck The punctuator to check against.
767 */
768static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
769{
770 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
771
772 if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR
773 && pToken->Class.Punctuator.chPunctuator == chCheck)
774 return true;
775
776 return false;
777}
778
779/**
780 * Check whether the next token in the input stream is a punctuator and matches the given
781 * character and skips it.
782 *
783 * @returns true if the token matched and was skipped.
784 * false otherwise.
785 * @param pTokenizer The tokenizer state.
786 * @param chCheck The punctuator to check against.
787 */
788static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
789{
790 bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck);
791 if (fEqual)
792 vdScriptTokenizerConsume(pTokenizer);
793
794 return fEqual;
795}
796
797/**
798 * Check whether the next token in the input stream is a keyword and matches the given
799 * keyword.
800 *
801 * @returns true if the token matched.
802 * false otherwise.
803 * @param pTokenizer The tokenizer state.
804 * @param enmKey The keyword to check against.
805 */
806static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
807{
808 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
809
810 if ( pToken->enmClass == VDTOKENCLASS_KEYWORD
811 && pToken->Class.Keyword.enmKeyword == enmKeyword)
812 return true;
813
814 return false;
815}
816
817/**
818 * Check whether the next token in the input stream is a keyword and matches the given
819 * keyword and skips it.
820 *
821 * @returns true if the token matched and was skipped.
822 * false otherwise.
823 * @param pTokenizer The tokenizer state.
824 * @param enmKey The keyword to check against.
825 */
826static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
827{
828 bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword);
829 if (fEqual)
830 vdScriptTokenizerConsume(pTokenizer);
831
832 return fEqual;
833}
834
835/**
836 * Check whether the next token in the input stream is a keyword and matches the given
837 * keyword.
838 *
839 * @returns true if the token matched.
840 * false otherwise.
841 * @param pTokenizer The tokenizer state.
842 * @param pszOp The operation to check against.
843 */
844static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
845{
846 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
847
848 if ( pToken->enmClass == VDTOKENCLASS_OPERATORS
849 && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp))
850 return true;
851
852 return false;
853}
854
855/**
856 * Check whether the next token in the input stream is an operator and matches the given
857 * keyword and skips it.
858 *
859 * @returns true if the token matched and was skipped.
860 * false otherwise.
861 * @param pTokenizer The tokenizer state.
862 * @param pszOp The operation to check against.
863 */
864static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
865{
866 bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp);
867 if (fEqual)
868 vdScriptTokenizerConsume(pTokenizer);
869
870 return fEqual;
871}
872
873/**
874 * Record an error while parsing.
875 *
876 * @returns VBox status code passed.
877 */
878static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
879{
880 NOREF(pThis);
881 NOREF(pszFmt);
882 RTPrintf(pszFmt);
883 return rc;
884}
885
886/**
887 * Puts the next identifier AST node on the stack.
888 *
889 * @returns VBox status code.
890 * @param pThis The script context.
891 * @param ppAstNodeIde Where to store the identifier AST node on success.
892 */
893static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde)
894{
895 int rc = VINF_SUCCESS;
896 PCVDSCRIPTTOKEN pToken;
897
898 LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde));
899
900 pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
901 if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER)
902 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n");
903 else
904 {
905 /* Create new AST node and push onto stack. */
906 PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde);
907 if (pAstNodeIde)
908 {
909 rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde);
910 AssertRC(rc);
911 pAstNodeIde->cchIde = pToken->Class.Ide.cchIde;
912
913 *ppAstNodeIde = pAstNodeIde;
914 vdScriptTokenizerConsume(pThis->pTokenizer);
915 }
916 else
917 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n");
918 }
919
920 LogFlowFunc(("returns %Rrc\n", rc));
921 return rc;
922}
923
924/**
925 * Parse a primary expression.
926 *
927 * @returns VBox status code.
928 * @param pThis The script context.
929 * @param ppAstNodeExpr Where to store the primary expression on success.
930 */
931static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
932{
933 int rc = VINF_SUCCESS;
934
935 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
936
937 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
938 {
939 rc = vdScriptParseExpression(pThis, ppAstNodeExpr);
940 if (RT_SUCCESS(rc)
941 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
942 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
943 }
944 else
945 {
946 PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
947 if (pExpr)
948 {
949 if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
950 {
951 PVDSCRIPTASTIDE pIde = NULL;
952 rc = vdScriptParseIde(pThis, &pIde);
953 if (RT_SUCCESS(rc))
954 {
955 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER;
956 pExpr->pIde = pIde;
957 }
958 }
959 else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST)
960 {
961 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
962 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST;
963 pExpr->u64 = pToken->Class.NumConst.u64;
964 vdScriptTokenizerConsume(pThis->pTokenizer);
965 }
966 else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST)
967 {
968 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
969 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST;
970 pExpr->pszStr = RTStrDup(pToken->Class.StringConst.pszString);
971 vdScriptTokenizerConsume(pThis->pTokenizer);
972
973 if (!pExpr->pszStr)
974 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n");
975 }
976 else
977 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n");
978
979 if (RT_FAILURE(rc))
980 vdScriptAstNodeFree(&pExpr->Core);
981 else
982 *ppAstNodeExpr = pExpr;
983 }
984 else
985 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
986 }
987
988 LogFlowFunc(("returns rc=%Rrc\n", rc));
989 return rc;
990}
991
992/**
993 * Parse an argument list for a function call.
994 *
995 * @returns VBox status code.
996 * @param pThis The script context.
997 * @param pFnCall The function call AST node.
998 */
999static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall)
1000{
1001 int rc = VINF_SUCCESS;
1002 PVDSCRIPTASTEXPR pExpr = NULL;
1003
1004 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1005
1006 rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
1007 if (RT_SUCCESS(rc))
1008 {
1009 RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
1010 while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
1011 {
1012 rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
1013 if (RT_SUCCESS(rc))
1014 RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
1015 else
1016 break;
1017 }
1018 if ( RT_SUCCESS(rc)
1019 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
1020 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
1021 }
1022
1023 LogFlowFunc(("returns rc=%Rrc\n", rc));
1024 return rc;
1025}
1026
1027/**
1028 * Parse a postfix expression.
1029 *
1030 * @returns VBox status code.
1031 * @param pThis The script context.
1032 * @param ppAstNodeExpr Where to store the expression AST node on success.
1033 *
1034 * @note Syntax:
1035 * postfix-expression:
1036 * primary-expression
1037 * postfix-expression ( argument-expression )
1038 * postfix-expression ++
1039 * postfix-expression --
1040 */
1041static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1042{
1043 int rc = VINF_SUCCESS;
1044 PVDSCRIPTASTEXPR pExpr = NULL;
1045
1046 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1047
1048 rc = vdScriptParsePrimaryExpression(pThis, &pExpr);
1049 if (RT_SUCCESS(rc))
1050 {
1051 while (true)
1052 {
1053 PVDSCRIPTASTEXPR pExprNew = NULL;
1054
1055 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
1056 {
1057 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1058 if (pExprNew)
1059 {
1060 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT;
1061 pExprNew->pExpr = pExpr;
1062 pExpr = pExprNew;
1063 }
1064 else
1065 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1066 }
1067 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
1068 {
1069 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1070 if (pExprNew)
1071 {
1072 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT;
1073 pExprNew->pExpr = pExpr;
1074 pExpr = pExprNew;
1075 }
1076 else
1077 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1078 }
1079 else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
1080 {
1081 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1082 if (pExprNew)
1083 {
1084 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL;
1085 RTListInit(&pExprNew->FnCall.ListArgs);
1086 if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
1087 rc = vdScriptParseFnCallArgumentList(pThis, pExprNew);
1088 pExprNew->FnCall.pFnIde = pExpr;
1089 pExpr = pExprNew;
1090 }
1091 else
1092 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1093 }
1094 else
1095 break;
1096
1097 if (RT_FAILURE(rc))
1098 break;
1099 }
1100
1101 if (RT_SUCCESS(rc))
1102 *ppAstNodeExpr = pExpr;
1103 else
1104 vdScriptAstNodeFree(&pExpr->Core);
1105 }
1106
1107 LogFlowFunc(("returns rc=%Rrc\n", rc));
1108 return rc;
1109}
1110
1111/**
1112 * Parse an unary expression.
1113 *
1114 * @returns VBox status code.
1115 * @param pThis The script context.
1116 * @param ppAstNodeExpr Where to store the expression AST node on success.
1117 *
1118 * @note Syntax:
1119 * unary-expression:
1120 * postfix-expression
1121 * ++ unary-expression
1122 * -- unary-expression
1123 * + unary-expression
1124 * - unary-expression
1125 * ~ unary-expression
1126 * ! unary-expression
1127 */
1128static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1129{
1130 int rc = VINF_SUCCESS;
1131 PVDSCRIPTASTEXPR pExpr = NULL;
1132 PVDSCRIPTASTEXPR pExprTop = NULL;
1133
1134 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1135
1136 while (true)
1137 {
1138 bool fQuit = false;
1139 PVDSCRIPTASTEXPR pExprNew = NULL;
1140
1141 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
1142 {
1143 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1144 if (pExprNew)
1145 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT;
1146 else
1147 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1148 }
1149 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
1150 {
1151 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1152 if (pExprNew)
1153 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT;
1154 else
1155 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1156 }
1157 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
1158 {
1159 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1160 if (pExprNew)
1161 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN;
1162 else
1163 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1164 }
1165 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
1166 {
1167 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1168 if (pExprNew)
1169 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN;
1170 else
1171 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1172 }
1173 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~"))
1174 {
1175 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1176 if (pExprNew)
1177 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT;
1178 else
1179 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1180 }
1181 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!"))
1182 {
1183 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1184 if (pExprNew)
1185 pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE;
1186 else
1187 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1188 }
1189 else
1190 {
1191 /* Must be a postfix expression. */
1192 rc = vdScriptParsePostfixExpression(pThis, &pExprNew);
1193 fQuit = true;
1194 }
1195
1196 if (RT_SUCCESS(rc))
1197 {
1198 if (!pExprTop)
1199 {
1200 pExprTop = pExprNew;
1201 pExpr = pExprNew;
1202 }
1203 else
1204 {
1205 pExpr->pExpr = pExprNew;
1206 pExpr = pExprNew;
1207 }
1208 if (fQuit)
1209 break;
1210 }
1211 else
1212 break;
1213 }
1214
1215 if (RT_SUCCESS(rc))
1216 *ppAstNodeExpr = pExprTop;
1217 else if (pExprTop)
1218 vdScriptAstNodeFree(&pExprTop->Core);
1219
1220 LogFlowFunc(("returns rc=%Rrc\n", rc));
1221 return rc;
1222}
1223
1224/**
1225 * Parse a multiplicative expression.
1226 *
1227 * @returns VBox status code.
1228 * @param pThis The script context.
1229 * @param ppAstNodeExpr Where to store the expression AST node on success.
1230 *
1231 * @note Syntax:
1232 * multiplicative-expression:
1233 * unary-expression
1234 * multiplicative-expression * unary-expression
1235 * multiplicative-expression / unary-expression
1236 * multiplicative-expression % unary-expression
1237 */
1238static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1239{
1240 int rc = VINF_SUCCESS;
1241 PVDSCRIPTASTEXPR pExpr = NULL;
1242
1243 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1244
1245 rc = vdScriptParseUnaryExpression(pThis, &pExpr);
1246 if (RT_SUCCESS(rc))
1247 {
1248 PVDSCRIPTASTEXPR pExprNew = NULL;
1249 while (RT_SUCCESS(rc))
1250 {
1251 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*"))
1252 {
1253 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1254 if (pExprNew)
1255 pExprNew->enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION;
1256 else
1257 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1258 }
1259 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/"))
1260 {
1261 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1262 if (pExprNew)
1263 pExprNew->enmType = VDSCRIPTEXPRTYPE_DIVISION;
1264 else
1265 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1266 }
1267 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%"))
1268 {
1269 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1270 if (pExprNew)
1271 pExprNew->enmType = VDSCRIPTEXPRTYPE_MODULUS;
1272 else
1273 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1274 }
1275 else
1276 break;
1277
1278 pExprNew->BinaryOp.pLeftExpr = pExpr;
1279 pExpr = pExprNew;
1280 rc = vdScriptParseUnaryExpression(pThis, &pExprNew);
1281 if (RT_SUCCESS(rc))
1282 pExpr->BinaryOp.pRightExpr = pExprNew;
1283 }
1284
1285 if (RT_SUCCESS(rc))
1286 *ppAstNodeExpr = pExpr;
1287 else
1288 vdScriptAstNodeFree(&pExpr->Core);
1289 }
1290
1291 LogFlowFunc(("returns rc=%Rrc\n", rc));
1292 return rc;
1293}
1294
1295/**
1296 * Parse a additive expression.
1297 *
1298 * @returns VBox status code.
1299 * @param pThis The script context.
1300 * @param ppAstNodeExpr Where to store the expression AST node on success.
1301 *
1302 * @note Syntax:
1303 * additive-expression:
1304 * multiplicative-expression
1305 * additive-expression + multiplicative-expression
1306 * additive-expression - multiplicative-expression
1307 */
1308static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1309{
1310 int rc = VINF_SUCCESS;
1311 PVDSCRIPTASTEXPR pExpr = NULL;
1312
1313 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1314
1315 rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr);
1316 if (RT_SUCCESS(rc))
1317 {
1318 PVDSCRIPTASTEXPR pExprNew = NULL;
1319 while (RT_SUCCESS(rc))
1320 {
1321 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
1322 {
1323 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1324 if (pExprNew)
1325 pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION;
1326 else
1327 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1328 }
1329 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
1330 {
1331 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1332 if (pExprNew)
1333 pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION;
1334 else
1335 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1336 }
1337 else
1338 break;
1339
1340 pExprNew->BinaryOp.pLeftExpr = pExpr;
1341 pExpr = pExprNew;
1342 rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew);
1343 if (RT_SUCCESS(rc))
1344 pExpr->BinaryOp.pRightExpr = pExprNew;
1345 }
1346
1347 if (RT_SUCCESS(rc))
1348 *ppAstNodeExpr = pExpr;
1349 else
1350 vdScriptAstNodeFree(&pExpr->Core);
1351 }
1352
1353 LogFlowFunc(("returns rc=%Rrc\n", rc));
1354 return rc;
1355}
1356
1357/**
1358 * Parse a shift expression.
1359 *
1360 * @returns VBox status code.
1361 * @param pThis The script context.
1362 * @param ppAstNodeExpr Where to store the expression AST node on success.
1363 *
1364 * @note Syntax:
1365 * shift-expression:
1366 * additive-expression
1367 * shift-expression << additive-expression
1368 * shift-expression >> additive-expression
1369 */
1370static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1371{
1372 int rc = VINF_SUCCESS;
1373 PVDSCRIPTASTEXPR pExpr = NULL;
1374
1375 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1376
1377 rc = vdScriptParseAdditiveExpression(pThis, &pExpr);
1378 if (RT_SUCCESS(rc))
1379 {
1380 PVDSCRIPTASTEXPR pExprNew = NULL;
1381 while (RT_SUCCESS(rc))
1382 {
1383 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<"))
1384 {
1385 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1386 if (pExprNew)
1387 pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL;
1388 else
1389 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1390 }
1391 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>"))
1392 {
1393 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1394 if (pExprNew)
1395 pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR;
1396 else
1397 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1398 }
1399 else
1400 break;
1401
1402 pExprNew->BinaryOp.pLeftExpr = pExpr;
1403 pExpr = pExprNew;
1404 rc = vdScriptParseAdditiveExpression(pThis, &pExprNew);
1405 if (RT_SUCCESS(rc))
1406 pExpr->BinaryOp.pRightExpr = pExprNew;
1407 }
1408
1409 if (RT_SUCCESS(rc))
1410 *ppAstNodeExpr = pExpr;
1411 else
1412 vdScriptAstNodeFree(&pExpr->Core);
1413 }
1414
1415 LogFlowFunc(("returns rc=%Rrc\n", rc));
1416 return rc;
1417}
1418
1419/**
1420 * Parse a relational expression.
1421 *
1422 * @returns VBox status code.
1423 * @param pThis The script context.
1424 * @param ppAstNodeExpr Where to store the expression AST node on success.
1425 *
1426 * @note Syntax:
1427 * relational-expression:
1428 * shift-expression
1429 * relational-expression < shift-expression
1430 * relational-expression > shift-expression
1431 * relational-expression >= shift-expression
1432 * relational-expression <= shift-expression
1433 */
1434static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1435{
1436 int rc = VINF_SUCCESS;
1437 PVDSCRIPTASTEXPR pExpr = NULL;
1438
1439 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1440
1441 rc = vdScriptParseShiftExpression(pThis, &pExpr);
1442 if (RT_SUCCESS(rc))
1443 {
1444 PVDSCRIPTASTEXPR pExprNew = NULL;
1445 while (RT_SUCCESS(rc))
1446 {
1447 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<"))
1448 {
1449 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1450 if (pExprNew)
1451 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER;
1452 else
1453 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1454 }
1455 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">"))
1456 {
1457 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1458 if (pExprNew)
1459 pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER;
1460 else
1461 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1462 }
1463 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">="))
1464 {
1465 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1466 if (pExprNew)
1467 pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL;
1468 else
1469 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1470 }
1471 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<="))
1472 {
1473 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1474 if (pExprNew)
1475 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL;
1476 else
1477 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1478 }
1479 else
1480 break;
1481
1482 pExprNew->BinaryOp.pLeftExpr = pExpr;
1483 pExpr = pExprNew;
1484 rc = vdScriptParseShiftExpression(pThis, &pExprNew);
1485 if (RT_SUCCESS(rc))
1486 pExpr->BinaryOp.pRightExpr = pExprNew;
1487 }
1488
1489 if (RT_SUCCESS(rc))
1490 *ppAstNodeExpr = pExpr;
1491 else
1492 vdScriptAstNodeFree(&pExpr->Core);
1493 }
1494
1495 LogFlowFunc(("returns rc=%Rrc\n", rc));
1496 return rc;
1497}
1498
1499/**
1500 * Parse a equality expression.
1501 *
1502 * @returns VBox status code.
1503 * @param pThis The script context.
1504 * @param ppAstNodeExpr Where to store the expression AST node on success.
1505 *
1506 * @note Syntax:
1507 * equality-expression:
1508 * relational-expression
1509 * equality-expression == relational-expression
1510 * equality-expression != relational-expression
1511 */
1512static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1513{
1514 int rc = VINF_SUCCESS;
1515 PVDSCRIPTASTEXPR pExpr = NULL;
1516
1517 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1518
1519 rc = vdScriptParseRelationalExpression(pThis, &pExpr);
1520 if (RT_SUCCESS(rc))
1521 {
1522 PVDSCRIPTASTEXPR pExprNew = NULL;
1523 while (RT_SUCCESS(rc))
1524 {
1525 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=="))
1526 {
1527 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1528 if (pExprNew)
1529 pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
1530 else
1531 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1532 }
1533 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!="))
1534 {
1535 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1536 if (pExprNew)
1537 pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL;
1538 else
1539 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1540 }
1541 else
1542 break;
1543
1544 pExprNew->BinaryOp.pLeftExpr = pExpr;
1545 pExpr = pExprNew;
1546 rc = vdScriptParseRelationalExpression(pThis, &pExprNew);
1547 if (RT_SUCCESS(rc))
1548 pExpr->BinaryOp.pRightExpr = pExprNew;
1549 }
1550
1551 if (RT_SUCCESS(rc))
1552 *ppAstNodeExpr = pExpr;
1553 else
1554 vdScriptAstNodeFree(&pExpr->Core);
1555 }
1556
1557 LogFlowFunc(("returns rc=%Rrc\n", rc));
1558 return rc;
1559}
1560
1561/**
1562 * Parse a bitwise and expression.
1563 *
1564 * @returns VBox status code.
1565 * @param pThis The script context.
1566 * @param ppAstNodeExpr Where to store the expression AST node on success.
1567 *
1568 * @note Syntax:
1569 * and-expression:
1570 * equality-expression
1571 * and-expression & equality-expression
1572 */
1573static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1574{
1575 int rc = VINF_SUCCESS;
1576 PVDSCRIPTASTEXPR pExpr = NULL;
1577
1578 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1579
1580 rc = vdScriptParseEqualityExpression(pThis, &pExpr);
1581 if (RT_SUCCESS(rc))
1582 {
1583 PVDSCRIPTASTEXPR pExprNew = NULL;
1584 while ( RT_SUCCESS(rc)
1585 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&"))
1586 {
1587 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1588 if (pExprNew)
1589 {
1590 pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
1591 pExprNew->BinaryOp.pLeftExpr = pExpr;
1592 pExpr = pExprNew;
1593 rc = vdScriptParseEqualityExpression(pThis, &pExprNew);
1594 if (RT_SUCCESS(rc))
1595 pExpr->BinaryOp.pRightExpr = pExprNew;
1596 }
1597 else
1598 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1599 }
1600
1601 if (RT_SUCCESS(rc))
1602 *ppAstNodeExpr = pExpr;
1603 else
1604 vdScriptAstNodeFree(&pExpr->Core);
1605 }
1606
1607 LogFlowFunc(("returns rc=%Rrc\n", rc));
1608 return rc;
1609}
1610
1611/**
1612 * Parse a bitwise xor expression.
1613 *
1614 * @returns VBox status code.
1615 * @param pThis The script context.
1616 * @param ppAstNodeExpr Where to store the expression AST node on success.
1617 *
1618 * @note Syntax:
1619 * xor-expression:
1620 * and-expression
1621 * xor-expression ^ equality-expression
1622 */
1623static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1624{
1625 int rc = VINF_SUCCESS;
1626 PVDSCRIPTASTEXPR pExpr = NULL;
1627
1628 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1629
1630 rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr);
1631 if (RT_SUCCESS(rc))
1632 {
1633 PVDSCRIPTASTEXPR pExprNew = NULL;
1634 while ( RT_SUCCESS(rc)
1635 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^"))
1636 {
1637 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1638 if (pExprNew)
1639 {
1640 pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR;
1641 pExprNew->BinaryOp.pLeftExpr = pExpr;
1642 pExpr = pExprNew;
1643 rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew);
1644 if (RT_SUCCESS(rc))
1645 pExpr->BinaryOp.pRightExpr = pExprNew;
1646 }
1647 else
1648 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1649 }
1650
1651 if (RT_SUCCESS(rc))
1652 *ppAstNodeExpr = pExpr;
1653 else
1654 vdScriptAstNodeFree(&pExpr->Core);
1655 }
1656
1657 LogFlowFunc(("returns rc=%Rrc\n", rc));
1658 return rc;
1659}
1660
1661/**
1662 * Parse a bitwise or expression.
1663 *
1664 * @returns VBox status code.
1665 * @param pThis The script context.
1666 * @param ppAstNodeExpr Where to store the expression AST node on success.
1667 *
1668 * @note Syntax:
1669 * or-expression:
1670 * xor-expression
1671 * or-expression | xor-expression
1672 */
1673static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1674{
1675 int rc = VINF_SUCCESS;
1676 PVDSCRIPTASTEXPR pExpr = NULL;
1677
1678 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1679
1680 rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr);
1681 if (RT_SUCCESS(rc))
1682 {
1683 PVDSCRIPTASTEXPR pExprNew = NULL;
1684 while ( RT_SUCCESS(rc)
1685 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|"))
1686 {
1687 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1688 if (pExprNew)
1689 {
1690 pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR;
1691 pExprNew->BinaryOp.pLeftExpr = pExpr;
1692 pExpr = pExprNew;
1693 rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew);
1694 if (RT_SUCCESS(rc))
1695 pExpr->BinaryOp.pRightExpr = pExprNew;
1696 }
1697 else
1698 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1699 }
1700
1701 if (RT_SUCCESS(rc))
1702 *ppAstNodeExpr = pExpr;
1703 else
1704 vdScriptAstNodeFree(&pExpr->Core);
1705 }
1706
1707 LogFlowFunc(("returns rc=%Rrc\n", rc));
1708 return rc;
1709}
1710
1711/**
1712 * Parse a logical and expression.
1713 *
1714 * @returns VBox status code.
1715 * @param pThis The script context.
1716 * @param ppAstNodeExpr Where to store the expression AST node on success.
1717 *
1718 * @note Syntax:
1719 * logical-and-expression:
1720 * or-expression
1721 * logical-and-expression | or-expression
1722 */
1723static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1724{
1725 int rc = VINF_SUCCESS;
1726 PVDSCRIPTASTEXPR pExpr = NULL;
1727
1728 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1729
1730 rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr);
1731 if (RT_SUCCESS(rc))
1732 {
1733 PVDSCRIPTASTEXPR pExprNew = NULL;
1734 while ( RT_SUCCESS(rc)
1735 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&"))
1736 {
1737 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1738 if (pExprNew)
1739 {
1740 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND;
1741 pExprNew->BinaryOp.pLeftExpr = pExpr;
1742 pExpr = pExprNew;
1743 rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew);
1744 if (RT_SUCCESS(rc))
1745 pExpr->BinaryOp.pRightExpr = pExprNew;
1746 }
1747 else
1748 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1749 }
1750
1751 if (RT_SUCCESS(rc))
1752 *ppAstNodeExpr = pExpr;
1753 else
1754 vdScriptAstNodeFree(&pExpr->Core);
1755 }
1756
1757 LogFlowFunc(("returns rc=%Rrc\n", rc));
1758 return rc;
1759}
1760
1761/**
1762 * Parse a logical or expression.
1763 *
1764 * @returns VBox status code.
1765 * @param pThis The script context.
1766 * @param ppAstNodeExpr Where to store the expression AST node on success.
1767 *
1768 * @note Syntax:
1769 * logical-or-expression:
1770 * logical-and-expression
1771 * logical-or-expression | logical-and-expression
1772 */
1773static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1774{
1775 int rc = VINF_SUCCESS;
1776 PVDSCRIPTASTEXPR pExpr = NULL;
1777
1778 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1779
1780 rc = vdScriptParseLogicalAndExpression(pThis, &pExpr);
1781 if (RT_SUCCESS(rc))
1782 {
1783 PVDSCRIPTASTEXPR pExprNew = NULL;
1784 while ( RT_SUCCESS(rc)
1785 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||"))
1786 {
1787 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1788 if (pExprNew)
1789 {
1790 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR;
1791 pExprNew->BinaryOp.pLeftExpr = pExpr;
1792 pExpr = pExprNew;
1793 rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew);
1794 if (RT_SUCCESS(rc))
1795 pExpr->BinaryOp.pRightExpr = pExprNew;
1796 }
1797 else
1798 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1799 }
1800
1801 if (RT_SUCCESS(rc))
1802 *ppAstNodeExpr = pExpr;
1803 else
1804 vdScriptAstNodeFree(&pExpr->Core);
1805 }
1806
1807 LogFlowFunc(("returns rc=%Rrc\n", rc));
1808 return rc;
1809}
1810
1811/**
1812 * Parse a conditional expression.
1813 *
1814 * @returns VBox status code.
1815 * @param pThis The script context.
1816 * @param ppAstNodeExpr Where to store the expression AST node on success.
1817 *
1818 * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression
1819 * so a conditional expression is equal to a logical-or-expression.
1820 */
1821static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1822{
1823 return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr);
1824}
1825
1826/**
1827 * Parse an assignment expression.
1828 *
1829 * @returns VBox status code.
1830 * @param pThis The script context.
1831 * @param ppAstNodeExpr Where to store the expression AST node on success.
1832 *
1833 * @note Syntax:
1834 * assignment-expression:
1835 * conditional-expression
1836 * unary-expression assignment-operator assignment-expression
1837 */
1838static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1839{
1840 int rc = VINF_SUCCESS;
1841 PVDSCRIPTASTEXPR pExpr;
1842
1843 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1844
1845 rc = vdScriptParseLogicalOrExpression(pThis, &pExpr);
1846 if (RT_SUCCESS(rc))
1847 {
1848 PVDSCRIPTASTEXPR pExprNew = NULL;
1849 while (RT_SUCCESS(rc))
1850 {
1851 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "="))
1852 {
1853 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1854 if (pExprNew)
1855 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN;
1856 else
1857 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1858 }
1859 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*="))
1860 {
1861 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1862 if (pExprNew)
1863 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT;
1864 else
1865 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1866 }
1867 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/="))
1868 {
1869 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1870 if (pExprNew)
1871 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV;
1872 else
1873 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1874 }
1875 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%="))
1876 {
1877 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1878 if (pExprNew)
1879 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD;
1880 else
1881 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1882 }
1883 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+="))
1884 {
1885 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1886 if (pExprNew)
1887 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD;
1888 else
1889 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1890 }
1891 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-="))
1892 {
1893 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1894 if (pExprNew)
1895 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB;
1896 else
1897 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1898 }
1899 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<="))
1900 {
1901 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1902 if (pExprNew)
1903 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL;
1904 else
1905 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1906 }
1907 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>="))
1908 {
1909 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1910 if (pExprNew)
1911 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR;
1912 else
1913 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1914 }
1915 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&="))
1916 {
1917 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1918 if (pExprNew)
1919 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND;
1920 else
1921 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1922 }
1923 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^="))
1924 {
1925 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1926 if (pExprNew)
1927 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR;
1928 else
1929 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1930 }
1931 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|="))
1932 {
1933 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1934 if (pExprNew)
1935 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR;
1936 else
1937 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1938 }
1939 else
1940 break;
1941
1942 pExprNew->BinaryOp.pLeftExpr = pExpr;
1943 pExpr = pExprNew;
1944 rc = vdScriptParseLogicalOrExpression(pThis, &pExprNew);
1945 if (RT_SUCCESS(rc))
1946 pExpr->BinaryOp.pRightExpr = pExprNew;
1947 }
1948
1949 if (RT_SUCCESS(rc))
1950 *ppAstNodeExpr = pExpr;
1951 else
1952 vdScriptAstNodeFree(&pExpr->Core);
1953 }
1954
1955 LogFlowFunc(("returns rc=%Rrc\n", rc));
1956 return rc;
1957}
1958
1959/**
1960 * Parse an expression.
1961 *
1962 * @returns VBox status code.
1963 * @param pThis The script context.
1964 * @param ppAstNodeExpr Where to store the expression AST node on success.
1965 *
1966 * @note Syntax:
1967 * expression:
1968 * assignment-expression
1969 * expression , assignment-expression
1970 */
1971static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1972{
1973 int rc = VINF_SUCCESS;
1974 PVDSCRIPTASTEXPR pAssignExpr = NULL;
1975
1976 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
1977
1978 rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
1979 if ( RT_SUCCESS(rc)
1980 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
1981 {
1982 PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1983 if (pListAssignExpr)
1984 {
1985 pListAssignExpr->enmType = VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST;
1986 RTListInit(&pListAssignExpr->ListExpr);
1987 RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
1988 do
1989 {
1990 rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
1991 if (RT_SUCCESS(rc))
1992 RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
1993 } while ( RT_SUCCESS(rc)
1994 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','));
1995
1996 if (RT_FAILURE(rc))
1997 vdScriptAstNodeFree(&pListAssignExpr->Core);
1998 else
1999 *ppAstNodeExpr = pListAssignExpr;
2000 }
2001 else
2002 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2003 }
2004 else if (RT_SUCCESS(rc))
2005 *ppAstNodeExpr = pAssignExpr;
2006
2007 LogFlowFunc(("returns rc=%Rrc\n", rc));
2008 return rc;
2009}
2010
2011/**
2012 * Parse an if statement.
2013 *
2014 * @returns VBox status code.
2015 * @param pThis The script context.
2016 * @param pAstNodeIf Uninitialized if AST node.
2017 *
2018 * @note The caller skipped the "if" token already.
2019 */
2020static int vdScriptParseIf(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIF pAstNodeIf)
2021{
2022 int rc = VINF_SUCCESS;
2023
2024 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2025 {
2026 PVDSCRIPTASTEXPR pCondExpr = NULL;
2027 rc = vdScriptParseExpression(pThis, &pCondExpr);
2028 if (RT_SUCCESS(rc))
2029 {
2030 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2031 {
2032 PVDSCRIPTASTSTMT pStmt = NULL;
2033 PVDSCRIPTASTSTMT pElseStmt = NULL;
2034 rc = vdScriptParseStatement(pThis, &pStmt);
2035 if ( RT_SUCCESS(rc)
2036 && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_ELSE))
2037 rc = vdScriptParseStatement(pThis, &pElseStmt);
2038
2039 if (RT_SUCCESS(rc))
2040 {
2041 pAstNodeIf->pCond = pCondExpr;
2042 pAstNodeIf->pTrueStmt = pStmt;
2043 pAstNodeIf->pElseStmt = pElseStmt;
2044 }
2045 else if (pStmt)
2046 vdScriptAstNodeFree(&pStmt->Core);
2047 }
2048 else
2049 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
2050
2051 if (RT_FAILURE(rc))
2052 vdScriptAstNodeFree(&pCondExpr->Core);
2053 }
2054 }
2055 else
2056 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
2057
2058 return rc;
2059}
2060
2061/**
2062 * Parse a switch statement.
2063 *
2064 * @returns VBox status code.
2065 * @param pThis The script context.
2066 * @param pAstNodeSwitch Uninitialized switch AST node.
2067 *
2068 * @note The caller skipped the "switch" token already.
2069 */
2070static int vdScriptParseSwitch(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSWITCH pAstNodeSwitch)
2071{
2072 int rc = VINF_SUCCESS;
2073
2074 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2075 {
2076 PVDSCRIPTASTEXPR pExpr = NULL;
2077
2078 rc = vdScriptParseExpression(pThis, &pExpr);
2079 if ( RT_SUCCESS(rc)
2080 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2081 {
2082 PVDSCRIPTASTSTMT pStmt = NULL;
2083 rc = vdScriptParseStatement(pThis, &pStmt);
2084 if (RT_SUCCESS(rc))
2085 {
2086 pAstNodeSwitch->pCond = pExpr;
2087 pAstNodeSwitch->pStmt = pStmt;
2088 }
2089 else
2090 vdScriptAstNodeFree(&pExpr->Core);
2091 }
2092 else if (RT_SUCCESS(rc))
2093 {
2094 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
2095 vdScriptAstNodeFree(&pExpr->Core);
2096 }
2097 }
2098 else
2099 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
2100
2101 return rc;
2102}
2103
2104/**
2105 * Parse a while or do ... while statement.
2106 *
2107 * @returns VBox status code.
2108 * @param pThis The script context.
2109 * @param pAstNodeWhile Uninitialized while AST node.
2110 *
2111 * @note The caller skipped the "while" or "do" token already.
2112 */
2113static int vdScriptParseWhile(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTWHILE pAstNodeWhile, bool fDoWhile)
2114{
2115 int rc = VINF_SUCCESS;
2116
2117 pAstNodeWhile->fDoWhile = fDoWhile;
2118
2119 if (fDoWhile)
2120 {
2121 PVDSCRIPTASTSTMT pStmt = NULL;
2122 rc = vdScriptParseStatement(pThis, &pStmt);
2123 if ( RT_SUCCESS(rc)
2124 && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
2125 {
2126 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2127 {
2128 PVDSCRIPTASTEXPR pExpr = NULL;
2129
2130 rc = vdScriptParseExpression(pThis, &pExpr);
2131 if ( RT_SUCCESS(rc)
2132 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2133 {
2134 pAstNodeWhile->pCond = pExpr;
2135 pAstNodeWhile->pStmt = pStmt;
2136 }
2137 else if (RT_SUCCESS(rc))
2138 {
2139 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
2140 vdScriptAstNodeFree(&pExpr->Core);
2141 }
2142 }
2143 else
2144 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
2145 }
2146 else if (RT_SUCCESS(rc))
2147 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"while\", got ...\n");
2148
2149 if ( RT_FAILURE(rc)
2150 && pStmt)
2151 vdScriptAstNodeFree(&pStmt->Core);
2152 }
2153 else
2154 {
2155 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2156 {
2157 PVDSCRIPTASTEXPR pExpr = NULL;
2158
2159 rc = vdScriptParseExpression(pThis, &pExpr);
2160 if ( RT_SUCCESS(rc)
2161 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2162 {
2163 PVDSCRIPTASTSTMT pStmt = NULL;
2164 rc = vdScriptParseStatement(pThis, &pStmt);
2165 if (RT_SUCCESS(rc))
2166 {
2167 pAstNodeWhile->pCond = pExpr;
2168 pAstNodeWhile->pStmt = pStmt;
2169 }
2170 else
2171 vdScriptAstNodeFree(&pExpr->Core);
2172 }
2173 else if (RT_SUCCESS(rc))
2174 {
2175 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
2176 vdScriptAstNodeFree(&pExpr->Core);
2177 }
2178 }
2179 else
2180 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
2181 }
2182
2183 return rc;
2184}
2185
2186/**
2187 * Parse a for statement.
2188 *
2189 * @returns VBox status code.
2190 * @param pThis The script context.
2191 * @param pAstNodeFor Uninitialized for AST node.
2192 *
2193 * @note The caller skipped the "for" token already.
2194 */
2195static int vdScriptParseFor(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTFOR pAstNodeFor)
2196{
2197 int rc = VINF_SUCCESS;
2198
2199 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2200 {
2201 PVDSCRIPTASTEXPR pExprStart = NULL;
2202 PVDSCRIPTASTEXPR pExprCond = NULL;
2203 PVDSCRIPTASTEXPR pExpr3 = NULL;
2204
2205 rc = vdScriptParseExpression(pThis, &pExprStart);
2206 if ( RT_SUCCESS(rc)
2207 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2208 {
2209 rc = vdScriptParseExpression(pThis, &pExprCond);
2210 if ( RT_SUCCESS(rc)
2211 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2212 {
2213 rc = vdScriptParseExpression(pThis, &pExpr3);
2214 if ( RT_SUCCESS(rc)
2215 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2216 {
2217 PVDSCRIPTASTSTMT pStmt = NULL;
2218 rc = vdScriptParseStatement(pThis, &pStmt);
2219 if (RT_SUCCESS(rc))
2220 {
2221 pAstNodeFor->pExprStart = pExprStart;
2222 pAstNodeFor->pExprCond = pExprCond;
2223 pAstNodeFor->pExpr3 = pExpr3;
2224 pAstNodeFor->pStmt = pStmt;
2225 }
2226 }
2227 }
2228 else if (RT_SUCCESS(rc))
2229 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2230 }
2231 else if (RT_SUCCESS(rc))
2232 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2233
2234 if (RT_FAILURE(rc))
2235 {
2236 if (pExprStart)
2237 vdScriptAstNodeFree(&pExprStart->Core);
2238 if (pExprCond)
2239 vdScriptAstNodeFree(&pExprCond->Core);
2240 if (pExpr3)
2241 vdScriptAstNodeFree(&pExpr3->Core);
2242 }
2243 }
2244 else
2245 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
2246
2247 return rc;
2248}
2249
2250/**
2251 * Parse a declaration.
2252 *
2253 * @returns VBox status code.
2254 * @param pThis The script context.
2255 * @param ppAstNodeDecl Where to store the declaration AST node on success.
2256 */
2257static int vdScriptParseDeclaration(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTDECL *ppAstNodeDecl)
2258{
2259 int rc = VERR_NOT_IMPLEMENTED;
2260 return rc;
2261}
2262
2263/**
2264 * Parse a statement.
2265 *
2266 * @returns VBox status code.
2267 * @param pThis The script context.
2268 * @param ppAstNodeStmt Where to store the statement AST node on success.
2269 */
2270static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt)
2271{
2272 int rc = VINF_SUCCESS;
2273
2274 /* Shortcut for a new compound statement. */
2275 if (vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, '{'))
2276 rc = vdScriptParseCompoundStatement(pThis, ppAstNodeStmt);
2277 else
2278 {
2279 PVDSCRIPTASTSTMT pAstNodeStmt = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
2280
2281 if (pAstNodeStmt)
2282 {
2283
2284 if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DEFAULT))
2285 {
2286 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
2287 {
2288 PVDSCRIPTASTSTMT pAstNodeStmtDef = NULL;
2289 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_DEFAULT;
2290 rc = vdScriptParseStatement(pThis, &pAstNodeStmtDef);
2291 if (RT_SUCCESS(rc))
2292 pAstNodeStmt->pStmt = pAstNodeStmtDef;
2293 }
2294 else
2295 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
2296 }
2297 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CASE))
2298 {
2299 PVDSCRIPTASTEXPR pAstNodeExpr = NULL;
2300 rc = vdScriptParseCondExpression(pThis, &pAstNodeExpr);
2301 if (RT_SUCCESS(rc))
2302 {
2303 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
2304 {
2305 PVDSCRIPTASTSTMT pAstNodeCaseStmt = NULL;
2306 rc = vdScriptParseStatement(pThis, &pAstNodeCaseStmt);
2307 if (RT_SUCCESS(rc))
2308 {
2309 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CASE;
2310 pAstNodeStmt->Case.pExpr = pAstNodeExpr;
2311 pAstNodeStmt->Case.pStmt = pAstNodeCaseStmt;
2312 }
2313 }
2314 else
2315 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
2316
2317 if (RT_FAILURE(rc))
2318 vdScriptAstNodeFree(&pAstNodeExpr->Core);
2319 }
2320 }
2321 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_IF))
2322 {
2323 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_IF;
2324 rc = vdScriptParseIf(pThis, &pAstNodeStmt->If);
2325 }
2326 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_SWITCH))
2327 {
2328 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_SWITCH;
2329 rc = vdScriptParseSwitch(pThis, &pAstNodeStmt->Switch);
2330 }
2331 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
2332 {
2333 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
2334 rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, false /* fDoWhile */);
2335 }
2336 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DO))
2337 {
2338 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
2339 rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, true /* fDoWhile */);
2340 }
2341 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_FOR))
2342 {
2343 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_FOR;
2344 rc = vdScriptParseFor(pThis, &pAstNodeStmt->For);
2345 }
2346 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONTINUE))
2347 {
2348 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CONTINUE;
2349 if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2350 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2351 }
2352 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_BREAK))
2353 {
2354 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_BREAK;
2355 if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2356 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2357 }
2358 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RETURN))
2359 {
2360 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_RETURN;
2361 if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2362 {
2363 rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
2364 if ( RT_SUCCESS(rc)
2365 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2366 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2367 }
2368 else
2369 pAstNodeStmt->pExpr = NULL; /* No expression for return. */
2370 }
2371 else
2372 {
2373 /* Must be an expression. */
2374 pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_EXPRESSION;
2375 rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
2376 if ( RT_SUCCESS(rc)
2377 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
2378 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
2379 }
2380
2381 if (RT_SUCCESS(rc))
2382 *ppAstNodeStmt = pAstNodeStmt;
2383 else
2384 vdScriptAstNodeFree(&pAstNodeStmt->Core);
2385 }
2386 else
2387 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating statement node\n");
2388 }
2389
2390 return rc;
2391}
2392
2393/**
2394 * Parses a compound statement.
2395 *
2396 * @returns VBox status code.
2397 * @param pThis The script context.
2398 * @param ppAstNodeCompound Where to store the compound AST node on success.
2399 */
2400static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound)
2401{
2402 int rc = VINF_SUCCESS;
2403
2404 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '{'))
2405 {
2406 PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
2407 if (pAstNodeCompound)
2408 {
2409 pAstNodeCompound->enmStmtType = VDSCRIPTSTMTTYPE_COMPOUND;
2410 RTListInit(&pAstNodeCompound->Compound.ListDecls);
2411 RTListInit(&pAstNodeCompound->Compound.ListStmts);
2412 while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '}'))
2413 {
2414 /*
2415 * Check whether we have a declaration or a statement.
2416 * For now we assume that 2 identifier tokens specify a declaration
2417 * (type + variable name). Having two consecutive identifers is not possible
2418 * for a statement.
2419 */
2420 if ( vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER
2421 && vdScriptTokenizerPeekNextClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
2422 {
2423 PVDSCRIPTASTDECL pAstNodeDecl = NULL;
2424 rc = vdScriptParseDeclaration(pThis, &pAstNodeDecl);
2425 if (RT_SUCCESS(rc))
2426 RTListAppend(&pAstNodeCompound->Compound.ListDecls, &pAstNodeDecl->Core.ListNode);
2427 }
2428 else
2429 {
2430 PVDSCRIPTASTSTMT pAstNodeStmt = NULL;
2431 rc = vdScriptParseStatement(pThis, &pAstNodeStmt);
2432 if (RT_SUCCESS(rc))
2433 RTListAppend(&pAstNodeCompound->Compound.ListStmts, &pAstNodeStmt->Core.ListNode);
2434 }
2435
2436 if (RT_FAILURE(rc))
2437 break;
2438 }
2439
2440 if (RT_SUCCESS(rc))
2441 *ppAstNodeCompound = pAstNodeCompound;
2442 else
2443 vdScriptAstNodeFree(&pAstNodeCompound->Core);
2444 }
2445 else
2446 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating compound statement node\n");
2447 }
2448 else
2449 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"{\" got...\n");
2450
2451
2452 return rc;
2453}
2454
2455/**
2456 * Parses a function definition from the given tokenizer.
2457 *
2458 * @returns VBox status code.
2459 * @param pThis The script context.
2460 */
2461static int vdScriptParseAddFnDef(PVDSCRIPTCTXINT pThis)
2462{
2463 int rc = VINF_SUCCESS;
2464 PVDSCRIPTASTIDE pRetType = NULL;
2465 PVDSCRIPTASTIDE pFnIde = NULL;
2466
2467 LogFlowFunc(("pThis=%p\n", pThis));
2468
2469 /* Put return type on the stack. */
2470 rc = vdScriptParseIde(pThis, &pRetType);
2471 if (RT_SUCCESS(rc))
2472 {
2473 /* Function name */
2474 rc = vdScriptParseIde(pThis, &pFnIde);
2475 if (RT_SUCCESS(rc))
2476 {
2477 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
2478 {
2479 PVDSCRIPTASTFN pAstNodeFn = (PVDSCRIPTASTFN)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTION);
2480
2481 if (pAstNodeFn)
2482 {
2483 pAstNodeFn->pFnIde = pFnIde;
2484 pAstNodeFn->pRetType = pRetType;
2485 RTListInit(&pAstNodeFn->ListArgs);
2486
2487 pFnIde = NULL;
2488 pRetType = NULL;
2489
2490 /* Parse parameter list, create empty parameter list AST node and put it on the stack. */
2491 while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
2492 {
2493 PVDSCRIPTASTIDE pArgType = NULL;
2494 PVDSCRIPTASTIDE pArgIde = NULL;
2495 /* Parse two identifiers, first one is the type, second the name. */
2496 rc = vdScriptParseIde(pThis, &pArgType);
2497 if (RT_SUCCESS(rc))
2498 rc = vdScriptParseIde(pThis, &pArgIde);
2499
2500 if (RT_SUCCESS(rc))
2501 {
2502 PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG);
2503 if (pAstNodeFnArg)
2504 {
2505 pAstNodeFnArg->pArgIde = pArgIde;
2506 pAstNodeFnArg->pType = pArgType;
2507 RTListAppend(&pAstNodeFn->ListArgs, &pAstNodeFnArg->Core.ListNode);
2508 pAstNodeFn->cArgs++;
2509 }
2510 else
2511 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function argument AST node\n");
2512 }
2513
2514 if (RT_FAILURE(rc))
2515 {
2516 if (pArgType)
2517 vdScriptAstNodeFree(&pArgType->Core);
2518 if (pArgIde)
2519 vdScriptAstNodeFree(&pArgIde->Core);
2520 }
2521
2522 if ( !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')
2523 && !vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, ')'))
2524 {
2525 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \",\" or \")\" got...\n");
2526 break;
2527 }
2528 }
2529
2530 /* Parse the compound or statement block now. */
2531 if (RT_SUCCESS(rc))
2532 {
2533 PVDSCRIPTASTSTMT pAstCompound = NULL;
2534
2535 rc = vdScriptParseCompoundStatement(pThis, &pAstCompound);
2536 if (RT_SUCCESS(rc))
2537 {
2538 /*
2539 * Link compound statement block to function AST node and add it to the
2540 * list of functions.
2541 */
2542 pAstNodeFn->pCompoundStmts = pAstCompound;
2543 RTListAppend(&pThis->ListAst, &pAstNodeFn->Core.ListNode);
2544 }
2545 }
2546
2547 if (RT_FAILURE(rc))
2548 vdScriptAstNodeFree(&pAstNodeFn->Core);
2549 }
2550 else
2551 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function AST node\n");
2552 }
2553 else
2554 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" got...\n");
2555 }
2556 }
2557
2558 if (RT_FAILURE(rc))
2559 {
2560 if (pRetType)
2561 vdScriptAstNodeFree(&pRetType->Core);
2562 if (pFnIde)
2563 vdScriptAstNodeFree(&pFnIde->Core);
2564 }
2565
2566 LogFlowFunc(("returns rc=%Rrc\n", rc));
2567 return rc;
2568}
2569
2570/**
2571 * Parses the script from the given tokenizer.
2572 *
2573 * @returns VBox status code.
2574 * @param pThis The script context.
2575 */
2576static int vdScriptParseFromTokenizer(PVDSCRIPTCTXINT pThis)
2577{
2578 int rc = VINF_SUCCESS;
2579
2580 LogFlowFunc(("pThis=%p\n", pThis));
2581
2582 /* This is a very very simple LL(1) parser, don't expect much from it for now :). */
2583 while ( RT_SUCCESS(rc)
2584 && !vdScriptTokenizerIsEos(pThis->pTokenizer))
2585 rc = vdScriptParseAddFnDef(pThis);
2586
2587 LogFlowFunc(("returns rc=%Rrc\n", rc));
2588 return rc;
2589}
2590
2591DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx)
2592{
2593 int rc = VINF_SUCCESS;
2594
2595 LogFlowFunc(("phScriptCtx=%p\n", phScriptCtx));
2596
2597 AssertPtrReturn(phScriptCtx, VERR_INVALID_POINTER);
2598
2599 PVDSCRIPTCTXINT pThis = (PVDSCRIPTCTXINT)RTMemAllocZ(sizeof(VDSCRIPTCTXINT));
2600 if (pThis)
2601 {
2602 pThis->hStrSpaceFn = NULL;
2603 RTListInit(&pThis->ListAst);
2604 *phScriptCtx = pThis;
2605 }
2606 else
2607 rc = VINF_SUCCESS;
2608
2609 LogFlowFunc(("returns rc=%Rrc\n", rc));
2610 return rc;
2611}
2612
2613static int vdScriptCtxDestroyFnSpace(PRTSTRSPACECORE pStr, void *pvUser)
2614{
2615 NOREF(pvUser);
2616
2617 /*
2618 * Just free the whole structure, the AST for internal functions will be
2619 * destroyed later.
2620 */
2621 RTMemFree(pStr);
2622 return VINF_SUCCESS;
2623}
2624
2625DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx)
2626{
2627 PVDSCRIPTCTXINT pThis = hScriptCtx;
2628
2629 AssertPtrReturnVoid(pThis);
2630
2631 LogFlowFunc(("hScriptCtx=%p\n", pThis));
2632
2633 RTStrSpaceDestroy(&pThis->hStrSpaceFn, vdScriptCtxDestroyFnSpace, NULL);
2634
2635 /** @todo: Go through the list and destroy all ASTs. */
2636 RTMemFree(pThis);
2637}
2638
2639DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PVDSCRIPTCALLBACK paCallbacks,
2640 unsigned cCallbacks, void *pvUser)
2641{
2642 int rc = VINF_SUCCESS;
2643 PVDSCRIPTCTXINT pThis = hScriptCtx;
2644
2645 LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n",
2646 pThis, paCallbacks, cCallbacks, pvUser));
2647
2648 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2649 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
2650 AssertReturn(cCallbacks > 0, VERR_INVALID_PARAMETER);
2651
2652 /** @todo: Unregister already registered callbacks in case of an error. */
2653 do
2654 {
2655 PVDSCRIPTFN pFn = NULL;
2656
2657 if (RTStrSpaceGet(&pThis->hStrSpaceFn, paCallbacks->pszFnName))
2658 {
2659 rc = VERR_DUPLICATE;
2660 break;
2661 }
2662
2663 pFn = (PVDSCRIPTFN)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTFN, aArgTypes[paCallbacks->cArgs]));
2664 if (!pFn)
2665 {
2666 rc = VERR_NO_MEMORY;
2667 break;
2668 }
2669
2670 /** @todo: Validate argument and returns types. */
2671 pFn->Core.pszString = paCallbacks->pszFnName;
2672 pFn->Core.cchString = strlen(pFn->Core.pszString);
2673 pFn->fExternal = true;
2674 pFn->Type.External.pfnCallback = paCallbacks->pfnCallback;
2675 pFn->Type.External.pvUser = pvUser;
2676 pFn->enmTypeRetn = paCallbacks->enmTypeReturn;
2677 pFn->cArgs = paCallbacks->cArgs;
2678
2679 for (unsigned i = 0; i < paCallbacks->cArgs; i++)
2680 pFn->aArgTypes[i] = paCallbacks->paArgs[i];
2681
2682 RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core);
2683 cCallbacks--;
2684 paCallbacks++;
2685 }
2686 while (cCallbacks);
2687
2688 LogFlowFunc(("returns rc=%Rrc\n", rc));
2689 return rc;
2690}
2691
2692DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript)
2693{
2694 int rc = VINF_SUCCESS;
2695 PVDSCRIPTCTXINT pThis = hScriptCtx;
2696
2697 LogFlowFunc(("hScriptCtx=%p pszScript=%p\n", pThis, pszScript));
2698
2699 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2700 AssertPtrReturn(pszScript, VERR_INVALID_POINTER);
2701
2702 PVDTOKENIZER pTokenizer = vdScriptTokenizerCreate(pszScript);
2703 if (pTokenizer)
2704 {
2705 pThis->pTokenizer = pTokenizer;
2706 rc = vdScriptParseFromTokenizer(pThis);
2707 pThis->pTokenizer = NULL;
2708 RTMemFree(pTokenizer);
2709 }
2710
2711 LogFlowFunc(("returns rc=%Rrc\n", rc));
2712 return rc;
2713}
2714
2715DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
2716 PVDSCRIPTARG paArgs, unsigned cArgs)
2717{
2718 return VERR_NOT_IMPLEMENTED;
2719}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette