/
process.h
289 lines (252 loc) · 9.65 KB
/
process.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
* The Doomsday Engine Project -- libcore
*
* Copyright © 2004-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/
#ifndef LIBDENG2_PROCESS_H
#define LIBDENG2_PROCESS_H
#include "../Script"
#include "../Time"
#include "../Context"
#include "../Function"
#include "../String"
#include "../Variable"
#include <list>
namespace de {
class ArrayValue;
template <typename Type>
QString scriptArgumentAsText(Type const &arg) {
return QString("%1").arg(arg);
}
template <>
inline QString scriptArgumentAsText(QString const &arg) {
if(arg.startsWith("$")) { // Verbatim?
return arg.mid(1);
}
QString quoted(arg);
quoted.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
return QString("\"%1\"").arg(quoted);
}
template <>
inline QString scriptArgumentAsText(String const &arg) {
return scriptArgumentAsText(QString(arg));
}
template <>
inline QString scriptArgumentAsText(char const * const &utf8) {
return scriptArgumentAsText(QString::fromUtf8(utf8));
}
inline void convertScriptArguments(QStringList &) {}
template <typename FirstArg, typename... Args>
void convertScriptArguments(QStringList &list, FirstArg const &firstArg, Args... args) {
list << scriptArgumentAsText(firstArg);
convertScriptArguments(list, args...);
}
/**
* Executes a script. The process maintains the execution environment, including things
* like local variables and keeping track of which statement is being executed.
*
* @ingroup script
*/
class DENG2_PUBLIC Process
{
public:
/// The process is running while an operation is attempted that requires the
/// process to be stopped. @ingroup errors
DENG2_ERROR(NotStoppedError);
/// Suspending or resuming fails. @ingroup errors
DENG2_ERROR(SuspendError);
/// Execution is taking too long to complete. @ingroup errors
DENG2_ERROR(HangError);
/// A process is always in one of these states.
enum State {
Running, /**< The process is running normally. */
Suspended, /**< The process has been suspended and will
* not continue running until restored. A
* process cannot restore itself from a
* suspended state. */
Stopped /**< The process has reached the end of the
* script or has been terminated. */
};
typedef std::list<Record *> Namespaces;
public:
/**
* Constructs a new process. The process is initialized to STOPPED state.
*
* @param externalGlobalNamespace Namespace to use as the global namespace
* of the process. If not specified, a private
* global namespace is created for the process.
* The process does not get ownership of the
* external global namespace.
*/
Process(Record *externalGlobalNamespace = 0);
/**
* Constructs a new process. The process is initialized to RUNNING state.
*
* @param script Script to run. No reference to the script is retained
* apart from pointing to its statements. The script instance
* must remain in existence while the process is running,
* as it is the owner of the statements.
*/
Process(Script const &script);
State state() const;
/// Determines the current depth of the call stack.
dsize depth() const;
/**
* Resets the process to an empty state. All existing content in the
* process's context stack is removed, leaving the process in a similar
* state than after construction.
*/
void clear();
/**
* Starts running the given script. Note that the process must be
* in the FINISHED state for this to be a valid operation.
*
* @param script Script to run. No reference to the script is retained
* apart from pointing to its statements. The script instance
* must remain in existence while the process is running,
* as it is the owner of the statements.
*/
void run(Script const &script);
/**
* Suspends or resumes execution of the script.
*/
void suspend(bool suspended = true);
/**
* Stops the execution of the script. The process is set to the
* FINISHED state, which means it must be rerun with a new script.
*/
void stop();
/**
* Execute the next command(s) in the script. Execution continues until
* the script leaves RUNNING state.
*/
void execute();
/**
* Finish the execution of the topmost context. Execution will
* continue from the second topmost context.
*
* @param returnValue Value to use as the return value from the
* context. Takes ownership of the value.
*/
void finish(Value *returnValue = 0);
/**
* Changes the working path of the process. File system access within the
* process is relative to the working path.
*
* @param newWorkingPath New working path for the process.
*/
void setWorkingPath(String const &newWorkingPath);
/**
* Returns the current working path.
*/
String const &workingPath() const;
/**
* Return an execution context. By default returns the topmost context.
*
* @param downDepth How many levels to go down. There are depth() levels
* in the context stack.
*
* @return Context at @a downDepth.
*/
Context &context(duint downDepth = 0);
/**
* Pushes a new context to the process's stack.
*
* @param context Context. Ownership taken.
*/
void pushContext(Context *context);
/**
* Pops the topmost context off the stack and returns it. Ownership given
* to caller.
*/
Context *popContext();
/**
* Performs a function call. A new context is created on the context
* stack and the function's statements are executed on the new stack.
* After the call is finished, the result value is pushed to the
* calling context's evaluator.
*
* @param function Function to call.
* @param arguments Arguments for the function. The array's first element
* must be a DictionaryValue containing values for the
* named arguments of the call. The rest of the array
* are the unnamed arguments.
* @param self Optional scope that becomes the value of the "self"
* variable. Ownership given to Process.
*/
void call(Function const &function, ArrayValue const &arguments,
Value *self = 0);
/**
* Collects the namespaces currently visible. This includes the process's
* own stack and the global namespaces.
*
* @param spaces The namespaces are collected here. The order is important:
* earlier namespaces shadow the subsequent ones.
*/
void namespaces(Namespaces &spaces) const;
/**
* Returns the global namespace of the process. This is always the bottommost context
* in the stack.
*/
Record &globals();
/**
* Returns the local namespace of the process. This is always the topmost context in
* the stack.
*/
Record &locals();
public:
/*
* Utilities for calling script functions from native code.
*/
enum CallResult { IgnoreResult, TakeResult };
/**
* Calls a script function. Native arguments are converted to script
* source text and then parsed into Values when the call is executed.
*
* Only non-named function arguments are supported by this method.
*
* @param result What to do with the result value.
* @param global Global namespace where to execute the call.
* @param function Name of the function.
* @param args Argument values for the function call.
*
* @return Depending on @a result, returns nullptr or the return value
* from the call. Caller gets ownership of the possibly returned Value.
*/
template <typename... Args>
static Value *scriptCall(CallResult result, Record &globals,
String const &function, Args... args)
{
QStringList argsList;
convertScriptArguments(argsList, args...);
Script script(QString("%1(%2)").arg(function).arg(argsList.join(',')));
Process proc(&globals);
proc.run(script);
proc.execute();
if(result == IgnoreResult) return nullptr;
// Return the result using the request value type.
return proc.context().evaluator().popResult();
}
template <typename ReturnValueType, typename... Args>
static ReturnValueType *scriptCall(Record &globals, String const &function, Args... args)
{
return static_cast<ReturnValueType *>(scriptCall(TakeResult, globals, function, args...));
}
private:
DENG2_PRIVATE(d)
};
} // namespace de
#endif /* LIBDENG2_PROCESS_H */