VDScriptInterp.cpp revision 5584051be7583d8e5d7ac7e1e03fe4260f460b8f
/** $Id$ */
/** @file
*
* VBox HDD container test utility - scripting engine, interpreter.
*/
/*
* 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.
*/
#define LOGGROUP LOGGROUP_DEFAULT
#include "VDScriptAst.h"
#include "VDScriptStack.h"
#include "VDScriptInternal.h"
/**
* Interpreter variable.
*/
typedef struct VDSCRIPTINTERPVAR
{
/** String space core. */
/** Value. */
/** Pointer to an interpreter variable. */
typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR;
/**
* Block scope.
*/
typedef struct VDSCRIPTINTERPSCOPE
{
/** Pointer to the enclosing scope if available. */
struct VDSCRIPTINTERPSCOPE *pParent;
/** String space of declared variables in this scope. */
/** Pointer to a scope block. */
typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE;
/**
* Function call.
*/
typedef struct VDSCRIPTINTERPFNCALL
{
/** Pointer to the caller of this function. */
struct VDSCRIPTINTERPFNCALL *pCaller;
/** Root scope of this function. */
/** Current scope in this function. */
/** Pointer to a function call. */
typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL;
/**
* Interpreter context.
*/
typedef struct VDSCRIPTINTERPCTX
{
/** Pointer to the script context. */
/** Current function call entry. */
/** Stack of calculated values. */
/** Evaluation control stack. */
/** Pointer to an interpreter context. */
typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX;
/**
* Interpreter control type.
*/
typedef enum VDSCRIPTINTERPCTRLTYPE
{
/** Function call to evaluate now, all values are computed
* and are stored on the value stack.
*/
/** Cleanup the function call, deleting the scope and restoring the previous one. */
/** If statement to evaluate now, the guard is on the stack. */
/** While or for statement. */
/** switch statement. */
/** Compound statement. */
/** 32bit blowup. */
VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff
/** Pointer to a control type. */
/**
* Interpreter stack control entry.
*/
typedef struct VDSCRIPTINTERPCTRL
{
/** Flag whether this entry points to an AST node to evaluate. */
bool fEvalAst;
/** Flag dependent data. */
union
{
/** Pointer to the AST node to interprete. */
/** Interpreter control structure. */
struct
{
/** Type of control. */
/** Function call data. */
struct
{
/** Function to call. */
} FnCall;
/** Compound statement. */
struct
{
/** The compound statement node. */
/** Current statement evaluated. */
} Compound;
/** Pointer to an AST node. */
} Ctrl;
};
/** Pointer to an exec stack control entry. */
typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL;
/**
* Record an error while interpreting.
*
* @returns VBox status code passed.
* @param pThis The interpreter context.
* @param rc The status code to record.
* @param RT_SRC_POS Position in the source code.
* @param pszFmt Format string.
*/
static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
{
return rc;
}
/**
* Pops the topmost value from the value stack.
*
* @returns nothing.
* @param pThis The interpreter context.
* @param pVal Where to store the value.
*/
{
if (!pValStack)
{
}
}
/**
* Pushes a given value onto the value stack.
*/
{
if (!pValStack)
return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack");
return VINF_SUCCESS;
}
/**
* Pushes an AST node onto the control stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param enmCtrlType The control entry type.
*/
{
if (pCtrl)
{
return VINF_SUCCESS;
}
return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
}
/**
* Pushes a control entry without additional data onto the stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param enmCtrlType The control entry type.
*/
{
if (pCtrl)
{
return VINF_SUCCESS;
}
return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
}
/**
* Pushes a compound statement control entry onto the stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pStmtFirst The first statement of the compound statement
*/
DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
{
if (pCtrl)
{
pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode);
return VINF_SUCCESS;
}
return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
}
/**
* Pushes a while statement control entry onto the stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pStmt The while statement.
*/
DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
{
int rc = VINF_SUCCESS;
if (pCtrl)
{
if ( RT_SUCCESS(rc)
{
/* Push the statement to execute for do ... while loops because they run at least once. */
if (RT_FAILURE(rc))
{
/* Cleanup while control statement and AST node. */
}
}
else if (RT_FAILURE(rc))
}
else
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
return rc;
}
/**
* Pushes an if statement control entry onto the stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pStmt The if statement.
*/
{
int rc = VINF_SUCCESS;
if (pCtrl)
{
}
else
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
return rc;
}
/**
* Pushes a for statement control entry onto the stack.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pStmt The while statement.
*/
DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
{
int rc = VINF_SUCCESS;
if (pCtrl)
{
/* Push the conditional first and the the initializer .*/
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
}
else
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
return rc;
}
/**
* Destroy variable string space callback.
*/
{
return VINF_SUCCESS;
}
/**
* Setsup a new scope in the current function call.
*
* @returns VBox status code.
* @param pThis The interpreter context.
*/
{
int rc = VINF_SUCCESS;
if (pScope)
{
}
else
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope");
return rc;
}
/**
* Destroys the current scope.
*
* @returns nothing.
* @param pThis The interpreter context.
*/
{
("Current scope is root scope of function call\n"));
}
/**
* Get the content of the given variable identifier from the current or parent scope.
*/
{
while ( !pVar
&& pScopeCurr)
{
if (pVar)
break;
}
return pVar;
}
/**
* Evaluate an expression.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pExpr The expression to evaluate.
*/
{
int rc = VINF_SUCCESS;
{
{
/* Push the numerical constant on the value stack. */
break;
}
{
/* Push the string literal on the value stack. */
break;
}
{
break;
}
{
/* Look it up and push the value onto the value stack. */
break;
}
AssertMsgFailed(("TODO\n"));
{
PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde);
if (pFn)
{
/* Push a function call control entry on the stack. */
if (pCtrlFn)
{
/* Push parameter expressions on the stack. */
while (pArg)
{
if (RT_FAILURE(rc))
break;
}
}
}
else
AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde));
break;
}
case VDSCRIPTEXPRTYPE_MODULUS:
case VDSCRIPTEXPRTYPE_LSR:
case VDSCRIPTEXPRTYPE_LSL:
case VDSCRIPTEXPRTYPE_LOWER:
case VDSCRIPTEXPRTYPE_HIGHER:
case VDSCRIPTEXPRTYPE_EQUAL:
case VDSCRIPTEXPRTYPE_ASSIGN:
AssertMsgFailed(("TODO\n"));
default:
}
return rc;
}
/**
* Evaluate a statement.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pStmt The statement to evaluate.
*/
{
int rc = VINF_SUCCESS;
switch (pStmt->enmStmtType)
{
{
/* Setup new scope. */
if (RT_SUCCESS(rc))
{
/** @todo: Declarations */
}
break;
}
{
break;
}
case VDSCRIPTSTMTTYPE_IF:
{
break;
}
case VDSCRIPTSTMTTYPE_SWITCH:
AssertMsgFailed(("TODO\n"));
break;
case VDSCRIPTSTMTTYPE_WHILE:
{
break;
}
case VDSCRIPTSTMTTYPE_RETURN:
{
/* Walk up the control stack until we reach a function cleanup entry. */
while ( pCtrl
{
/* Cleanup up any compound statement scope. */
}
break;
}
case VDSCRIPTSTMTTYPE_FOR:
{
break;
}
{
/* Remove everything up to a loop control entry. */
while ( pCtrl
{
/* Cleanup up any compound statement scope. */
}
/* Put the conditionals for while and for loops onto the control stack again. */
("Invalid statement type, must be for or while loop\n"));
break;
}
case VDSCRIPTSTMTTYPE_BREAK:
{
/* Remove everything including the loop control statement. */
while ( pCtrl
{
/* Cleanup up any compound statement scope. */
}
break;
}
case VDSCRIPTSTMTTYPE_CASE:
case VDSCRIPTSTMTTYPE_DEFAULT:
AssertMsgFailed(("TODO\n"));
break;
default:
}
return rc;
}
/**
* Evaluates the given AST node.
*
* @returns VBox statuse code.
* @param pThis The interpreter context.
* @param pAstNode The AST node to interpret.
*/
{
int rc = VERR_NOT_IMPLEMENTED;
{
{
AssertMsgFailed(("TODO\n"));
break;
}
{
break;
}
{
break;
}
/* These should never ever appear here. */
case VDSCRIPTASTCLASS_INVALID:
default:
}
return rc;
}
/**
* Evaluate a function call.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pFn The function execute.
*/
{
int rc = VINF_SUCCESS;
{
/* Add function call cleanup marker on the stack first. */
if (RT_SUCCESS(rc))
{
/* Create function call frame and set it up. */
if (pFnCall)
{
/* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */
{
if (pVar)
{
}
else
{
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable");
break;
}
}
if (RT_SUCCESS(rc))
{
/*
* Push compount statement on the control stack and make the newly created
* call frame the current one.
*/
if (RT_SUCCESS(rc))
}
if (RT_FAILURE(rc))
{
}
}
else
rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame");
}
}
else
{
/* External function call, build the argument list. */
{
if (paArgs)
{
}
else
"Out of memory creating argument array for external function call");
}
else
}
return rc;
}
/**
* Evaluate interpreter control statement.
*
* @returns VBox status code.
* @param pThis The interpreter context.
* @param pCtrl The control entry to evaluate.
*/
static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl)
{
int rc = VINF_SUCCESS;
{
{
break;
}
{
/* Delete function call entry. */
break;
}
{
{
/* Evaluated last statement, cleanup and remove the control statement from the stack. */
}
else
{
/* Push the current statement onto the control stack and move on. */
if (RT_SUCCESS(rc))
{
pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts,
}
}
break;
}
{
/* Check whether the condition passed. */
("Value on stack is not of boolean type\n"));
if (Cond.f)
{
/* Execute the loop another round. */
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
}
else
{
("Not a for statement\n"));
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
if (RT_FAILURE(rc))
}
}
}
else
break;
}
{
/* Check whether the condition passed. */
("Value on stack is not of boolean type\n"));
if (Cond.f)
{
/* Execute the true branch. */
}
break;
}
default:
AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n",
}
return rc;
}
/**
* The interpreter evaluation core loop.
*
* @returns VBox status code.
* @param pThis The interpreter context.
*/
{
int rc = VINF_SUCCESS;
while (pCtrl)
{
{
}
else
}
return rc;
}
{
int rc = VINF_SUCCESS;
if (pFn)
{
{
/* Push the arguments onto the stack. */
/** @todo: Check expected and given argument types. */
for (unsigned i = 0; i < cArgs; i++)
{
}
if (RT_SUCCESS(rc))
{
/* Setup function call frame and parameters. */
if (RT_SUCCESS(rc))
{
/* Run the interpreter. */
}
}
}
else
rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs);
}
else
rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn);
return rc;
}