VirtualBox

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

Last change on this file was 103522, checked in by vboxsync, 3 months ago

Storage/testcase: Some unused variable fixes, bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use