VirtualBox

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

Last change on this file since 97613 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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