Skip to content

Commit

Permalink
Changes related to different handling of logical locations
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulKlint committed Feb 25, 2024
1 parent d8607ce commit 0029a07
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 45 deletions.
13 changes: 4 additions & 9 deletions doc/TypePal/Configuration/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,22 +267,17 @@ For all dependencies (in facts, calculators and requirements) a calculator needs

#### createLogicalLoc
```rascal
/* Configuration field */ loc (str id, IdRole idRole, loc physicalLoc, str modelName, PathConfig pcfg) createLogicalLoc
/* Configuration field */ loc (Define def, str modelName, PathConfig pcfg) createLogicalLoc
```
Internally, TypePal operates on physical source locations, e.g., source locations that point to a specific location in a source file. This all works fine, until modules and separate compilation come into play. Case in point is a function `f` declared in file A that is used in file B. When file A is edited, the source location of `f` will most likely change, while in most cases the definition of `f` has not been changed. Any information based on `f`'s original source location will be broken. To solve this problem _logical source locations_ are introduced that abstract away from the precise source locations and are thus better.

The function `createLogicalLoc` gives complete freedom in the way such logical locations are encoded. In the case of Rascal, `f` will, for instance, be encoded as `|rascal+function:///A/f|`.
Given a Define `def`, the function `createLogicalLoc` returns the logical version of `def.defined` if desired, otherwise it returns `def.defined` unmodified. This function gives complete freedom for which `IdRole`s logical locations will be generated and in what way they are encoded. In the case of a simple functional language called `fun`, the function `f` will, for instance, be encoded as `|fun+function:///A/f|`. In the case of Rascal where extensive function overloading is present, we generate `|rascal+function:///A/f$abcde|`, where `$abcde` is a prefix of the md5hash of the complete function declaration of `f`.


:::warning
When a function is overloaded, measures have to be taken to create unique logical locations for each overloaded version of that function.
When a function is overloaded, measures have to be taken to create unique logical locations for each overloaded variant of that function. A naive solution is to use the line number of the declaration. A more solid solution is to use a hash of the actual contents of the function to distingush each function variant.
:::

#### roleNeedsLogicalLoc
```rascal
/* Configuration field */ set[IdRole] roleNeedsLogicalLoc = {};
```
The set `roleNeedsLogicalLoc` defines the IdRoles for which a logical location will be generated. Typically, these will be roles for names of high-level concepts such as modules, functions, data types and the like.

### Verbosity

The verbosity of TypePal can be controlled with several configurations settings.
Expand Down
70 changes: 40 additions & 30 deletions src/analysis/typepal/Collector.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
}
loc globalScope = |global-scope:///|;
Defines defines = {};
//map[IdRole, int] idRoleCounter = ();
map[loc,loc] logical2physical = ();
map[tuple[str orgId, IdRole idRole], int] alreadyDefined = ();
Expand Down Expand Up @@ -258,20 +257,20 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
bool building = true;
void defineLogicalLoc(str id, IdRole idRole, loc physicalLoc){
if(idRole notin config.roleNeedslogicalLoc){
return;
}
def_tup = <id, idRole>;
i = alreadyDefined[def_tup] ? 0;
alreadyDefined[def_tup] = i + 1;
suffix = i == 0 ? "" : "$<"<i>">";
logicalLoc = config.createLogicalLoc(id, idRole, physicalLoc, modelName, config.typepalPathConfig) + suffix;
if(logicalLoc != physicalLoc){
logical2physical[logicalLoc] = physicalLoc;
}
}
//void defineLogicalLoc(str id, IdRole idRole, loc physicalLoc){
// if(idRole notin config.roleNeedslogicalLoc){
// return;
// }
// def_tup = <id, idRole>;
// i = alreadyDefined[def_tup] ? 0;
// alreadyDefined[def_tup] = i + 1;
// suffix = i == 0 ? "" : "$<"<i>">";
//
// logicalLoc = config.createLogicalLoc(id, idRole, physicalLoc, modelName, config.typepalPathConfig) + suffix;
// if(logicalLoc != physicalLoc){
// logical2physical[logicalLoc] = physicalLoc;
// }
//}
void _define(str orgId, IdRole idRole, value def, DefInfo info){
if(building){
Expand All @@ -296,11 +295,11 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
}
}
}
lubDefinesPerLubScope[currentLubScope] += <currentScope, nname, orgId, idRole, /*0,*/ l, info>;
lubDefinesPerLubScope[currentLubScope] += <currentScope, nname, orgId, idRole, l, info>;
} else {
defineLogicalLoc(orgId, idRole, l);
//defineLogicalLoc(orgId, idRole, l);
//println("define: add to definesPerLubScope[<currentLubScope>]: <<currentScope, nname, orgId, idRole, uid, l, info>>");
definesPerLubScope[currentLubScope] += <currentScope, nname, orgId, idRole, /*uid,*/ l, info>;
definesPerLubScope[currentLubScope] += <currentScope, nname, orgId, idRole, l, info>;

}
} else {
Expand Down Expand Up @@ -371,7 +370,7 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
if(info is defTypeLub){
throw TypePalUsage("`defLub` cannot be used in combination with `defineInScope`");
} else {
defineLogicalLoc(orgId, idRole, l);
//defineLogicalLoc(orgId, idRole, l);
defines += <definingScope, nname, orgId, idRole, /*uid,*/ l, info>;
}
} else {
Expand Down Expand Up @@ -985,6 +984,23 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
paths += tm.paths;
}

