VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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