/[svn]/linuxsampler/trunk/src/scriptvm/ScriptVM.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/scriptvm/ScriptVM.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2611 - (show annotations) (download)
Mon Jun 9 19:20:37 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 11258 byte(s)
* Fixed crash when loading an instrument script.
* Fixed "init" script handler only to be executed once:
  when the script was loaded.
* Fixed aftertouch script event which always had value zero
  and controller number was set to aftertouch value instead.
* gig Engine: Fixed handling of "smartmidi" dimension, which
  was recognized as "unknown" dimension.
* Fixed script function gig_set_dim_zone(): was accessing
  wrong event.
* ls_instr_script command line tool: is now not limited to
  core language scripts, but can now also parse sampler format
  dependent instrument scripts, with the respective specific
  built-in script variables and functions.
* ScriptVM: Fixed runtime behavior of "and" and "or" binary
  script expressions, which also evaluated the right hand side
  of the expression even if the left hand side already failed
  the overall expression semantic to become true.
* Bumped version (1.0.0.svn46).

1 /*
2 * Copyright (c) 2014 Christian Schoenebeck
3 *
4 * http://www.linuxsampler.org
5 *
6 * This file is part of LinuxSampler and released under the same terms.
7 * See README file for details.
8 */
9
10 #include "ScriptVM.h"
11
12 #include <string.h>
13 #include "../common/global_private.h"
14 #include "tree.h"
15
16 #define DEBUG_SCRIPTVM_CORE 0
17
18 int InstrScript_parse(LinuxSampler::ParserContext*);
19
20 namespace LinuxSampler {
21
22 static void _printIndents(int n) {
23 for (int i = 0; i < n; ++i) printf(" ");
24 fflush(stdout);
25 }
26
27 static int _requiredMaxStackSizeFor(Statement* statement, int depth = 0) {
28 if (!statement) return 1;
29
30 switch (statement->statementType()) {
31 case STMT_LEAF:
32 #if DEBUG_SCRIPTVM_CORE
33 _printIndents(depth);
34 printf("-> STMT_LEAF\n");
35 #endif
36 return 1;
37
38 case STMT_LIST: {
39 #if DEBUG_SCRIPTVM_CORE
40 _printIndents(depth);
41 printf("-> STMT_LIST\n");
42 #endif
43 Statements* stmts = (Statements*) statement;
44 int max = 0;
45 for (int i = 0; stmts->statement(i); ++i) {
46 int size = _requiredMaxStackSizeFor( stmts->statement(i), depth+1 );
47 if (max < size) max = size;
48 }
49 return max + 1;
50 }
51
52 case STMT_BRANCH: {
53 #if DEBUG_SCRIPTVM_CORE
54 _printIndents(depth);
55 printf("-> STMT_BRANCH\n");
56 #endif
57 BranchStatement* branchStmt = (BranchStatement*) statement;
58 int max = 0;
59 for (int i = 0; branchStmt->branch(i); ++i) {
60 int size = _requiredMaxStackSizeFor( branchStmt->branch(i), depth+1 );
61 if (max < size) max = size;
62 }
63 return max + 1;
64 }
65
66 case STMT_LOOP: {
67 #if DEBUG_SCRIPTVM_CORE
68 _printIndents(depth);
69 printf("-> STMT_LOOP\n");
70 #endif
71 While* whileStmt = (While*) statement;
72 if (whileStmt->statements())
73 return _requiredMaxStackSizeFor( whileStmt->statements() ) + 1;
74 else
75 return 1;
76 }
77 }
78
79 return 1; // actually just to avoid compiler warning
80 }
81
82 static int _requiredMaxStackSizeFor(EventHandlers* handlers) {
83 int max = 1;
84 for (int i = 0; i < handlers->size(); ++i) {
85 int size = _requiredMaxStackSizeFor(handlers->eventHandler(i));
86 if (max < size) max = size;
87 }
88 return max;
89 }
90
91 ScriptVM::ScriptVM() : m_parserContext(NULL), fnWait(this) {
92 }
93
94 ScriptVM::~ScriptVM() {
95 }
96
97 VMParserContext* ScriptVM::loadScript(const String& s) {
98 std::istringstream iss(s);
99 return loadScript(&iss);
100 }
101
102 VMParserContext* ScriptVM::loadScript(std::istream* is) {
103 ParserContext* context = new ParserContext(this);
104 //printf("parserCtx=0x%lx\n", (uint64_t)context);
105
106 context->registerBuiltInConstIntVariables( builtInConstIntVariables() );
107 context->registerBuiltInIntVariables( builtInIntVariables() );
108 context->registerBuiltInIntArrayVariables( builtInIntArrayVariables() );
109
110 context->createScanner(is);
111
112 InstrScript_parse(context);
113 dmsg(2,("Allocating %d bytes of global int VM memory.\n", context->globalIntVarCount * sizeof(int)));
114 dmsg(2,("Allocating %d of global VM string variables.\n", context->globalStrVarCount));
115 if (!context->globalIntMemory)
116 context->globalIntMemory = new ArrayList<int>();
117 if (!context->globalStrMemory)
118 context->globalStrMemory = new ArrayList<String>();
119 context->globalIntMemory->resize(context->globalIntVarCount);
120 memset(&((*context->globalIntMemory)[0]), 0, context->globalIntVarCount * sizeof(int));
121
122 context->globalStrMemory->resize(context->globalStrVarCount);
123
124 context->destroyScanner();
125
126 return context;
127 }
128
129 void ScriptVM::dumpParsedScript(VMParserContext* context) {
130 ParserContext* ctx = dynamic_cast<ParserContext*>(context);
131 if (!ctx) {
132 std::cerr << "No VM context. So nothing to dump.\n";
133 return;
134 }
135 if (!ctx->handlers) {
136 std::cerr << "No event handlers defined in script. So nothing to dump.\n";
137 return;
138 }
139 if (!ctx->globalIntMemory) {
140 std::cerr << "Internal error: no global memory assigend to script VM.\n";
141 return;
142 }
143 ctx->handlers->dump();
144 }
145
146 VMExecContext* ScriptVM::createExecContext(VMParserContext* parserContext) {
147 ParserContext* parserCtx = dynamic_cast<ParserContext*>(parserContext);
148 ExecContext* execCtx = new ExecContext();
149
150 if (parserCtx->requiredMaxStackSize < 0) {
151 parserCtx->requiredMaxStackSize =
152 _requiredMaxStackSizeFor(&*parserCtx->handlers);
153 }
154 execCtx->stack.resize(parserCtx->requiredMaxStackSize);
155 dmsg(2,("Created VM exec context with %d bytes VM stack size.\n",
156 parserCtx->requiredMaxStackSize * sizeof(ExecContext::StackFrame)));
157 //printf("execCtx=0x%lx\n", (uint64_t)execCtx);
158 const int polySize = parserCtx->polyphonicIntVarCount;
159 execCtx->polyphonicIntMemory.resize(polySize);
160 memset(&execCtx->polyphonicIntMemory[0], 0, polySize * sizeof(int));
161
162 dmsg(2,("Allocated %d bytes polyphonic memory.\n", polySize * sizeof(int)));
163 return execCtx;
164 }
165
166 VMFunction* ScriptVM::functionByName(const String& name) {
167 if (name == "message") return &fnMessage;
168 else if (name == "exit") return &fnExit;
169 else if (name == "wait") return &fnWait;
170 return NULL;
171 }
172
173 std::map<String,VMIntRelPtr*> ScriptVM::builtInIntVariables() {
174 return std::map<String,VMIntRelPtr*>();
175 }
176
177 std::map<String,VMInt8Array*> ScriptVM::builtInIntArrayVariables() {
178 return std::map<String,VMInt8Array*>();
179 }
180
181 std::map<String,int> ScriptVM::builtInConstIntVariables() {
182 return std::map<String,int>();
183 }
184
185 VMParserContext* ScriptVM::currentVMParserContext() {
186 return m_parserContext;
187 }
188
189 VMExecContext* ScriptVM::currentVMExecContext() {
190 if (!m_parserContext) return NULL;
191 return m_parserContext->execContext;
192 }
193
194 VMExecStatus_t ScriptVM::exec(VMParserContext* parserContext, VMExecContext* execContex, VMEventHandler* handler) {
195 m_parserContext = dynamic_cast<ParserContext*>(parserContext);
196 if (!m_parserContext) {
197 std::cerr << "No VM parser context provided. Did you load a script?.\n";
198 return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR);
199 }
200
201 // a ParserContext object is always tied to exactly one ScriptVM object
202 assert(m_parserContext->functionProvider == this);
203
204 ExecContext* ctx = dynamic_cast<ExecContext*>(execContex);
205 if (!ctx) {
206 std::cerr << "Invalid VM exec context.\n";
207 return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR);
208 }
209 EventHandler* h = dynamic_cast<EventHandler*>(handler);
210 if (!h) return VM_EXEC_NOT_RUNNING;
211
212 m_parserContext->execContext = ctx;
213
214 ctx->status = VM_EXEC_RUNNING;
215 StmtFlags_t flags = STMT_SUCCESS;
216
217 int& frameIdx = ctx->stackFrame;
218 if (frameIdx < 0) { // start condition ...
219 frameIdx = -1;
220 ctx->pushStack(h);
221 }
222
223 while (flags == STMT_SUCCESS && frameIdx >= 0) {
224 if (frameIdx >= ctx->stack.size()) { // should never happen, otherwise it's a bug ...
225 std::cerr << "CRITICAL: VM stack overflow! (" << frameIdx << ")\n";
226 flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
227 break;
228 }
229
230 ExecContext::StackFrame& frame = ctx->stack[frameIdx];
231 switch (frame.statement->statementType()) {
232 case STMT_LEAF: {
233 #if DEBUG_SCRIPTVM_CORE
234 _printIndents(frameIdx);
235 printf("-> STMT_LEAF\n");
236 #endif
237 LeafStatement* leaf = (LeafStatement*) frame.statement;
238 flags = leaf->exec();
239 ctx->popStack();
240 break;
241 }
242
243 case STMT_LIST: {
244 #if DEBUG_SCRIPTVM_CORE
245 _printIndents(frameIdx);
246 printf("-> STMT_LIST subidx=%d\n", frame.subindex);
247 #endif
248 Statements* stmts = (Statements*) frame.statement;
249 if (stmts->statement(frame.subindex)) {
250 ctx->pushStack(
251 stmts->statement(frame.subindex++)
252 );
253 } else {
254 #if DEBUG_SCRIPTVM_CORE
255 _printIndents(frameIdx);
256 printf("[END OF LIST] subidx=%d\n", frame.subindex);
257 #endif
258 ctx->popStack();
259 }
260 break;
261 }
262
263 case STMT_BRANCH: {
264 #if DEBUG_SCRIPTVM_CORE
265 _printIndents(frameIdx);
266 printf("-> STMT_BRANCH\n");
267 #endif
268 if (frame.subindex < 0) ctx->popStack();
269 else {
270 BranchStatement* branchStmt = (BranchStatement*) frame.statement;
271 frame.subindex = branchStmt->evalBranch();
272 if (frame.subindex >= 0) {
273 ctx->pushStack(
274 branchStmt->branch(frame.subindex)
275 );
276 frame.subindex = -1;
277 } else ctx->popStack();
278 }
279 break;
280 }
281
282 case STMT_LOOP: {
283 #if DEBUG_SCRIPTVM_CORE
284 _printIndents(frameIdx);
285 printf("-> STMT_LOOP\n");
286 #endif
287 While* whileStmt = (While*) frame.statement;
288 if (whileStmt->evalLoopStartCondition() && whileStmt->statements()) {
289 ctx->pushStack(
290 whileStmt->statements()
291 );
292 } else ctx->popStack();
293 }
294 }
295 }
296
297 if (flags & STMT_SUSPEND_SIGNALLED) {
298 ctx->status = VM_EXEC_SUSPENDED;
299 } else {
300 ctx->status = VM_EXEC_NOT_RUNNING;
301 if (flags & STMT_ERROR_OCCURRED)
302 ctx->status = VM_EXEC_ERROR;
303 ctx->reset();
304 }
305
306 m_parserContext->execContext = NULL;
307 m_parserContext = NULL;
308 return ctx->status;
309 }
310
311 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC