VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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