VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/VDScriptInterp.cpp@ 55747

Last change on this file since 55747 was 48957, checked in by vboxsync, 11 years ago

Storage: Whitespace cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.5 KB
Line 
1/** $Id: VDScriptInterp.cpp 48957 2013-10-07 22:04:36Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - scripting engine, interpreter.
5 */
6
7/*
8 * Copyright (C) 2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <iprt/list.h>
20#include <iprt/mem.h>
21#include <iprt/assert.h>
22#include <iprt/string.h>
23#include <iprt/stream.h>
24
25#include <VBox/log.h>
26
27#include "VDScriptAst.h"
28#include "VDScriptStack.h"
29#include "VDScriptInternal.h"
30
31/**
32 * Interpreter variable.
33 */
34typedef struct VDSCRIPTINTERPVAR
35{
36 /** String space core. */
37 RTSTRSPACECORE Core;
38 /** Value. */
39 VDSCRIPTARG Value;
40} VDSCRIPTINTERPVAR;
41/** Pointer to an interpreter variable. */
42typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR;
43
44/**
45 * Block scope.
46 */
47typedef struct VDSCRIPTINTERPSCOPE
48{
49 /** Pointer to the enclosing scope if available. */
50 struct VDSCRIPTINTERPSCOPE *pParent;
51 /** String space of declared variables in this scope. */
52 RTSTRSPACE hStrSpaceVar;
53} VDSCRIPTINTERPSCOPE;
54/** Pointer to a scope block. */
55typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE;
56
57/**
58 * Function call.
59 */
60typedef struct VDSCRIPTINTERPFNCALL
61{
62 /** Pointer to the caller of this function. */
63 struct VDSCRIPTINTERPFNCALL *pCaller;
64 /** Root scope of this function. */
65 VDSCRIPTINTERPSCOPE ScopeRoot;
66 /** Current scope in this function. */
67 PVDSCRIPTINTERPSCOPE pScopeCurr;
68} VDSCRIPTINTERPFNCALL;
69/** Pointer to a function call. */
70typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL;
71
72/**
73 * Interpreter context.
74 */
75typedef struct VDSCRIPTINTERPCTX
76{
77 /** Pointer to the script context. */
78 PVDSCRIPTCTXINT pScriptCtx;
79 /** Current function call entry. */
80 PVDSCRIPTINTERPFNCALL pFnCallCurr;
81 /** Stack of calculated values. */
82 VDSCRIPTSTACK StackValues;
83 /** Evaluation control stack. */
84 VDSCRIPTSTACK StackCtrl;
85} VDSCRIPTINTERPCTX;
86/** Pointer to an interpreter context. */
87typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX;
88
89/**
90 * Interpreter control type.
91 */
92typedef enum VDSCRIPTINTERPCTRLTYPE
93{
94 VDSCRIPTINTERPCTRLTYPE_INVALID = 0,
95 /** Function call to evaluate now, all values are computed
96 * and are stored on the value stack.
97 */
98 VDSCRIPTINTERPCTRLTYPE_FN_CALL,
99 /** Cleanup the function call, deleting the scope and restoring the previous one. */
100 VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP,
101 /** If statement to evaluate now, the guard is on the stack. */
102 VDSCRIPTINTERPCTRLTYPE_IF,
103 /** While or for statement. */
104 VDSCRIPTINTERPCTRLTYPE_LOOP,
105 /** switch statement. */
106 VDSCRIPTINTERPCTRLTYPE_SWITCH,
107 /** Compound statement. */
108 VDSCRIPTINTERPCTRLTYPE_COMPOUND,
109 /** 32bit blowup. */
110 VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff
111} VDSCRIPTINTERPCTRLTYPE;
112/** Pointer to a control type. */
113typedef VDSCRIPTINTERPCTRLTYPE *PVDSCRIPTINTERPCTRLTYPE;
114
115/**
116 * Interpreter stack control entry.
117 */
118typedef struct VDSCRIPTINTERPCTRL
119{
120 /** Flag whether this entry points to an AST node to evaluate. */
121 bool fEvalAst;
122 /** Flag dependent data. */
123 union
124 {
125 /** Pointer to the AST node to interprete. */
126 PVDSCRIPTASTCORE pAstNode;
127 /** Interpreter control structure. */
128 struct
129 {
130 /** Type of control. */
131 VDSCRIPTINTERPCTRLTYPE enmCtrlType;
132 /** Function call data. */
133 struct
134 {
135 /** Function to call. */
136 PVDSCRIPTFN pFn;
137 } FnCall;
138 /** Compound statement. */
139 struct
140 {
141 /** The compound statement node. */
142 PVDSCRIPTASTSTMT pStmtCompound;
143 /** Current statement evaluated. */
144 PVDSCRIPTASTSTMT pStmtCurr;
145 } Compound;
146 /** Pointer to an AST node. */
147 PVDSCRIPTASTCORE pAstNode;
148 } Ctrl;
149 };
150} VDSCRIPTINTERPCTRL;
151/** Pointer to an exec stack control entry. */
152typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL;
153
154/**
155 * Record an error while interpreting.
156 *
157 * @returns VBox status code passed.
158 * @param pThis The interpreter context.
159 * @param rc The status code to record.
160 * @param RT_SRC_POS Position in the source code.
161 * @param pszFmt Format string.
162 */
163static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
164{
165 RTPrintf(pszFmt);
166 return rc;
167}
168
169/**
170 * Pops the topmost value from the value stack.
171 *
172 * @returns nothing.
173 * @param pThis The interpreter context.
174 * @param pVal Where to store the value.
175 */
176DECLINLINE(void) vdScriptInterpreterPopValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
177{
178 PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUsed(&pThis->StackValues);
179 if (!pValStack)
180 {
181 RT_ZERO(*pVal);
182 AssertPtrReturnVoid(pValStack);
183 }
184
185 *pVal = *pValStack;
186 vdScriptStackPop(&pThis->StackValues);
187}
188
189/**
190 * Pushes a given value onto the value stack.
191 */
192DECLINLINE(int) vdScriptInterpreterPushValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
193{
194 PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUnused(&pThis->StackValues);
195 if (!pValStack)
196 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack");
197
198 *pValStack = *pVal;
199 vdScriptStackPush(&pThis->StackValues);
200 return VINF_SUCCESS;
201}
202
203/**
204 * Pushes an AST node onto the control stack.
205 *
206 * @returns VBox status code.
207 * @param pThis The interpreter context.
208 * @param enmCtrlType The control entry type.
209 */
210DECLINLINE(int) vdScriptInterpreterPushAstEntry(PVDSCRIPTINTERPCTX pThis,
211 PVDSCRIPTASTCORE pAstNode)
212{
213 PVDSCRIPTINTERPCTRL pCtrl = NULL;
214
215 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
216
217 if (pCtrl)
218 {
219 pCtrl->fEvalAst = true;
220 pCtrl->pAstNode = pAstNode;
221 vdScriptStackPush(&pThis->StackCtrl);
222 return VINF_SUCCESS;
223 }
224
225 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
226}
227
228/**
229 * Pushes a control entry without additional data onto the stack.
230 *
231 * @returns VBox status code.
232 * @param pThis The interpreter context.
233 * @param enmCtrlType The control entry type.
234 */
235DECLINLINE(int) vdScriptInterpreterPushNonDataCtrlEntry(PVDSCRIPTINTERPCTX pThis,
236 VDSCRIPTINTERPCTRLTYPE enmCtrlType)
237{
238 PVDSCRIPTINTERPCTRL pCtrl = NULL;
239
240 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
241 if (pCtrl)
242 {
243 pCtrl->fEvalAst = false;
244 pCtrl->Ctrl.enmCtrlType = enmCtrlType;
245 vdScriptStackPush(&pThis->StackCtrl);
246 return VINF_SUCCESS;
247 }
248
249 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
250}
251
252/**
253 * Pushes a compound statement control entry onto the stack.
254 *
255 * @returns VBox status code.
256 * @param pThis The interpreter context.
257 * @param pStmtFirst The first statement of the compound statement
258 */
259DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
260{
261 PVDSCRIPTINTERPCTRL pCtrl = NULL;
262
263 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
264 if (pCtrl)
265 {
266 pCtrl->fEvalAst = false;
267 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_COMPOUND;
268 pCtrl->Ctrl.Compound.pStmtCompound = pStmt;
269 pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode);
270 vdScriptStackPush(&pThis->StackCtrl);
271 return VINF_SUCCESS;
272 }
273
274 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
275}
276
277/**
278 * Pushes a while statement control entry onto the stack.
279 *
280 * @returns VBox status code.
281 * @param pThis The interpreter context.
282 * @param pStmt The while statement.
283 */
284DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
285{
286 int rc = VINF_SUCCESS;
287 PVDSCRIPTINTERPCTRL pCtrl = NULL;
288
289 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
290 if (pCtrl)
291 {
292 pCtrl->fEvalAst = false;
293 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
294 pCtrl->Ctrl.pAstNode = &pStmt->Core;
295 vdScriptStackPush(&pThis->StackCtrl);
296
297 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pCond->Core);
298 if ( RT_SUCCESS(rc)
299 && pStmt->While.fDoWhile)
300 {
301 /* Push the statement to execute for do ... while loops because they run at least once. */
302 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pStmt->Core);
303 if (RT_FAILURE(rc))
304 {
305 /* Cleanup while control statement and AST node. */
306 vdScriptStackPop(&pThis->StackCtrl);
307 vdScriptStackPop(&pThis->StackCtrl);
308 }
309 }
310 else if (RT_FAILURE(rc))
311 vdScriptStackPop(&pThis->StackCtrl); /* Cleanup the while control statement. */
312 }
313 else
314 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
315
316 return rc;
317}
318
319/**
320 * Pushes an if statement control entry onto the stack.
321 *
322 * @returns VBox status code.
323 * @param pThis The interpreter context.
324 * @param pStmt The if statement.
325 */
326static int vdScriptInterpreterPushIfCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
327{
328 int rc = VINF_SUCCESS;
329 PVDSCRIPTINTERPCTRL pCtrl = NULL;
330
331 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
332 if (pCtrl)
333 {
334 pCtrl->fEvalAst = false;
335 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_IF;
336 pCtrl->Ctrl.pAstNode = &pStmt->Core;
337 vdScriptStackPush(&pThis->StackCtrl);
338
339 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->If.pCond->Core);
340 }
341 else
342 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
343
344 return rc;
345}
346
347/**
348 * Pushes a for statement control entry onto the stack.
349 *
350 * @returns VBox status code.
351 * @param pThis The interpreter context.
352 * @param pStmt The while statement.
353 */
354DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
355{
356 int rc = VINF_SUCCESS;
357 PVDSCRIPTINTERPCTRL pCtrl = NULL;
358
359 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
360 if (pCtrl)
361 {
362 pCtrl->fEvalAst = false;
363 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
364 pCtrl->Ctrl.pAstNode = &pStmt->Core;
365 vdScriptStackPush(&pThis->StackCtrl);
366
367 /* Push the conditional first and the the initializer .*/
368 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprCond->Core);
369 if (RT_SUCCESS(rc))
370 {
371 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprStart->Core);
372 if (RT_FAILURE(rc))
373 vdScriptStackPop(&pThis->StackCtrl);
374 }
375 }
376 else
377 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
378
379 return rc;
380}
381
382/**
383 * Destroy variable string space callback.
384 */
385static DECLCALLBACK(int) vdScriptInterpreterVarSpaceDestroy(PRTSTRSPACECORE pStr, void *pvUser)
386{
387 RTMemFree(pStr);
388 return VINF_SUCCESS;
389}
390
391/**
392 * Setsup a new scope in the current function call.
393 *
394 * @returns VBox status code.
395 * @param pThis The interpreter context.
396 */
397static int vdScriptInterpreterScopeCreate(PVDSCRIPTINTERPCTX pThis)
398{
399 int rc = VINF_SUCCESS;
400 PVDSCRIPTINTERPSCOPE pScope = (PVDSCRIPTINTERPSCOPE)RTMemAllocZ(sizeof(VDSCRIPTINTERPSCOPE));
401 if (pScope)
402 {
403 pScope->pParent = pThis->pFnCallCurr->pScopeCurr;
404 pScope->hStrSpaceVar = NULL;
405 pThis->pFnCallCurr->pScopeCurr = pScope;
406 }
407 else
408 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope");
409
410 return rc;
411}
412
413/**
414 * Destroys the current scope.
415 *
416 * @returns nothing.
417 * @param pThis The interpreter context.
418 */
419static void vdScriptInterpreterScopeDestroyCurr(PVDSCRIPTINTERPCTX pThis)
420{
421 AssertMsgReturnVoid(pThis->pFnCallCurr->pScopeCurr != &pThis->pFnCallCurr->ScopeRoot,
422 ("Current scope is root scope of function call\n"));
423
424 PVDSCRIPTINTERPSCOPE pScope = pThis->pFnCallCurr->pScopeCurr;
425 pThis->pFnCallCurr->pScopeCurr = pScope->pParent;
426 RTStrSpaceDestroy(&pScope->hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
427 RTMemFree(pScope);
428}
429
430/**
431 * Get the content of the given variable identifier from the current or parent scope.
432 */
433static PVDSCRIPTINTERPVAR vdScriptInterpreterGetVar(PVDSCRIPTINTERPCTX pThis, const char *pszVar)
434{
435 PVDSCRIPTINTERPSCOPE pScopeCurr = pThis->pFnCallCurr->pScopeCurr;
436 PVDSCRIPTINTERPVAR pVar = NULL;
437
438 while ( !pVar
439 && pScopeCurr)
440 {
441 pVar = (PVDSCRIPTINTERPVAR)RTStrSpaceGet(&pScopeCurr->hStrSpaceVar, pszVar);
442 if (pVar)
443 break;
444 pScopeCurr = pScopeCurr->pParent;
445 }
446
447
448 return pVar;
449}
450
451/**
452 * Evaluate an expression.
453 *
454 * @returns VBox status code.
455 * @param pThis The interpreter context.
456 * @param pExpr The expression to evaluate.
457 */
458static int vdScriptInterpreterEvaluateExpression(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTEXPR pExpr)
459{
460 int rc = VINF_SUCCESS;
461
462 switch (pExpr->enmType)
463 {
464 case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
465 {
466 /* Push the numerical constant on the value stack. */
467 VDSCRIPTARG NumConst;
468 NumConst.enmType = VDSCRIPTTYPE_UINT64;
469 NumConst.u64 = pExpr->u64;
470 rc = vdScriptInterpreterPushValue(pThis, &NumConst);
471 break;
472 }
473 case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
474 {
475 /* Push the string literal on the value stack. */
476 VDSCRIPTARG StringConst;
477 StringConst.enmType = VDSCRIPTTYPE_STRING;
478 StringConst.psz = pExpr->pszStr;
479 rc = vdScriptInterpreterPushValue(pThis, &StringConst);
480 break;
481 }
482 case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
483 {
484 VDSCRIPTARG BoolConst;
485 BoolConst.enmType = VDSCRIPTTYPE_BOOL;
486 BoolConst.f = pExpr->f;
487 rc = vdScriptInterpreterPushValue(pThis, &BoolConst);
488 break;
489 }
490 case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
491 {
492 /* Look it up and push the value onto the value stack. */
493 PVDSCRIPTINTERPVAR pVar = vdScriptInterpreterGetVar(pThis, pExpr->pIde->aszIde);
494
495 AssertPtrReturn(pVar, VERR_IPE_UNINITIALIZED_STATUS);
496 rc = vdScriptInterpreterPushValue(pThis, &pVar->Value);
497 break;
498 }
499 case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
500 case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
501 AssertMsgFailed(("TODO\n"));
502 case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
503 {
504 PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde);
505 if (pFn)
506 {
507 /* Push a function call control entry on the stack. */
508 PVDSCRIPTINTERPCTRL pCtrlFn = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
509 if (pCtrlFn)
510 {
511 pCtrlFn->fEvalAst = false;
512 pCtrlFn->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_FN_CALL;
513 pCtrlFn->Ctrl.FnCall.pFn = pFn;
514 vdScriptStackPush(&pThis->StackCtrl);
515
516 /* Push parameter expressions on the stack. */
517 PVDSCRIPTASTEXPR pArg = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTEXPR, Core.ListNode);
518 while (pArg)
519 {
520 rc = vdScriptInterpreterPushAstEntry(pThis, &pArg->Core);
521 if (RT_FAILURE(rc))
522 break;
523 pArg = RTListGetNext(&pExpr->FnCall.ListArgs, pArg, VDSCRIPTASTEXPR, Core.ListNode);
524 }
525 }
526 }
527 else
528 AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde));
529 break;
530 }
531 case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
532 case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
533 case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
534 case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
535 case VDSCRIPTEXPRTYPE_UNARY_INVERT:
536 case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
537 case VDSCRIPTEXPRTYPE_MULTIPLICATION:
538 case VDSCRIPTEXPRTYPE_DIVISION:
539 case VDSCRIPTEXPRTYPE_MODULUS:
540 case VDSCRIPTEXPRTYPE_ADDITION:
541 case VDSCRIPTEXPRTYPE_SUBTRACTION:
542 case VDSCRIPTEXPRTYPE_LSR:
543 case VDSCRIPTEXPRTYPE_LSL:
544 case VDSCRIPTEXPRTYPE_LOWER:
545 case VDSCRIPTEXPRTYPE_HIGHER:
546 case VDSCRIPTEXPRTYPE_LOWEREQUAL:
547 case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
548 case VDSCRIPTEXPRTYPE_EQUAL:
549 case VDSCRIPTEXPRTYPE_NOTEQUAL:
550 case VDSCRIPTEXPRTYPE_BITWISE_AND:
551 case VDSCRIPTEXPRTYPE_BITWISE_XOR:
552 case VDSCRIPTEXPRTYPE_BITWISE_OR:
553 case VDSCRIPTEXPRTYPE_LOGICAL_AND:
554 case VDSCRIPTEXPRTYPE_LOGICAL_OR:
555 case VDSCRIPTEXPRTYPE_ASSIGN:
556 case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
557 case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
558 case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
559 case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
560 case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
561 case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
562 case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
563 case VDSCRIPTEXPRTYPE_ASSIGN_AND:
564 case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
565 case VDSCRIPTEXPRTYPE_ASSIGN_OR:
566 case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
567 AssertMsgFailed(("TODO\n"));
568 default:
569 AssertMsgFailed(("Invalid expression type: %d\n", pExpr->enmType));
570 }
571 return rc;
572}
573
574/**
575 * Evaluate a statement.
576 *
577 * @returns VBox status code.
578 * @param pThis The interpreter context.
579 * @param pStmt The statement to evaluate.
580 */
581static int vdScriptInterpreterEvaluateStatement(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
582{
583 int rc = VINF_SUCCESS;
584
585 switch (pStmt->enmStmtType)
586 {
587 case VDSCRIPTSTMTTYPE_COMPOUND:
588 {
589 /* Setup new scope. */
590 rc = vdScriptInterpreterScopeCreate(pThis);
591 if (RT_SUCCESS(rc))
592 {
593 /** @todo: Declarations */
594 rc = vdScriptInterpreterPushCompoundCtrlEntry(pThis, pStmt);
595 }
596 break;
597 }
598 case VDSCRIPTSTMTTYPE_EXPRESSION:
599 {
600 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->pExpr->Core);
601 break;
602 }
603 case VDSCRIPTSTMTTYPE_IF:
604 {
605 rc = vdScriptInterpreterPushIfCtrlEntry(pThis, pStmt);
606 break;
607 }
608 case VDSCRIPTSTMTTYPE_SWITCH:
609 AssertMsgFailed(("TODO\n"));
610 break;
611 case VDSCRIPTSTMTTYPE_WHILE:
612 {
613 rc = vdScriptInterpreterPushWhileCtrlEntry(pThis, pStmt);
614 break;
615 }
616 case VDSCRIPTSTMTTYPE_RETURN:
617 {
618 /* Walk up the control stack until we reach a function cleanup entry. */
619 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
620 while ( pCtrl
621 && ( pCtrl->fEvalAst
622 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP))
623 {
624 /* Cleanup up any compound statement scope. */
625 if ( !pCtrl->fEvalAst
626 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
627 vdScriptInterpreterScopeDestroyCurr(pThis);
628
629 vdScriptStackPop(&pThis->StackCtrl);
630 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
631 }
632 AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, return outside of function\n"));
633 break;
634 }
635 case VDSCRIPTSTMTTYPE_FOR:
636 {
637 rc = vdScriptInterpreterPushForCtrlEntry(pThis, pStmt);
638 break;
639 }
640 case VDSCRIPTSTMTTYPE_CONTINUE:
641 {
642 /* Remove everything up to a loop control entry. */
643 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
644 while ( pCtrl
645 && ( pCtrl->fEvalAst
646 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
647 {
648 /* Cleanup up any compound statement scope. */
649 if ( !pCtrl->fEvalAst
650 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
651 vdScriptInterpreterScopeDestroyCurr(pThis);
652
653 vdScriptStackPop(&pThis->StackCtrl);
654 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
655 }
656 AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, continue outside of loop\n"));
657
658 /* Put the conditionals for while and for loops onto the control stack again. */
659 PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
660
661 AssertMsg( pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE
662 || pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
663 ("Invalid statement type, must be for or while loop\n"));
664
665 if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR)
666 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
667 else if (!pLoopStmt->While.fDoWhile)
668 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
669 break;
670 }
671 case VDSCRIPTSTMTTYPE_BREAK:
672 {
673 /* Remove everything including the loop control statement. */
674 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
675 while ( pCtrl
676 && ( pCtrl->fEvalAst
677 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
678 {
679 /* Cleanup up any compound statement scope. */
680 if ( !pCtrl->fEvalAst
681 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
682 vdScriptInterpreterScopeDestroyCurr(pThis);
683
684 vdScriptStackPop(&pThis->StackCtrl);
685 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
686 }
687 AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, break outside of loop\n"));
688 vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
689 break;
690 }
691 case VDSCRIPTSTMTTYPE_CASE:
692 case VDSCRIPTSTMTTYPE_DEFAULT:
693 AssertMsgFailed(("TODO\n"));
694 break;
695 default:
696 AssertMsgFailed(("Invalid statement type: %d\n", pStmt->enmStmtType));
697 }
698
699 return rc;
700}
701
702/**
703 * Evaluates the given AST node.
704 *
705 * @returns VBox statuse code.
706 * @param pThis The interpreter context.
707 * @param pAstNode The AST node to interpret.
708 */
709static int vdScriptInterpreterEvaluateAst(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTCORE pAstNode)
710{
711 int rc = VERR_NOT_IMPLEMENTED;
712
713 switch (pAstNode->enmClass)
714 {
715 case VDSCRIPTASTCLASS_DECLARATION:
716 {
717 AssertMsgFailed(("TODO\n"));
718 break;
719 }
720 case VDSCRIPTASTCLASS_STATEMENT:
721 {
722 rc = vdScriptInterpreterEvaluateStatement(pThis, (PVDSCRIPTASTSTMT)pAstNode);
723 break;
724 }
725 case VDSCRIPTASTCLASS_EXPRESSION:
726 {
727 rc = vdScriptInterpreterEvaluateExpression(pThis, (PVDSCRIPTASTEXPR)pAstNode);
728 break;
729 }
730 /* These should never ever appear here. */
731 case VDSCRIPTASTCLASS_IDENTIFIER:
732 case VDSCRIPTASTCLASS_FUNCTION:
733 case VDSCRIPTASTCLASS_FUNCTIONARG:
734 case VDSCRIPTASTCLASS_INVALID:
735 default:
736 AssertMsgFailed(("Invalid AST node class: %d\n", pAstNode->enmClass));
737 }
738
739 return rc;
740}
741
742/**
743 * Evaluate a function call.
744 *
745 * @returns VBox status code.
746 * @param pThis The interpreter context.
747 * @param pFn The function execute.
748 */
749static int vdScriptInterpreterFnCall(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTFN pFn)
750{
751 int rc = VINF_SUCCESS;
752
753 if (!pFn->fExternal)
754 {
755 PVDSCRIPTASTFN pAstFn = pFn->Type.Internal.pAstFn;
756
757 /* Add function call cleanup marker on the stack first. */
758 rc = vdScriptInterpreterPushNonDataCtrlEntry(pThis, VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP);
759 if (RT_SUCCESS(rc))
760 {
761 /* Create function call frame and set it up. */
762 PVDSCRIPTINTERPFNCALL pFnCall = (PVDSCRIPTINTERPFNCALL)RTMemAllocZ(sizeof(VDSCRIPTINTERPFNCALL));
763 if (pFnCall)
764 {
765 pFnCall->pCaller = pThis->pFnCallCurr;
766 pFnCall->ScopeRoot.pParent = NULL;
767 pFnCall->ScopeRoot.hStrSpaceVar = NULL;
768 pFnCall->pScopeCurr = &pFnCall->ScopeRoot;
769
770 /* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */
771 PVDSCRIPTASTFNARG pArg = RTListGetFirst(&pAstFn->ListArgs, VDSCRIPTASTFNARG, Core.ListNode);
772 for (unsigned i = 0; i < pAstFn->cArgs; i++)
773 {
774 PVDSCRIPTINTERPVAR pVar = (PVDSCRIPTINTERPVAR)RTMemAllocZ(sizeof(VDSCRIPTINTERPVAR));
775 if (pVar)
776 {
777 pVar->Core.pszString = pArg->pArgIde->aszIde;
778 pVar->Core.cchString = pArg->pArgIde->cchIde;
779 vdScriptInterpreterPopValue(pThis, &pVar->Value);
780 bool fInserted = RTStrSpaceInsert(&pFnCall->ScopeRoot.hStrSpaceVar, &pVar->Core);
781 Assert(fInserted);
782 }
783 else
784 {
785 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable");
786 break;
787 }
788 pArg = RTListGetNext(&pAstFn->ListArgs, pArg, VDSCRIPTASTFNARG, Core.ListNode);
789 }
790
791 if (RT_SUCCESS(rc))
792 {
793 /*
794 * Push compount statement on the control stack and make the newly created
795 * call frame the current one.
796 */
797 rc = vdScriptInterpreterPushAstEntry(pThis, &pAstFn->pCompoundStmts->Core);
798 if (RT_SUCCESS(rc))
799 pThis->pFnCallCurr = pFnCall;
800 }
801
802 if (RT_FAILURE(rc))
803 {
804 RTStrSpaceDestroy(&pFnCall->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
805 RTMemFree(pFnCall);
806 }
807 }
808 else
809 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame");
810 }
811 }
812 else
813 {
814 /* External function call, build the argument list. */
815 if (pFn->cArgs)
816 {
817 PVDSCRIPTARG paArgs = (PVDSCRIPTARG)RTMemAllocZ(pFn->cArgs * sizeof(VDSCRIPTARG));
818 if (paArgs)
819 {
820 for (unsigned i = 0; i < pFn->cArgs; i++)
821 vdScriptInterpreterPopValue(pThis, &paArgs[i]);
822
823 rc = pFn->Type.External.pfnCallback(paArgs, pFn->Type.External.pvUser);
824 RTMemFree(paArgs);
825 }
826 else
827 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS,
828 "Out of memory creating argument array for external function call");
829 }
830 else
831 rc = pFn->Type.External.pfnCallback(NULL, pFn->Type.External.pvUser);
832 }
833
834 return rc;
835}
836
837/**
838 * Evaluate interpreter control statement.
839 *
840 * @returns VBox status code.
841 * @param pThis The interpreter context.
842 * @param pCtrl The control entry to evaluate.
843 */
844static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl)
845{
846 int rc = VINF_SUCCESS;
847
848 Assert(!pCtrl->fEvalAst);
849 switch (pCtrl->Ctrl.enmCtrlType)
850 {
851 case VDSCRIPTINTERPCTRLTYPE_FN_CALL:
852 {
853 PVDSCRIPTFN pFn = pCtrl->Ctrl.FnCall.pFn;
854
855 vdScriptStackPop(&pThis->StackCtrl);
856 rc = vdScriptInterpreterFnCall(pThis, pFn);
857 break;
858 }
859 case VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP:
860 {
861 vdScriptStackPop(&pThis->StackCtrl);
862
863 /* Delete function call entry. */
864 AssertPtr(pThis->pFnCallCurr);
865 PVDSCRIPTINTERPFNCALL pFnCallFree = pThis->pFnCallCurr;
866
867 pThis->pFnCallCurr = pFnCallFree->pCaller;
868 Assert(pFnCallFree->pScopeCurr == &pFnCallFree->ScopeRoot);
869 RTStrSpaceDestroy(&pFnCallFree->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
870 RTMemFree(pFnCallFree);
871 break;
872 }
873 case VDSCRIPTINTERPCTRLTYPE_COMPOUND:
874 {
875 if (!pCtrl->Ctrl.Compound.pStmtCurr)
876 {
877 /* Evaluated last statement, cleanup and remove the control statement from the stack. */
878 vdScriptInterpreterScopeDestroyCurr(pThis);
879 vdScriptStackPop(&pThis->StackCtrl);
880 }
881 else
882 {
883 /* Push the current statement onto the control stack and move on. */
884 rc = vdScriptInterpreterPushAstEntry(pThis, &pCtrl->Ctrl.Compound.pStmtCurr->Core);
885 if (RT_SUCCESS(rc))
886 {
887 pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts,
888 pCtrl->Ctrl.Compound.pStmtCurr, VDSCRIPTASTSTMT, Core.ListNode);
889 }
890 }
891 break;
892 }
893 case VDSCRIPTINTERPCTRLTYPE_LOOP:
894 {
895 PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
896
897 /* Check whether the condition passed. */
898 VDSCRIPTARG Cond;
899 vdScriptInterpreterPopValue(pThis, &Cond);
900 AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
901 ("Value on stack is not of boolean type\n"));
902
903 if (Cond.f)
904 {
905 /* Execute the loop another round. */
906 if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE)
907 {
908 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
909 if (RT_SUCCESS(rc))
910 {
911 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pStmt->Core);
912 if (RT_FAILURE(rc))
913 vdScriptStackPop(&pThis->StackCtrl);
914 }
915 }
916 else
917 {
918 AssertMsg(pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
919 ("Not a for statement\n"));
920
921 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
922 if (RT_SUCCESS(rc))
923 {
924 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExpr3->Core);
925 if (RT_SUCCESS(rc))
926 {
927 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pStmt->Core);
928 if (RT_FAILURE(rc))
929 vdScriptStackPop(&pThis->StackCtrl);
930 }
931
932 if (RT_FAILURE(rc))
933 vdScriptStackPop(&pThis->StackCtrl);
934 }
935 }
936 }
937 else
938 vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
939 break;
940 }
941 case VDSCRIPTINTERPCTRLTYPE_IF:
942 {
943 PVDSCRIPTASTSTMT pIfStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
944
945 vdScriptStackPop(&pThis->StackCtrl); /* Remove if control statement. */
946
947 /* Check whether the condition passed. */
948 VDSCRIPTARG Cond;
949 vdScriptInterpreterPopValue(pThis, &Cond);
950 AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
951 ("Value on stack is not of boolean type\n"));
952
953 if (Cond.f)
954 {
955 /* Execute the true branch. */
956 rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pTrueStmt->Core);
957 }
958 else if (pIfStmt->If.pElseStmt)
959 rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pElseStmt->Core);
960
961 break;
962 }
963 default:
964 AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n",
965 pCtrl->Ctrl.enmCtrlType));
966 }
967
968 return rc;
969}
970
971/**
972 * The interpreter evaluation core loop.
973 *
974 * @returns VBox status code.
975 * @param pThis The interpreter context.
976 */
977static int vdScriptInterpreterEvaluate(PVDSCRIPTINTERPCTX pThis)
978{
979 int rc = VINF_SUCCESS;
980 PVDSCRIPTINTERPCTRL pCtrl = NULL;
981
982 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
983 while (pCtrl)
984 {
985 if (pCtrl->fEvalAst)
986 {
987 PVDSCRIPTASTCORE pAstNode = pCtrl->pAstNode;
988 vdScriptStackPop(&pThis->StackCtrl);
989
990 rc = vdScriptInterpreterEvaluateAst(pThis, pAstNode);
991 }
992 else
993 rc = vdScriptInterpreterEvaluateCtrlEntry(pThis, pCtrl);
994
995 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
996 }
997
998 return rc;
999}
1000
1001DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
1002 PVDSCRIPTARG paArgs, unsigned cArgs,
1003 PVDSCRIPTARG pRet)
1004{
1005 int rc = VINF_SUCCESS;
1006 VDSCRIPTINTERPCTX InterpCtx;
1007 PVDSCRIPTFN pFn = NULL;
1008
1009 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1010 AssertPtrReturn(pszFn, VERR_INVALID_POINTER);
1011 AssertReturn( (!cArgs && !paArgs)
1012 || (cArgs && paArgs), VERR_INVALID_PARAMETER);
1013
1014 InterpCtx.pScriptCtx = pThis;
1015 InterpCtx.pFnCallCurr = NULL;
1016 vdScriptStackInit(&InterpCtx.StackValues, sizeof(VDSCRIPTARG));
1017 vdScriptStackInit(&InterpCtx.StackCtrl, sizeof(VDSCRIPTINTERPCTRL));
1018
1019 pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->hStrSpaceFn, pszFn);
1020 if (pFn)
1021 {
1022 if (cArgs == pFn->cArgs)
1023 {
1024 /* Push the arguments onto the stack. */
1025 /** @todo: Check expected and given argument types. */
1026 for (unsigned i = 0; i < cArgs; i++)
1027 {
1028 PVDSCRIPTARG pArg = (PVDSCRIPTARG)vdScriptStackGetUnused(&InterpCtx.StackValues);
1029 *pArg = paArgs[i];
1030 vdScriptStackPush(&InterpCtx.StackValues);
1031 }
1032
1033 if (RT_SUCCESS(rc))
1034 {
1035 /* Setup function call frame and parameters. */
1036 rc = vdScriptInterpreterFnCall(&InterpCtx, pFn);
1037 if (RT_SUCCESS(rc))
1038 {
1039 /* Run the interpreter. */
1040 rc = vdScriptInterpreterEvaluate(&InterpCtx);
1041 vdScriptStackDestroy(&InterpCtx.StackValues);
1042 vdScriptStackDestroy(&InterpCtx.StackCtrl);
1043 }
1044 }
1045 }
1046 else
1047 rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs);
1048 }
1049 else
1050 rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn);
1051
1052
1053 return rc;
1054}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette