VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 104.3 KB
Line 
1/* $Id: VDScript.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBox HDD container test utility - scripting engine.
4 */
5
6/*
7 * Copyright (C) 2013-2022 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 * @returns nothing.
341 * @param pTokenizer The tokenizer state.
342 */
343DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer)
344{
345 pTokenizer->pszInput++;
346 pTokenizer->Pos.iChStart++;
347 pTokenizer->Pos.iChEnd++;
348}
349
350/**
351 * Returns the next char in the input buffer without advancing it.
352 *
353 * @returns Next character in the input buffer.
354 * @param pTokenizer The tokenizer state.
355 */
356DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer)
357{
358 return vdScriptTokenizerIsEos(pTokenizer)
359 ? '\0'
360 : *(pTokenizer->pszInput + 1);
361}
362
363/**
364 * Returns the next character in the input buffer advancing the internal
365 * position.
366 *
367 * @returns Next character in the stream.
368 * @param pTokenizer The tokenizer state.
369 */
370DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer)
371{
372 char ch;
373
374 if (vdScriptTokenizerIsEos(pTokenizer))
375 ch = '\0';
376 else
377 ch = *pTokenizer->pszInput;
378
379 return ch;
380}
381
382/**
383 * Sets a new line for the tokenizer.
384 *
385 * @returns nothing.
386 * @param pTokenizer The tokenizer state.
387 */
388DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip)
389{
390 pTokenizer->pszInput += cSkip;
391 pTokenizer->Pos.iLine++;
392 pTokenizer->Pos.iChStart = 1;
393 pTokenizer->Pos.iChEnd = 1;
394}
395
396/**
397 * Checks whether the current position in the input stream is a new line
398 * and skips it.
399 *
400 * @returns Flag whether there was a new line at the current position
401 * in the input buffer.
402 * @param pTokenizer The tokenizer state.
403 */
404DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer)
405{
406 bool fNewline = true;
407
408 if ( vdScriptTokenizerGetCh(pTokenizer) == '\r'
409 && vdScriptTokenizerPeekCh(pTokenizer) == '\n')
410 vdScriptTokenizerNewLine(pTokenizer, 2);
411 else if (vdScriptTokenizerGetCh(pTokenizer) == '\n')
412 vdScriptTokenizerNewLine(pTokenizer, 1);
413 else
414 fNewline = false;
415
416 return fNewline;
417}
418
419/**
420 * Skips a multi line comment.
421 *
422 * @returns nothing.
423 * @param pTokenizer The tokenizer state.
424 */
425DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer)
426{
427 while ( !vdScriptTokenizerIsEos(pTokenizer)
428 && ( vdScriptTokenizerGetCh(pTokenizer) != '*'
429 || vdScriptTokenizerPeekCh(pTokenizer) != '/'))
430 {
431 if (!vdScriptTokenizerIsSkipNewLine(pTokenizer))
432 vdScriptTokenizerSkipCh(pTokenizer);
433 }
434
435 if (!vdScriptTokenizerIsEos(pTokenizer))
436 vdScriptTokenizerSkipCh(pTokenizer);
437 if (!vdScriptTokenizerIsEos(pTokenizer))
438 vdScriptTokenizerSkipCh(pTokenizer);
439}
440
441/**
442 * Skip all whitespace starting from the current input buffer position.
443 * Skips all present comments too.
444 *
445 * @returns nothing.
446 * @param pTokenizer The tokenizer state.
447 */
448DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer)
449{
450 while (!vdScriptTokenizerIsEos(pTokenizer))
451 {
452 while ( vdScriptTokenizerGetCh(pTokenizer) == ' '
453 || vdScriptTokenizerGetCh(pTokenizer) == '\t')
454 vdScriptTokenizerSkipCh(pTokenizer);
455
456 if ( !vdScriptTokenizerIsEos(pTokenizer)
457 && !vdScriptTokenizerIsSkipNewLine(pTokenizer))
458 {
459 if ( vdScriptTokenizerGetCh(pTokenizer) == '/'
460 && vdScriptTokenizerPeekCh(pTokenizer) == '*')
461 {
462 vdScriptTokenizerSkipCh(pTokenizer);
463 vdScriptTokenizerSkipCh(pTokenizer);
464 vdScriptTokenizerSkipComment(pTokenizer);
465 }
466 else
467 break; /* Skipped everything, next is some real content. */
468 }
469 }
470}
471
472/**
473 * Get an identifier token from the tokenizer.
474 *
475 * @returns nothing.
476 * @param pTokenizer The tokenizer state.
477 * @param pToken The uninitialized token.
478 */
479static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
480{
481 char ch;
482 unsigned cchIde = 0;
483 bool fIsKeyword = false;
484 const char *pszIde = pTokenizer->pszInput;
485
486 pToken->Pos = pTokenizer->Pos;
487
488 Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' );
489
490 do
491 {
492 cchIde++;
493 vdScriptTokenizerSkipCh(pTokenizer);
494 ch = vdScriptTokenizerGetCh(pTokenizer);
495 }
496 while (RT_C_IS_ALNUM(ch) || ch == '_');
497
498 /* Check whether we got an identifier or an reserved keyword. */
499 for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++)
500 {
501 if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword))
502 {
503 fIsKeyword = true;
504 pToken->enmClass = VDTOKENCLASS_KEYWORD;
505 pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword;
506 break;
507 }
508 }
509
510 if (!fIsKeyword)
511 {
512 pToken->enmClass = VDTOKENCLASS_IDENTIFIER;
513 pToken->Class.Ide.pszIde = pszIde;
514 pToken->Class.Ide.cchIde = cchIde;
515 }
516 pToken->Pos.iChEnd += cchIde;
517}
518
519/**
520 * Get a numerical constant from the tokenizer.
521 *
522 * @returns nothing.
523 * @param pTokenizer The tokenizer state.
524 * @param pToken The uninitialized token.
525 */
526static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
527{
528 char *pszNext = NULL;
529
530 Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer)));
531
532 /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */
533 pToken->enmClass = VDTOKENCLASS_NUMCONST;
534 int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64);
535 Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES); NOREF(rc);
536 /** @todo Handle number to big, throw a warning */
537
538 unsigned cchNumber = pszNext - pTokenizer->pszInput;
539 for (unsigned i = 0; i < cchNumber; i++)
540 vdScriptTokenizerSkipCh(pTokenizer);
541
542 /* Check for a supported suffix, supported are K|M|G. */
543 if (vdScriptTokenizerGetCh(pTokenizer) == 'K')
544 {
545 pToken->Class.NumConst.u64 *= _1K;
546 vdScriptTokenizerSkipCh(pTokenizer);
547 }
548 else if (vdScriptTokenizerGetCh(pTokenizer) == 'M')
549 {
550 pToken->Class.NumConst.u64 *= _1M;
551 vdScriptTokenizerSkipCh(pTokenizer);
552 }
553 else if (vdScriptTokenizerGetCh(pTokenizer) == 'G')
554 {
555 pToken->Class.NumConst.u64 *= _1G;
556 vdScriptTokenizerSkipCh(pTokenizer);
557 }
558 else if (vdScriptTokenizerGetCh(pTokenizer) == 'T')
559 {
560 pToken->Class.NumConst.u64 *= _1T;
561 vdScriptTokenizerSkipCh(pTokenizer);
562 }
563}
564
565/**
566 * Parses a string constant.
567 *
568 * @returns nothing.
569 * @param pTokenizer The tokenizer state.
570 * @param pToken The uninitialized token.
571 *
572 * @remarks: No escape sequences allowed at this time.
573 */
574static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
575{
576 unsigned cchStr = 0;
577
578 Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"');
579 vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */
580
581 pToken->enmClass = VDTOKENCLASS_STRINGCONST;
582 pToken->Pos = pTokenizer->Pos;
583 pToken->Class.StringConst.pszString = pTokenizer->pszInput;
584
585 while (vdScriptTokenizerGetCh(pTokenizer) != '\"')
586 {
587 cchStr++;
588 vdScriptTokenizerSkipCh(pTokenizer);
589 }
590
591 vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */
592
593 pToken->Class.StringConst.cchString = cchStr;
594 pToken->Pos.iChEnd += cchStr;
595}
596
597/**
598 * Get the end of stream token.
599 *
600 * @returns nothing.
601 * @param pTokenizer The tokenizer state.
602 * @param pToken The uninitialized token.
603 */
604static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
605{
606 Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0');
607
608 pToken->enmClass = VDTOKENCLASS_EOS;
609 pToken->Pos = pTokenizer->Pos;
610}
611
612/**
613 * Get operator or punctuator token.
614 *
615 * @returns nothing.
616 * @param pTokenizer The tokenizer state.
617 * @param pToken The uninitialized token.
618 */
619static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
620{
621 bool fOpFound = false;
622
623 pToken->enmClass = VDTOKENCLASS_INVALID;
624 pToken->Pos = pTokenizer->Pos;
625
626 /*
627 * Use table based approach here, not the fastest solution but enough for our purpose
628 * for now.
629 */
630 for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++)
631 {
632 if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp))
633 {
634 memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp));
635
636 int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp);
637 AssertRC(rc);
638
639 pToken->enmClass = VDTOKENCLASS_OPERATORS;
640 pToken->Pos.iChEnd += (unsigned)g_aScriptOps[i].cchOp;
641
642 /** @todo Make this prettier. */
643 for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++)
644 vdScriptTokenizerSkipCh(pTokenizer);
645 fOpFound = true;
646 break;
647 }
648 }
649
650 if (!fOpFound)
651 {
652 for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++)
653 {
654 if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp))
655 {
656 pToken->Pos.iChEnd += (unsigned)g_aScriptPunctuators[i].cchOp;
657 pToken->enmClass = VDTOKENCLASS_PUNCTUATOR;
658 pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp;
659
660 vdScriptTokenizerSkipCh(pTokenizer);
661 fOpFound = true;
662 break;
663 }
664 }
665 }
666}
667
668/**
669 * Read the next token from the tokenizer stream.
670 *
671 * @returns nothing.
672 * @param pTokenizer The tokenizer to read from.
673 * @param pToken Uninitialized token to fill the token data into.
674 */
675static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
676{
677 /* Skip all eventually existing whitespace, newlines and comments first. */
678 vdScriptTokenizerSkipWhitespace(pTokenizer);
679
680 char ch = vdScriptTokenizerGetCh(pTokenizer);
681 if (RT_C_IS_ALPHA(ch) || ch == '_')
682 vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken);
683 else if (RT_C_IS_DIGIT(ch))
684 vdScriptTokenizerGetNumberConst(pTokenizer, pToken);
685 else if (ch == '\"')
686 vdScriptTokenizerGetStringConst(pTokenizer, pToken);
687 else if (ch == '\0')
688 vdScriptTokenizerGetEos(pTokenizer, pToken);
689 else
690 vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken);
691}
692
693/**
694 * Create a new tokenizer.
695 *
696 * @returns Pointer to the new tokenizer state on success.
697 * NULL if out of memory.
698 * @param pszInput The input to create the tokenizer for.
699 */
700static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput)
701{
702 PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER));
703 if (pTokenizer)
704 {
705 pTokenizer->pszInput = pszInput;
706 pTokenizer->Pos.iLine = 1;
707 pTokenizer->Pos.iChStart = 1;
708 pTokenizer->Pos.iChEnd = 1;
709 pTokenizer->pTokenCurr = &pTokenizer->Token1;
710 pTokenizer->pTokenNext = &pTokenizer->Token2;
711 /* Fill the tokenizer with two first tokens. */
712 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
713 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
714 }
715
716 return pTokenizer;
717}
718
719#if 0 /** @todo unused */
720/**
721 * Destroys a given tokenizer state.
722 *
723 * @returns nothing.
724 * @param pTokenizer The tokenizer to destroy.
725 */
726static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer)
727{
728 RTMemFree(pTokenizer);
729}
730#endif
731
732/**
733 * Get the current token in the input stream.
734 *
735 * @returns Pointer to the next token in the stream.
736 * @param pTokenizer The tokenizer to destroy.
737 */
738DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer)
739{
740 return pTokenizer->pTokenCurr;
741}
742
743/**
744 * Get the class of the current token.
745 *
746 * @returns Class of the current token.
747 * @param pTokenizer The tokenizer state.
748 */
749DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer)
750{
751 return pTokenizer->pTokenCurr->enmClass;
752}
753
754/**
755 * Returns the token class of the next token in the stream.
756 *
757 * @returns Token class of the next token.
758 * @param pTokenizer The tokenizer state.
759 */
760DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer)
761{
762 return pTokenizer->pTokenNext->enmClass;
763}
764
765/**
766 * Consume the current token advancing to the next in the stream.
767 *
768 * @returns nothing.
769 * @param pTokenizer The tokenizer state.
770 */
771static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer)
772{
773 PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr;
774
775 /* Switch next token to current token and read in the next token. */
776 pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
777 pTokenizer->pTokenNext = pTokenTmp;
778 vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
779}
780
781/**
782 * Check whether the next token in the input stream is a punctuator and matches the given
783 * character.
784 *
785 * @returns true if the token matched.
786 * false otherwise.
787 * @param pTokenizer The tokenizer state.
788 * @param chCheck The punctuator to check against.
789 */
790static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
791{
792 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
793
794 if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR
795 && pToken->Class.Punctuator.chPunctuator == chCheck)
796 return true;
797
798 return false;
799}
800
801/**
802 * Check whether the next token in the input stream is a punctuator and matches the given
803 * character and skips it.
804 *
805 * @returns true if the token matched and was skipped.
806 * false otherwise.
807 * @param pTokenizer The tokenizer state.
808 * @param chCheck The punctuator to check against.
809 */
810static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
811{
812 bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck);
813 if (fEqual)
814 vdScriptTokenizerConsume(pTokenizer);
815
816 return fEqual;
817}
818
819/**
820 * Check whether the next token in the input stream is a keyword and matches the given
821 * keyword.
822 *
823 * @returns true if the token matched.
824 * false otherwise.
825 * @param pTokenizer The tokenizer state.
826 * @param enmKey The keyword to check against.
827 */
828static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
829{
830 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
831
832 if ( pToken->enmClass == VDTOKENCLASS_KEYWORD
833 && pToken->Class.Keyword.enmKeyword == enmKeyword)
834 return true;
835
836 return false;
837}
838
839/**
840 * Check whether the next token in the input stream is a keyword and matches the given
841 * keyword and skips it.
842 *
843 * @returns true if the token matched and was skipped.
844 * false otherwise.
845 * @param pTokenizer The tokenizer state.
846 * @param enmKey The keyword to check against.
847 */
848static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
849{
850 bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword);
851 if (fEqual)
852 vdScriptTokenizerConsume(pTokenizer);
853
854 return fEqual;
855}
856
857/**
858 * Check whether the next token in the input stream is a keyword and matches the given
859 * keyword.
860 *
861 * @returns true if the token matched.
862 * false otherwise.
863 * @param pTokenizer The tokenizer state.
864 * @param pszOp The operation to check against.
865 */
866static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
867{
868 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
869
870 if ( pToken->enmClass == VDTOKENCLASS_OPERATORS
871 && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp))
872 return true;
873
874 return false;
875}
876
877/**
878 * Check whether the next token in the input stream is an operator and matches the given
879 * keyword and skips it.
880 *
881 * @returns true if the token matched and was skipped.
882 * false otherwise.
883 * @param pTokenizer The tokenizer state.
884 * @param pszOp The operation to check against.
885 */
886static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
887{
888 bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp);
889 if (fEqual)
890 vdScriptTokenizerConsume(pTokenizer);
891
892 return fEqual;
893}
894
895/**
896 * Record an error while parsing.
897 *
898 * @returns VBox status code passed.
899 */
900static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
901{
902 RT_NOREF1(pThis); RT_SRC_POS_NOREF();
903 va_list va;
904 va_start(va, pszFmt);
905 RTPrintfV(pszFmt, va);
906 va_end(va);
907 return rc;
908}
909
910/**
911 * Puts the next identifier AST node on the stack.
912 *
913 * @returns VBox status code.
914 * @param pThis The script context.
915 * @param ppAstNodeIde Where to store the identifier AST node on success.
916 */
917static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde)
918{
919 int rc = VINF_SUCCESS;
920 PCVDSCRIPTTOKEN pToken;
921
922 LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde));
923
924 pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
925 if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER)
926 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n");
927 else
928 {
929 /* Create new AST node and push onto stack. */
930 PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde);
931 if (pAstNodeIde)
932 {
933 rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde);
934 AssertRC(rc);
935 pAstNodeIde->cchIde = (unsigned)pToken->Class.Ide.cchIde;
936
937 *ppAstNodeIde = pAstNodeIde;
938 vdScriptTokenizerConsume(pThis->pTokenizer);
939 }
940 else
941 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n");
942 }
943
944 LogFlowFunc(("returns %Rrc\n", rc));
945 return rc;
946}
947
948/**
949 * Parse a primary expression.
950 *
951 * @returns VBox status code.
952 * @param pThis The script context.
953 * @param ppAstNodeExpr Where to store the primary expression on success.
954 */
955static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
956{
957 int rc = VINF_SUCCESS;
958
959 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
960
961 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
962 {
963 rc = vdScriptParseExpression(pThis, ppAstNodeExpr);
964 if (RT_SUCCESS(rc)
965 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
966 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
967 }
968 else
969 {
970 PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
971 if (pExpr)
972 {
973 if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
974 {
975 PVDSCRIPTASTIDE pIde = NULL;
976 rc = vdScriptParseIde(pThis, &pIde);
977 if (RT_SUCCESS(rc))
978 {
979 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER;
980 pExpr->pIde = pIde;
981 }
982 }
983 else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST)
984 {
985 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
986 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST;
987 pExpr->u64 = pToken->Class.NumConst.u64;
988 vdScriptTokenizerConsume(pThis->pTokenizer);
989 }
990 else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST)
991 {
992 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
993 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST;
994 pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString);
995 vdScriptTokenizerConsume(pThis->pTokenizer);
996
997 if (!pExpr->pszStr)
998 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n");
999 }
1000 else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_KEYWORD)
1001 {
1002 PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
1003 pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN;
1004
1005 if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_TRUE)
1006 pExpr->f = true;
1007 else if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_FALSE)
1008 pExpr->f = false;
1009 else
1010 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n");
1011 vdScriptTokenizerConsume(pThis->pTokenizer);
1012 }
1013 else
1014 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n");
1015
1016 if (RT_FAILURE(rc))
1017 vdScriptAstNodeFree(&pExpr->Core);
1018 else
1019 *ppAstNodeExpr = pExpr;
1020 }
1021 else
1022 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1023 }
1024
1025 LogFlowFunc(("returns rc=%Rrc\n", rc));
1026 return rc;
1027}
1028
1029/**
1030 * Parse an argument list for a function call.
1031 *
1032 * @returns VBox status code.
1033 * @param pThis The script context.
1034 * @param pFnCall The function call AST node.
1035 */
1036static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall)
1037{
1038 int rc = VINF_SUCCESS;
1039 PVDSCRIPTASTEXPR pExpr = NULL;
1040
1041 LogFlowFunc(("pThis=%p pFnCall=%p\n", pThis, pFnCall));
1042
1043 rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
1044 if (RT_SUCCESS(rc))
1045 {
1046 RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
1047 while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
1048 {
1049 rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
1050 if (RT_SUCCESS(rc))
1051 RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
1052 else
1053 break;
1054 }
1055 if ( RT_SUCCESS(rc)
1056 && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
1057 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
1058 }
1059
1060 LogFlowFunc(("returns rc=%Rrc\n", rc));
1061 return rc;
1062}
1063
1064/**
1065 * Parse a postfix expression.
1066 *
1067 * @returns VBox status code.
1068 * @param pThis The script context.
1069 * @param ppAstNodeExpr Where to store the expression AST node on success.
1070 *
1071 * @note Syntax:
1072 * postfix-expression:
1073 * primary-expression
1074 * postfix-expression ( argument-expression )
1075 * postfix-expression ++
1076 * postfix-expression --
1077 * postfix-expression . identifier
1078 * postfix-expression -> identifier
1079 * @note: Not supported so far are:
1080 * ( type-name ) { initializer-list }
1081 * ( type-name ) { initializer-list , }
1082 */
1083static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1084{
1085 int rc = VINF_SUCCESS;
1086 PVDSCRIPTASTEXPR pExpr = NULL;
1087
1088 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1089
1090 rc = vdScriptParsePrimaryExpression(pThis, &pExpr);
1091 if (RT_SUCCESS(rc))
1092 {
1093 while (true)
1094 {
1095 PVDSCRIPTASTEXPR pExprNew = NULL;
1096
1097 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
1098 {
1099 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1100 if (pExprNew)
1101 {
1102 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT;
1103 pExprNew->pExpr = pExpr;
1104 pExpr = pExprNew;
1105 }
1106 else
1107 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1108 }
1109 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
1110 {
1111 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1112 if (pExprNew)
1113 {
1114 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT;
1115 pExprNew->pExpr = pExpr;
1116 pExpr = pExprNew;
1117 }
1118 else
1119 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1120 }
1121 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "->"))
1122 {
1123 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1124 if (pExprNew)
1125 {
1126 PVDSCRIPTASTIDE pIde = NULL;
1127 rc = vdScriptParseIde(pThis, &pIde);
1128 if (RT_SUCCESS(rc))
1129 {
1130 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DEREFERENCE;
1131 pExprNew->Deref.pIde = pIde;
1132 pExprNew->Deref.pExpr = pExpr;
1133 pExpr = pExprNew;
1134 }
1135 else
1136 vdScriptAstNodeFree(&pExprNew->Core);
1137 }
1138 else
1139 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1140 }
1141 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "."))
1142 {
1143 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1144 if (pExprNew)
1145 {
1146 PVDSCRIPTASTIDE pIde = NULL;
1147 rc = vdScriptParseIde(pThis, &pIde);
1148 if (RT_SUCCESS(rc))
1149 {
1150 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DOT;
1151 pExprNew->Deref.pIde = pIde;
1152 pExprNew->Deref.pExpr = pExpr;
1153 pExpr = pExprNew;
1154 }
1155 else
1156 vdScriptAstNodeFree(&pExprNew->Core);
1157 }
1158 else
1159 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1160 }
1161 else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
1162 {
1163 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1164 if (pExprNew)
1165 {
1166 pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL;
1167 RTListInit(&pExprNew->FnCall.ListArgs);
1168 if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
1169 rc = vdScriptParseFnCallArgumentList(pThis, pExprNew);
1170 pExprNew->FnCall.pFnIde = pExpr;
1171 pExpr = pExprNew;
1172 }
1173 else
1174 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1175 }
1176 else
1177 break;
1178
1179 if (RT_FAILURE(rc))
1180 break;
1181 }
1182
1183 if (RT_SUCCESS(rc))
1184 *ppAstNodeExpr = pExpr;
1185 else
1186 vdScriptAstNodeFree(&pExpr->Core);
1187 }
1188
1189 LogFlowFunc(("returns rc=%Rrc\n", rc));
1190 return rc;
1191}
1192
1193/**
1194 * Parse an unary expression.
1195 *
1196 * @returns VBox status code.
1197 * @param pThis The script context.
1198 * @param ppAstNodeExpr Where to store the expression AST node on success.
1199 *
1200 * @note Syntax:
1201 * unary-expression:
1202 * postfix-expression
1203 * ++ unary-expression
1204 * -- unary-expression
1205 * + cast-expression
1206 * - cast-expression
1207 * ~ cast-expression
1208 * ! cast-expression
1209 * & cast-expression
1210 * * cast-expression
1211 */
1212static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1213{
1214 int rc = VINF_SUCCESS;
1215 PVDSCRIPTASTEXPR pExpr = NULL;
1216 PVDSCRIPTASTEXPR pExprTop = NULL;
1217
1218 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1219
1220 /** @todo Think about a more beautiful way of parsing this. */
1221 while (true)
1222 {
1223 bool fQuit = false;
1224 bool fCastExprFollows = false;
1225 PVDSCRIPTASTEXPR pExprNew = NULL;
1226 VDSCRIPTEXPRTYPE enmType = VDSCRIPTEXPRTYPE_INVALID;
1227
1228 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
1229 enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT;
1230 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
1231 enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT;
1232 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
1233 {
1234 enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN;
1235 fCastExprFollows = true;
1236 }
1237 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
1238 {
1239 enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN;
1240 fCastExprFollows = true;
1241 }
1242 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~"))
1243 {
1244 enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT;
1245 fCastExprFollows = true;
1246 }
1247 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!"))
1248 {
1249 enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE;
1250 fCastExprFollows = true;
1251 }
1252 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&"))
1253 {
1254 enmType = VDSCRIPTEXPRTYPE_UNARY_REFERENCE;
1255 fCastExprFollows = true;
1256 }
1257 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*"))
1258 {
1259 enmType = VDSCRIPTEXPRTYPE_UNARY_DEREFERENCE;
1260 fCastExprFollows = true;
1261 }
1262
1263 if (enmType != VDSCRIPTEXPRTYPE_INVALID)
1264 {
1265 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1266 if (pExprNew)
1267 pExprNew->enmType = enmType;
1268 else
1269 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1270
1271 if ( RT_SUCCESS(rc)
1272 && fCastExprFollows)
1273 {
1274 PVDSCRIPTASTEXPR pCastExpr = NULL;
1275
1276 rc = vdScriptParseCastExpression(pThis, &pCastExpr);
1277 if (RT_SUCCESS(rc))
1278 pExprNew->pExpr = pCastExpr;
1279 else
1280 vdScriptAstNodeFree(&pExprNew->Core);
1281 fQuit = true;
1282 }
1283 }
1284 else
1285 {
1286 /* Must be a postfix expression. */
1287 rc = vdScriptParsePostfixExpression(pThis, &pExprNew);
1288 fQuit = true;
1289 }
1290
1291 if (RT_SUCCESS(rc))
1292 {
1293 if (!pExprTop)
1294 {
1295 pExprTop = pExprNew;
1296 pExpr = pExprNew;
1297 }
1298 else
1299 {
1300 pExpr->pExpr = pExprNew;
1301 pExpr = pExprNew;
1302 }
1303 if (fQuit)
1304 break;
1305 }
1306 else
1307 break;
1308 }
1309
1310 if (RT_SUCCESS(rc))
1311 *ppAstNodeExpr = pExprTop;
1312 else if (pExprTop)
1313 vdScriptAstNodeFree(&pExprTop->Core);
1314
1315 LogFlowFunc(("returns rc=%Rrc\n", rc));
1316 return rc;
1317}
1318
1319#if 0 /* unused */
1320/**
1321 * Parse a storage class specifier.
1322 *
1323 * @returns nothing.
1324 * @param pThis The script context.
1325 * @param penmStorageClass Where to return the parsed storage classe.
1326 * Contains VDSCRIPTASTSTORAGECLASS_INVALID if no
1327 * valid storage class specifier was found.
1328 *
1329 * @note Syntax:
1330 * typedef
1331 * extern
1332 * static
1333 * auto
1334 * register
1335 */
1336static void vdScriptParseStorageClassSpecifier(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTORAGECLASS penmStorageClass)
1337{
1338 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_INVALID;
1339
1340 if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_TYPEDEF))
1341 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_TYPEDEF;
1342 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_EXTERN))
1343 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_EXTERN;
1344 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_STATIC))
1345 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_STATIC;
1346 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_AUTO))
1347 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_AUTO;
1348 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_REGISTER))
1349 *penmStorageClass = VDSCRIPTASTSTORAGECLASS_REGISTER;
1350}
1351#endif /* unused */
1352
1353#if 0 /* unused */
1354/**
1355 * Parse a type qualifier.
1356 *
1357 * @returns nothing.
1358 * @param pThis The script context.
1359 * @param penmTypeQualifier Where to return the parsed type qualifier.
1360 * Contains VDSCRIPTASTTYPEQUALIFIER_INVALID if no
1361 * valid type qualifier was found.
1362 *
1363 * @note Syntax:
1364 * const
1365 * restrict
1366 * volatile
1367 */
1368static void vdScriptParseTypeQualifier(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTTYPEQUALIFIER penmTypeQualifier)
1369{
1370 *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_INVALID;
1371
1372 if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONST))
1373 *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_CONST;
1374 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RESTRICT))
1375 *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_RESTRICT;
1376 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_VOLATILE))
1377 *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_VOLATILE;
1378}
1379#endif /* unused */
1380
1381#if 0
1382/**
1383 * Parse a struct or union 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 * @param enmTypeSpecifier The type specifier to identify whete this is a struct or a union.
1389 */
1390static int vdScriptParseStructOrUnionSpecifier(PVDSCRIPTCTXINT pThis, , enmTypeSpecifier)
1391{
1392 int rc = VINF_SUCCESS;
1393
1394 return rc;
1395}
1396
1397/**
1398 * Parse a type specifier.
1399 *
1400 * @returns VBox status code.
1401 * @param pThis The script context.
1402 * @param ppAstTypeSpec Where to store the type specifier AST node on success.
1403 *
1404 * @note Syntax:
1405 * struct-or-union-specifier
1406 * enum-specifier
1407 * typedef-name (identifier: includes void, bool, uint8_t, int8_t, ... for basic integer types)
1408 */
1409static int vdScriptParseTypeSpecifier(PVDSCRIPTCTXINT pThis, )
1410{
1411 int rc = VINF_SUCCESS;
1412
1413 if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_STRUCT))
1414 rc = vdScriptParseStructOrUnionSpecifier(pThis, , VDSCRIPTASTTYPESPECIFIER_STRUCT);
1415 else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_UNION))
1416 rc = vdScriptParseStructOrUnionSpecifier(pThis, , VDSCRIPTASTTYPESPECIFIER_UNION);
1417 else
1418 {
1419 PVDSCRIPTASTIDE pIde = NULL;
1420
1421 rc = vdScriptParseIde(pThis, &pIde);
1422 if (RT_SUCCESS(rc))
1423 {
1424 AssertMsgFailed(("TODO\n")); /* Parse identifier. */
1425 }
1426 }
1427
1428 return rc;
1429}
1430#endif
1431
1432/**
1433 * Parse a cast expression.
1434 *
1435 * @returns VBox status code.
1436 * @param pThis The script context.
1437 * @param ppAstNodeExpr Where to store the expression AST node on success.
1438 *
1439 * @note Syntax:
1440 * cast-expression:
1441 * unary-expression
1442 * ( type-name ) cast-expression
1443 */
1444static int vdScriptParseCastExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1445{
1446 int rc = VINF_SUCCESS;
1447
1448 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1449
1450#if 0
1451 if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
1452 {
1453 PVDSCRIPTASTTYPE pTypeName = NULL;
1454 rc = vdScriptParseTypeName(pThis, &pTypeName);
1455 if ( RT_SUCCESS(rc)
1456 && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
1457 {
1458 PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1459 if (pExpr)
1460 {
1461 pExpr->enmType = VDSCRIPTEXPRTYPE_CAST;
1462 rc = vdScriptParseCastExpression(pThis, &pExpr->Cast.pExpr); /** @todo Kill recursion. */
1463 if (RT_SUCCESS(rc))
1464 pExpr->Cast.pTypeName = pTypeName;
1465 else
1466 vdScriptAstNodeFree(&pExpr->Core);
1467 }
1468 else
1469 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1470
1471 if (RT_FAILURE(rc))
1472 vdScriptAstNodeFree(&pTypeName->Core);
1473 }
1474 else if (RT_SUCCESS(rc))
1475 rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
1476 }
1477 else
1478#endif
1479 rc = vdScriptParseUnaryExpression(pThis, ppAstNodeExpr);
1480
1481 return rc;
1482}
1483
1484/**
1485 * Parse a multiplicative expression.
1486 *
1487 * @returns VBox status code.
1488 * @param pThis The script context.
1489 * @param ppAstNodeExpr Where to store the expression AST node on success.
1490 *
1491 * @note Syntax:
1492 * multiplicative-expression:
1493 * cast-expression
1494 * multiplicative-expression * cast-expression
1495 * multiplicative-expression / cast-expression
1496 * multiplicative-expression % cast-expression
1497 */
1498static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1499{
1500 int rc = VINF_SUCCESS;
1501 PVDSCRIPTASTEXPR pExpr = NULL;
1502
1503 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1504
1505 rc = vdScriptParseCastExpression(pThis, &pExpr);
1506 if (RT_SUCCESS(rc))
1507 {
1508 while (RT_SUCCESS(rc))
1509 {
1510 VDSCRIPTEXPRTYPE enmType = VDSCRIPTEXPRTYPE_INVALID;
1511 PVDSCRIPTASTEXPR pExprNew = NULL;
1512
1513 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*"))
1514 enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION;
1515 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/"))
1516 enmType = VDSCRIPTEXPRTYPE_DIVISION;
1517 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%"))
1518 enmType = VDSCRIPTEXPRTYPE_MODULUS;
1519 else
1520 break;
1521
1522 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1523 if (pExprNew)
1524 pExprNew->enmType = enmType;
1525 else
1526 {
1527 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1528 break;
1529 }
1530
1531 pExprNew->BinaryOp.pLeftExpr = pExpr;
1532 pExpr = pExprNew;
1533 rc = vdScriptParseCastExpression(pThis, &pExprNew);
1534 if (RT_SUCCESS(rc))
1535 pExpr->BinaryOp.pRightExpr = pExprNew;
1536 }
1537
1538 if (RT_SUCCESS(rc))
1539 *ppAstNodeExpr = pExpr;
1540 else
1541 vdScriptAstNodeFree(&pExpr->Core);
1542 }
1543
1544 LogFlowFunc(("returns rc=%Rrc\n", rc));
1545 return rc;
1546}
1547
1548/**
1549 * Parse a additive expression.
1550 *
1551 * @returns VBox status code.
1552 * @param pThis The script context.
1553 * @param ppAstNodeExpr Where to store the expression AST node on success.
1554 *
1555 * @note Syntax:
1556 * additive-expression:
1557 * multiplicative-expression
1558 * additive-expression + multiplicative-expression
1559 * additive-expression - multiplicative-expression
1560 */
1561static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1562{
1563 int rc = VINF_SUCCESS;
1564 PVDSCRIPTASTEXPR pExpr = NULL;
1565
1566 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1567
1568 rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr);
1569 if (RT_SUCCESS(rc))
1570 {
1571 PVDSCRIPTASTEXPR pExprNew = NULL;
1572 while (RT_SUCCESS(rc))
1573 {
1574 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
1575 {
1576 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1577 if (pExprNew)
1578 pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION;
1579 else
1580 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1581 }
1582 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
1583 {
1584 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1585 if (pExprNew)
1586 pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION;
1587 else
1588 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1589 }
1590 else
1591 break;
1592
1593 pExprNew->BinaryOp.pLeftExpr = pExpr;
1594 pExpr = pExprNew;
1595 rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew);
1596 if (RT_SUCCESS(rc))
1597 pExpr->BinaryOp.pRightExpr = pExprNew;
1598 }
1599
1600 if (RT_SUCCESS(rc))
1601 *ppAstNodeExpr = pExpr;
1602 else
1603 vdScriptAstNodeFree(&pExpr->Core);
1604 }
1605
1606 LogFlowFunc(("returns rc=%Rrc\n", rc));
1607 return rc;
1608}
1609
1610/**
1611 * Parse a shift expression.
1612 *
1613 * @returns VBox status code.
1614 * @param pThis The script context.
1615 * @param ppAstNodeExpr Where to store the expression AST node on success.
1616 *
1617 * @note Syntax:
1618 * shift-expression:
1619 * additive-expression
1620 * shift-expression << additive-expression
1621 * shift-expression >> additive-expression
1622 */
1623static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1624{
1625 int rc = VINF_SUCCESS;
1626 PVDSCRIPTASTEXPR pExpr = NULL;
1627
1628 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1629
1630 rc = vdScriptParseAdditiveExpression(pThis, &pExpr);
1631 if (RT_SUCCESS(rc))
1632 {
1633 PVDSCRIPTASTEXPR pExprNew = NULL;
1634 while (RT_SUCCESS(rc))
1635 {
1636 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<"))
1637 {
1638 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1639 if (pExprNew)
1640 pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL;
1641 else
1642 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1643 }
1644 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>"))
1645 {
1646 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1647 if (pExprNew)
1648 pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR;
1649 else
1650 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1651 }
1652 else
1653 break;
1654
1655 pExprNew->BinaryOp.pLeftExpr = pExpr;
1656 pExpr = pExprNew;
1657 rc = vdScriptParseAdditiveExpression(pThis, &pExprNew);
1658 if (RT_SUCCESS(rc))
1659 pExpr->BinaryOp.pRightExpr = pExprNew;
1660 }
1661
1662 if (RT_SUCCESS(rc))
1663 *ppAstNodeExpr = pExpr;
1664 else
1665 vdScriptAstNodeFree(&pExpr->Core);
1666 }
1667
1668 LogFlowFunc(("returns rc=%Rrc\n", rc));
1669 return rc;
1670}
1671
1672/**
1673 * Parse a relational expression.
1674 *
1675 * @returns VBox status code.
1676 * @param pThis The script context.
1677 * @param ppAstNodeExpr Where to store the expression AST node on success.
1678 *
1679 * @note Syntax:
1680 * relational-expression:
1681 * shift-expression
1682 * relational-expression < shift-expression
1683 * relational-expression > shift-expression
1684 * relational-expression >= shift-expression
1685 * relational-expression <= shift-expression
1686 */
1687static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1688{
1689 int rc = VINF_SUCCESS;
1690 PVDSCRIPTASTEXPR pExpr = NULL;
1691
1692 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1693
1694 rc = vdScriptParseShiftExpression(pThis, &pExpr);
1695 if (RT_SUCCESS(rc))
1696 {
1697 PVDSCRIPTASTEXPR pExprNew = NULL;
1698 while (RT_SUCCESS(rc))
1699 {
1700 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<"))
1701 {
1702 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1703 if (pExprNew)
1704 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER;
1705 else
1706 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1707 }
1708 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">"))
1709 {
1710 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1711 if (pExprNew)
1712 pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER;
1713 else
1714 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1715 }
1716 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">="))
1717 {
1718 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1719 if (pExprNew)
1720 pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL;
1721 else
1722 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1723 }
1724 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<="))
1725 {
1726 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1727 if (pExprNew)
1728 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL;
1729 else
1730 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1731 }
1732 else
1733 break;
1734
1735 pExprNew->BinaryOp.pLeftExpr = pExpr;
1736 pExpr = pExprNew;
1737 rc = vdScriptParseShiftExpression(pThis, &pExprNew);
1738 if (RT_SUCCESS(rc))
1739 pExpr->BinaryOp.pRightExpr = pExprNew;
1740 }
1741
1742 if (RT_SUCCESS(rc))
1743 *ppAstNodeExpr = pExpr;
1744 else
1745 vdScriptAstNodeFree(&pExpr->Core);
1746 }
1747
1748 LogFlowFunc(("returns rc=%Rrc\n", rc));
1749 return rc;
1750}
1751
1752/**
1753 * Parse a equality expression.
1754 *
1755 * @returns VBox status code.
1756 * @param pThis The script context.
1757 * @param ppAstNodeExpr Where to store the expression AST node on success.
1758 *
1759 * @note Syntax:
1760 * equality-expression:
1761 * relational-expression
1762 * equality-expression == relational-expression
1763 * equality-expression != relational-expression
1764 */
1765static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1766{
1767 int rc = VINF_SUCCESS;
1768 PVDSCRIPTASTEXPR pExpr = NULL;
1769
1770 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1771
1772 rc = vdScriptParseRelationalExpression(pThis, &pExpr);
1773 if (RT_SUCCESS(rc))
1774 {
1775 PVDSCRIPTASTEXPR pExprNew = NULL;
1776 while (RT_SUCCESS(rc))
1777 {
1778 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=="))
1779 {
1780 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1781 if (pExprNew)
1782 pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
1783 else
1784 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1785 }
1786 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!="))
1787 {
1788 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1789 if (pExprNew)
1790 pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL;
1791 else
1792 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1793 }
1794 else
1795 break;
1796
1797 pExprNew->BinaryOp.pLeftExpr = pExpr;
1798 pExpr = pExprNew;
1799 rc = vdScriptParseRelationalExpression(pThis, &pExprNew);
1800 if (RT_SUCCESS(rc))
1801 pExpr->BinaryOp.pRightExpr = pExprNew;
1802 }
1803
1804 if (RT_SUCCESS(rc))
1805 *ppAstNodeExpr = pExpr;
1806 else
1807 vdScriptAstNodeFree(&pExpr->Core);
1808 }
1809
1810 LogFlowFunc(("returns rc=%Rrc\n", rc));
1811 return rc;
1812}
1813
1814/**
1815 * Parse a bitwise and expression.
1816 *
1817 * @returns VBox status code.
1818 * @param pThis The script context.
1819 * @param ppAstNodeExpr Where to store the expression AST node on success.
1820 *
1821 * @note Syntax:
1822 * and-expression:
1823 * equality-expression
1824 * and-expression & equality-expression
1825 */
1826static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1827{
1828 int rc = VINF_SUCCESS;
1829 PVDSCRIPTASTEXPR pExpr = NULL;
1830
1831 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1832
1833 rc = vdScriptParseEqualityExpression(pThis, &pExpr);
1834 if (RT_SUCCESS(rc))
1835 {
1836 PVDSCRIPTASTEXPR pExprNew = NULL;
1837 while ( RT_SUCCESS(rc)
1838 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&"))
1839 {
1840 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1841 if (pExprNew)
1842 {
1843 pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
1844 pExprNew->BinaryOp.pLeftExpr = pExpr;
1845 pExpr = pExprNew;
1846 rc = vdScriptParseEqualityExpression(pThis, &pExprNew);
1847 if (RT_SUCCESS(rc))
1848 pExpr->BinaryOp.pRightExpr = pExprNew;
1849 }
1850 else
1851 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1852 }
1853
1854 if (RT_SUCCESS(rc))
1855 *ppAstNodeExpr = pExpr;
1856 else
1857 vdScriptAstNodeFree(&pExpr->Core);
1858 }
1859
1860 LogFlowFunc(("returns rc=%Rrc\n", rc));
1861 return rc;
1862}
1863
1864/**
1865 * Parse a bitwise xor expression.
1866 *
1867 * @returns VBox status code.
1868 * @param pThis The script context.
1869 * @param ppAstNodeExpr Where to store the expression AST node on success.
1870 *
1871 * @note Syntax:
1872 * xor-expression:
1873 * and-expression
1874 * xor-expression ^ equality-expression
1875 */
1876static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1877{
1878 int rc = VINF_SUCCESS;
1879 PVDSCRIPTASTEXPR pExpr = NULL;
1880
1881 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1882
1883 rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr);
1884 if (RT_SUCCESS(rc))
1885 {
1886 PVDSCRIPTASTEXPR pExprNew = NULL;
1887 while ( RT_SUCCESS(rc)
1888 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^"))
1889 {
1890 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1891 if (pExprNew)
1892 {
1893 pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR;
1894 pExprNew->BinaryOp.pLeftExpr = pExpr;
1895 pExpr = pExprNew;
1896 rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew);
1897 if (RT_SUCCESS(rc))
1898 pExpr->BinaryOp.pRightExpr = pExprNew;
1899 }
1900 else
1901 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1902 }
1903
1904 if (RT_SUCCESS(rc))
1905 *ppAstNodeExpr = pExpr;
1906 else
1907 vdScriptAstNodeFree(&pExpr->Core);
1908 }
1909
1910 LogFlowFunc(("returns rc=%Rrc\n", rc));
1911 return rc;
1912}
1913
1914/**
1915 * Parse a bitwise or expression.
1916 *
1917 * @returns VBox status code.
1918 * @param pThis The script context.
1919 * @param ppAstNodeExpr Where to store the expression AST node on success.
1920 *
1921 * @note Syntax:
1922 * or-expression:
1923 * xor-expression
1924 * or-expression | xor-expression
1925 */
1926static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1927{
1928 int rc = VINF_SUCCESS;
1929 PVDSCRIPTASTEXPR pExpr = NULL;
1930
1931 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1932
1933 rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr);
1934 if (RT_SUCCESS(rc))
1935 {
1936 PVDSCRIPTASTEXPR pExprNew = NULL;
1937 while ( RT_SUCCESS(rc)
1938 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|"))
1939 {
1940 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1941 if (pExprNew)
1942 {
1943 pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR;
1944 pExprNew->BinaryOp.pLeftExpr = pExpr;
1945 pExpr = pExprNew;
1946 rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew);
1947 if (RT_SUCCESS(rc))
1948 pExpr->BinaryOp.pRightExpr = pExprNew;
1949 }
1950 else
1951 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
1952 }
1953
1954 if (RT_SUCCESS(rc))
1955 *ppAstNodeExpr = pExpr;
1956 else
1957 vdScriptAstNodeFree(&pExpr->Core);
1958 }
1959
1960 LogFlowFunc(("returns rc=%Rrc\n", rc));
1961 return rc;
1962}
1963
1964/**
1965 * Parse a logical and expression.
1966 *
1967 * @returns VBox status code.
1968 * @param pThis The script context.
1969 * @param ppAstNodeExpr Where to store the expression AST node on success.
1970 *
1971 * @note Syntax:
1972 * logical-and-expression:
1973 * or-expression
1974 * logical-and-expression | or-expression
1975 */
1976static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
1977{
1978 int rc = VINF_SUCCESS;
1979 PVDSCRIPTASTEXPR pExpr = NULL;
1980
1981 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
1982
1983 rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr);
1984 if (RT_SUCCESS(rc))
1985 {
1986 PVDSCRIPTASTEXPR pExprNew = NULL;
1987 while ( RT_SUCCESS(rc)
1988 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&"))
1989 {
1990 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
1991 if (pExprNew)
1992 {
1993 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND;
1994 pExprNew->BinaryOp.pLeftExpr = pExpr;
1995 pExpr = pExprNew;
1996 rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew);
1997 if (RT_SUCCESS(rc))
1998 pExpr->BinaryOp.pRightExpr = pExprNew;
1999 }
2000 else
2001 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2002 }
2003
2004 if (RT_SUCCESS(rc))
2005 *ppAstNodeExpr = pExpr;
2006 else
2007 vdScriptAstNodeFree(&pExpr->Core);
2008 }
2009
2010 LogFlowFunc(("returns rc=%Rrc\n", rc));
2011 return rc;
2012}
2013
2014/**
2015 * Parse a logical or expression.
2016 *
2017 * @returns VBox status code.
2018 * @param pThis The script context.
2019 * @param ppAstNodeExpr Where to store the expression AST node on success.
2020 *
2021 * @note Syntax:
2022 * logical-or-expression:
2023 * logical-and-expression
2024 * logical-or-expression | logical-and-expression
2025 */
2026static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
2027{
2028 int rc = VINF_SUCCESS;
2029 PVDSCRIPTASTEXPR pExpr = NULL;
2030
2031 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
2032
2033 rc = vdScriptParseLogicalAndExpression(pThis, &pExpr);
2034 if (RT_SUCCESS(rc))
2035 {
2036 PVDSCRIPTASTEXPR pExprNew = NULL;
2037 while ( RT_SUCCESS(rc)
2038 && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||"))
2039 {
2040 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2041 if (pExprNew)
2042 {
2043 pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR;
2044 pExprNew->BinaryOp.pLeftExpr = pExpr;
2045 pExpr = pExprNew;
2046 rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew);
2047 if (RT_SUCCESS(rc))
2048 pExpr->BinaryOp.pRightExpr = pExprNew;
2049 }
2050 else
2051 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2052 }
2053
2054 if (RT_SUCCESS(rc))
2055 *ppAstNodeExpr = pExpr;
2056 else
2057 vdScriptAstNodeFree(&pExpr->Core);
2058 }
2059
2060 LogFlowFunc(("returns rc=%Rrc\n", rc));
2061 return rc;
2062}
2063
2064/**
2065 * Parse a conditional expression.
2066 *
2067 * @returns VBox status code.
2068 * @param pThis The script context.
2069 * @param ppAstNodeExpr Where to store the expression AST node on success.
2070 *
2071 * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression
2072 * so a conditional expression is equal to a logical-or-expression.
2073 */
2074static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
2075{
2076 return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr);
2077}
2078
2079#if 0 /* unused */
2080/**
2081 * Parse a constant expression.
2082 *
2083 * @returns VBox status code.
2084 * @param pThis The script context.
2085 * @param ppAstNodeExpr Where to store the expression AST node on success.
2086 *
2087 * @note Syntax:
2088 * constant-expression:
2089 * conditional-expression
2090 */
2091static int vdScriptParseConstExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
2092{
2093 return vdScriptParseCondExpression(pThis, ppAstNodeExpr);
2094}
2095#endif
2096
2097/**
2098 * Parse an assignment expression.
2099 *
2100 * @returns VBox status code.
2101 * @param pThis The script context.
2102 * @param ppAstNodeExpr Where to store the expression AST node on success.
2103 *
2104 * @note Syntax:
2105 * assignment-expression:
2106 * conditional-expression
2107 * unary-expression assignment-operator assignment-expression
2108 */
2109static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
2110{
2111 int rc = VINF_SUCCESS;
2112 PVDSCRIPTASTEXPR pExpr;
2113
2114 LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr));
2115
2116 rc = vdScriptParseLogicalOrExpression(pThis, &pExpr);
2117 if (RT_SUCCESS(rc))
2118 {
2119 PVDSCRIPTASTEXPR pExprNew = NULL;
2120 while (RT_SUCCESS(rc))
2121 {
2122 if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "="))
2123 {
2124 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2125 if (pExprNew)
2126 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN;
2127 else
2128 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2129 }
2130 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*="))
2131 {
2132 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2133 if (pExprNew)
2134 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT;
2135 else
2136 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2137 }
2138 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/="))
2139 {
2140 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2141 if (pExprNew)
2142 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV;
2143 else
2144 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2145 }
2146 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%="))
2147 {
2148 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2149 if (pExprNew)
2150 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD;
2151 else
2152 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2153 }
2154 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+="))
2155 {
2156 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2157 if (pExprNew)
2158 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD;
2159 else
2160 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2161 }
2162 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-="))
2163 {
2164 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2165 if (pExprNew)
2166 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB;
2167 else
2168 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2169 }
2170 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<="))
2171 {
2172 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2173 if (pExprNew)
2174 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL;
2175 else
2176 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2177 }
2178 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>="))
2179 {
2180 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2181 if (pExprNew)
2182 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR;
2183 else
2184 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2185 }
2186 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&="))
2187 {
2188 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2189 if (pExprNew)
2190 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND;
2191 else
2192 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2193 }
2194 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^="))
2195 {
2196 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2197 if (pExprNew)
2198 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR;
2199 else
2200 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2201 }
2202 else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|="))
2203 {
2204 pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
2205 if (pExprNew)
2206 pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR;
2207 else
2208 rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
2209 }
2210 else
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.

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