map[loc,loc] buildLogical2physical(Defines defines){
map[loc,loc] my_logical2physical = logical2physical;
for(Define def <- defines){
logicalLoc = config.createLogicalLoc(def, modelName, config.typepalPathConfig);
if(logicalLoc != def.defined){
if(my_logical2physical[logicalLoc]?){
if(my_logical2physical[logicalLoc] != def.defined){
messages += error("<def.id>: <logicalLoc> has identical definitions at two locations: <my_logical2physical[logicalLoc]> and <def.defined>", def.defined);
}
}
my_logical2physical[logicalLoc] = def.defined;
}
}
//iprintln(my_logical2physical);
return my_logical2physical;
}

TModel _run(){
if(building){
building = false;
Expand Down Expand Up @@ -1023,19 +1039,13 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co
dm[id] = (dm[id] ? {}) + {<idRole, defined>};
definesMap[scope] = dm;
}
//for(<loc scope, str id, IdRole idRole, loc defined, DefInfo defInfo> <- defines){
// definesMap[<scope, id>] = definesMap[<scope, id>]? ? definesMap[<scope, id>] + {<idRole, defined>} : {<idRole, defined>};
//}
tm.definesMap = definesMap;

//tm.logical2physical = logical2physical;
tm.logical2physical = buildLogical2physical(defines);
defines = {};
tm.messages = messages;
//return resolvePath(tm);
tm.logical2physical = logical2physical;
//for(l <- domain(logical2physical)){
// if(size(logical2physical[l]) > 1){
// println("*** Collector::_run: <l> =\> <logical2physical[l]>");
// }
//}
tm.messages = messages;

return tm;
} else {
throw TypePalUsage("Cannot call `run` on Collector after `run`");
Expand Down
8 changes: 4 additions & 4 deletions src/analysis/typepal/ConfigurableScopeGraph.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ str reduceToURIChars(str s){
}
}
loc defaultLogicalLoc(str id, IdRole idRole, loc physicalLoc, str modelName, PathConfig _pcfg){
path = physicalLoc.path;
return |<"<modelName>+<prettyRole(idRole)>">://<path>/<reduceToURIChars(id)>|;
loc defaultLogicalLoc(Define def, str modelName, PathConfig _pcfg){
path = def.defined.path;
return |<"<modelName>+<prettyRole(def.idRole)>">://<path>/<reduceToURIChars(def.id)>|;
}

// Extends TypePalConfig defined in analysis::typepal::ScopeGraph
Expand Down Expand Up @@ -136,7 +136,7 @@ data TypePalConfig(

bool(loc def, TModel tm) reportUnused = defaultReportUnused,

loc (str id, IdRole idRole, loc physicalLoc, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc
loc (Define def, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc
);


Expand Down
2 changes: 1 addition & 1 deletion src/analysis/typepal/Solver.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extend analysis::typepal::Messenger;

void checkAllTypesAvailable(TModel tm){
//println("checkAllTypesAvailable: <tm.modelName>");
for(tup: <loc _, str _, str _, IdRole _, loc defined, DefInfo defInfo> <- tm.defines){
for(tup: <loc _, str _, str _, IdRole _, loc _, DefInfo defInfo> <- tm.defines){
if(!(defInfo has atype)){
throw "checkTypesAvailable: <tm.modelName>, <tup>";
}
Expand Down
1 change: 0 additions & 1 deletion src/analysis/typepal/TModel.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ module analysis::typepal::TModel
*/
import String;
import Message;
import List;
import Node;
import IO;

Expand Down
4 changes: 4 additions & 0 deletions src/analysis/typepal/TestFramework.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
}
module analysis::typepal::TestFramework

/*
Test framework for TypePal. Basically, tests are collected in .ttl files and are executed by this framework.
*/

import ParseTree;
import IO;
import String;
Expand Down

0 comments on commit 0029a07

Please sign in to comment.