VirtualBox

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

Last change on this file since 45482 was 44964, checked in by vboxsync, 12 years ago

Storage/testcase: A bit more work on the scripting engine

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