VirtualBox

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

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

Storage/testcase: Updates for VDScript, parser not quite working yet

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

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