Merge remote branch 'master'
[prop.git] / tests / test_gc5.pcc
blob1cd0ba6061c97dc8994ce156a36cf93314c3c387
1 //
2 //  Testing finalization with garbage collection.
3 //  We use the class BGC_F (BGC with finalization) for garbage collection.
4 //  In addition of reclaiming garbage, BGC_F will also call the finalization
5 //  method (the destructor) for each object freed.
6 //
7 #include <iostream.h>
9 //
10 //  The following auxiliary class keeps track of allocation and finalization.
12 class LOGGER {
13 public:
14    static int allocation_count;
15    static int deallocation_count;
16    LOGGER() { allocation_count++; }
17   ~LOGGER() { deallocation_count++; }
20 int LOGGER::allocation_count   = 0;
21 int LOGGER::deallocation_count = 0;
24 //  Define an algebraic datatype.  Currently, rewriting can only be 
25 //  performed on datatypes(and not views) in Prop.  
26 //  If replacement is to be performed on a datatype, then it should
27 //  be declared using the ``rewrite'' qualifier.
28 //  In this example, we'll use garbage collection with rewriting.
30 //  We'll inherit from LOGGER so that allocation and finalization
31 //  of each EXP cell is logged.
33 datatype EXP : LOGGER :: collectable rewrite 
34    = om
35    | num (int)
36    | var (char)
37    | add (EXP, EXP)
38    | sub (EXP, EXP)
39    | mul (EXP, EXP)
40    | div (EXP, EXP)
41    ;
44 //  Datatype instantiation generates any additional methods and definitions.
45 //  In this case garbage collection tracing methods are generated.
46 //  As always, this should be placed in an implementation file.
48 instantiate datatype EXP;
51 //  Define a method that prints an expression.  This is a simple
52 //  inductive definition
54 ostream& operator << (ostream& f, EXP e)
55 {  match (e) {
56       om:       { return f << "om"; }
57    |  num i:    { return f << i; }
58    |  var v:    { return f << v; }
59    |  add(a,b): { return f << '(' << a << " + " << b << ')'; }
60    |  sub(a,b): { return f << '(' << a << " - " << b << ')'; }
61    |  mul(a,b): { return f << '(' << a << " * " << b << ')'; }
62    |  div(a,b): { return f << '(' << a << " / " << b << ')'; }
63    }
67 //  Define the interface to a ``rewriting class.''  A rewriting class 
68 //  is simply a C++ class with rewriting rules attached.  In real programs
69 //  this definition should be placed in some definition (i.e. .ph) files.
70 //  
71 //  In parenthesis, we must list all datatypes involved.   Unlike
72 //  simple pattern matching, rewriting can involve a set of mutually
73 //  recursive (or mutually exclusive, if desired) datatype definitions.
74 //  So in general this is a comma delimited list.   
76 //  In this example it involves only the datatype EXP.
78 rewrite class Simplify (EXP)
79 {  // nothing here for now.
80 public:
81    Simplify() {}
85 //  Now we define the rewriting rules in the rewriting class Simplify.  These
86 //  rules should be placed in an implementation file (.pcc, .pC, .pc++ etc).
88 //  In this brief sample class we have some rules that perform 
89 //  simple constant folding and strength reduction.
91 //  Currently, all the rules for a rewrite class must be placed in
92 //  the same rewrite construct.  This will probably change in the future
93 //  once I work out the details on incremental tree automata compilation.
95 rewrite Simplify {
96    add (num 0, x):                    rewrite(x); 
97 |  add (x, num 0):                    rewrite(x);  
98 |  sub (x, num 0):                    rewrite(x);  
99 |  mul (x, num 0):                    rewrite(num(0));  
100 |  mul (num 0, x):                    rewrite(num(0));  
101 |  mul (x, num 1):                    rewrite(x);       
102 |  mul (num 1, x):                    rewrite(x); 
103 |  mul (x, num 2):                    rewrite(add(x,x)); 
104 |  mul (num 2, x):                    rewrite(add(x,x)); 
105 |  div (x, num 1):                    rewrite(x);        
106 |  add (num x, num y):                rewrite(num(x + y)); 
107 |  sub (num x, num y):                rewrite(num(x - y)); 
108 |  mul (num x, num y):                rewrite(num(x * y)); 
109 |  div (num x, num y) where (y != 0): rewrite(num(x / y)); 
110 |  div (_, num 0):                    { /* cout << "Division by zero!\n"; */ }
111 |  div (zero as num 0, x):            rewrite(zero); 
115 //  Now defines the function that uses all this stuff.
117 void do_my_stuff()
119    //
120    // Instantiate a rewriting class 
121    //
122    Simplify sim;
123    EXP t1, term, term2;
125    for (int trials = 1; trials < 10000; trials++) {
126       //
127       // (0 + x * 2) / (1 * 5 + 1 * 3) / (0 / y);
128       //
129       t1 = div(div(add(num(0), mul(var('x'),num(2))), 
130                    add(mul(num(1), num(5)),mul(num(1),num(3)))),
131                div(num(0),var('y')));
132       term = mul(t1,t1);
134       //
135       //  Rewrite the big term above.
136       // 
137       // cout << "Before: " << term << '\n';
138       sim(term);
139       // cout << "After: " << term << '\n';
141       //
142       //  Rewrite it again.  It should have no effect since the term
143       //  is already in normal form.
144       //
145       sim(term);
146       // cout << "Again (should have no effect): " << term << '\n';
148       //
149       //  Rewrite some other term.
150       //
151       term2 = add(sub(num(3),num(3)), var('z'));
152       // cout << "Before: " << term2 << '\n';
153       sim(term2);
154       // cout << "After: " << term2 << '\n';
155    }
158 int main()
160    //
161    // Turn on finalization
162    //
163    GC::get_default_gc().set_finalization(true); 
165    cout << "Now performing some rewrites and generating some garbage.\n"
166            "(See also the test 'test_rc1')\n" << flush;
167    // GC::get_default_gc().set_verbosity(0);
168    do_my_stuff();
169    cout << "Finished.  Now I'll force one more GC to clean up.\n"
170            "There should be at most a few kilobytes of retention at the end.\n"
171            "The GC should be able to recognized a large part of the heap as\n"
172            "garbage.  See below:\n";
173    // GC::get_default_gc().set_verbosity(1);
174    GC::garbage_collect();
175    cout << "The following allocation count should be very close to the\n"
176            "finalization count.  If they are very far apart that means\n"
177            "something is wrong.\n" 
178         << "Allocation count        = " << LOGGER::allocation_count << '\n'
179         << "Number of finalizations = " << LOGGER::deallocation_count << '\n';
180    if (GC::get_default_gc().statistics().bytes_used <= 1024)
181       cout << "Seems like it's working well for your platform.   Lucky dog!\n";
182    return 0;