VirtualBox

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

Last change on this file since 53654 was 52415, checked in by vboxsync, 10 years ago

Storage/tstVDIo: fixed a typo and use RT_STR_TUPLE()

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