1 /*
2 * The Doomsday Engine Project -- libcore
3 *
4 * Copyright © 2004-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * LGPL: http://www.gnu.org/licenses/lgpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details. You should have received a copy of
16 * the GNU Lesser General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de/Evaluator"
21 #include "de/Expression"
22 #include "de/Value"
23 #include "de/Context"
24 #include "de/Process"
25
26 #include <QList>
27
28 namespace de {
29
DENG2_PIMPL(Evaluator)30 DENG2_PIMPL(Evaluator)
31 {
32 /// The context that owns this evaluator.
33 Context &context;
34
35 struct ScopedExpression {
36 Expression const *expression;
37 Value *scope; // owned
38
39 ScopedExpression(Expression const *e = 0, Value *s = 0)
40 : expression(e), scope(s)
41 {}
42 Record *names() const
43 {
44 if (!scope) return nullptr;
45 return scope->memberScope();
46 }
47 };
48 struct ScopedResult {
49 Value *result;
50 Value *scope; // owned
51
52 ScopedResult(Value *v, Value *s = 0) : result(v), scope(s) {}
53 };
54
55 typedef QList<ScopedExpression> Expressions;
56 typedef QList<ScopedResult> Results;
57
58 /// The expression that is currently being evaluated.
59 Expression const *current;
60
61 /// Namespace for the current expression.
62 Record *names;
63
64 Expressions expressions;
65 Results results;
66
67 /// Returned when there is no result to give.
68 NoneValue noResult;
69
70 Impl(Public *i, Context &owner)
71 : Base(i)
72 , context(owner)
73 , current(0)
74 , names(0)
75 {}
76
77 ~Impl()
78 {
79 DENG2_ASSERT(expressions.isEmpty());
80 clearNames();
81 clearResults();
82 }
83
84 void clearNames()
85 {
86 if (names)
87 {
88 names = 0;
89 }
90 }
91
92 void clearResults()
93 {
94 foreach (ScopedResult const &i, results)
95 {
96 delete i.result;
97 delete i.scope;
98 }
99 results.clear();
100 }
101
102 void clearExpressions()
103 {
104 while (!expressions.empty())
105 {
106 ScopedExpression top = expressions.takeLast();
107 clearNames();
108 names = top.names();
109 delete top.scope;
110 }
111 }
112
113 void pushResult(Value *value, Value *scope = 0 /*take*/)
114 {
115 // NULLs are not pushed onto the results expressions as they indicate that
116 // no result was given.
117 if (value)
118 {
119 /*qDebug() << "Evaluator: Pushing result" << value << value->asText() << "in scope"
120 << (scope? scope->asText() : "null")
121 << "result stack size:" << results.size();*/
122 results << ScopedResult(value, scope);
123 }
124 else
125 {
126 DENG2_ASSERT(scope == nullptr);
127 }
128 }
129
130 Value &result()
131 {
132 if (results.isEmpty())
133 {
134 return noResult;
135 }
136 return *results.first().result;
137 }
138
139 Value &evaluate(Expression const *expression)
140 {
141 DENG2_ASSERT(names == nullptr);
142 DENG2_ASSERT(expressions.empty());
143
144 //qDebug() << "Evaluator: Starting evaluation of" << expression;
145
146 // Begin a new evaluation operation.
147 current = expression;
148 expression->push(self());
149
150 // Clear the result stack.
151 clearResults();
152
153 while (!expressions.empty())
154 {
155 // Continue by processing the next step in the evaluation.
156 ScopedExpression top = expressions.takeLast();
157 clearNames();
158 names = top.names();
159 /*qDebug() << "Evaluator: Evaluating latest scoped expression" << top.expression
160 << "in" << (top.scope? names->asText() : "null scope");*/
161 pushResult(top.expression->evaluate(self()), top.scope);
162 }
163
164 // During function call evaluation the process's context changes. We should
165 // now be back at the level we started from.
166 DENG2_ASSERT(&self().process().context() == &context);
167
168 // Exactly one value should remain in the result stack: the result of the
169 // evaluated expression.
170 DENG2_ASSERT(self().hasResult());
171
172 clearNames();
173 current = nullptr;
174 return result();
175 }
176 };
177
178 } // namespace de
179
180 namespace de {
181
Evaluator(Context & owner)182 Evaluator::Evaluator(Context &owner) : d(new Impl(this, owner))
183 {}
184
context()185 Context &Evaluator::context()
186 {
187 return d->context;
188 }
189
process()190 Process &Evaluator::process()
191 {
192 return d->context.process();
193 }
194
process() const195 Process const &Evaluator::process() const
196 {
197 return d->context.process();
198 }
199
reset()200 void Evaluator::reset()
201 {
202 d->current = nullptr;
203
204 d->clearExpressions();
205 d->clearNames();
206 }
207
evaluate(Expression const * expression)208 Value &Evaluator::evaluate(Expression const *expression)
209 {
210 return d->evaluate(expression);
211 }
212
namespaces(Namespaces & spaces) const213 void Evaluator::namespaces(Namespaces &spaces) const
214 {
215 if (d->names)
216 {
217 // A specific namespace has been defined.
218 spaces.clear();
219 spaces.push_back({d->names, Context::Namespace});
220 }
221 else
222 {
223 // Collect namespaces from the process's call stack.
224 process().namespaces(spaces);
225 }
226 }
227
localNamespace() const228 Record *Evaluator::localNamespace() const
229 {
230 Namespaces spaces;
231 namespaces(spaces);
232 DENG2_ASSERT(!spaces.empty());
233 DENG2_ASSERT(spaces.front().names != 0);
234 return spaces.front().names;
235 }
236
hasResult() const237 bool Evaluator::hasResult() const
238 {
239 return d->results.size() == 1;
240 }
241
result()242 Value &Evaluator::result()
243 {
244 return d->result();
245 }
246
push(Expression const * expression,Value * scope)247 void Evaluator::push(Expression const *expression, Value *scope)
248 {
249 d->expressions.push_back(Impl::ScopedExpression(expression, scope));
250 }
251
pushResult(Value * value)252 void Evaluator::pushResult(Value *value)
253 {
254 d->pushResult(value);
255 }
256
popResult(Value ** evaluationScope)257 Value *Evaluator::popResult(Value **evaluationScope)
258 {
259 DENG2_ASSERT(d->results.size() > 0);
260
261 Impl::ScopedResult result = d->results.takeLast();
262 /*qDebug() << "Evaluator: Popping result" << result.result << result.result->asText()
263 << "in scope" << (result.scope? result.scope->asText() : "null");*/
264
265 if (evaluationScope)
266 {
267 *evaluationScope = result.scope;
268 }
269 else
270 {
271 delete result.scope; // Was owned by us and the caller didn't want it.
272 }
273
274 return result.result;
275 }
276
277 } // namespace de
278