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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2885 - (hide annotations) (download)
Fri Apr 22 15:37:45 2016 UTC (8 years ago) by schoenebeck
File size: 12783 byte(s)
* Instrument script classes now exported with the liblinuxsampler C++ API.
* Added new API method ScriptVM::syntaxHighlighting() which provides
  a convenient syntax highlighting backend for external instrument
  script editor applications.
* Bumped version (2.0.0.svn5).

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

  ViewVC Help
Powered by ViewVC