1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #include "../util.h"
5 
6 #include <openvdb_ax/ast/AST.h>
7 #include <openvdb_ax/ast/Scanners.h>
8 
9 #include <cppunit/extensions/HelperMacros.h>
10 
11 #include <string>
12 
13 using namespace openvdb::ax::ast;
14 using namespace openvdb::ax::ast::tokens;
15 
16 namespace {
17 
18 // No dependencies
19 // - use @a once (read), no other variables
20 const std::vector<std::string> none = {
21     "@a;",
22     "@a+1;",
23     "@a=1;",
24     "@a=func(5);",
25     "-@a;",
26     "@a[0]=1;",
27     "if(true) @a = 1;",
28     "if(@a) a=1;",
29     "if (@e) if (@b) if (@c) @a;",
30     "@b = c ? : @a;",
31     "@a ? : c = 1;",
32     "for (@a; @b; @c) ;",
33     "while (@a) ;",
34     "for(;true;) @a = 1;"
35 };
36 
37 // Self dependencies
38 // - use @a potentially multiple times (read/write), no other variables
39 const std::vector<std::string> self = {
40     "@a=@a;",
41     "@a+=1;",
42     "++@a;",
43     "@a--;",
44     "func(@a);",
45     "--@a + 1;",
46     "if(@a) @a = 1;",
47     "if(@a) ; else @a = 1;",
48     "@a ? : @a = 2;",
49     "for (@b;@a;@c) @a = 0;",
50     "while(@a) @a = 1;"
51 };
52 
53 // Code where @a should have a direct dependency on @b only
54 // - use @a once (read/write), use @b once
55 const std::vector<std::string> direct = {
56     "@a=@b;",
57     "@a=-@b;",
58     "@a=@b;",
59     "@a=1+@b;",
60     "@a=func(@b);",
61     "@a=++@b;",
62     "if(@b) @a=1;",
63     "if(@b) {} else { @a=1; }",
64     "@b ? @a = 1 : 0;",
65     "@b ? : @a = 1;",
66     "for (;@b;) @a = 1;",
67     "while (@b) @a = 1;"
68 };
69 
70 // Code where @a should have a direct dependency on @b only
71 // - use @a once (read/write), use @b once, b a vector
72 const std::vector<std::string> directvec = {
73     "@a=v@b.x;",
74     "@a=v@b[0];",
75     "@a=v@b.x + 1;",
76     "@a=v@b[0] * 3;",
77     "if (v@b[0]) @a = 3;",
78 };
79 
80 // Code where @a should have dependencies on @b and c (c always first)
81 const std::vector<std::string> indirect = {
82     "c=@b; @a=c;",
83     "c=@b; @a=c[0];",
84     "c = {@b,1,2}; @a=c;",
85     "int c=@b; @a=c;",
86     "int c; c=@b; @a=c;",
87     "if (c = @b) @a = c;",
88     "(c = @b) ? @a=c : 0;",
89     "(c = @b) ? : @a=c;",
90     "for(int c = @b; true; e) @a = c;",
91     "for(int c = @b;; @a = c) ;",
92     "for(int c = @b; c; e) @a = c;",
93     "for(c = @b; c; e) @a = c;",
94     "int c; for(c = @b; c; e) @a = c;",
95     "for(; c = @b;) @a = c;",
96     "while(int c = @b) @a = c;",
97 };
98 
99 }
100 
101 class TestScanners : public CppUnit::TestCase
102 {
103 public:
104 
105     CPPUNIT_TEST_SUITE(TestScanners);
106     CPPUNIT_TEST(testVisitNodeType);
107     CPPUNIT_TEST(testFirstLastLocation);
108     CPPUNIT_TEST(testAttributeDependencyTokens);
109     // CPPUNIT_TEST(testVariableDependencies);
110     CPPUNIT_TEST_SUITE_END();
111 
112     void testVisitNodeType();
113     void testFirstLastLocation();
114     void testAttributeDependencyTokens();
115     // void testVariableDependencies();
116 };
117 
118 CPPUNIT_TEST_SUITE_REGISTRATION(TestScanners);
119 
testVisitNodeType()120 void TestScanners::testVisitNodeType()
121 {
122     size_t count = 0;
123     auto counter = [&](const Node&) -> bool {
124         ++count; return true;
125     };
126 
127     // "int64@a;"
128     Node::Ptr node(new Attribute("a", CoreType::INT64));
129 
130     visitNodeType<Node>(*node, counter);
131     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
132 
133     count = 0;
134     visitNodeType<Local>(*node, counter);
135     CPPUNIT_ASSERT_EQUAL(size_t(0), count);
136 
137     count = 0;
138     visitNodeType<Variable>(*node, counter);
139     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
140 
141     count = 0;
142     visitNodeType<Attribute>(*node, counter);
143     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
144 
145     // "{1.0f, 2.0, 3};"
146     node.reset(new ArrayPack( {
147         new Value<float>(1.0f),
148         new Value<double>(2.0),
149         new Value<int64_t>(3)
150     }));
151 
152     count = 0;
153     visitNodeType<Node>(*node, counter);
154     CPPUNIT_ASSERT_EQUAL(size_t(4), count);
155 
156     count = 0;
157     visitNodeType<Local>(*node, counter);
158     CPPUNIT_ASSERT_EQUAL(size_t(0), count);
159 
160     count = 0;
161     visitNodeType<ValueBase>(*node, counter);
162     CPPUNIT_ASSERT_EQUAL(size_t(3), count);
163 
164     count = 0;
165     visitNodeType<ArrayPack>(*node, counter);
166     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
167 
168     count = 0;
169     visitNodeType<Expression>(*node, counter);
170     CPPUNIT_ASSERT_EQUAL(size_t(4), count);
171 
172     count = 0;
173     visitNodeType<Statement>(*node, counter);
174     CPPUNIT_ASSERT_EQUAL(size_t(4), count);
175 
176     // "@a += v@b.x = x %= 1;"
177     // @note 9 explicit nodes
178     node.reset(new AssignExpression(
179         new Attribute("a", CoreType::FLOAT, true),
180         new AssignExpression(
181             new ArrayUnpack(
182                 new Attribute("b", CoreType::VEC3F),
183                 new Value<int32_t>(0)
184             ),
185             new AssignExpression(
186                 new Local("x"),
187                 new Value<int32_t>(1),
188                 OperatorToken::MODULO
189                 )
190             ),
191         OperatorToken::PLUS
192     ));
193 
194     count = 0;
195     visitNodeType<Node>(*node, counter);
196     CPPUNIT_ASSERT_EQUAL(size_t(9), count);
197 
198     count = 0;
199     visitNodeType<Local>(*node, counter);
200     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
201 
202     count = 0;
203     visitNodeType<Attribute>(*node, counter);
204     CPPUNIT_ASSERT_EQUAL(size_t(2), count);
205 
206     count = 0;
207     visitNodeType<Value<int>>(*node, counter);
208     CPPUNIT_ASSERT_EQUAL(size_t(2), count);
209 
210     count = 0;
211     visitNodeType<ArrayUnpack>(*node, counter);
212     CPPUNIT_ASSERT_EQUAL(size_t(1), count);
213 
214     count = 0;
215     visitNodeType<AssignExpression>(*node, counter);
216     CPPUNIT_ASSERT_EQUAL(size_t(3), count);
217 
218     count = 0;
219     visitNodeType<Expression>(*node, counter);
220     CPPUNIT_ASSERT_EQUAL(size_t(9), count);
221 }
222 
testFirstLastLocation()223 void TestScanners::testFirstLastLocation()
224 {
225     // The list of above code sets which are expected to have the same
226     // first and last use of @a.
227     const std::vector<const std::vector<std::string>*> snippets {
228         &none,
229         &direct,
230         &indirect
231     };
232 
233     for (const auto& samples : snippets) {
234         for (const std::string& code : *samples) {
235             const Tree::ConstPtr tree = parse(code.c_str());
236             CPPUNIT_ASSERT(tree);
237             const Variable* first = firstUse(*tree, "@a");
238             const Variable* last = lastUse(*tree, "@a");
239             CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Unable to locate first @a AST node", code), first);
240             CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Unable to locate last @a AST node", code), last);
241             CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Invalid first/last AST node comparison", code),
242                 first == last);
243         }
244     }
245 
246     // Test some common edge cases
247 
248     // @a = @a;
249     Node::Ptr node(new AssignExpression(
250         new Attribute("a", CoreType::FLOAT),
251         new Attribute("a", CoreType::FLOAT)));
252 
253     const Node* expectedFirst =
254         static_cast<AssignExpression*>(node.get())->lhs();
255     const Node* expectedLast =
256         static_cast<AssignExpression*>(node.get())->rhs();
257     CPPUNIT_ASSERT(expectedFirst != expectedLast);
258 
259     const Node* first = firstUse(*node, "@a");
260     const Node* last = lastUse(*node, "@a");
261 
262     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node", "@a=@a"),
263         first, expectedFirst);
264     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node", "@a=@a"),
265         last, expectedLast);
266 
267     // for(@a;@a;@a) { @a; }
268     node.reset(new Loop(
269         tokens::FOR,
270         new Attribute("a", CoreType::FLOAT),
271         new Block(new Attribute("a", CoreType::FLOAT)),
272         new Attribute("a", CoreType::FLOAT),
273         new Attribute("a", CoreType::FLOAT)
274         ));
275 
276     expectedFirst = static_cast<Loop*>(node.get())->initial();
277     expectedLast = static_cast<Loop*>(node.get())->body()->child(0);
278     CPPUNIT_ASSERT(expectedFirst != expectedLast);
279 
280     first = firstUse(*node, "@a");
281     last = lastUse(*node, "@a");
282 
283     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
284         "for(@a;@a;@a) { @a; }"), first, expectedFirst);
285     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
286         "for(@a;@a;@a) { @a; }"), last, expectedLast);
287 
288     // do { @a; } while(@a);
289     node.reset(new Loop(
290         tokens::DO,
291         new Attribute("a", CoreType::FLOAT),
292         new Block(new Attribute("a", CoreType::FLOAT)),
293         nullptr,
294         nullptr
295         ));
296 
297     expectedFirst = static_cast<Loop*>(node.get())->body()->child(0);
298     expectedLast = static_cast<Loop*>(node.get())->condition();
299     CPPUNIT_ASSERT(expectedFirst != expectedLast);
300 
301     first = firstUse(*node, "@a");
302     last = lastUse(*node, "@a");
303 
304     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
305         "do { @a; } while(@a);"), first, expectedFirst);
306     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
307         "do { @a; } while(@a);"), last, expectedLast);
308 
309     // if (@a) {} else if (@a) {} else { @a; }
310     node.reset(new ConditionalStatement(
311         new Attribute("a", CoreType::FLOAT),
312         new Block(),
313         new Block(
314             new ConditionalStatement(
315                 new Attribute("a", CoreType::FLOAT),
316                 new Block(),
317                 new Block(new Attribute("a", CoreType::FLOAT))
318                 )
319             )
320         ));
321 
322     expectedFirst = static_cast<ConditionalStatement*>(node.get())->condition();
323     expectedLast =
324         static_cast<const ConditionalStatement*>(
325             static_cast<ConditionalStatement*>(node.get())
326                 ->falseBranch()->child(0))
327                     ->falseBranch()->child(0);
328 
329     CPPUNIT_ASSERT(expectedFirst != expectedLast);
330 
331     first = firstUse(*node, "@a");
332     last = lastUse(*node, "@a");
333 
334     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
335         "if (@a) {} else if (1) {} else { @a; }"), first, expectedFirst);
336     CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Unexpected location of @a AST node",
337         "if (@a) {} else if (1) {} else { @a; }"), last, expectedLast);
338 }
339 
testAttributeDependencyTokens()340 void TestScanners::testAttributeDependencyTokens()
341 {
342     for (const std::string& code : none) {
343         const Tree::ConstPtr tree = parse(code.c_str());
344         CPPUNIT_ASSERT(tree);
345         std::vector<std::string> dependencies;
346         attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies);
347 
348         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Expected 0 deps", code),
349             dependencies.empty());
350     }
351 
352     for (const std::string& code : self) {
353         const Tree::ConstPtr tree = parse(code.c_str());
354         CPPUNIT_ASSERT(tree);
355         std::vector<std::string> dependencies;
356         attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies);
357         CPPUNIT_ASSERT(!dependencies.empty());
358         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
359             dependencies.front(), std::string("float@a"));
360     }
361 
362     for (const std::string& code : direct) {
363         const Tree::ConstPtr tree = parse(code.c_str());
364         CPPUNIT_ASSERT(tree);
365         std::vector<std::string> dependencies;
366         attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies);
367         CPPUNIT_ASSERT(!dependencies.empty());
368         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
369             dependencies.front(), std::string("float@b"));
370     }
371 
372     for (const std::string& code : directvec) {
373         const Tree::ConstPtr tree = parse(code.c_str());
374         CPPUNIT_ASSERT(tree);
375         std::vector<std::string> dependencies;
376         attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies);
377         CPPUNIT_ASSERT(!dependencies.empty());
378         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
379             dependencies.front(), std::string("vec3f@b"));
380     }
381 
382     for (const std::string& code : indirect) {
383         const Tree::ConstPtr tree = parse(code.c_str());
384         CPPUNIT_ASSERT(tree);
385         std::vector<std::string> dependencies;
386         attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies);
387         CPPUNIT_ASSERT(!dependencies.empty());
388         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
389             dependencies.front(), std::string("float@b"));
390     }
391 
392     // Test a more complicated code snippet. Note that this also checks the
393     // order which isn't strictly necessary
394 
395     const std::string complex =
396         "int a = func(1,@e);"
397         "pow(@d, a);"
398         "mat3f m = 0;"
399         "scale(m, v@v);"
400         ""
401         "float f1 = 0;"
402         "float f2 = 0;"
403         "float f3 = 0;"
404         ""
405         "f3 = @f;"
406         "f2 = f3;"
407         "f1 = f2;"
408         "if (@a - @e > f1) {"
409         "    @b = func(m);"
410         "    if (true) {"
411         "        ++@c[0] = a;"
412         "    }"
413         "}";
414 
415     const Tree::ConstPtr tree = parse(complex.c_str());
416     CPPUNIT_ASSERT(tree);
417     std::vector<std::string> dependencies;
418     attributeDependencyTokens(*tree, "b", tokens::CoreType::FLOAT, dependencies);
419     // @b should depend on: @a, @e, @f, v@v
420     CPPUNIT_ASSERT_EQUAL(4ul, dependencies.size());
421     CPPUNIT_ASSERT_EQUAL(dependencies[0], std::string("float@a"));
422     CPPUNIT_ASSERT_EQUAL(dependencies[1], std::string("float@e"));
423     CPPUNIT_ASSERT_EQUAL(dependencies[2], std::string("float@f"));
424     CPPUNIT_ASSERT_EQUAL(dependencies[3], std::string("vec3f@v"));
425 
426     // @c should depend on: @a, @c, @d, @e, @f
427     dependencies.clear();
428     attributeDependencyTokens(*tree, "c", tokens::CoreType::FLOAT, dependencies);
429     CPPUNIT_ASSERT_EQUAL(5ul, dependencies.size());
430     CPPUNIT_ASSERT_EQUAL(dependencies[0], std::string("float@a"));
431     CPPUNIT_ASSERT_EQUAL(dependencies[1], std::string("float@c"));
432     CPPUNIT_ASSERT_EQUAL(dependencies[2], std::string("float@d"));
433     CPPUNIT_ASSERT_EQUAL(dependencies[3], std::string("float@e"));
434     CPPUNIT_ASSERT_EQUAL(dependencies[4], std::string("float@f"));
435 
436 
437     // @d should depend on: @d, @e
438     dependencies.clear();
439     attributeDependencyTokens(*tree, "d", tokens::CoreType::FLOAT, dependencies);
440     CPPUNIT_ASSERT_EQUAL(2ul, dependencies.size());
441     CPPUNIT_ASSERT_EQUAL(dependencies[0], std::string("float@d"));
442     CPPUNIT_ASSERT_EQUAL(dependencies[1], std::string("float@e"));
443 
444     // @e should depend on itself
445     dependencies.clear();
446     attributeDependencyTokens(*tree, "e", tokens::CoreType::FLOAT, dependencies);
447     CPPUNIT_ASSERT_EQUAL(1ul, dependencies.size());
448     CPPUNIT_ASSERT_EQUAL(dependencies[0], std::string("float@e"));
449 
450     // @f should depend on nothing
451     dependencies.clear();
452     attributeDependencyTokens(*tree, "f", tokens::CoreType::FLOAT, dependencies);
453     CPPUNIT_ASSERT(dependencies.empty());
454 
455     // @v should depend on: v@v
456     dependencies.clear();
457     attributeDependencyTokens(*tree, "v", tokens::CoreType::VEC3F, dependencies);
458     CPPUNIT_ASSERT_EQUAL(1ul, dependencies.size());
459     CPPUNIT_ASSERT_EQUAL(dependencies[0], std::string("vec3f@v"));
460 }
461 
462 /*
463 void TestScanners::testVariableDependencies()
464 {
465     for (const std::string& code : none) {
466         const Tree::ConstPtr tree = parse(code.c_str());
467         CPPUNIT_ASSERT(tree);
468         const Variable* last = lastUse(*tree, "@a");
469         CPPUNIT_ASSERT(last);
470 
471         std::vector<const Variable*> vars;
472         variableDependencies(*last, vars);
473         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Expected 0 deps", code),
474             vars.empty());
475     }
476 
477     for (const std::string& code : self) {
478         const Tree::ConstPtr tree = parse(code.c_str());
479         CPPUNIT_ASSERT(tree);
480         const Variable* last = lastUse(*tree, "@a");
481         CPPUNIT_ASSERT(last);
482 
483         std::vector<const Variable*> vars;
484         variableDependencies(*last, vars);
485         const Variable* var = vars.front();
486         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
487             var->isType<Attribute>());
488         const Attribute* attrib = static_cast<const Attribute*>(var);
489         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
490             std::string("float@a"), attrib->tokenname());
491     }
492 
493     for (const std::string& code : direct) {
494         const Tree::ConstPtr tree = parse(code.c_str());
495         CPPUNIT_ASSERT(tree);
496         const Variable* last = lastUse(*tree, "@a");
497         CPPUNIT_ASSERT(last);
498 
499         std::vector<const Variable*> vars;
500         variableDependencies(*last, vars);
501 
502         const Variable* var = vars.front();
503         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
504             var->isType<Attribute>());
505         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
506             std::string("b"), var->name());
507     }
508 
509     for (const std::string& code : indirect) {
510         const Tree::ConstPtr tree = parse(code.c_str());
511         CPPUNIT_ASSERT(tree);
512         const Variable* last = lastUse(*tree, "@a");
513         CPPUNIT_ASSERT(last);
514 
515         std::vector<const Variable*> vars;
516         variableDependencies(*last, vars);
517 
518         // check c
519         const Variable* var = vars[0];
520         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
521             var->isType<Local>());
522         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
523             std::string("c"), var->name());
524 
525         // check @b
526         var = vars[1];
527         CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
528             var->isType<Attribute>());
529         CPPUNIT_ASSERT_EQUAL_MESSAGE(ERROR_MSG("Invalid variable dependency", code),
530             std::string("b"), var->name());
531     }
532 
533     // Test a more complicated code snippet. Note that this also checks the
534     // order which isn't strictly necessary
535 
536     const std::string complex =
537         "int a = func(1,@e);"
538         "pow(@d, a);"
539         "mat3f m = 0;"
540         "scale(m, v@v);"
541         ""
542         "float f1 = 0;"
543         "float f2 = 0;"
544         "float f3 = 0;"
545         ""
546         "f3 = @f;"
547         "f2 = f3;"
548         "f1 = f2;"
549         "if (@a - @e > f1) {"
550         "    @b = func(m);"
551         "    if (true) {"
552         "        ++@c[0] = a;"
553         "    }"
554         "}";
555 
556     const Tree::ConstPtr tree = parse(complex.c_str());
557     CPPUNIT_ASSERT(tree);
558     const Variable* lasta = lastUse(*tree, "@a");
559     const Variable* lastb = lastUse(*tree, "@b");
560     const Variable* lastc = lastUse(*tree, "@c");
561     const Variable* lastd = lastUse(*tree, "@d");
562     const Variable* laste = lastUse(*tree, "@e");
563     const Variable* lastf = lastUse(*tree, "@f");
564     const Variable* lastv = lastUse(*tree, "vec3f@v");
565     CPPUNIT_ASSERT(lasta);
566     CPPUNIT_ASSERT(lastb);
567     CPPUNIT_ASSERT(lastc);
568     CPPUNIT_ASSERT(lastd);
569     CPPUNIT_ASSERT(laste);
570     CPPUNIT_ASSERT(lastf);
571     CPPUNIT_ASSERT(lastv);
572 
573     std::vector<const Variable*> vars;
574     variableDependencies(*lasta, vars);
575     CPPUNIT_ASSERT(vars.empty());
576 
577     // @b should depend on: m, m, v@v, @a, @e, @e, f1, f2, f3, @f
578     variableDependencies(*lastb, vars);
579     CPPUNIT_ASSERT_EQUAL(10ul, vars.size());
580     CPPUNIT_ASSERT(vars[0]->isType<Local>());
581     CPPUNIT_ASSERT(vars[0]->name() == "m");
582     CPPUNIT_ASSERT(vars[1]->isType<Local>());
583     CPPUNIT_ASSERT(vars[1]->name() == "m");
584     CPPUNIT_ASSERT(vars[2]->isType<Attribute>());
585     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[2])->tokenname() == "vec3f@v");
586     CPPUNIT_ASSERT(vars[3]->isType<Attribute>());
587     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[3])->tokenname() == "float@a");
588     CPPUNIT_ASSERT(vars[4]->isType<Attribute>());
589     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[4])->tokenname() == "float@e");
590     CPPUNIT_ASSERT(vars[5]->isType<Attribute>());
591     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[5])->tokenname() == "float@e");
592     CPPUNIT_ASSERT(vars[6]->isType<Local>());
593     CPPUNIT_ASSERT(vars[6]->name() == "f1");
594     CPPUNIT_ASSERT(vars[7]->isType<Local>());
595     CPPUNIT_ASSERT(vars[7]->name() == "f2");
596     CPPUNIT_ASSERT(vars[8]->isType<Local>());
597     CPPUNIT_ASSERT(vars[8]->name() == "f3");
598     CPPUNIT_ASSERT(vars[9]->isType<Attribute>());
599     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[9])->tokenname() == "float@f");
600 
601     // @c should depend on: @c, a, @e, @d, a, @e, @a, @e, f1, f2, f3, @f
602     vars.clear();
603     variableDependencies(*lastc, vars);
604     CPPUNIT_ASSERT_EQUAL(11ul, vars.size());
605     CPPUNIT_ASSERT(vars[0]->isType<Attribute>());
606     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[0])->tokenname() == "float@c");
607     CPPUNIT_ASSERT(vars[1]->isType<Local>());
608     CPPUNIT_ASSERT(vars[1]->name() == "a");
609     CPPUNIT_ASSERT(vars[2]->isType<Attribute>());
610     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[2])->tokenname() == "float@e");
611     CPPUNIT_ASSERT(vars[3]->isType<Attribute>());
612     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[3])->tokenname() == "float@d");
613     CPPUNIT_ASSERT(vars[4]->isType<Local>());
614     CPPUNIT_ASSERT(vars[4]->name() == "a");
615     CPPUNIT_ASSERT(vars[5]->isType<Attribute>());
616     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[5])->tokenname() == "float@a");
617     CPPUNIT_ASSERT(vars[6]->isType<Attribute>());
618     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[6])->tokenname() == "float@e");
619     CPPUNIT_ASSERT(vars[7]->isType<Local>());
620     CPPUNIT_ASSERT(vars[7]->name() == "f1");
621     CPPUNIT_ASSERT(vars[8]->isType<Local>());
622     CPPUNIT_ASSERT(vars[8]->name() == "f2");
623     CPPUNIT_ASSERT(vars[9]->isType<Local>());
624     CPPUNIT_ASSERT(vars[9]->name() == "f3");
625     CPPUNIT_ASSERT(vars[10]->isType<Attribute>());
626     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[10])->tokenname() == "float@f");
627 
628     // @d should depend on: @d, a, @e
629     vars.clear();
630     variableDependencies(*lastd, vars);
631     CPPUNIT_ASSERT_EQUAL(3ul, vars.size());
632     CPPUNIT_ASSERT(vars[0]->isType<Attribute>());
633     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[0])->tokenname() == "float@d");
634     CPPUNIT_ASSERT(vars[1]->isType<Local>());
635     CPPUNIT_ASSERT(vars[1]->name() == "a");
636     CPPUNIT_ASSERT(vars[2]->isType<Attribute>());
637     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[2])->tokenname() == "float@e");
638 
639     // @e should depend on itself
640     vars.clear();
641     variableDependencies(*laste, vars);
642     CPPUNIT_ASSERT_EQUAL(1ul, vars.size());
643     CPPUNIT_ASSERT(vars[0]->isType<Attribute>());
644     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[0])->tokenname() == "float@e");
645 
646     // @f should depend on nothing
647     vars.clear();
648     variableDependencies(*lastf, vars);
649     CPPUNIT_ASSERT(vars.empty());
650 
651     // @v should depend on: m, v@v
652     vars.clear();
653     variableDependencies(*lastv, vars);
654     CPPUNIT_ASSERT_EQUAL(2ul, vars.size());
655     CPPUNIT_ASSERT(vars[0]->isType<Local>());
656     CPPUNIT_ASSERT(vars[0]->name() == "m");
657     CPPUNIT_ASSERT(vars[1]->isType<Attribute>());
658     CPPUNIT_ASSERT(static_cast<const Attribute*>(vars[1])->tokenname() == "vec3f@v");
659 }
660 */
661 
662