#charset "us-ascii" /* * 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. */ #include #pragma sourceTextGroup(off) /* */ AstoSequencer: PreinitObject { objCount = 0 classCount = 0 modsCount = 0 astoCount = 0 noStgCount = 0 stgLu = static (new LookupTable()) baseStoLu = static (new LookupTable()) astoVec = static (new Vector(10)) display() { local sortedKeys; "\b@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ "; "\b"; "\nObject Sequence Analysis: "; "\b\t- Total: <> "; "\b\t- Non-sequenced objects: <> "; "\n\t\t- classes: <> "; "\n\t\t- objects: <> "; "\b\t- Sequenced definitions: <> "; "\n\t\t- internals: <> "; "\n\t\t- objects: <> "; "\bSource Text Group Definitions "; 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; }); "(<> groups): "; foreach (local key in sortedKeys) { "\n\tGroup <> (<>):\t<> "; } "\bObject Seequence: "; forEachAsto(Object, nil, new function(o) { "\n\t<> \t\t<>: <> "; }); "\b@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ "; "\b"; } 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; }); } setSourceTextGroupOrder(obj, value) { obj.sourceTextGroupOrder = value; } setSourceTextGroupName(obj, value) { obj.sourceTextGroupName = value; } setSourceTextSymName(obj, value) { obj.sourceTextSymName = value; } clearSourceTextGroup(obj) { obj.sourceTextGroup = nil; } } /* ------------------------------------------------------------------------ */ /* * 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); try { vector = AstoSequencer.astoVec.subset(new function(obj) { return obj.ofKind(cls) || obj == cls; }); 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; try { stgLu = new LookupTable(); vector = AstoSequencer.astoVec.subset(new function(obj) { return obj.ofKind(cls) || obj == cls; }); /* 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 */ } }