Index: trunk/src/VBox/Storage/testcase/VDScript.cpp
===================================================================
--- trunk/src/VBox/Storage/testcase/VDScript.cpp	(revision 44811)
+++ trunk/src/VBox/Storage/testcase/VDScript.cpp	(revision 44811)
@@ -0,0 +1,1624 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @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...
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/string.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/ctype.h>
+
+#include <VBox/log.h>
+
+#include "VDScript.h"
+#include "VDScriptAst.h"
+
+/**
+ * Position information.
+ */
+typedef struct VDSRCPOS
+{
+    /** Line in the source. */
+    unsigned       iLine;
+    /** Current start character .*/
+    unsigned       iChStart;
+    /** Current end character. */
+    unsigned       iChEnd;
+} VDSRCPOS;
+/** Pointer to a source position. */
+typedef struct VDSRCPOS *PVDSRCPOS;
+
+/**
+ * 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_DEFAULT,
+    VDSCRIPTTOKENKEYWORD_RETURN,
+    VDSCRIPTTOKENKEYWORD_SWITCH,
+    VDSCRIPTTOKENKEYWORD_WHILE,
+    VDSCRIPTTOKENKEYWORD_BREAK,
+    VDSCRIPTTOKENKEYWORD_ELSE,
+    VDSCRIPTTOKENKEYWORD_CASE,
+    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
+        {
+            /** Flag whether the number is negative. */
+            bool fNegative;
+            union
+            {
+                uint64_t u64;
+                int64_t  i64;
+            };
+        } 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;
+/** Pointer to a tokenize state. */
+typedef struct VDTOKENIZER *PVDTOKENIZER;
+
+/**
+ * Script function which can be called.
+ */
+typedef struct VDSCRIPTFN
+{
+    /** String space core. */
+    RTSTRSPACECORE               Core;
+    /** Flag whether function is defined in the source or was
+     * registered from the outside. */
+    bool                         fExternal;
+    /** Flag dependent data. */
+    union
+    {
+        /** Data for functions defined in the source. */
+        struct
+        {
+            /** Pointer to the AST defining the function. */
+            PVDSCRIPTASTFN       pAstFn;
+        } Internal;
+        /** Data for external defined functions. */
+        struct
+        {
+            /** Callback function. */
+            PFNVDSCRIPTCALLBACK  pfnCallback;
+            /** Opaque user data. */
+            void                *pvUser;
+        } External;
+    } Type;
+    /** Return type of the function. */
+    VDSCRIPTTYPE                 enmTypeRetn;
+    /** Number of arguments the function takes. */
+    unsigned                     cArgs;
+    /** Variable sized array of argument types. */
+    VDSCRIPTTYPE                 aArgTypes[1];
+} VDSCRIPTFN;
+typedef VDSCRIPTFN *PVDSCRIPTFN;
+
+/**
+ * Script context.
+ */
+typedef struct VDSCRIPTCTXINT
+{
+    /** String space of external registered and source defined functions. */
+    RTSTRSPACE        hStrSpaceFn;
+    /** List of ASTs for functions - VDSCRIPTASTFN. */
+    RTLISTANCHOR      ListAst;
+    /** Pointer to the current tokenizer state. */
+    PVDTOKENIZER      pTokenizer;
+} VDSCRIPTCTXINT;
+/** Pointer to a script context. */
+typedef VDSCRIPTCTXINT *PVDSCRIPTCTXINT;
+
+/**
+ * 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},
+    {"=",   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[] =
+{
+    {"continue", 8, VDSCRIPTTOKENKEYWORD_CONTINUE},
+    {"default",  7, VDSCRIPTTOKENKEYWORD_DEFAULT},
+    {"return",   6, VDSCRIPTTOKENKEYWORD_RETURN},
+    {"switch",   6, VDSCRIPTTOKENKEYWORD_SWITCH},
+    {"while",    5, VDSCRIPTTOKENKEYWORD_WHILE},
+    {"break",    5, VDSCRIPTTOKENKEYWORD_BREAK},
+    {"else",     4, VDSCRIPTTOKENKEYWORD_ELSE},
+    {"case",     4, VDSCRIPTTOKENKEYWORD_CASE},
+    {"for",      3, VDSCRIPTTOKENKEYWORD_FOR},
+    {"if",       2, VDSCRIPTTOKENKEYWORD_IF},
+    {"do",       2, VDSCRIPTTOKENKEYWORD_DO}
+};
+
+static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound);
+static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt);
+
+/**
+ * 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 (   *pTokenizer->pszInput == '\r'
+        && vdScriptTokenizerPeekCh(pTokenizer) == '\n')
+        vdScriptTokenizerNewLine(pTokenizer, 2);
+    else if (*pTokenizer->pszInput == '\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)
+           && *pTokenizer->pszInput != '*'
+           && vdScriptTokenizerPeekCh(pTokenizer) != '/')
+    {
+        if (!vdScriptTokenizerIsSkipNewLine(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 (true)
+    {
+        while (  !vdScriptTokenizerIsEos(pTokenizer)
+               && (   vdScriptTokenizerGetCh(pTokenizer) == ' '
+                   || vdScriptTokenizerGetCh(pTokenizer) == '\t'))
+            vdScriptTokenizerSkipCh(pTokenizer);
+
+        if (   !vdScriptTokenizerIsEos(pTokenizer)
+            && !vdScriptTokenizerIsSkipNewLine(pTokenizer))
+        {
+            if (   vdScriptTokenizerGetCh(pTokenizer) == '/'
+                && vdScriptTokenizerPeekCh(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;
+    size_t 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));
+
+    /* 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)
+{
+    unsigned uBase = 10;
+    size_t cchNumber = 0;
+
+    Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer)));
+
+    if (vdScriptTokenizerGetCh(pTokenizer) == '0')
+    {
+        /* Maybe octal or hex representation. */
+    }
+}
+
+/**
+ * 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)
+{
+    size_t 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 += 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 += 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;
+}
+
+/**
+ * Destroys a given tokenizer state.
+ *
+ * @returns nothing.
+ * @param   pTokenizer    The tokenizer to destroy.
+ */
+static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer)
+{
+    RTMemFree(pTokenizer);
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Record an error while parsing.
+ *
+ * @returns VBox status code passed.
+ */
+static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
+{
+    NOREF(pThis);
+    NOREF(pszFmt);
+    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 = pToken->Class.Ide.cchIde;
+
+            *ppAstNodeIde = pAstNodeIde;
+        }
+        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 conditional expression.
+ *
+ * @returns VBox status code.
+ * @param   pThis                The script context.
+ * @param   ppAstNodeExpr        Where to store the expression AST node on success.
+ */
+static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+    int rc = VERR_NOT_IMPLEMENTED;
+    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.
+ */
+static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+    int rc = VERR_NOT_IMPLEMENTED;
+    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, ')'))
+                {
+                    pAstNodeWhile->pCond = pExpr;
+                    pAstNodeWhile->pStmt = pStmt;
+                }
+                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;
+    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;
+                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
+            {
+                /* 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;
+
+                    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);
+                        }
+                    }
+
+                    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 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);
+
+    /** @todo: Go through the list and destroy all ASTs. */
+    RTMemFree(pThis);
+}
+
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PVDSCRIPTCALLBACK 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_OFFSETOF(VDSCRIPTFN, aArgTypes[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->aArgTypes[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)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
Index: trunk/src/VBox/Storage/testcase/VDScript.h
===================================================================
--- trunk/src/VBox/Storage/testcase/VDScript.h	(revision 44811)
+++ trunk/src/VBox/Storage/testcase/VDScript.h	(revision 44811)
@@ -0,0 +1,143 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScript_h__
+#define _VDScript_h__
+
+/** Handle to the scripting context. */
+typedef struct VDSCRIPTCTXINT *VDSCRIPTCTX;
+/** Pointer to a scripting context handle. */
+typedef VDSCRIPTCTX *PVDSCRIPTCTX;
+
+/**
+ * Supprted primitive types in the scripting engine.
+ */
+typedef enum VDSCRIPTTYPE
+{
+    /** Invalid type, do not use. */
+    VDSCRIPTTYPE_INVALID = 0,
+    /** void type, used for no return value of methods. */
+    VDSCRIPTTYPE_VOID,
+    /** unsigned 8bit integer. */
+    VDSCRIPTTYPE_UINT8,
+    VDSCRIPTTYPE_INT8,
+    VDSCRIPTTYPE_UINT16,
+    VDSCRIPTTYPE_INT16,
+    VDSCRIPTTYPE_UINT32,
+    VDSCRIPTTYPE_INT32,
+    VDSCRIPTTYPE_UINT64,
+    VDSCRIPTTYPE_INT64,
+    VDSCRIPTTYPE_STRING,
+    VDSCRIPTTYPE_BOOL,
+    /** As usual, the 32bit blowup hack. */
+    VDSCRIPTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTTYPE;
+/** Pointer to a type. */
+typedef VDSCRIPTTYPE *PVDSCRIPTTYPE;
+
+/**
+ * Script argument.
+ */
+typedef union VDSCRIPTARG
+{
+    uint8_t     u8;
+    int8_t      i8;
+    uint16_t    u16;
+    int16_t     i16;
+    uint32_t    u32;
+    int32_t     i32;
+    uint64_t    u64;
+    int64_t     i64;
+    const char *psz;
+    bool        f;
+} VDSCRIPTARG;
+/** Pointer to an argument. */
+typedef VDSCRIPTARG *PVDSCRIPTARG;
+
+/** Script callback. */
+typedef DECLCALLBACK(int) FNVDSCRIPTCALLBACK(PVDSCRIPTARG paScriptArgs, void *pvUser);
+/** Pointer to a script callback. */
+typedef FNVDSCRIPTCALLBACK *PFNVDSCRIPTCALLBACK;
+
+/**
+ * Callback registration structure.
+ */
+typedef struct VDSCRIPTCALLBACK
+{
+    /** The function name. */
+    const char            *pszFnName;
+    /** The return type of the function. */
+    VDSCRIPTTYPE           enmTypeReturn;
+    /** Number of arguments this method takes. */
+    unsigned               cArgs;
+    /** Pointer to the array of argument types. */
+    PVDSCRIPTTYPE          paArgs;
+    /** The callback handler. */
+    PFNVDSCRIPTCALLBACK    pfnCallback;
+} VDSCRIPTCALLBACK;
+/** Pointer to a callback register entry. */
+typedef VDSCRIPTCALLBACK *PVDSCRIPTCALLBACK;
+
+/**
+ * Create a new scripting context.
+ *
+ * @returns VBox status code.
+ * @param   phScriptCtx    Where to store the scripting context on success.
+ */
+DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx);
+
+/**
+ * Destroys the given scripting context.
+ *
+ * @returns nothing.
+ * @param   hScriptCtx     The script context to destroy.
+ */
+DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx);
+
+/**
+ * Register callbacks for the scripting context.
+ *
+ * @returns VBox status code.
+ * @param   hScriptCtx     The script context handle.
+ * @param   paCallbacks    Pointer to the callbacks to register.
+ * @param   cCallbacks     Number of callbacks in the array.
+ * @param   pvUser         Opaque user data to pass on the callback invocation.
+ */
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PVDSCRIPTCALLBACK paCallbacks,
+                                             unsigned cCallbacks, void *pvUser);
+
+/**
+ * Load a given script into the context.
+ *
+ * @returns VBox status code.
+ * @param   hScriptCtx     The script context handle.
+ * @param   pszScript      Pointer to the char buffer containing the script.
+ */
+DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript);
+
+/**
+ * Execute a given method in the script context.
+ *
+ * @returns VBox status code.
+ * @param   hScriptCtx     The script context handle.
+ * @param   pszFnCall      The method to call.
+ * @param   paArgs         Pointer to arguments to pass.
+ * @param   cArgs          Number of arguments.
+ */
+DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
+                                  PVDSCRIPTARG paArgs, unsigned cArgs);
+
+#endif /* _VDScript_h__ */
Index: trunk/src/VBox/Storage/testcase/VDScriptAst.cpp
===================================================================
--- trunk/src/VBox/Storage/testcase/VDScriptAst.cpp	(revision 44811)
+++ trunk/src/VBox/Storage/testcase/VDScriptAst.cpp	(revision 44811)
@@ -0,0 +1,102 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine AST node related functions.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode)
+{
+    switch (pAstNode->enmClass)
+    {
+        case VDSCRIPTASTCLASS_FUNCTION:
+        {
+            /** @todo. */
+            break;
+        }
+        case VDSCRIPTASTCLASS_FUNCTIONARG:
+        {
+            PVDSCRIPTASTFNARG pAstNodeArg = (PVDSCRIPTASTFNARG)pAstNode;
+            if (pAstNodeArg->pType)
+                vdScriptAstNodeFree(&pAstNodeArg->pType->Core);
+            if (pAstNodeArg->pArgIde)
+                vdScriptAstNodeFree(&pAstNodeArg->pArgIde->Core);
+            break;
+        }
+        case VDSCRIPTASTCLASS_IDENTIFIER:
+            break;
+        case VDSCRIPTASTCLASS_DECLARATION:
+            break;
+        case VDSCRIPTASTCLASS_STATEMENT:
+            break;
+        case VDSCRIPTASTCLASS_INVALID:
+        default:
+            AssertMsgFailedReturnVoid(("Invalid AST node class given %d\n", pAstNode->enmClass));
+    }
+
+    RTMemFree(pAstNode);
+}
+
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass)
+{
+    size_t cbAlloc = 0;
+
+    switch (enmClass)
+    {
+        case VDSCRIPTASTCLASS_FUNCTION:
+            cbAlloc = sizeof(VDSCRIPTASTFN);
+            break;
+        case VDSCRIPTASTCLASS_FUNCTIONARG:
+            cbAlloc = sizeof(VDSCRIPTASTFNARG);
+            break;
+        case VDSCRIPTASTCLASS_DECLARATION:
+            cbAlloc = sizeof(VDSCRIPTASTDECL);
+            break;
+        case VDSCRIPTASTCLASS_STATEMENT:
+            cbAlloc = sizeof(VDSCRIPTASTSTMT);
+            break;
+        case VDSCRIPTASTCLASS_IDENTIFIER:
+        case VDSCRIPTASTCLASS_INVALID:
+        default:
+            AssertMsgFailedReturn(("Invalid AST node class given %d\n", enmClass), NULL);
+    }
+
+    PVDSCRIPTASTCORE pAstNode = (PVDSCRIPTASTCORE)RTMemAllocZ(cbAlloc);
+    if (pAstNode)
+    {
+        pAstNode->enmClass = enmClass;
+        RTListInit(&pAstNode->ListNode);
+    }
+
+    return pAstNode;
+}
+
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde)
+{
+    PVDSCRIPTASTIDE pAstNode = (PVDSCRIPTASTIDE)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTASTIDE, aszIde[cchIde + 1]));
+    if (pAstNode)
+    {
+        pAstNode->Core.enmClass = VDSCRIPTASTCLASS_IDENTIFIER;
+        RTListInit(&pAstNode->Core.ListNode);
+    }
+
+    return pAstNode;
+}
Index: trunk/src/VBox/Storage/testcase/VDScriptAst.h
===================================================================
--- trunk/src/VBox/Storage/testcase/VDScriptAst.h	(revision 44811)
+++ trunk/src/VBox/Storage/testcase/VDScriptAst.h	(revision 44811)
@@ -0,0 +1,315 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, AST related structures.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptAst_h__
+#define _VDScriptAst_h__
+
+#include <iprt/list.h>
+
+/**
+ * AST node classes.
+ */
+typedef enum VDSCRIPTASTCLASS
+{
+    /** Invalid. */
+    VDSCRIPTASTCLASS_INVALID = 0,
+    /** Function node. */
+    VDSCRIPTASTCLASS_FUNCTION,
+    /** Function argument. */
+    VDSCRIPTASTCLASS_FUNCTIONARG,
+    /** Identifier node. */
+    VDSCRIPTASTCLASS_IDENTIFIER,
+    /** Declaration node. */
+    VDSCRIPTASTCLASS_DECLARATION,
+    /** Statement node. */
+    VDSCRIPTASTCLASS_STATEMENT,
+    /** Expression node. */
+    VDSCRIPTASTCLASS_EXPRESSION,
+    /** @todo: Add if ... else, loops, ... */
+    /** 32bit blowup. */
+    VDSCRIPTASTCLASS_32BIT_HACK = 0x7fffffff
+} VDSCRIPTASTCLASS;
+/** Pointer to an AST node class. */
+typedef VDSCRIPTASTCLASS *PVDSCRIPTASTCLASS;
+
+/**
+ * Core AST structure.
+ */
+typedef struct VDSCRIPTASTCORE
+{
+    /** The node class, used for verification. */
+    VDSCRIPTASTCLASS enmClass;
+    /** List which might be used. */
+    RTLISTNODE       ListNode;
+} VDSCRIPTASTCORE;
+/** Pointer to an AST core structure. */
+typedef VDSCRIPTASTCORE *PVDSCRIPTASTCORE;
+
+/** Pointer to an statement node - forward declaration. */
+typedef struct VDSCRIPTASTSTMT *PVDSCRIPTASTSTMT;
+
+/**
+ * AST identifier node.
+ */
+typedef struct VDSCRIPTASTIDE
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE    Core;
+    /** Number of characters in the identifier, excluding the zero terminator. */
+    unsigned           cchIde;
+    /** Identifier, variable size. */
+    char               aszIde[1];
+} VDSCRIPTASTIDE;
+/** Pointer to an identifer node. */
+typedef VDSCRIPTASTIDE *PVDSCRIPTASTIDE;
+
+/**
+ * AST declaration node.
+ */
+typedef struct VDSCRIPTASTDECL
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE    Core;
+    /** @todo */
+} VDSCRIPTASTDECL;
+/** Pointer to an declaration node. */
+typedef VDSCRIPTASTDECL *PVDSCRIPTASTDECL;
+
+/**
+ * AST expression node.
+ */
+typedef struct VDSCRIPTASTEXPR
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE    Core;
+    /** @todo */
+    unsigned           uDummy;
+} VDSCRIPTASTEXPR;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTEXPR *PVDSCRIPTASTEXPR;
+
+/**
+ * AST if node.
+ */
+typedef struct VDSCRIPTASTIF
+{
+    /** Conditional expression. */
+    PVDSCRIPTASTEXPR   pCond;
+    /** The true branch */
+    PVDSCRIPTASTSTMT   pTrueStmt;
+    /** The else branch, can be NULL if no else branch. */
+    PVDSCRIPTASTSTMT   pElseStmt;
+} VDSCRIPTASTIF;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTIF *PVDSCRIPTASTIF;
+
+/**
+ * AST switch node.
+ */
+typedef struct VDSCRIPTASTSWITCH
+{
+    /** Conditional expression. */
+    PVDSCRIPTASTEXPR   pCond;
+    /** The statement to follow. */
+    PVDSCRIPTASTSTMT   pStmt;
+} VDSCRIPTASTSWITCH;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTSWITCH *PVDSCRIPTASTSWITCH;
+
+/**
+ * AST while or do ... while node.
+ */
+typedef struct VDSCRIPTASTWHILE
+{
+    /** Flag whether this is a do while loop. */
+    bool               fDoWhile;
+    /** Conditional expression. */
+    PVDSCRIPTASTEXPR   pCond;
+    /** The statement to follow. */
+    PVDSCRIPTASTSTMT   pStmt;
+} VDSCRIPTASTWHILE;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTWHILE *PVDSCRIPTASTWHILE;
+
+/**
+ * AST for node.
+ */
+typedef struct VDSCRIPTASTFOR
+{
+    /** Initializer expression. */
+    PVDSCRIPTASTEXPR   pExprStart;
+    /** The exit condition. */
+    PVDSCRIPTASTEXPR   pExprCond;
+    /** The third expression (normally used to increase/decrease loop variable). */
+    PVDSCRIPTASTEXPR   pExpr3;
+    /** The for loop body. */
+    PVDSCRIPTASTSTMT   pStmt;
+} VDSCRIPTASTFOR;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTFOR *PVDSCRIPTASTFOR;
+
+/**
+ * Statement types.
+ */
+typedef enum VDSCRIPTSTMTTYPE
+{
+    /** Invalid. */
+    VDSCRIPTSTMTTYPE_INVALID = 0,
+    /** Labeled statement. */
+    VDSCRIPTSTMTTYPE_LABELED,
+    /** Compound statement. */
+    VDSCRIPTSTMTTYPE_COMPOUND,
+    /** Expression statement. */
+    VDSCRIPTSTMTTYPE_EXPRESSION,
+    /** if statement. */
+    VDSCRIPTSTMTTYPE_IF,
+    /** switch statement. */
+    VDSCRIPTSTMTTYPE_SWITCH,
+    /** while statement. */
+    VDSCRIPTSTMTTYPE_WHILE,
+    /** for statement. */
+    VDSCRIPTSTMTTYPE_FOR,
+    /** continue statement. */
+    VDSCRIPTSTMTTYPE_CONTINUE,
+    /** break statement. */
+    VDSCRIPTSTMTTYPE_BREAK,
+    /** return statement. */
+    VDSCRIPTSTMTTYPE_RETURN,
+    /** case statement. */
+    VDSCRIPTSTMTTYPE_CASE,
+    /** default statement. */
+    VDSCRIPTSTMTTYPE_DEFAULT,
+    /** 32bit hack. */
+    VDSCRIPTSTMTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTSTMTTYPE;
+/** Pointer to a statement type. */
+typedef VDSCRIPTSTMTTYPE *PVDSCRIPTSTMTTYPE;
+
+/**
+ * AST statement node.
+ */
+typedef struct VDSCRIPTASTSTMT
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE    Core;
+    /** Statement type */
+    VDSCRIPTSTMTTYPE   enmStmtType;
+    /** Statement type dependent data. */
+    union
+    {
+        /** Labeled statement (case, default). */
+        struct
+        {
+            /** Conditional expression, if NULL this is a statement for "default" */
+            PVDSCRIPTASTEXPR   pCondExpr;
+            /** Statement to execute. */
+            PVDSCRIPTASTSTMT   pExec;
+        } Labeled;
+        /** Compound statement. */
+        struct
+        {
+            /** List of declarations - VDSCRIPTASTDECL. */
+            RTLISTANCHOR       ListDecls;
+            /** List of statements - VDSCRIPTASTSTMT. */
+            RTLISTANCHOR       ListStmts;
+        } Compound;
+        /** case statement. */
+        struct
+        {
+            /** Pointer to the expression. */
+            PVDSCRIPTASTEXPR   pExpr;
+            /** Pointer to the statement. */
+            PVDSCRIPTASTSTMT   pStmt;
+        } Case;
+        /** "if" statement. */
+        VDSCRIPTASTIF          If;
+        /** "switch" statement. */
+        VDSCRIPTASTSWITCH      Switch;
+        /** "while" or "do ... while" loop. */
+        VDSCRIPTASTWHILE       While;
+        /** "for" loop. */
+        VDSCRIPTASTFOR         For;
+        /** Pointer to another statement. */
+        PVDSCRIPTASTSTMT       pStmt;
+        /** Expression statement. */
+        PVDSCRIPTASTEXPR       pExpr;
+    };
+} VDSCRIPTASTSTMT;
+
+/**
+ * AST node for one function argument.
+ */
+typedef struct VDSCRIPTASTFNARG
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE    Core;
+    /** Identifier describing the type of the argument. */
+    PVDSCRIPTASTIDE    pType;
+    /** The name of the argument. */
+    PVDSCRIPTASTIDE    pArgIde;
+} VDSCRIPTASTFNARG;
+/** Pointer to a AST function argument node. */
+typedef VDSCRIPTASTFNARG *PVDSCRIPTASTFNARG;
+
+/**
+ * AST node describing a function.
+ */
+typedef struct VDSCRIPTASTFN
+{
+    /** Core structure. */
+    VDSCRIPTASTCORE      Core;
+    /** Identifier describing the return type. */
+    PVDSCRIPTASTIDE      pRetType;
+    /** Name of the function. */
+    PVDSCRIPTASTIDE      pFnIde;
+    /** Number of arguments in the list. */
+    unsigned             cArgs;
+    /** Argument list - VDSCRIPTASTFNARG. */
+    RTLISTANCHOR         ListArgs;
+    /** Compound statement node. */
+    PVDSCRIPTASTSTMT     pCompoundStmts;
+} VDSCRIPTASTFN;
+/** Pointer to a function AST node. */
+typedef VDSCRIPTASTFN *PVDSCRIPTASTFN;
+
+/**
+ * Free the given AST node and all subsequent nodes pointed to
+ * by the given node.
+ *
+ * @returns nothing.
+ * @param   pAstNode    The AST node to free.
+ */
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode);
+
+/**
+ * Allocate a non variable in size AST node of the given class.
+ *
+ * @returns Pointer to the allocated node.
+ *          NULL if out of memory.
+ * @param   enmClass    The class of the AST node.
+ */
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass);
+
+/**
+ * Allocate a IDE node which can hold the given number of characters.
+ *
+ * @returns Pointer to the allocated node.
+ *          NULL if out of memory.
+ * @param   cchIde      Number of characters which can be stored in the node.
+ */
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde);
+
+#endif /* _VDScriptAst_h__ */
