VDScript.cpp revision c299849be8ae3544715e8806e1433cae67d93e2e
/** $Id$ */
/** @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;
* 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...
*
* The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken
*/
#define LOGGROUP LOGGROUP_DEFAULT
#include "VDScriptAst.h"
#include "VDScriptInternal.h"
/**
* VD script token class.
*/
typedef enum VDTOKENCLASS
{
/** Invalid. */
VDTOKENCLASS_INVALID = 0,
/** Identifier class. */
/** Numerical constant. */
/** String constant. */
/** Operators */
/** Reserved keyword */
/** Punctuator */
/** End of stream */
/** 32bit hack. */
VDTOKENCLASS_32BIT_HACK = 0x7fffffff
} VDTOKENCLASS;
/** Pointer to a token class. */
typedef VDTOKENCLASS *PVDTOKENCLASS;
/**
* Keyword types.
*/
typedef enum VDSCRIPTTOKENKEYWORD
{
VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff
/** Pointer to a keyword type. */
typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD;
/**
* VD script token.
*/
typedef struct VDSCRIPTTOKEN
{
/** Token class. */
/** Token position in the source buffer. */
/** 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. */
} Ide;
/** Numerical constant. */
struct
{
} 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. */
} StringConst;
/** Operator */
struct
{
/** The operator string. */
} Operator;
/** Keyword. */
struct
{
/** The keyword type. */
} Keyword;
/** Punctuator. */
struct
{
/** The punctuator in question. */
char chPunctuator;
} Punctuator;
} Class;
/** 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. */
/** Token 1. */
/** Token 2. */
/** Pointer to the current active token. */
/** The next token in the input stream (used for peeking). */
} VDTOKENIZER;
/**
* Operators entry.
*/
typedef struct VDSCRIPTOP
{
/** Operator string. */
const char *pszOp;
/** Size of the operator in characters without zero terminator. */
} 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. */
/** Keyword type. */
/** */
typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD;
/**
* Known keywords.
*/
static VDSCRIPTKEYWORD g_aKeywords[] =
{
};
static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound);
static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
/**
* 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.
*/
{
}
/**
* Skip one character in the input stream.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
*/
{
pTokenizer->pszInput++;
}
/**
* Returns the next char in the input buffer without advancing it.
*
* @returns Next character in the input buffer.
* @param pTokenizer The tokenizer state.
*/
{
return vdScriptTokenizerIsEos(pTokenizer)
? '\0'
}
/**
* Returns the next character in the input buffer advancing the internal
* position.
*
* @returns Next character in the stream.
* @param pTokenizer The tokenizer state.
*/
{
char ch;
ch = '\0';
else
return ch;
}
/**
* Sets a new line for the tokenizer.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
*/
{
}
/**
* 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.
*/
{
bool fNewline = true;
else
fNewline = false;
return fNewline;
}
/**
* Skips a multi line comment.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
*/
{
while ( !vdScriptTokenizerIsEos(pTokenizer)
{
}
if (!vdScriptTokenizerIsEos(pTokenizer))
if (!vdScriptTokenizerIsEos(pTokenizer))
}
/**
* Skip all whitespace starting from the current input buffer position.
* Skips all present comments too.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
*/
{
while (!vdScriptTokenizerIsEos(pTokenizer))
{
if ( !vdScriptTokenizerIsEos(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.
*/
{
char ch;
bool fIsKeyword = false;
do
{
cchIde++;
}
/* Check whether we got an identifier or an reserved keyword. */
for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++)
{
{
fIsKeyword = true;
break;
}
}
if (!fIsKeyword)
{
}
}
/**
* Get a numerical constant from the tokenizer.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
* @param pToken The uninitialized token.
*/
{
unsigned uBase = 10;
/* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */
/** @todo: Handle number to big, throw a warning */
for (unsigned i = 0; i < cchNumber; i++)
/* Check for a supported suffix, supported are K|M|G. */
{
}
{
}
{
}
{
}
}
/**
* Parses a string constant.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
* @param pToken The uninitialized token.
*
* @remarks: No escape sequences allowed at this time.
*/
{
{
cchStr++;
}
}
/**
* Get the end of stream token.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
* @param pToken The uninitialized token.
*/
{
}
/**
* 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;
/*
* 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++)
{
{
int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp);
/** @todo: Make this prettier. */
for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++)
fOpFound = true;
break;
}
}
if (!fOpFound)
{
for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++)
{
{
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.
*/
{
/* Skip all eventually existing whitespace, newlines and comments first. */
else if (RT_C_IS_DIGIT(ch))
else if (ch == '\"')
else if (ch == '\0')
else
}
/**
* 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.
*/
{
if (pTokenizer)
{
/* Fill the tokenizer with two first tokens. */
}
return pTokenizer;
}
/**
* Destroys a given tokenizer state.
*
* @returns nothing.
* @param pTokenizer The tokenizer to destroy.
*/
{
}
/**
* Get the current token in the input stream.
*
* @returns Pointer to the next token in the stream.
* @param pTokenizer The tokenizer to destroy.
*/
{
return pTokenizer->pTokenCurr;
}
/**
* Get the class of the current token.
*
* @returns Class of the current token.
* @param pTokenizer The tokenizer state.
*/
{
}
/**
* Returns the token class of the next token in the stream.
*
* @returns Token class of the next token.
* @param pTokenizer The tokenizer state.
*/
{
}
/**
* Consume the current token advancing to the next in the stream.
*
* @returns nothing.
* @param pTokenizer The tokenizer state.
*/
{
/* Switch next token to current token and read in the next token. */
}
/**
* 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.
*/
{
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.
*/
{
if (fEqual)
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)
{
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)
{
if (fEqual)
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.
*/
{
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.
*/
{
if (fEqual)
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, ...)
{
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.
*/
{
int rc = VINF_SUCCESS;
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n");
else
{
/* Create new AST node and push onto stack. */
if (pAstNodeIde)
{
rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde);
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n");
}
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.
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
{
if (RT_SUCCESS(rc)
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
}
else
{
if (pExpr)
{
{
if (RT_SUCCESS(rc))
{
}
}
{
}
{
pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString);
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n");
}
{
pExpr->f = true;
pExpr->f = false;
else
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n");
}
else
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n");
if (RT_FAILURE(rc))
else
*ppAstNodeExpr = pExpr;
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
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.
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
{
if (RT_SUCCESS(rc))
else
break;
}
if ( RT_SUCCESS(rc)
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
}
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 , }
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (true)
{
{
if (pExprNew)
{
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
{
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
{
}
else
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
{
}
else
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (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
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
/** @todo: Think about a more beautiful way of parsing this. */
while (true)
{
bool fQuit = false;
bool fCastExprFollows = false;
{
fCastExprFollows = true;
}
{
fCastExprFollows = true;
}
{
fCastExprFollows = true;
}
{
fCastExprFollows = true;
}
{
fCastExprFollows = true;
}
{
fCastExprFollows = true;
}
if (enmType != VDSCRIPTEXPRTYPE_INVALID)
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
if ( RT_SUCCESS(rc)
&& fCastExprFollows)
{
if (RT_SUCCESS(rc))
else
fQuit = true;
}
}
else
{
/* Must be a postfix expression. */
fQuit = true;
}
if (RT_SUCCESS(rc))
{
if (!pExprTop)
{
}
else
{
}
if (fQuit)
break;
}
else
break;
}
if (RT_SUCCESS(rc))
else if (pExprTop)
return rc;
}
/**
* 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)
{
}
/**
* 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)
{
}
#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.
*/
{
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)
*/
{
int rc = VINF_SUCCESS;
else
{
if (RT_SUCCESS(rc))
{
}
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
#if 0
{
if ( RT_SUCCESS(rc)
{
if (pExpr)
{
if (RT_SUCCESS(rc))
else
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
if (RT_FAILURE(rc))
}
else if (RT_SUCCESS(rc))
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
}
else
#endif
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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
else
break;
if (pExprNew)
else
{
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
break;
}
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else
break;
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else
break;
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else
break;
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else
break;
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while ( RT_SUCCESS(rc)
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
}
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
}
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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while ( RT_SUCCESS(rc)
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
}
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
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while ( RT_SUCCESS(rc)
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
}
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
}
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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while ( RT_SUCCESS(rc)
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
}
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
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while ( RT_SUCCESS(rc)
{
if (pExprNew)
{
if (RT_SUCCESS(rc))
}
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
}
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.
*/
{
}
/**
* 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
*/
{
}
/**
* 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;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(rc))
{
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
{
if (pExprNew)
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else
break;
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
*ppAstNodeExpr = pExpr;
else
}
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
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
if ( RT_SUCCESS(rc)
{
PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
if (pListAssignExpr)
{
do
{
if (RT_SUCCESS(rc))
} while ( RT_SUCCESS(rc)
if (RT_FAILURE(rc))
else
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
}
else if (RT_SUCCESS(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.
*/
{
int rc = VINF_SUCCESS;
{
if (RT_SUCCESS(rc))
{
{
if ( RT_SUCCESS(rc)
if (RT_SUCCESS(rc))
{
}
else if (pStmt)
}
else
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
if (RT_FAILURE(rc))
}
}
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.
*/
{
int rc = VINF_SUCCESS;
{
if ( RT_SUCCESS(rc)
{
if (RT_SUCCESS(rc))
{
}
else
}
else if (RT_SUCCESS(rc))
{
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
}
}
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;
if (fDoWhile)
{
if ( RT_SUCCESS(rc)
{
{
if ( RT_SUCCESS(rc)
{
{
}
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");
}
}
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)
}
else
{
{
if ( RT_SUCCESS(rc)
{
if (RT_SUCCESS(rc))
{
}
else
}
else if (RT_SUCCESS(rc))
{
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
}
}
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.
*/
{
int rc = VINF_SUCCESS;
{
if ( RT_SUCCESS(rc)
{
if ( RT_SUCCESS(rc)
{
if ( RT_SUCCESS(rc)
{
if (RT_SUCCESS(rc))
{
}
}
}
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)
if (pExprCond)
if (pExpr3)
}
}
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.
*/
{
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.
*/
{
int rc = VINF_SUCCESS;
/* Shortcut for a new compound statement. */
else
{
if (pAstNodeStmt)
{
{
{
if (RT_SUCCESS(rc))
}
else
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
}
{
if (RT_SUCCESS(rc))
{
{
if (RT_SUCCESS(rc))
{
}
}
else
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
if (RT_FAILURE(rc))
}
}
{
}
{
}
{
}
{
}
{
}
{
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
}
{
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
}
{
{
if ( RT_SUCCESS(rc)
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
}
else
}
else
{
/* Must be an expression. */
if ( RT_SUCCESS(rc)
rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
}
if (RT_SUCCESS(rc))
else
}
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;
{
PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
if (pAstNodeCompound)
{
{
/*
* 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 (RT_SUCCESS(rc))
}
else
{
if (RT_SUCCESS(rc))
}
if (RT_FAILURE(rc))
break;
}
if (RT_SUCCESS(rc))
else
}
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.
*/
{
int rc = VINF_SUCCESS;
/* Put return type on the stack. */
if (RT_SUCCESS(rc))
{
/* Function name */
if (RT_SUCCESS(rc))
{
{
if (pAstNodeFn)
{
/* Parse parameter list, create empty parameter list AST node and put it on the stack. */
{
/* Parse two identifiers, first one is the type, second the name. */
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG);
if (pAstNodeFnArg)
{
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)
if (pArgIde)
}
{
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))
{
if (RT_SUCCESS(rc))
{
/*
* Link compound statement block to function AST node and add it to the
* list of functions.
*/
if (pFn)
{
/** @todo: Parameters. */
}
else
rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating memory for function\n");
}
}
if (RT_FAILURE(rc))
}
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)
if (pFnIde)
}
return rc;
}
/**
* Parses the script from the given tokenizer.
*
* @returns VBox status code.
* @param pThis The script context.
*/
{
int rc = VINF_SUCCESS;
/* This is a very very simple LL(1) parser, don't expect much from it for now :). */
while ( RT_SUCCESS(rc)
return rc;
}
{
int rc = VINF_SUCCESS;
if (pThis)
{
*phScriptCtx = pThis;
}
else
rc = VINF_SUCCESS;
return rc;
}
{
/*
* Just free the whole structure, the AST for internal functions will be
* destroyed later.
*/
return VINF_SUCCESS;
}
{
/* Go through list of function ASTs and destroy them. */
{
}
}
DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks,
unsigned cCallbacks, void *pvUser)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n",
/** @todo: Unregister already registered callbacks in case of an error. */
do
{
{
rc = VERR_DUPLICATE;
break;
}
if (!pFn)
{
rc = VERR_NO_MEMORY;
break;
}
/** @todo: Validate argument and returns types. */
for (unsigned i = 0; i < paCallbacks->cArgs; i++)
cCallbacks--;
paCallbacks++;
}
while (cCallbacks);
return rc;
}
{
int rc = VINF_SUCCESS;
if (pTokenizer)
{
}
return rc;
}
{
}