/*
* Copyright (c) 2008 by Kevin Forchione. All rights reserved.
*
* This file is part of the TADS 3 Absolute Source Text Order
*
* asto.t
*
* Version 2.0
*
* Adds the property absSourceTextOrder to each non-class object, and sets
* the property to an integer giving the "absolute" order of the object
* definition in the program source.
*
* This property is useful because it lets you reliably determine the
* order of objects in the program source.
*
* In addition, this module provides object statistics with
* the AstoSequencer.display() method.
*
* The module also includes a function, forEachAsto() that will loop
* over all objects defining an absolute source text order. The parameters
* are similar to the forEachInstance() function provided by _main.t.
*
* THIS MODULE REQUIRES THAT YOU COMPILE EACH APPLICABLE MODULE
* WITH THE
*
* #pragma sourceTextGroup(on)
*
* OR COMPILE WITH THE "-Gstg" OPTION.
*/
execute()
{
local key, value, prev, symLu, strLu, sortedKeys, symName;
/*
* Buld Global Symbol and String Lookups for sourceTextSymName
*/
symLu = t3GetGlobalSymbols();
strLu = new LookupTable();
symLu.forEachAssoc(new function(key, value)
{
strLu[value] = key;
});
/*
* Loop over all Object instances and classes
*/
for (local o = firstObj(Object, ObjAll); o != nil; o = nextObj(o, Object, ObjAll))
{
/*
* Count all objects
*/
objCount++;
/*
* If the object doesn't define sourceTExtGroup then
* count it and continue looping.
*/
if (o.propDefined(&sourceTextGroup, PropDefDirectly) == nil)
{
noStgCount++;
/*
* If the object is a class, count it and continue looping
*/
if (o.isClass())
{
classCount++;
}
continue;
}
/*
* If the object is a modification count it
*/
if (o.isClass())
{
modsCount++;
}
astoVec.append(o);
/*
* Count this object as an "absolute" source text order object
*/
astoCount++;
/*
* Build key for source text group and base source text order
* lookup tables.
*/
key = [o.sourceTextGroup.sourceTextGroupName,
o.sourceTextGroup.sourceTextGroupOrder];
/*
* Retrieve the value from the source text group lookup
* for this key.
*/
value = stgLu[key];
/*
* If we have a value, compare it with the value
* of the object's source text order. If the object
* has a greater value then store it in the table.
*/
if (value)
{
if (o.sourceTextOrder > value)
stgLu[key] = o.sourceTextOrder;
}
/*
* Store the object's source text order in the lookup.
*/
else stgLu[key] = o.sourceTextOrder;
}
/*
* Sort the source text group lookup keys
*/
sortedKeys = stgLu.keysToList.sort(nil, new function(k1, k2)
{
if (k1[2] < k2[2])
return -1;
else if (k1[2] > k2[2])
return 1;
else return 0;
});
/*
* Build the base Source Text Order Lookup.
*/
prev = 0;
foreach (local key in sortedKeys)
{
value = stgLu[key];
baseStoLu[key] = prev;
prev += value;
}
/*
* Loop over each object again and set its "absolute"
* source text order (if appropriate).
*/
for (local o = firstObj(Object, ObjAll); o != nil; o = nextObj(o, Object, ObjAll))
{
if (o.propDefined(&sourceTextGroup, PropDefDirectly))
{
key = [o.sourceTextGroup.sourceTextGroupName,
o.sourceTextGroup.sourceTextGroupOrder];
value = baseStoLu[key];
value += o.sourceTextOrder;
o.absSourceTextOrder = value;
/*
* Set the object's source text group order from
* its source text group object.
*/
setSourceTextGroupOrder(o, o.sourceTextGroup.sourceTextGroupOrder);
/*
* Set the object's source text group name from
* its source text group object.
*/
setSourceTextGroupName(o, o.sourceTextGroup.sourceTextGroupName);
/*
* Set the object's source text sym name from
* the symbol and string lookup table.
*/
symName = strLu[o];
if (symName == nil)
{
local scList;
symName = '* ';
scList = o.getSuperclassList();
for (local index = 1; index <= scList.length(); ++index)
{
symName += strLu[scList[index]];
if (index < scList.length())
symName += ', ';
}
symName += ' *';
}
else if (toInteger(symName))
{
local sc, cName;
cName = symName;
__modsLoop:
while(toInteger(cName))
{
for (local c = firstObj(Object, ObjAll); c != nil; c = nextObj(c, Object, ObjAll))
{
if (c.getSuperclassList().car() == o)
{
cName = strLu[c];
break __modsLoop;
}
}
}
symName += ( ' (' + cName + ')');
}
setSourceTextSymName(o, symName);
/*
* Clear the object's source text group.
*/
clearSourceTextGroup(o);
}
}
astoVec.sort(nil, new function(a, b)
{
if (a.absSourceTextOrder < b.absSourceTextOrder)
return -1;
else if (a.absSourceTextOrder > b.absSourceTextOrder)
return 1;
else return 0;
});
}
/* ------------------------------------------------------------------------ */
/*
* For convenience, a simple object iterator function. This function
* invokes a callback function for each instance of the given class
* having an "absolute" source text order. The order of iteration is
* either in descending or ascending order of "absolute" source text order.
*
* The callback is invoked with one argument, which gives the current
* instance. The callback can "break" out of the loop by throwing a
* BreakLoopSignal, which can be done conveniently using the breakLoop
* macro.
*/
forEachAsto(cls, desc, func)
{
local vector = new Vector(AstoSequencer.astoCount);
vector.sort(desc, new function(a, b)
{
if (a.absSourceTextOrder < b.absSourceTextOrder)
return -1;
else
if (a.absSourceTextOrder > b.absSourceTextOrder)
return 1;
else return 0;
});
vector.forEach(func);
}
catch (BreakLoopSignal sig)
{
/*
* ignore the signal - it simply means we want to terminate the
* loop and return to the caller
*/
}
}
/* ------------------------------------------------------------------------ */
/*
* For convenience, a simple object iterator function. This function
* invokes a callback function for each instance of the given class
* having a source text group and source text order property. The
* order of iteration is determined by the sort order of both properties.
*
* The callback is invoked with one argument, which gives the current
* instance. The callback can "break" out of the loop by throwing a
* BreakLoopSignal, which can be done conveniently using the breakLoop
* macro.
*/
forEachStgSto(cls, stgOrder, stoOrder, func)
{
local vector, stgLu, stgo, sortedKeys, sortedList;
/* loop over all asto of the given class */
foreach (local obj in vector.toList())
{
if (obj.propDefined(&absSourceTextOrder, PropDefDirectly))
{
stgo = obj.sourceTextGroupOrder;
if (stgLu.isKeyPresent(obj.sourceTextGroupOrder))
stgLu[stgo] = (stgLu[stgo] + obj);
else
stgLu[stgo] = ([] + obj);
}
}
sortedKeys = stgLu.keysToList().sort(stgOrder);
foreach (local key in sortedKeys)
{
sortedList = stgLu[key].sort(stoOrder, new function(a, b)
{
if (a.sourceTextOrder < b.sourceTextOrder)
return -1;
else if (a.sourceTextOrder > b.sourceTextOrder)
return 1;
else return 0;
});
foreach (local obj in sortedList)
func(obj);
}
}
catch (BreakLoopSignal sig)
{
/*
* ignore the signal - it simply means we want to terminate the
* loop and return to the caller
*/
}
}