Skip to content

Commit

Permalink
Merge pull request #16 from TypeFox/if/conversion-algorithm
Browse files Browse the repository at this point in the history
Conversion check for cycle introduction and transitivity check
  • Loading branch information
insafuhrmann authored Oct 14, 2024
2 parents cbdf797 + a9a1cd7 commit 9d47f82
Showing 1 changed file with 45 additions and 13 deletions.
58 changes: 45 additions & 13 deletions packages/typir/src/features/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,16 @@ export class DefaultTypeConversion implements TypeConversion {
edge.mode = mode;
}

// check, that the new edges did not introduce cycles
this.checkForCycles(mode);
}

protected checkForCycles(mode: ConversionModeForSpecification): void {
if (mode === 'IMPLICIT_EXPLICIT') {
this.checkForCyclesLogic(mode);
} else {
// all other modes allow cycles
/* check that the new edges did not introduce cycles
* if it did, the from node will be reachable via a cycle path
*/
const hasIntroducedCycle = this.existsEdgePath(from, from, mode);
if (hasIntroducedCycle) {
throw new Error(`Adding the conversion from ${from.identifier} to ${to.identifier} with mode ${mode} has introduced a cycle in the type graph.`);
}
}
}
protected checkForCyclesLogic(_mode: ConversionModeForSpecification): void {
// TODO check for cycles and throw an Error in case of found cycles
}

protected isTransitive(mode: ConversionModeForSpecification): boolean {
// by default, only IMPLICIT is transitive!
Expand Down Expand Up @@ -180,11 +176,47 @@ export class DefaultTypeConversion implements TypeConversion {
return 'NONE';
}

protected isTransitivelyConvertable(_from: Type, _to: Type, _mode: ConversionModeForSpecification): boolean {
// TODO calculate transitive relationship
protected existsEdgePath(from: Type, to: Type, mode: ConversionModeForSpecification): boolean {
const visited: Set<Type> = new Set();
const stack: Type[] = [from];

while (stack.length > 0) {
const current = stack.pop()!;
visited.add(current);

const outgoingEdges = current.getOutgoingEdges<ConversionEdge>(ConversionEdge);
for (const edge of outgoingEdges) {
if (edge.mode === mode) {
if (edge.to === to) {
/* It was possible to reach our goal type using this path.
* Base case that also catches the case in which start and end are the same
* (is there a cycle?). Therefore it is allowed to have been "visited".
* True will only be returned if there is a real path (cycle) made up of edges
*/
return true;
}
if (!visited.has(edge.to)) {
/* The target node of this edge has not been visited before and is also not our goal node
* Add it to the stack and investigate this path later.
*/
stack.push(edge.to);
}
}
}
}

// Fall through means that we could not reach the goal type
return false;
}

protected isTransitivelyConvertable(from: Type, to: Type, mode: ConversionModeForSpecification): boolean {
if (from === to) {
return true;
} else {
return(this.existsEdgePath(from, to, mode));
}
}

isImplicitExplicitConvertible(from: Type, to: Type): boolean {
return this.getConversion(from, to) === 'IMPLICIT_EXPLICIT';
}
Expand Down

0 comments on commit 9d47f82

Please sign in to comment.