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