VirtualBox

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

Last change on this file since 102493 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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