summaryrefslogtreecommitdiff
path: root/src/core/script/ScriptEngine.hpp
blob: 3c61cb0ac814c001f8855cdc155a3cacabb48f89 (plain)
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
/*
    Ousía
    Copyright (C) 2014, 2015  Benjamin Paaßen, Andreas Stöckel

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _OUSIA_SCRIPT_ENGINE_HPP_
#define _OUSIA_SCRIPT_ENGINE_HPP_

#include <map>
#include <string>
#include <exception>

#include <core/Utils.hpp>

#include "Variant.hpp"

// TODO: Provide more Exception classes than ScriptEngineException -- one for
// internal errors, one for script errors

// TODO: Allow reporting multiple exceptions (e.g. to report all syntax errors
// at once)

// TODO: Add API that allow pre-compilation of scripts

namespace ousia {
namespace script {

/**
 * Class used for signaling errors while executing code or registering variables
 * in the script engine.
 */
class ScriptEngineException : public std::exception {
public:
	/**
	 * Line and column at which the exception occured. Set to -1 if the error
	 * does not correspond to a line or column.
	 */
	const int line, col;

	/**
	 * Error message.
	 */
	const std::string msg;

	/**
	 * ScriptEngineException constructor.
	 *
	 * @param line in the script code at which the exception occured.
	 * @param col in the script code at which the exception occured.
	 * @param msg is the message containing the reason for the exception.
	 */
	ScriptEngineException(int line, int col, const std::string &msg);

	/**
	 * ScriptEngineException constructor.
	 *
	 * @param msg is the message containing the reason for the exception.
	 */
	ScriptEngineException(const std::string &msg);

	/**
	 * Returns the error message.
	 */
	virtual const char *what() const noexcept override;
};

/**
 * The ScriptEngineScope class represents an execution scope -- an execution
 * scope is the base class
 */
class ScriptEngineScope {
private:
	/**
	 * Helper used to check the given identifiers for their validity.
	 *
	 * @param name is the name of the identifier that should be checked.
	 * @throws ScriptEngineException if the given identifier is invalid.
	 */
	static void checkIdentifier(const std::string &name)
	{
		if (!Utils::isIdentifier(name)) {
			throw ScriptEngineException{"Invalid identifier \"" + name + "\""};
		}
	}

protected:
	/**
	 * Implementation of the run function.
	 */
	virtual Variant doRun(const std::string &code) = 0;

	/**
	 * Implementation of the setVariable function.
	 */
	virtual void doSetVariable(const std::string &name, const Variant &val,
	                           bool constant) = 0;

	/**
	 * Implementation of the getVariable function.
	 */
	virtual Variant doGetVariable(const std::string &name) = 0;

public:
	/**
	 * Virtual destructor. Must be overwritten by implementing classes.
	 */
	virtual ~ScriptEngineScope(){};

	/**
	 * Runs the given code in the excution context.
	 *
	 * @param code is a string containing the code the script engine should run.
	 * @return a variant containg the result of the executed code.
	 * @throws ScriptEngineException if an error occured during code execution.
	 */
	Variant run(const std::string &code) { return doRun(code); }

	/**
	 * Sets the value of a variable in the scope with the given name.
	 *
	 * @param name is the name of the variable in the scope. Must be a
	 * well-formed identifier.
	 * @param val is the value of the variable.
	 * @param constant if true, the value of the variable cannot be changed by
	 * the script code.
	 * @throws ScriptEngineException if name is not a well-formed identifier.
	 */
	void setVariable(const std::string &name, const Variant &val,
	                 bool constant = false)
	{
		checkIdentifier(name);
		doSetVariable(name, val, constant);
	}

	/**
	 * Reads the value of the variable with the given name.
	 *
	 * @param name is the name of the variable. The name must be well-formed.
	 * @return the value of the variable, or a NULL variant if the variable does
	 * not exist.
	 * @throws ScriptEngineException if name is not a well-formed identifier.
	 */
	Variant getVariable(const std::string &name)
	{
		checkIdentifier(name);
		return doGetVariable(name);
	}
};

/**
 * The abstract ScriptEngine class is used to provide an interface for script
 * engine implementations. A script engine implementation has to provide a
 * function which creates an execution scope.
 */
class ScriptEngine {
public:
	/**
	 * Requests an execution scope from the script engine implementation. The
	 * calling code is responsible for disposing the returned pointer.
	 */
	virtual ScriptEngineScope *createScope() = 0;
};

/**
 * The ScriptEngineFactory class is a central registry for ScriptEngine
 * instances and factory of ScriptEngineScope instances for a certain scripting
 * language.
 */
class ScriptEngineFactory {
private:
	/**
	 * Internal map between the script language name and the actual script
	 * engine instance.
	 */
	std::map<std::string, ScriptEngine *> registry;

public:
	/**
	 * Registers a ScriptEngine instance for a new scripting language.
	 *
	 * @param name is the name of the scripting language as MIME, e.g.
	 * "text/javascript"
	 * @param engine is the backend that should be registered.
	 */
	void registerScriptEngine(const std::string &name, ScriptEngine *engine);

	/**
	 * Removes a script engine from the registry.
	 *
	 * @param name is the name of the script engine that
	 */
	bool unregisterScriptEngine(const std::string &name);

	/**
	 * Creates an execution scope for the scripting language with the given
	 * name.
	 *
	 * @param name is the name of the scripting language for which the scope
	 * is being created.
	 * @return a pointer to the new execution scope or null if a script engine
	 * with the given name does not exist. The caller of this function is
	 * responsible
	 */
	ScriptEngineScope *createScope(const std::string &name) const;
};
}
}

#endif /* _OUSIA_SCRIPT_ENGINE_HPP_ */