diff options
Diffstat (limited to 'src/VBox/Storage/testcase/VDScript.cpp')
-rw-r--r-- | src/VBox/Storage/testcase/VDScript.cpp | 3020 |
1 files changed, 3020 insertions, 0 deletions
diff --git a/src/VBox/Storage/testcase/VDScript.cpp b/src/VBox/Storage/testcase/VDScript.cpp new file mode 100644 index 00000000..95a4c118 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScript.cpp @@ -0,0 +1,3020 @@ +/* $Id: VDScript.cpp $ */ +/** @file + * VBox HDD container test utility - scripting engine. + */ + +/* + * Copyright (C) 2013-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/** @page pg_vd_script VDScript - Simple scripting language for VD I/O testing. + * + * This component implements a very simple scripting language to make testing the VD + * library more flexible and testcases faster to implement without the need to recompile + * everything after it changed. + * The language is a small subset of the C language. It doesn't support unions, structs, + * global variables, typedefed types or pointers (yet). It also adds a boolean and a string type. + * Strings are immutable and only to print messages from the script. + * There are also not the default types like int or unsigned because theire ranges are architecture + * dependent. Instead VDScript uses uint8_t, int8_t, ... as primitive types. + * + * Why inventing a completely new language? + * + * Well it is not a completely new language to start with, it is a subset of C and the + * language can be extended later on to reach the full C language later on. + * Second, there is no static typed scripting language I like which could be implemented + * and finally because I can ;) + * The code implementing the scripting engine is designed to be easily incorporated into other + * code. Could be used as a scripting language for the VBox debugger for example or in the scm + * tool to automatically rewrite C code using the AST VDSCript generates... + * + * The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken + * from: http://www.csci.csusb.edu/dick/samples/c.syntax.html + * and: http://slps.github.com/zoo/c/iso-9899-tc3.html + * and: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf + */ + +#define LOGGROUP LOGGROUP_DEFAULT +#include <iprt/ctype.h> +#include <iprt/errcore.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/stream.h> +#include <iprt/string.h> + +#include <VBox/log.h> + +#include "VDScriptAst.h" +#include "VDScriptInternal.h" + +/** + * VD script token class. + */ +typedef enum VDTOKENCLASS +{ + /** Invalid. */ + VDTOKENCLASS_INVALID = 0, + /** Identifier class. */ + VDTOKENCLASS_IDENTIFIER, + /** Numerical constant. */ + VDTOKENCLASS_NUMCONST, + /** String constant. */ + VDTOKENCLASS_STRINGCONST, + /** Operators */ + VDTOKENCLASS_OPERATORS, + /** Reserved keyword */ + VDTOKENCLASS_KEYWORD, + /** Punctuator */ + VDTOKENCLASS_PUNCTUATOR, + /** End of stream */ + VDTOKENCLASS_EOS, + /** 32bit hack. */ + VDTOKENCLASS_32BIT_HACK = 0x7fffffff +} VDTOKENCLASS; +/** Pointer to a token class. */ +typedef VDTOKENCLASS *PVDTOKENCLASS; + +/** + * Keyword types. + */ +typedef enum VDSCRIPTTOKENKEYWORD +{ + VDSCRIPTTOKENKEYWORD_INVALID = 0, + VDSCRIPTTOKENKEYWORD_CONTINUE, + VDSCRIPTTOKENKEYWORD_REGISTER, + VDSCRIPTTOKENKEYWORD_RESTRICT, + VDSCRIPTTOKENKEYWORD_VOLATILE, + VDSCRIPTTOKENKEYWORD_TYPEDEF, + VDSCRIPTTOKENKEYWORD_DEFAULT, + VDSCRIPTTOKENKEYWORD_EXTERN, + VDSCRIPTTOKENKEYWORD_STATIC, + VDSCRIPTTOKENKEYWORD_RETURN, + VDSCRIPTTOKENKEYWORD_SWITCH, + VDSCRIPTTOKENKEYWORD_STRUCT, + VDSCRIPTTOKENKEYWORD_WHILE, + VDSCRIPTTOKENKEYWORD_BREAK, + VDSCRIPTTOKENKEYWORD_CONST, + VDSCRIPTTOKENKEYWORD_FALSE, + VDSCRIPTTOKENKEYWORD_TRUE, + VDSCRIPTTOKENKEYWORD_ELSE, + VDSCRIPTTOKENKEYWORD_CASE, + VDSCRIPTTOKENKEYWORD_AUTO, + VDSCRIPTTOKENKEYWORD_FOR, + VDSCRIPTTOKENKEYWORD_IF, + VDSCRIPTTOKENKEYWORD_DO, + VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff +} VDSCRIPTTOKENKEYWORD; +/** Pointer to a keyword type. */ +typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD; + +/** + * VD script token. + */ +typedef struct VDSCRIPTTOKEN +{ + /** Token class. */ + VDTOKENCLASS enmClass; + /** Token position in the source buffer. */ + VDSRCPOS Pos; + /** Data based on the token class. */ + union + { + /** Identifier. */ + struct + { + /** Pointer to the start of the identifier. */ + const char *pszIde; + /** Number of characters for the identifier excluding the null terminator. */ + size_t cchIde; + } Ide; + /** Numerical constant. */ + struct + { + uint64_t u64; + } NumConst; + /** String constant */ + struct + { + /** Pointer to the start of the string constant. */ + const char *pszString; + /** Number of characters of the string, including the null terminator. */ + size_t cchString; + } StringConst; + /** Operator */ + struct + { + /** The operator string. */ + char aszOp[4]; /** Maximum of 3 for >>= + null terminator. */ + } Operator; + /** Keyword. */ + struct + { + /** The keyword type. */ + VDSCRIPTTOKENKEYWORD enmKeyword; + } Keyword; + /** Punctuator. */ + struct + { + /** The punctuator in question. */ + char chPunctuator; + } Punctuator; + } Class; +} VDSCRIPTTOKEN; +/** Pointer to a script token. */ +typedef VDSCRIPTTOKEN *PVDSCRIPTTOKEN; +/** Pointer to a const script token. */ +typedef const VDSCRIPTTOKEN *PCVDSCRIPTTOKEN; + +/** + * Tokenizer state. + */ +typedef struct VDTOKENIZER +{ + /** Char buffer to read from. */ + const char *pszInput; + /** Current position ininput buffer. */ + VDSRCPOS Pos; + /** Token 1. */ + VDSCRIPTTOKEN Token1; + /** Token 2. */ + VDSCRIPTTOKEN Token2; + /** Pointer to the current active token. */ + PVDSCRIPTTOKEN pTokenCurr; + /** The next token in the input stream (used for peeking). */ + PVDSCRIPTTOKEN pTokenNext; +} VDTOKENIZER; + +/** + * Operators entry. + */ +typedef struct VDSCRIPTOP +{ + /** Operator string. */ + const char *pszOp; + /** Size of the operator in characters without zero terminator. */ + size_t cchOp; +} VDSCRIPTOP; +/** Pointer to a script operator. */ +typedef VDSCRIPTOP *PVDSCRIPTOP; + +/** + * Known operators array, sort from higest character count to lowest. + */ +static VDSCRIPTOP g_aScriptOps[] = +{ + {">>=", 3}, + {"<<=", 3}, + {"+=", 2}, + {"-=", 2}, + {"/=", 2}, + {"%=", 2}, + {"&=", 2}, + {"|=", 2}, + {"^=", 2}, + {"&&", 2}, + {"||", 2}, + {"<<", 2}, + {">>", 2}, + {"++", 2}, + {"--", 2}, + {"==", 2}, + {"!=", 2}, + {">=", 2}, + {"<=", 2}, + {"->", 2}, + {"=", 1}, + {"+", 1}, + {"-", 1}, + {"*", 1}, + {"/", 1}, + {"%", 1}, + {"|", 1}, + {"&", 1}, + {"^", 1}, + {"<", 1}, + {">", 1}, + {"!", 1}, + {"~", 1}, + {".", 1} +}; + +/** + * Known punctuators. + */ +static VDSCRIPTOP g_aScriptPunctuators[] = +{ + {"(", 1}, + {")", 1}, + {"{", 1}, + {"}", 1}, + {",", 1}, + {";", 1}, +}; + +/** + * Keyword entry. + */ +typedef struct VDSCRIPTKEYWORD +{ + /** Keyword string. */ + const char *pszKeyword; + /** Size of the string in characters without zero terminator. */ + size_t cchKeyword; + /** Keyword type. */ + VDSCRIPTTOKENKEYWORD enmKeyword; +} VDSCRIPTKEYWORD; +/** */ +typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD; + +/** + * Known keywords. + */ +static VDSCRIPTKEYWORD g_aKeywords[] = +{ + {RT_STR_TUPLE("continue"), VDSCRIPTTOKENKEYWORD_CONTINUE}, + {RT_STR_TUPLE("register"), VDSCRIPTTOKENKEYWORD_REGISTER}, + {RT_STR_TUPLE("restrict"), VDSCRIPTTOKENKEYWORD_RESTRICT}, + {RT_STR_TUPLE("volatile"), VDSCRIPTTOKENKEYWORD_VOLATILE}, + {RT_STR_TUPLE("typedef"), VDSCRIPTTOKENKEYWORD_TYPEDEF}, + {RT_STR_TUPLE("default"), VDSCRIPTTOKENKEYWORD_DEFAULT}, + {RT_STR_TUPLE("extern"), VDSCRIPTTOKENKEYWORD_EXTERN}, + {RT_STR_TUPLE("static"), VDSCRIPTTOKENKEYWORD_STATIC}, + {RT_STR_TUPLE("return"), VDSCRIPTTOKENKEYWORD_RETURN}, + {RT_STR_TUPLE("switch"), VDSCRIPTTOKENKEYWORD_SWITCH}, + {RT_STR_TUPLE("struct"), VDSCRIPTTOKENKEYWORD_STRUCT}, + {RT_STR_TUPLE("while"), VDSCRIPTTOKENKEYWORD_WHILE}, + {RT_STR_TUPLE("break"), VDSCRIPTTOKENKEYWORD_BREAK}, + {RT_STR_TUPLE("const"), VDSCRIPTTOKENKEYWORD_CONST}, + {RT_STR_TUPLE("false"), VDSCRIPTTOKENKEYWORD_FALSE}, + {RT_STR_TUPLE("true"), VDSCRIPTTOKENKEYWORD_TRUE}, + {RT_STR_TUPLE("else"), VDSCRIPTTOKENKEYWORD_ELSE}, + {RT_STR_TUPLE("case"), VDSCRIPTTOKENKEYWORD_CASE}, + {RT_STR_TUPLE("auto"), VDSCRIPTTOKENKEYWORD_AUTO}, + {RT_STR_TUPLE("for"), VDSCRIPTTOKENKEYWORD_FOR}, + {RT_STR_TUPLE("if"), VDSCRIPTTOKENKEYWORD_IF}, + {RT_STR_TUPLE("do"), VDSCRIPTTOKENKEYWORD_DO} +}; + +static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound); +static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt); +static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); +static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); +static int vdScriptParseCastExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); +#if 0 /* unused */ +static int vdScriptParseConstExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); +#endif + +/** + * Returns whether the tokenizer reached the end of the stream. + * + * @returns true if the tokenizer reached the end of stream marker + * false otherwise. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(bool) vdScriptTokenizerIsEos(PVDTOKENIZER pTokenizer) +{ + return *pTokenizer->pszInput == '\0'; +} + +/** + * Skip one character in the input stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer) +{ + pTokenizer->pszInput++; + pTokenizer->Pos.iChStart++; + pTokenizer->Pos.iChEnd++; +} + +/** + * Returns the next char in the input buffer without advancing it. + * + * @returns Next character in the input buffer. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer) +{ + return vdScriptTokenizerIsEos(pTokenizer) + ? '\0' + : *(pTokenizer->pszInput + 1); +} + +/** + * Returns the next character in the input buffer advancing the internal + * position. + * + * @returns Next character in the stream. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer) +{ + char ch; + + if (vdScriptTokenizerIsEos(pTokenizer)) + ch = '\0'; + else + ch = *pTokenizer->pszInput; + + return ch; +} + +/** + * Sets a new line for the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip) +{ + pTokenizer->pszInput += cSkip; + pTokenizer->Pos.iLine++; + pTokenizer->Pos.iChStart = 1; + pTokenizer->Pos.iChEnd = 1; +} + +/** + * Checks whether the current position in the input stream is a new line + * and skips it. + * + * @returns Flag whether there was a new line at the current position + * in the input buffer. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer) +{ + bool fNewline = true; + + if ( vdScriptTokenizerGetCh(pTokenizer) == '\r' + && vdScriptTokenizerPeekCh(pTokenizer) == '\n') + vdScriptTokenizerNewLine(pTokenizer, 2); + else if (vdScriptTokenizerGetCh(pTokenizer) == '\n') + vdScriptTokenizerNewLine(pTokenizer, 1); + else + fNewline = false; + + return fNewline; +} + +/** + * Skips a multi line comment. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer) +{ + while ( !vdScriptTokenizerIsEos(pTokenizer) + && ( vdScriptTokenizerGetCh(pTokenizer) != '*' + || vdScriptTokenizerPeekCh(pTokenizer) != '/')) + { + if (!vdScriptTokenizerIsSkipNewLine(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); + } + + if (!vdScriptTokenizerIsEos(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); + if (!vdScriptTokenizerIsEos(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); +} + +/** + * Skip all whitespace starting from the current input buffer position. + * Skips all present comments too. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer) +{ + while (!vdScriptTokenizerIsEos(pTokenizer)) + { + while ( vdScriptTokenizerGetCh(pTokenizer) == ' ' + || vdScriptTokenizerGetCh(pTokenizer) == '\t') + vdScriptTokenizerSkipCh(pTokenizer); + + if ( !vdScriptTokenizerIsEos(pTokenizer) + && !vdScriptTokenizerIsSkipNewLine(pTokenizer)) + { + if ( vdScriptTokenizerGetCh(pTokenizer) == '/' + && vdScriptTokenizerPeekCh(pTokenizer) == '*') + { + vdScriptTokenizerSkipCh(pTokenizer); + vdScriptTokenizerSkipCh(pTokenizer); + vdScriptTokenizerSkipComment(pTokenizer); + } + else + break; /* Skipped everything, next is some real content. */ + } + } +} + +/** + * Get an identifier token from the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + char ch; + unsigned cchIde = 0; + bool fIsKeyword = false; + const char *pszIde = pTokenizer->pszInput; + + pToken->Pos = pTokenizer->Pos; + + Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' ); + + do + { + cchIde++; + vdScriptTokenizerSkipCh(pTokenizer); + ch = vdScriptTokenizerGetCh(pTokenizer); + } + while (RT_C_IS_ALNUM(ch) || ch == '_'); + + /* Check whether we got an identifier or an reserved keyword. */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++) + { + if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword)) + { + fIsKeyword = true; + pToken->enmClass = VDTOKENCLASS_KEYWORD; + pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword; + break; + } + } + + if (!fIsKeyword) + { + pToken->enmClass = VDTOKENCLASS_IDENTIFIER; + pToken->Class.Ide.pszIde = pszIde; + pToken->Class.Ide.cchIde = cchIde; + } + pToken->Pos.iChEnd += cchIde; +} + +/** + * Get a numerical constant from the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + char *pszNext = NULL; + + Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer))); + + /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */ + pToken->enmClass = VDTOKENCLASS_NUMCONST; + int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64); + Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES); NOREF(rc); + /** @todo Handle number to big, throw a warning */ + + unsigned cchNumber = pszNext - pTokenizer->pszInput; + for (unsigned i = 0; i < cchNumber; i++) + vdScriptTokenizerSkipCh(pTokenizer); + + /* Check for a supported suffix, supported are K|M|G. */ + if (vdScriptTokenizerGetCh(pTokenizer) == 'K') + { + pToken->Class.NumConst.u64 *= _1K; + vdScriptTokenizerSkipCh(pTokenizer); + } + else if (vdScriptTokenizerGetCh(pTokenizer) == 'M') + { + pToken->Class.NumConst.u64 *= _1M; + vdScriptTokenizerSkipCh(pTokenizer); + } + else if (vdScriptTokenizerGetCh(pTokenizer) == 'G') + { + pToken->Class.NumConst.u64 *= _1G; + vdScriptTokenizerSkipCh(pTokenizer); + } + else if (vdScriptTokenizerGetCh(pTokenizer) == 'T') + { + pToken->Class.NumConst.u64 *= _1T; + vdScriptTokenizerSkipCh(pTokenizer); + } +} + +/** + * Parses a string constant. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + * + * @remarks: No escape sequences allowed at this time. + */ +static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + unsigned cchStr = 0; + + Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"'); + vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */ + + pToken->enmClass = VDTOKENCLASS_STRINGCONST; + pToken->Pos = pTokenizer->Pos; + pToken->Class.StringConst.pszString = pTokenizer->pszInput; + + while (vdScriptTokenizerGetCh(pTokenizer) != '\"') + { + cchStr++; + vdScriptTokenizerSkipCh(pTokenizer); + } + + vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */ + + pToken->Class.StringConst.cchString = cchStr; + pToken->Pos.iChEnd += cchStr; +} + +/** + * Get the end of stream token. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0'); + + pToken->enmClass = VDTOKENCLASS_EOS; + pToken->Pos = pTokenizer->Pos; +} + +/** + * Get operator or punctuator token. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + bool fOpFound = false; + + pToken->enmClass = VDTOKENCLASS_INVALID; + pToken->Pos = pTokenizer->Pos; + + /* + * Use table based approach here, not the fastest solution but enough for our purpose + * for now. + */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++) + { + if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp)) + { + memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp)); + + int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp); + AssertRC(rc); + + pToken->enmClass = VDTOKENCLASS_OPERATORS; + pToken->Pos.iChEnd += (unsigned)g_aScriptOps[i].cchOp; + + /** @todo Make this prettier. */ + for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++) + vdScriptTokenizerSkipCh(pTokenizer); + fOpFound = true; + break; + } + } + + if (!fOpFound) + { + for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++) + { + if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp)) + { + pToken->Pos.iChEnd += (unsigned)g_aScriptPunctuators[i].cchOp; + pToken->enmClass = VDTOKENCLASS_PUNCTUATOR; + pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp; + + vdScriptTokenizerSkipCh(pTokenizer); + fOpFound = true; + break; + } + } + } +} + +/** + * Read the next token from the tokenizer stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer to read from. + * @param pToken Uninitialized token to fill the token data into. + */ +static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + /* Skip all eventually existing whitespace, newlines and comments first. */ + vdScriptTokenizerSkipWhitespace(pTokenizer); + + char ch = vdScriptTokenizerGetCh(pTokenizer); + if (RT_C_IS_ALPHA(ch) || ch == '_') + vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken); + else if (RT_C_IS_DIGIT(ch)) + vdScriptTokenizerGetNumberConst(pTokenizer, pToken); + else if (ch == '\"') + vdScriptTokenizerGetStringConst(pTokenizer, pToken); + else if (ch == '\0') + vdScriptTokenizerGetEos(pTokenizer, pToken); + else + vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken); +} + +/** + * Create a new tokenizer. + * + * @returns Pointer to the new tokenizer state on success. + * NULL if out of memory. + * @param pszInput The input to create the tokenizer for. + */ +static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput) +{ + PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER)); + if (pTokenizer) + { + pTokenizer->pszInput = pszInput; + pTokenizer->Pos.iLine = 1; + pTokenizer->Pos.iChStart = 1; + pTokenizer->Pos.iChEnd = 1; + pTokenizer->pTokenCurr = &pTokenizer->Token1; + pTokenizer->pTokenNext = &pTokenizer->Token2; + /* Fill the tokenizer with two first tokens. */ + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr); + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext); + } + + return pTokenizer; +} + +#if 0 /** @todo unused */ +/** + * Destroys a given tokenizer state. + * + * @returns nothing. + * @param pTokenizer The tokenizer to destroy. + */ +static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer) +{ + RTMemFree(pTokenizer); +} +#endif + +/** + * Get the current token in the input stream. + * + * @returns Pointer to the next token in the stream. + * @param pTokenizer The tokenizer to destroy. + */ +DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenCurr; +} + +/** + * Get the class of the current token. + * + * @returns Class of the current token. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenCurr->enmClass; +} + +/** + * Returns the token class of the next token in the stream. + * + * @returns Token class of the next token. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenNext->enmClass; +} + +/** + * Consume the current token advancing to the next in the stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer) +{ + PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr; + + /* Switch next token to current token and read in the next token. */ + pTokenizer->pTokenCurr = pTokenizer->pTokenNext; + pTokenizer->pTokenNext = pTokenTmp; + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext); +} + +/** + * Check whether the next token in the input stream is a punctuator and matches the given + * character. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param chCheck The punctuator to check against. + */ +static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR + && pToken->Class.Punctuator.chPunctuator == chCheck) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is a punctuator and matches the given + * character and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param chCheck The punctuator to check against. + */ +static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck) +{ + bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param enmKey The keyword to check against. + */ +static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_KEYWORD + && pToken->Class.Keyword.enmKeyword == enmKeyword) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param enmKey The keyword to check against. + */ +static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword) +{ + bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param pszOp The operation to check against. + */ +static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_OPERATORS + && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp)) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is an operator and matches the given + * keyword and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param pszOp The operation to check against. + */ +static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp) +{ + bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Record an error while parsing. + * + * @returns VBox status code passed. + */ +static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...) +{ + RT_NOREF1(pThis); RT_SRC_POS_NOREF(); + va_list va; + va_start(va, pszFmt); + RTPrintfV(pszFmt, va); + va_end(va); + return rc; +} + +/** + * Puts the next identifier AST node on the stack. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeIde Where to store the identifier AST node on success. + */ +static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde) +{ + int rc = VINF_SUCCESS; + PCVDSCRIPTTOKEN pToken; + + LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde)); + + pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n"); + else + { + /* Create new AST node and push onto stack. */ + PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde); + if (pAstNodeIde) + { + rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde); + AssertRC(rc); + pAstNodeIde->cchIde = (unsigned)pToken->Class.Ide.cchIde; + + *ppAstNodeIde = pAstNodeIde; + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n"); + } + + LogFlowFunc(("returns %Rrc\n", rc)); + return rc; +} + +/** + * Parse a primary expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the primary expression on success. + */ +static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + rc = vdScriptParseExpression(pThis, ppAstNodeExpr); + if (RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + } + else + { + PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExpr) + { + if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER) + { + PVDSCRIPTASTIDE pIde = NULL; + rc = vdScriptParseIde(pThis, &pIde); + if (RT_SUCCESS(rc)) + { + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER; + pExpr->pIde = pIde; + } + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST; + pExpr->u64 = pToken->Class.NumConst.u64; + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST; + pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString); + vdScriptTokenizerConsume(pThis->pTokenizer); + + if (!pExpr->pszStr) + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n"); + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_KEYWORD) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN; + + if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_TRUE) + pExpr->f = true; + else if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_FALSE) + pExpr->f = false; + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n"); + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pExpr->Core); + else + *ppAstNodeExpr = pExpr; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an argument list for a function call. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pFnCall The function call AST node. + */ +static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p pFnCall=%p\n", pThis, pFnCall)); + + rc = vdScriptParseAssignmentExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode); + while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')) + { + rc = vdScriptParseAssignmentExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode); + else + break; + } + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a postfix expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * postfix-expression: + * primary-expression + * postfix-expression ( argument-expression ) + * postfix-expression ++ + * postfix-expression -- + * postfix-expression . identifier + * postfix-expression -> identifier + * @note: Not supported so far are: + * ( type-name ) { initializer-list } + * ( type-name ) { initializer-list , } + */ +static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParsePrimaryExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + while (true) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT; + pExprNew->pExpr = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT; + pExprNew->pExpr = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "->")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + PVDSCRIPTASTIDE pIde = NULL; + rc = vdScriptParseIde(pThis, &pIde); + if (RT_SUCCESS(rc)) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DEREFERENCE; + pExprNew->Deref.pIde = pIde; + pExprNew->Deref.pExpr = pExpr; + pExpr = pExprNew; + } + else + vdScriptAstNodeFree(&pExprNew->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ".")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + PVDSCRIPTASTIDE pIde = NULL; + rc = vdScriptParseIde(pThis, &pIde); + if (RT_SUCCESS(rc)) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DOT; + pExprNew->Deref.pIde = pIde; + pExprNew->Deref.pExpr = pExpr; + pExpr = pExprNew; + } + else + vdScriptAstNodeFree(&pExprNew->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL; + RTListInit(&pExprNew->FnCall.ListArgs); + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParseFnCallArgumentList(pThis, pExprNew); + pExprNew->FnCall.pFnIde = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + if (RT_FAILURE(rc)) + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an unary expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * unary-expression: + * postfix-expression + * ++ unary-expression + * -- unary-expression + * + cast-expression + * - cast-expression + * ~ cast-expression + * ! cast-expression + * & cast-expression + * * cast-expression + */ +static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + PVDSCRIPTASTEXPR pExprTop = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + /** @todo Think about a more beautiful way of parsing this. */ + while (true) + { + bool fQuit = false; + bool fCastExprFollows = false; + PVDSCRIPTASTEXPR pExprNew = NULL; + VDSCRIPTEXPRTYPE enmType = VDSCRIPTEXPRTYPE_INVALID; + + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++")) + enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT; + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--")) + enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT; + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN; + fCastExprFollows = true; + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN; + fCastExprFollows = true; + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT; + fCastExprFollows = true; + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE; + fCastExprFollows = true; + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_REFERENCE; + fCastExprFollows = true; + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*")) + { + enmType = VDSCRIPTEXPRTYPE_UNARY_DEREFERENCE; + fCastExprFollows = true; + } + + if (enmType != VDSCRIPTEXPRTYPE_INVALID) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = enmType; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + + if ( RT_SUCCESS(rc) + && fCastExprFollows) + { + PVDSCRIPTASTEXPR pCastExpr = NULL; + + rc = vdScriptParseCastExpression(pThis, &pCastExpr); + if (RT_SUCCESS(rc)) + pExprNew->pExpr = pCastExpr; + else + vdScriptAstNodeFree(&pExprNew->Core); + fQuit = true; + } + } + else + { + /* Must be a postfix expression. */ + rc = vdScriptParsePostfixExpression(pThis, &pExprNew); + fQuit = true; + } + + if (RT_SUCCESS(rc)) + { + if (!pExprTop) + { + pExprTop = pExprNew; + pExpr = pExprNew; + } + else + { + pExpr->pExpr = pExprNew; + pExpr = pExprNew; + } + if (fQuit) + break; + } + else + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExprTop; + else if (pExprTop) + vdScriptAstNodeFree(&pExprTop->Core); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +#if 0 /* unused */ +/** + * Parse a storage class specifier. + * + * @returns nothing. + * @param pThis The script context. + * @param penmStorageClass Where to return the parsed storage classe. + * Contains VDSCRIPTASTSTORAGECLASS_INVALID if no + * valid storage class specifier was found. + * + * @note Syntax: + * typedef + * extern + * static + * auto + * register + */ +static void vdScriptParseStorageClassSpecifier(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTORAGECLASS penmStorageClass) +{ + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_INVALID; + + if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_TYPEDEF)) + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_TYPEDEF; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_EXTERN)) + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_EXTERN; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_STATIC)) + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_STATIC; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_AUTO)) + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_AUTO; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_REGISTER)) + *penmStorageClass = VDSCRIPTASTSTORAGECLASS_REGISTER; +} +#endif /* unused */ + +#if 0 /* unused */ +/** + * Parse a type qualifier. + * + * @returns nothing. + * @param pThis The script context. + * @param penmTypeQualifier Where to return the parsed type qualifier. + * Contains VDSCRIPTASTTYPEQUALIFIER_INVALID if no + * valid type qualifier was found. + * + * @note Syntax: + * const + * restrict + * volatile + */ +static void vdScriptParseTypeQualifier(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTTYPEQUALIFIER penmTypeQualifier) +{ + *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_INVALID; + + if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONST)) + *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_CONST; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RESTRICT)) + *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_RESTRICT; + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_VOLATILE)) + *penmTypeQualifier = VDSCRIPTASTTYPEQUALIFIER_VOLATILE; +} +#endif /* unused */ + +#if 0 +/** + * Parse a struct or union specifier. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstTypeSpec Where to store the type specifier AST node on success. + * @param enmTypeSpecifier The type specifier to identify whete this is a struct or a union. + */ +static int vdScriptParseStructOrUnionSpecifier(PVDSCRIPTCTXINT pThis, , enmTypeSpecifier) +{ + int rc = VINF_SUCCESS; + + return rc; +} + +/** + * Parse a type specifier. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstTypeSpec Where to store the type specifier AST node on success. + * + * @note Syntax: + * struct-or-union-specifier + * enum-specifier + * typedef-name (identifier: includes void, bool, uint8_t, int8_t, ... for basic integer types) + */ +static int vdScriptParseTypeSpecifier(PVDSCRIPTCTXINT pThis, ) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_STRUCT)) + rc = vdScriptParseStructOrUnionSpecifier(pThis, , VDSCRIPTASTTYPESPECIFIER_STRUCT); + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_UNION)) + rc = vdScriptParseStructOrUnionSpecifier(pThis, , VDSCRIPTASTTYPESPECIFIER_UNION); + else + { + PVDSCRIPTASTIDE pIde = NULL; + + rc = vdScriptParseIde(pThis, &pIde); + if (RT_SUCCESS(rc)) + { + AssertMsgFailed(("TODO\n")); /* Parse identifier. */ + } + } + + return rc; +} +#endif + +/** + * Parse a cast expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * cast-expression: + * unary-expression + * ( type-name ) cast-expression + */ +static int vdScriptParseCastExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + +#if 0 + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTTYPE pTypeName = NULL; + rc = vdScriptParseTypeName(pThis, &pTypeName); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExpr) + { + pExpr->enmType = VDSCRIPTEXPRTYPE_CAST; + rc = vdScriptParseCastExpression(pThis, &pExpr->Cast.pExpr); /** @todo Kill recursion. */ + if (RT_SUCCESS(rc)) + pExpr->Cast.pTypeName = pTypeName; + else + vdScriptAstNodeFree(&pExpr->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pTypeName->Core); + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + } + else +#endif + rc = vdScriptParseUnaryExpression(pThis, ppAstNodeExpr); + + return rc; +} + +/** + * Parse a multiplicative expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * multiplicative-expression: + * cast-expression + * multiplicative-expression * cast-expression + * multiplicative-expression / cast-expression + * multiplicative-expression % cast-expression + */ +static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseCastExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + while (RT_SUCCESS(rc)) + { + VDSCRIPTEXPRTYPE enmType = VDSCRIPTEXPRTYPE_INVALID; + PVDSCRIPTASTEXPR pExprNew = NULL; + + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*")) + enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION; + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/")) + enmType = VDSCRIPTEXPRTYPE_DIVISION; + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%")) + enmType = VDSCRIPTEXPRTYPE_MODULUS; + else + break; + + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = enmType; + else + { + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + break; + } + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseCastExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a additive expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * additive-expression: + * multiplicative-expression + * additive-expression + multiplicative-expression + * additive-expression - multiplicative-expression + */ +static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a shift expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * shift-expression: + * additive-expression + * shift-expression << additive-expression + * shift-expression >> additive-expression + */ +static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseAdditiveExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseAdditiveExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a relational expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * relational-expression: + * shift-expression + * relational-expression < shift-expression + * relational-expression > shift-expression + * relational-expression >= shift-expression + * relational-expression <= shift-expression + */ +static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseShiftExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseShiftExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a equality expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * equality-expression: + * relational-expression + * equality-expression == relational-expression + * equality-expression != relational-expression + */ +static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseRelationalExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "==")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseRelationalExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise and expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * and-expression: + * equality-expression + * and-expression & equality-expression + */ +static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseEqualityExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseEqualityExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise xor expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * xor-expression: + * and-expression + * xor-expression ^ equality-expression + */ +static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise or expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * or-expression: + * xor-expression + * or-expression | xor-expression + */ +static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a logical and expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * logical-and-expression: + * or-expression + * logical-and-expression | or-expression + */ +static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a logical or expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * logical-or-expression: + * logical-and-expression + * logical-or-expression | logical-and-expression + */ +static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseLogicalAndExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a conditional expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression + * so a conditional expression is equal to a logical-or-expression. + */ +static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr); +} + +#if 0 /* unused */ +/** + * Parse a constant expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * constant-expression: + * conditional-expression + */ +static int vdScriptParseConstExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + return vdScriptParseCondExpression(pThis, ppAstNodeExpr); +} +#endif + +/** + * Parse an assignment expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * assignment-expression: + * conditional-expression + * unary-expression assignment-operator assignment-expression + */ +static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseLogicalOrExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseLogicalOrExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * expression: + * assignment-expression + * expression , assignment-expression + */ +static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pAssignExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n", pThis, ppAstNodeExpr)); + + rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')) + { + PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pListAssignExpr) + { + pListAssignExpr->enmType = VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST; + RTListInit(&pListAssignExpr->ListExpr); + RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode); + do + { + rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr); + if (RT_SUCCESS(rc)) + RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode); + } while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pListAssignExpr->Core); + else + *ppAstNodeExpr = pListAssignExpr; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pAssignExpr; + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an if statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeIf Uninitialized if AST node. + * + * @note The caller skipped the "if" token already. + */ +static int vdScriptParseIf(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIF pAstNodeIf) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pCondExpr = NULL; + rc = vdScriptParseExpression(pThis, &pCondExpr); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + PVDSCRIPTASTSTMT pElseStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_ELSE)) + rc = vdScriptParseStatement(pThis, &pElseStmt); + + if (RT_SUCCESS(rc)) + { + pAstNodeIf->pCond = pCondExpr; + pAstNodeIf->pTrueStmt = pStmt; + pAstNodeIf->pElseStmt = pElseStmt; + } + else if (pStmt) + vdScriptAstNodeFree(&pStmt->Core); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pCondExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a switch statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeSwitch Uninitialized switch AST node. + * + * @note The caller skipped the "switch" token already. + */ +static int vdScriptParseSwitch(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSWITCH pAstNodeSwitch) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeSwitch->pCond = pExpr; + pAstNodeSwitch->pStmt = pStmt; + } + else + vdScriptAstNodeFree(&pExpr->Core); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a while or do ... while statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeWhile Uninitialized while AST node. + * + * @note The caller skipped the "while" or "do" token already. + */ +static int vdScriptParseWhile(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTWHILE pAstNodeWhile, bool fDoWhile) +{ + int rc = VINF_SUCCESS; + + pAstNodeWhile->fDoWhile = fDoWhile; + + if (fDoWhile) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + pAstNodeWhile->pCond = pExpr; + pAstNodeWhile->pStmt = pStmt; + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"while\", got ...\n"); + + if ( RT_FAILURE(rc) + && pStmt) + vdScriptAstNodeFree(&pStmt->Core); + } + else + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeWhile->pCond = pExpr; + pAstNodeWhile->pStmt = pStmt; + } + else + vdScriptAstNodeFree(&pExpr->Core); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + } + + return rc; +} + +/** + * Parse a for statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeFor Uninitialized for AST node. + * + * @note The caller skipped the "for" token already. + */ +static int vdScriptParseFor(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTFOR pAstNodeFor) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExprStart = NULL; + PVDSCRIPTASTEXPR pExprCond = NULL; + PVDSCRIPTASTEXPR pExpr3 = NULL; + + rc = vdScriptParseExpression(pThis, &pExprStart); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pExprCond); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pExpr3); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeFor->pExprStart = pExprStart; + pAstNodeFor->pExprCond = pExprCond; + pAstNodeFor->pExpr3 = pExpr3; + pAstNodeFor->pStmt = pStmt; + } + } + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + + if (RT_FAILURE(rc)) + { + if (pExprStart) + vdScriptAstNodeFree(&pExprStart->Core); + if (pExprCond) + vdScriptAstNodeFree(&pExprCond->Core); + if (pExpr3) + vdScriptAstNodeFree(&pExpr3->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a declaration. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeDecl Where to store the declaration AST node on success. + */ +static int vdScriptParseDeclaration(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTDECL *ppAstNodeDecl) +{ + int rc = VERR_NOT_IMPLEMENTED; + RT_NOREF2(pThis, ppAstNodeDecl); + return rc; +} + +/** + * Parse a statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeStmt Where to store the statement AST node on success. + */ +static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt) +{ + int rc = VINF_SUCCESS; + + /* Shortcut for a new compound statement. */ + if (vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, '{')) + rc = vdScriptParseCompoundStatement(pThis, ppAstNodeStmt); + else + { + PVDSCRIPTASTSTMT pAstNodeStmt = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT); + + if (pAstNodeStmt) + { + + if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DEFAULT)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':')) + { + PVDSCRIPTASTSTMT pAstNodeStmtDef = NULL; + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_DEFAULT; + rc = vdScriptParseStatement(pThis, &pAstNodeStmtDef); + if (RT_SUCCESS(rc)) + pAstNodeStmt->pStmt = pAstNodeStmtDef; + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CASE)) + { + PVDSCRIPTASTEXPR pAstNodeExpr = NULL; + rc = vdScriptParseCondExpression(pThis, &pAstNodeExpr); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':')) + { + PVDSCRIPTASTSTMT pAstNodeCaseStmt = NULL; + rc = vdScriptParseStatement(pThis, &pAstNodeCaseStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CASE; + pAstNodeStmt->Case.pExpr = pAstNodeExpr; + pAstNodeStmt->Case.pStmt = pAstNodeCaseStmt; + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pAstNodeExpr->Core); + } + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_IF)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_IF; + rc = vdScriptParseIf(pThis, &pAstNodeStmt->If); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_SWITCH)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_SWITCH; + rc = vdScriptParseSwitch(pThis, &pAstNodeStmt->Switch); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE; + rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, false /* fDoWhile */); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DO)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE; + rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, true /* fDoWhile */); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_FOR)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_FOR; + rc = vdScriptParseFor(pThis, &pAstNodeStmt->For); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONTINUE)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CONTINUE; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_BREAK)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_BREAK; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RETURN)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_RETURN; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr); + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else + pAstNodeStmt->pExpr = NULL; /* No expression for return. */ + } + else + { + /* Must be an expression. */ + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_EXPRESSION; + rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr); + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeStmt = pAstNodeStmt; + else + vdScriptAstNodeFree(&pAstNodeStmt->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating statement node\n"); + } + + return rc; +} + +/** + * Parses a compound statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeCompound Where to store the compound AST node on success. + */ +static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '{')) + { + PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT); + if (pAstNodeCompound) + { + pAstNodeCompound->enmStmtType = VDSCRIPTSTMTTYPE_COMPOUND; + RTListInit(&pAstNodeCompound->Compound.ListDecls); + RTListInit(&pAstNodeCompound->Compound.ListStmts); + while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '}')) + { + /* + * Check whether we have a declaration or a statement. + * For now we assume that 2 identifier tokens specify a declaration + * (type + variable name). Having two consecutive identifers is not possible + * for a statement. + */ + if ( vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER + && vdScriptTokenizerPeekNextClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER) + { + PVDSCRIPTASTDECL pAstNodeDecl = NULL; + rc = vdScriptParseDeclaration(pThis, &pAstNodeDecl); + if (RT_SUCCESS(rc)) + RTListAppend(&pAstNodeCompound->Compound.ListDecls, &pAstNodeDecl->Core.ListNode); + } + else + { + PVDSCRIPTASTSTMT pAstNodeStmt = NULL; + rc = vdScriptParseStatement(pThis, &pAstNodeStmt); + if (RT_SUCCESS(rc)) + RTListAppend(&pAstNodeCompound->Compound.ListStmts, &pAstNodeStmt->Core.ListNode); + } + + if (RT_FAILURE(rc)) + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeCompound = pAstNodeCompound; + else + vdScriptAstNodeFree(&pAstNodeCompound->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating compound statement node\n"); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"{\" got...\n"); + + + return rc; +} + +/** + * Parses a function definition from the given tokenizer. + * + * @returns VBox status code. + * @param pThis The script context. + */ +static int vdScriptParseAddFnDef(PVDSCRIPTCTXINT pThis) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTIDE pRetType = NULL; + PVDSCRIPTASTIDE pFnIde = NULL; + + LogFlowFunc(("pThis=%p\n", pThis)); + + /* Put return type on the stack. */ + rc = vdScriptParseIde(pThis, &pRetType); + if (RT_SUCCESS(rc)) + { + /* Function name */ + rc = vdScriptParseIde(pThis, &pFnIde); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTFN pAstNodeFn = (PVDSCRIPTASTFN)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTION); + + if (pAstNodeFn) + { + pAstNodeFn->pFnIde = pFnIde; + pAstNodeFn->pRetType = pRetType; + RTListInit(&pAstNodeFn->ListArgs); + + pFnIde = NULL; + pRetType = NULL; + + /* Parse parameter list, create empty parameter list AST node and put it on the stack. */ + while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTIDE pArgType = NULL; + PVDSCRIPTASTIDE pArgIde = NULL; + /* Parse two identifiers, first one is the type, second the name. */ + rc = vdScriptParseIde(pThis, &pArgType); + if (RT_SUCCESS(rc)) + rc = vdScriptParseIde(pThis, &pArgIde); + + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG); + if (pAstNodeFnArg) + { + pAstNodeFnArg->pArgIde = pArgIde; + pAstNodeFnArg->pType = pArgType; + RTListAppend(&pAstNodeFn->ListArgs, &pAstNodeFnArg->Core.ListNode); + pAstNodeFn->cArgs++; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function argument AST node\n"); + } + + if (RT_FAILURE(rc)) + { + if (pArgType) + vdScriptAstNodeFree(&pArgType->Core); + if (pArgIde) + vdScriptAstNodeFree(&pArgIde->Core); + } + + if ( !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',') + && !vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \",\" or \")\" got...\n"); + break; + } + } + + /* Parse the compound or statement block now. */ + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTSTMT pAstCompound = NULL; + + rc = vdScriptParseCompoundStatement(pThis, &pAstCompound); + if (RT_SUCCESS(rc)) + { + /* + * Link compound statement block to function AST node and add it to the + * list of functions. + */ + pAstNodeFn->pCompoundStmts = pAstCompound; + RTListAppend(&pThis->ListAst, &pAstNodeFn->Core.ListNode); + + PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTMemAllocZ(sizeof(VDSCRIPTFN)); + if (pFn) + { + pFn->Core.pszString = pAstNodeFn->pFnIde->aszIde; + pFn->Core.cchString = strlen(pFn->Core.pszString); + pFn->fExternal = false; + pFn->Type.Internal.pAstFn = pAstNodeFn; + /** @todo Parameters. */ + RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating memory for function\n"); + } + } + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pAstNodeFn->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function AST node\n"); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" got...\n"); + } + } + + if (RT_FAILURE(rc)) + { + if (pRetType) + vdScriptAstNodeFree(&pRetType->Core); + if (pFnIde) + vdScriptAstNodeFree(&pFnIde->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parses the script from the given tokenizer. + * + * @returns VBox status code. + * @param pThis The script context. + */ +static int vdScriptParseFromTokenizer(PVDSCRIPTCTXINT pThis) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p\n", pThis)); + + /* This is a very very simple LL(1) parser, don't expect much from it for now :). */ + while ( RT_SUCCESS(rc) + && !vdScriptTokenizerIsEos(pThis->pTokenizer)) + rc = vdScriptParseAddFnDef(pThis); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("phScriptCtx=%p\n", phScriptCtx)); + + AssertPtrReturn(phScriptCtx, VERR_INVALID_POINTER); + + PVDSCRIPTCTXINT pThis = (PVDSCRIPTCTXINT)RTMemAllocZ(sizeof(VDSCRIPTCTXINT)); + if (pThis) + { + pThis->hStrSpaceFn = NULL; + RTListInit(&pThis->ListAst); + *phScriptCtx = pThis; + } + else + rc = VINF_SUCCESS; + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +static DECLCALLBACK(int) vdScriptCtxDestroyFnSpace(PRTSTRSPACECORE pStr, void *pvUser) +{ + NOREF(pvUser); + + /* + * Just free the whole structure, the AST for internal functions will be + * destroyed later. + */ + RTMemFree(pStr); + return VINF_SUCCESS; +} + +DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx) +{ + PVDSCRIPTCTXINT pThis = hScriptCtx; + + AssertPtrReturnVoid(pThis); + + LogFlowFunc(("hScriptCtx=%p\n", pThis)); + + RTStrSpaceDestroy(&pThis->hStrSpaceFn, vdScriptCtxDestroyFnSpace, NULL); + + /* Go through list of function ASTs and destroy them. */ + PVDSCRIPTASTCORE pIter; + PVDSCRIPTASTCORE pIterNext; + RTListForEachSafe(&pThis->ListAst, pIter, pIterNext, VDSCRIPTASTCORE, ListNode) + { + RTListNodeRemove(&pIter->ListNode); + RTListInit(&pIter->ListNode); + vdScriptAstNodeFree(pIter); + } + + RTMemFree(pThis); +} + +DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks, + unsigned cCallbacks, void *pvUser) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTCTXINT pThis = hScriptCtx; + + LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n", + pThis, paCallbacks, cCallbacks, pvUser)); + + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER); + AssertReturn(cCallbacks > 0, VERR_INVALID_PARAMETER); + + /** @todo Unregister already registered callbacks in case of an error. */ + do + { + PVDSCRIPTFN pFn = NULL; + + if (RTStrSpaceGet(&pThis->hStrSpaceFn, paCallbacks->pszFnName)) + { + rc = VERR_DUPLICATE; + break; + } + + pFn = (PVDSCRIPTFN)RTMemAllocZ(RT_UOFFSETOF_DYN(VDSCRIPTFN, aenmArgTypes[paCallbacks->cArgs])); + if (!pFn) + { + rc = VERR_NO_MEMORY; + break; + } + + /** @todo Validate argument and returns types. */ + pFn->Core.pszString = paCallbacks->pszFnName; + pFn->Core.cchString = strlen(pFn->Core.pszString); + pFn->fExternal = true; + pFn->Type.External.pfnCallback = paCallbacks->pfnCallback; + pFn->Type.External.pvUser = pvUser; + pFn->enmTypeRetn = paCallbacks->enmTypeReturn; + pFn->cArgs = paCallbacks->cArgs; + + for (unsigned i = 0; i < paCallbacks->cArgs; i++) + pFn->aenmArgTypes[i] = paCallbacks->paArgs[i]; + + RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core); + cCallbacks--; + paCallbacks++; + } + while (cCallbacks); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTCTXINT pThis = hScriptCtx; + + LogFlowFunc(("hScriptCtx=%p pszScript=%p\n", pThis, pszScript)); + + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(pszScript, VERR_INVALID_POINTER); + + PVDTOKENIZER pTokenizer = vdScriptTokenizerCreate(pszScript); + if (pTokenizer) + { + pThis->pTokenizer = pTokenizer; + rc = vdScriptParseFromTokenizer(pThis); + pThis->pTokenizer = NULL; + RTMemFree(pTokenizer); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall, + PVDSCRIPTARG paArgs, unsigned cArgs) +{ + PVDSCRIPTCTXINT pThis = hScriptCtx; + VDSCRIPTARG Ret; + return vdScriptCtxInterprete(pThis, pszFnCall, paArgs, cArgs, &Ret); +} |