VirtualBox

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

Last change on this file since 65055 was 64532, checked in by vboxsync, 8 years ago

more typos

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