source: nrEngine/src/Script.cpp @ 27

Revision 27, 12.9 KB checked in by art, 12 years ago (diff)

nrScript: using of subscripts is now possible

Line 
1/***************************************************************************
2 *                                                                         *
3 *   (c) Art Tevs, MPI Informatik Saarbruecken                             *
4 *       mailto: <tevs@mpi-sb.mpg.de>                                      *
5 *                                                                         *
6 *   This program is free software; you can redistribute it and/or modify  *
7 *   it under the terms of the GNU General Public License as published by  *
8 *   the Free Software Foundation; either version 2 of the License, or     *
9 *   (at your option) any later version.                                   *
10 *                                                                         *
11 ***************************************************************************/
12
13
14//----------------------------------------------------------------------------------
15// Includes
16//----------------------------------------------------------------------------------
17#include "Script.h"
18#include "ScriptEngine.h"
19#include "Clock.h"
20#include "Log.h"
21#include "Kernel.h"
22#include <boost/algorithm/string/trim.hpp>
23
24namespace nrEngine {
25
26        //----------------------------------------------------------------------------------
27        Script::Script() : IScript("Script")
28        {
29                mLastLine = 0;
30                mLoop = NO_LOOP;
31                mTime = 0;
32                bRunStepwise = true;
33                mStopTime = 0;
34                mRunningTimeLength = 0;
35        }
36
37        //----------------------------------------------------------------------------------
38        Script::~Script()
39        {
40                unloadRes();
41        }
42
43
44        //----------------------------------------------------------------------------------
45        Result Script::loadFromString(const std::string& str)
46        {
47                // set task name as resource name
48                setTaskName(getResName() + "_ScriptTask");
49
50                // setup content
51                mContent = str;
52
53                // parse it
54                Result ret = parse(mContent);
55                if (ret == OK) notifyLoaded();
56
57                return ret;
58        }
59
60        //------------------------------------------------------------------------------
61        std::string Script::cleanScript(const std::string& script)
62        {
63                // check the lenght of the script
64                std::stringstream str(script);
65                if (script.length() == 0 || !str.good()) return script;
66                std::string resultScript;
67
68                // get linewise
69                char buf[2048];
70                std::string buffer;
71                std::string::size_type pos;
72                std::string line;
73
74                // for the whole stream do following
75                while (!str.eof())
76                {
77                        // get line
78                        str.getline(buf, 2048);
79                        buffer = std::string(buf);
80
81                        // remove comments
82                        pos = buffer.find("//");
83                        if (pos == std::string::npos)
84                                line = buffer;
85                        else
86                                line = buffer.substr(0, pos);
87
88                        // trim the line
89                        boost::algorithm::trim(line);
90
91                        // if the line is empty, so get the next one
92                        if (line.length() == 0) continue;
93
94                        // if we are here, so we do not have any comments or empty lines
95                        resultScript += line;
96                        if (!str.eof()) resultScript += std::string("\n");
97                }
98
99                return resultScript;
100        }
101
102        //------------------------------------------------------------------------------
103        std::string Script::parseSubscripts(const std::string& script)
104        {
105                // vars
106                int depth = 0, count = 0;
107
108                // for parsing we are using stack-algorithm
109                std::vector<std::string> stack;
110                stack.push_back(std::string(""));
111                std::string* top = &stack[stack.size()-1];
112
113                // go through the given script
114                for (unsigned int i=0; i < script.length(); i++)
115                {
116                        top = &(stack[stack.size()-1]);
117                        if (script.at(i) == '{')
118                        {
119                                depth ++;
120                                stack.push_back(std::string(""));
121                        }
122                        else if (script.at(i) == '}')
123                        {
124                                depth --;
125                                count ++;
126
127                                if (depth >= 0)
128                                {
129                                        // create name for the new subscript
130                                        std::stringstream subscriptName;
131                                        subscriptName << getResName() << std::string("_sub_") << count << std::string("_") << depth;
132
133                                        // current top element is a new script, so create it
134                                        ResourcePtr<IScript> scr = Engine::sResourceManager()->createResource(subscriptName.str(), getResName(), "Script");
135
136                                        // if script could be created, so fill it and store, to execute later
137                                        if (scr.isNull())
138                                        {
139                                                NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script (%s): Subscript could not be created. Maybe no proper loader exists. Script will not run.\n", getResName().c_str());
140                                                return std::string("");
141                                        }else{
142                                                scr.lockPure();
143                                                        scr->loadFromString(*top);
144                                                scr.unlockPure();
145                                                mSubscripts.push_back(scr);
146                                        }
147
148                                        // remove top element
149                                        stack.pop_back();
150                                }
151                        }else{
152                                *top += script.at(i);
153                        }
154                }
155
156
157                // check if closing brackets were complete
158                if (depth != 0)
159                {
160                        NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script (%s): Subscript-brackets were not used properly. Depth is %d, must be 0. Script could not run as expected!", getResName().c_str(), depth);
161                }
162
163                // return mainscript back
164                return cleanScript(stack[0]);
165        }
166
167        //------------------------------------------------------------------------------
168        Result Script::parse(const std::string& script)
169        {
170                // remove all non-commands from the script
171                // check for subscripts
172                std::stringstream str(parseSubscripts(cleanScript(script)));
173                if (script.length() == 0 || !str.good()) return OK;
174
175                //printf("%s: \n%s\n---\n\n", getResName().c_str(),str.str().c_str());
176                // get linewise
177                char buf[1024];
178                std::string::size_type pos;
179                std::string line;
180
181                while (!str.eof())
182                {
183                        // get line
184                        str.getline(buf, 1024);
185                        line = std::string(buf);
186
187                        // check for script parameters
188                        pos = line.find('^');
189                        if (pos != std::string::npos)
190                        {
191                                std::string param = boost::algorithm::trim(line.substr(pos+1));
192                                if (!setParameter(param)){
193                                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown local parameter %s in line \"%s\"", param.c_str(), line.c_str());
194                                        return SCRIPT_PARSE_ERROR;
195                                }
196                                continue;
197                        }
198
199                        // check for the timestamp
200                        pos = line.find('|');
201                        Command cmd;
202                        cmd.timestamp = 0;
203                        cmd.estimatedStart = 0;
204
205                        // timestamp found, so do extra stuff with this kind of commands
206                        if(pos != std::string::npos )
207                        {
208                                // retrieve the timestamp and the command and store it into the command buffer
209                                std::string time = boost::algorithm::trim(line.substr(0,pos));
210                                std::string command = line.substr(pos+1);
211
212                                // try to convert the time and add the command to the list
213                                try{
214                                        cmd.timestamp = boost::lexical_cast<float32>(time);
215                                        tokenize(command, cmd.cmd, cmd.args);
216                                        if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
217                                        {
218                                                if (cmd.cmd == std::string("_stop_"))
219                                                {
220                                                        mRunningTimeLength = cmd.timestamp;
221                                                }else
222                                                        mTimedCommand.push_back(cmd);
223                                        }
224                                }catch(...){
225                                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown syntax in \"%s\"\n", line.c_str());
226                                        return SCRIPT_PARSE_ERROR;
227                                }
228
229                        // timestamp not found, so it is a simple command
230                        }else{
231                                tokenize(line, cmd.cmd, cmd.args);
232                                if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
233                                {
234                                        mCommand.push_back(cmd);
235                                }
236                        }
237                }
238
239                resetCommandFifo();
240                resetTimedCommandFifo();
241
242                // if we got any command so we are valid
243                return OK;
244        }
245
246        //------------------------------------------------------------------------------
247        void Script::tokenize(const std::string& str, std::string& cmd, std::vector<std::string>& tokens)
248        {
249                // do not tokenize if we do not get any token
250                if (str.length() == 0) return;
251
252                using namespace std;
253
254                // tokenize on space characters
255                const string delimiters = " ";
256
257                // Skip delimiters at beginning.
258                string::size_type lastPos = str.find_first_not_of(delimiters, 0);
259
260                // Find first "non-delimiter".
261                string::size_type pos     = str.find_first_of(delimiters, lastPos);
262
263                while (pos != string::npos || lastPos != string::npos)
264                {
265                        // Found a token, add it to the vector.
266                        if (pos  - lastPos > 0)
267                                tokens.push_back(str.substr(lastPos, pos - lastPos));
268
269                        // Skip delimiters.  Note the "not_of"
270                        lastPos = str.find_first_not_of(delimiters, pos);
271
272                        // Find next "non-delimiter"
273                        pos = str.find_first_of(delimiters, lastPos);
274                }
275
276                // now the first element in the token list is the command name
277                cmd = tokens[0];
278        }
279
280        //------------------------------------------------------------------------------
281        bool Script::setParameter(const std::string& param){
282
283                if (param == "loop")
284                        mLoop |= LOOP;
285                else if (param == "loop_seq")
286                        mLoop |= LOOP_COMMAND;
287                else if (param == "loop_timed")
288                        mLoop |= LOOP_TIMED_COMMAND;
289                else if (param == "runcomplete")
290                        bRunStepwise = false;
291                else if (param == "runonce")
292                        setRunOnce(true);
293                else
294                        return false;
295
296                return true;
297        }
298
299        //------------------------------------------------------------------------------
300        void Script::resetCommandFifo()
301        {
302                // the untimed command fifo is very simple
303                // the commands are executed in the way they were found in the file
304                mCommandFifo.clear();
305                for (uint32 i=0; i < mCommand.size(); i++){
306                        mCommandFifo.push_front(i);
307                }
308        }
309
310        //------------------------------------------------------------------------------
311        void Script::resetTimedCommandFifo()
312        {
313                // the timed commands are also setted in the fifo
314                // the estimated end time is also computed
315                mTimedCommandFifo.clear();
316                for (uint32 i=0; i < mTimedCommand.size(); i++)
317                {
318                        mTimedCommandFifo.push_front(i);
319                        mTimedCommand[i].estimatedStart = Engine::sClock()->getTime() + mTimedCommand[i].timestamp;
320                }
321        }
322
323        //----------------------------------------------------------------------------------
324        Result Script::step()
325        {
326                // update time
327                mTime = Engine::sClock()->getTime();
328                if (mStopTime == 0 && mRunningTimeLength > 0)
329                        mStopTime = mTime + mRunningTimeLength;
330
331                // ----- timed commands (quasi parallel execution) -----
332                // scan through the command fifo
333                std::list<int32>::iterator it;
334                for (it = mTimedCommandFifo.begin(); it != mTimedCommandFifo.end(); )
335                {
336                        int32 id = *it;
337                        mTimedCommand[id].time = mTime;
338
339                        //printf("%s: %f %f\n", mTimedCommand[id].cmd.c_str(), mTimedCommand[id].estimatedStart, mTimedCommand[id].time);
340
341                        // if the estimated start time is reeached, so start the command and remove it from the queue
342                        if (mTimedCommand[id].estimatedStart < mTimedCommand[id].time)
343                        {
344                                // remove the command from the fifo
345                                it = mTimedCommandFifo.erase(it);
346
347                                // call the commando
348                                Engine::sScriptEngine()->call(mTimedCommand[id].cmd, mTimedCommand[id].args);
349                        }else
350                                it ++;
351                }
352
353                // ----- sequentiall commands -----
354                if (mCommandFifo.size())
355                {
356                        // get the id of the command
357                        int32 id = mCommandFifo.back();
358                        mCommandFifo.pop_back();
359
360                        // check if we have a stop commando
361                        if (mCommand[id].cmd != std::string("_stop_"))
362                        {
363                                Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
364                        }else{
365                                Engine::sKernel()->RemoveTask(this->getTaskID());
366                        }
367                        // check for time reset command
368                        if (mCommand[id].cmd != std::string("_reset_script_time_"))
369                        {
370                                resetTimedCommandFifo();
371                        }
372                }
373
374                // reset the command list
375                reset();
376
377                // check whenever execution time of the script exceeds
378                if (mTime > mStopTime && mStopTime > 0)
379                {
380                                Engine::sKernel()->RemoveTask(this->getTaskID());
381                }
382
383                return OK;
384        }
385
386        //----------------------------------------------------------------------------------
387        bool Script::hasCommands()
388        {
389                if (mCommandFifo.size() == 0 && mTimedCommandFifo.size() == 0 && !(mLoop & LOOP_COMMAND) && !(mLoop & LOOP_TIMED_COMMAND))
390                        return false;
391
392                return true;
393        }
394
395        //----------------------------------------------------------------------------------
396        Result Script::run()
397        {
398                // check whenver this script is running stepwise
399                if (bRunStepwise) return step();
400
401                // make full run of the script
402                return fullRun();
403        }
404
405
406        //----------------------------------------------------------------------------------
407        Result Script::fullRun()
408        {
409
410                // check for looped script
411                static bool warned = false;
412                if (!warned && mLoop & LOOP_COMMAND){
413                        warned = true;
414                        NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script: %s - you are executing a looped script!!! Check this!!!", getResName().c_str());
415                }
416
417                // loop the content of the script if script should be looped
418                bool bLoop = false;
419                do {
420                        // we go through the whole command list and execute it.
421                        while (mCommandFifo.size() > 0)
422                        {
423                                // get the id of the command
424                                int32 id = mCommandFifo.back();
425                                mCommandFifo.pop_back();
426
427                                // execute it
428                                Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
429                        }
430
431                        // reset the list and check if the task is removed from the kernel
432                        reset();
433                        bLoop = hasCommands();
434
435                } while(bLoop && (mLoop & LOOP_COMMAND));
436
437                return OK;
438        }
439
440        //--------------------------------------------------------------------
441        void Script::onStartScript()
442        {
443                // do run all subscripts specified in this script
444                for (unsigned int i = 0; i < mSubscripts.size(); i++)
445                        mSubscripts[i]->execute();
446
447                // reset the script, so it can run
448                resetCommandFifo();
449                resetTimedCommandFifo();
450        }
451
452        //----------------------------------------------------------------------------------
453        bool Script::reset()
454        {
455                if (mCommandFifo.size() == 0 && (mLoop & LOOP_COMMAND)){
456                        resetCommandFifo();
457                }else if (mTimedCommandFifo.size() == 0 && (mLoop & LOOP_TIMED_COMMAND)){
458                        resetTimedCommandFifo();
459                }
460                return true;
461        }
462
463};
464
Note: See TracBrowser for help on using the repository browser.