VirtualBox

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

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

Storage/VDScript: Interpreter updates

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