1 /*
2 * Copyright 2006-2008 The FLWOR Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "stdafx.h"
17
18 #include "compiler/translator/prolog_graph.h"
19
20 #include "context/static_context.h"
21
22 #include "util/string_util.h"
23
24 #include "zorbatypes/zstring.h"
25
26 #include "diagnostics/xquery_exception.h"
27 #include "diagnostics/dict.h"
28 #include "diagnostics/util_macros.h"
29
30 namespace zorba
31 {
32
33
34 /*******************************************************************************
35
36 ********************************************************************************/
reportCycle(const QueryLoc & loc,const PrologGraphVertex * v)37 void PrologGraph::reportCycle(const QueryLoc& loc, const PrologGraphVertex* v)
38 {
39 std::string moduleNS = theModuleSctx->get_module_namespace();
40 zstring varName;
41 if ( v )
42 varName = BUILD_STRING('$', v->getVarExpr()->get_name()->getStringValue());
43
44 RAISE_ERROR(err::XQST0054, loc, ERROR_PARAMS(varName));
45 }
46
47
48 /*******************************************************************************
49
50 ********************************************************************************/
addEdge(const PrologGraphVertex & v1,const PrologGraphVertex & v2)51 void PrologGraph::addEdge(const PrologGraphVertex& v1, const PrologGraphVertex& v2)
52 {
53 if (v1.isVar() && v2.isVar() && v1.getVarExpr() == v2.getVarExpr())
54 {
55 zstring varName = '$' + v1.getVarExpr()->get_name()->getStringValue();
56
57 RAISE_ERROR(err::XPST0008, v2.getVarExpr()->get_loc(),
58 ERROR_PARAMS(varName, ZED(VariabledUndeclared)));
59 }
60
61 addEdge(theGraph, v1, v2);
62 }
63
64
65 /*******************************************************************************
66 This method is part of the mechanism for detecting cycles in the dependency
67 graph among prolog variables. The method does not actually detect the cycles
68 but re-orders the declarations of prolog vars (i.e., reorders theGlobalVars
69 list) so that if var v2 depends on var v1, then v1 appears before v2 in the
70 list (and as a result, v1 will be evaluated before v2 during runtime).
71
72 Circular dependencies among prolog vars can appear only when udfs are involved.
73 Here is an example:
74
75 declare variable $var := local:func1();
76
77 declare function local:func1()
78 {
79 local:func2()
80 };
81
82 declare function local:func2()
83 {
84 local:func3()
85 };
86
87 declare variable $var2 := local:func2();
88
89 declare function local:func3()
90 {
91 $var2
92 };
93
94 boolean($var)
95
96
97 In this query, the following cycle exists : var2 --> func2 --> func3 --> var2
98 ********************************************************************************/
reorder_globals(std::list<GlobalBinding> & prologVarBindings)99 void PrologGraph::reorder_globals(std::list<GlobalBinding>& prologVarBindings)
100 {
101 GraphImpl::const_iterator g_ite;
102 GraphImpl::const_iterator g_end = theGraph.end();
103
104 std::vector<const var_expr*>::const_iterator v_ite;
105 std::vector<const var_expr*>::const_iterator v_end = theVarDecls.end();
106
107 std::vector<const function*>::const_iterator fend = theFuncDecls.end();
108
109 // STEP 1: Floyd-Warshall transitive closure of edges starting from functions
110 // At each substep we are adding fn2 -> fn1 -> var/fn dependencies. The resulting
111 // graph has the following property: If there is a path P in the original graph
112 // such that P starts at a udf node F, ends at a var node V and contains only
113 // udf nodes in between, then in the augmented graph there is a direct edge
114 // F --> V. As a result, to go from one variable to another, we never need to
115 // cross 2 or more consequtive udf nodes.
116
117 std::vector<const function*>::const_iterator f1_ite;
118 std::vector<const function*>::const_iterator f2_ite;
119
120 for (f1_ite = theFuncDecls.begin(); f1_ite != fend; ++f1_ite)
121 {
122 const function* f1 = *f1_ite;
123 GraphImpl::iterator f1_graph_entry = theGraph.find(f1);
124
125 if (f1_graph_entry == g_end)
126 continue;
127
128 for (f2_ite = theFuncDecls.begin(); f2_ite != fend; ++f2_ite)
129 {
130 const function* f2 = *f2_ite;
131 GraphImpl::const_iterator f2_graph_entry = theGraph.find(f2);
132
133 // if f2 depends on f1, then f2 depends on every var/udf that f1 depends on.
134
135 // If f2 does not depend on anything, skip it
136 if (f2_graph_entry == g_end)
137 continue;
138
139 VSet* f1_vset = f1_graph_entry->second;
140 VSet* f2_vset = f2_graph_entry->second;
141
142 // If f2 does not depend on f1, skip it
143 if (f2_vset->find(f1) == f2_vset->end())
144 continue;
145
146 f2_vset->insert(f1_vset->begin(), f1_vset->end());
147 }
148 }
149
150 // STEP 2: Add "1-step" var -> fn -> var/fn dependencies. Steps 1 & 2 together
151 // guarantee that if there is a path P from variable v1 to variable v2 in the
152 // original graph, then in the augmented graph there is a path P' from v1 to
153 // v2 such that P' does not contain any udfs. Therefore, the augmented graph
154 // contains a subgraph VG that consists of edges among variables only and which
155 // which reflects the same (direct and transitive) dependencies among vars as
156 // the original graph.
157
158 for (v_ite = theVarDecls.begin(); v_ite != v_end; ++v_ite)
159 {
160 const var_expr* v1 = *v_ite;
161 GraphImpl::const_iterator v1_graph_entry = theGraph.find(v1);
162
163 if (v1_graph_entry == g_end)
164 continue;
165
166 VSet* v1_vset = v1_graph_entry->second;
167 VSet::const_iterator v1_vset_ite = v1_vset->begin();
168 VSet::const_iterator v1_vset_end = v1_vset->end();
169
170 for (; v1_vset_ite != v1_vset_end; ++v1_vset_ite)
171 {
172 const PrologGraphVertex& f = *v1_vset_ite;
173
174 if (f.getKind() == PrologGraphVertex::FUN)
175 {
176 GraphImpl::iterator f_graph_entry = theGraph.find(f);
177
178 if (f_graph_entry == g_end)
179 continue;
180
181 VSet* f_vset = f_graph_entry->second;
182 VSet::const_iterator f_vset_ite = f_vset->begin();
183 VSet::const_iterator f_vset_end = f_vset->end();
184
185 for (; f_vset_ite != f_vset_end; ++f_vset_ite)
186 {
187 const PrologGraphVertex& v2 = *f_vset_ite;
188
189 if (v2.getKind() == PrologGraphVertex::VAR)
190 {
191 if (v2 == v1)
192 {
193 reportCycle(v2.getVarExpr()->get_loc(), &v2);
194 }
195
196 addEdge(v1, v2);
197 }
198 }
199 }
200 }
201 }
202
203 // STEP 3 Check for cycles. First we extract graph VG from the original graph.
204 // We also make a copy of theVarDecls vector into varDels. Then we repeat the
205 // following steps until varDecls gets empty:
206 // 1. We look for a var V in varDecls that does not depend on any other var.
207 // 2. If no such var is found, a cycle exists, and we raise an error.
208 // 3. We remove V from varDecls and from each dependency set that V appears in.
209 // (TODO: use faster algorithm)
210
211 GraphImpl v_graph;
212 std::set<const var_expr*> varDecls;
213
214 for (g_ite = theGraph.begin(); g_ite != g_end; ++g_ite)
215 {
216 const PrologGraphVertex& v1 = (*g_ite).first;
217
218 if (v1.getKind() == PrologGraphVertex::VAR)
219 {
220 VSet* v1_vset = (*g_ite).second;
221 VSet::const_iterator v1_vset_ite = v1_vset->begin();
222 VSet::const_iterator v1_vset_end = v1_vset->end();
223
224 for (; v1_vset_ite != v1_vset_end; ++v1_vset_ite)
225 {
226 const PrologGraphVertex& v2 = *v1_vset_ite;
227
228 if (v2.getKind() == PrologGraphVertex::VAR)
229 {
230 PrologGraph::addEdge(v_graph, v1, v2);
231 varDecls.insert(v1.getVarExpr());
232 varDecls.insert(v2.getVarExpr());
233 }
234 }
235 }
236 }
237
238 g_end = v_graph.end();
239
240 while (!varDecls.empty())
241 {
242 const var_expr* var = NULL;
243
244 std::set<const var_expr*>::iterator v_ite = varDecls.begin();
245 std::set<const var_expr*>::iterator v_end = varDecls.end();
246
247 for (; v_ite != v_end; ++v_ite)
248 {
249 GraphImpl::iterator v_graph_entry = v_graph.find(*v_ite);
250 if (v_graph_entry == g_end ||
251 v_graph_entry->second->empty())
252 {
253 var = *v_ite;
254 varDecls.erase(v_ite);
255 break;
256 }
257 }
258
259 if (var == NULL)
260 {
261 for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
262 {
263 delete (*g_ite).second;
264 }
265
266 reportCycle((*varDecls.begin())->get_loc(), 0);
267 }
268
269 for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
270 {
271 VSet* vset = (*g_ite).second;
272 VSet::iterator vset_entry = vset->find(var);
273
274 if (vset_entry != vset->end())
275 {
276 vset->erase(vset_entry);
277 }
278 }
279 }
280
281 for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
282 {
283 delete (*g_ite).second;
284 }
285
286 // STEP 4: topologically sort global vars.
287 // Implemented using non-recursive (stack-based) DFS traversal. This algorithm
288 // unfortunately does not detect cycles.
289 // Note that steps 1 & 2 are required: we cannot sort the entire set of prolog
290 // vars + udfs, because functions are allowed to be mutually recursive.
291
292 g_end = theGraph.end();
293
294 std::vector<const var_expr*> topsorted_vars; // dependencies first
295 std::set<const var_expr*> visited;
296
297 std::stack<std::pair<ulong, const var_expr*> > todo; // format: action code + var_expr
298 // need to declare the reverse end iterator here because of a bug in older gcc's
299 // (see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11729)
300 std::vector<const var_expr*>::const_reverse_iterator lEnd = theVarDecls.rend();
301 for (std::vector<const var_expr*>::const_reverse_iterator it = theVarDecls.rbegin();
302 it != lEnd;
303 ++it)
304 {
305 todo.push(std::pair<ulong, const var_expr*>(1, (*it)));
306 }
307
308 while (! todo.empty())
309 {
310 const var_expr* var = todo.top().second;
311 ulong action = todo.top().first;
312 todo.pop();
313
314 switch (action)
315 {
316 case 0: // finish notification
317 {
318 topsorted_vars.push_back(var);
319 break;
320 }
321 case 1: // visit request
322 {
323 if (visited.find(var) == visited.end())
324 {
325 visited.insert(var);
326 todo.push(std::pair<ulong, const var_expr*>(0, var));
327
328 GraphImpl::const_iterator var_graph_entry = theGraph.find(var);
329
330 if (var_graph_entry != g_end)
331 {
332 VSet* var_vset = var_graph_entry->second;
333
334 VSet::const_iterator var_vset_ite = var_vset->begin();
335 VSet::const_iterator var_vset_end = var_vset->end();
336 for (; var_vset_ite != var_vset_end; ++var_vset_ite)
337 {
338 if (var_vset_ite->getKind() == PrologGraphVertex::VAR)
339 {
340 todo.push(std::pair<ulong, const var_expr*>(1, var_vset_ite->getVarExpr()));
341 }
342 }
343 }
344 }
345 break;
346 }
347 default:
348 assert(false);
349 }
350 }
351
352 // STEP 5: reorder thePrologVars according to topological order
353 std::map<const var_expr*, GlobalBinding> gvmap;
354 std::list<GlobalBinding>::iterator bindIte = prologVarBindings.begin();
355 for (; bindIte != prologVarBindings.end(); ++bindIte)
356 {
357 gvmap[(*bindIte).theVar] = *bindIte;
358 }
359
360 prologVarBindings.clear();
361
362 for (std::vector<const var_expr*>::iterator i = topsorted_vars.begin();
363 i != topsorted_vars.end(); ++i)
364 {
365 std::map<const var_expr*, GlobalBinding>::iterator p = gvmap.find(*i);
366
367 if (p != gvmap.end())
368 prologVarBindings.push_back((*p).second);
369 }
370 }
371
372
373 } // namespace zorba
374 /* vim:set et sw=2 ts=2: */
375