-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: extensions for the
NodeMapper
(#606)
### Summary of Changes The `NodeMapper` has gained new capabilities. It can now map * results to yields, * parameters to references, * placeholders to references. These are needed for various validation checks. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
- Loading branch information
1 parent
25c8707
commit 4fd8d86
Showing
23 changed files
with
735 additions
and
321 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Returns the unique element in the array, or `undefined` if none or multiple exist. | ||
*/ | ||
export const uniqueOrUndefined = <T>(elements: T[]): T | undefined => { | ||
if (elements.length === 1) { | ||
return elements[0]; | ||
} | ||
|
||
return undefined; | ||
}; | ||
|
||
/** | ||
* Returns the elements of the array that are labeled the same as a previous element. The first element with a label is | ||
* not included. Neither are elements with an undefined label. | ||
*/ | ||
export const duplicatesBy = function* <T, K>( | ||
elements: Iterable<T>, | ||
labeler: (element: T) => K | undefined, | ||
): Generator<T, void> { | ||
const knownLabels = new Set<K>(); | ||
|
||
for (const element of elements) { | ||
const label = labeler(element); | ||
if (label === undefined) { | ||
continue; | ||
} | ||
|
||
if (knownLabels.has(label)) { | ||
yield element; | ||
} else { | ||
knownLabels.add(label); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Handles the mapping of objects, usually nodes of an Safe-DS AST, to their IDs. | ||
*/ | ||
export class IdManager<T extends WeakKey> { | ||
/** | ||
* Maps an object to an ID. | ||
*/ | ||
private objToId: WeakMap<T, Id> = new WeakMap(); | ||
|
||
/** | ||
* The next available ID. | ||
*/ | ||
private nextId = 0; | ||
|
||
/** | ||
* Assigns the next available ID to the given object unless it already has one and returns the ID for this object. | ||
*/ | ||
assignId(obj: T): Id { | ||
if (!this.objToId.has(obj)) { | ||
this.objToId.set(obj, this.nextId++); | ||
} | ||
return this.objToId.get(obj)!; | ||
} | ||
|
||
/** | ||
* Removes all mappings between object and ID and resets the counter. | ||
*/ | ||
reset() { | ||
this.objToId = new WeakMap(); | ||
this.nextId = 0; | ||
} | ||
} | ||
|
||
export type Id = number; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import { | ||
isSdsAssignment, | ||
isSdsAttribute, | ||
isSdsBlockLambdaResult, | ||
isSdsClass, | ||
isSdsDeclaration, | ||
isSdsEnum, | ||
isSdsEnumVariant, | ||
isSdsFunction, | ||
isSdsModule, | ||
isSdsModuleMember, | ||
isSdsPlaceholder, | ||
isSdsSegment, | ||
isSdsTypeParameterList, | ||
SdsAbstractCall, | ||
SdsAnnotatedObject, | ||
SdsAnnotationCall, | ||
SdsArgument, | ||
SdsAssignee, | ||
SdsAssignment, | ||
SdsBlock, | ||
SdsBlockLambda, | ||
SdsBlockLambdaResult, | ||
SdsCallable, | ||
SdsClass, | ||
SdsClassMember, | ||
SdsDeclaration, | ||
SdsEnum, | ||
SdsEnumVariant, | ||
SdsImport, | ||
SdsImportedDeclaration, | ||
SdsLiteral, | ||
SdsLiteralType, | ||
SdsModule, | ||
SdsModuleMember, | ||
SdsNamedTypeDeclaration, | ||
SdsParameter, | ||
SdsPlaceholder, | ||
SdsQualifiedImport, | ||
SdsResult, | ||
SdsResultList, | ||
SdsStatement, | ||
SdsTypeArgument, | ||
SdsTypeArgumentList, | ||
SdsTypeParameter, | ||
SdsTypeParameterList, | ||
} from '../generated/ast.js'; | ||
import { AstNode, getContainerOfType, stream } from 'langium'; | ||
|
||
// ------------------------------------------------------------------------------------------------- | ||
// Checks | ||
// ------------------------------------------------------------------------------------------------- | ||
|
||
export const isInternal = (node: SdsDeclaration): boolean => { | ||
return isSdsSegment(node) && node.visibility === 'internal'; | ||
}; | ||
|
||
export const isNamedArgument = (node: SdsArgument): boolean => { | ||
return Boolean(node.parameter); | ||
}; | ||
|
||
export const isNamedTypeArgument = (node: SdsTypeArgument): boolean => { | ||
return Boolean(node.typeParameter); | ||
}; | ||
|
||
export const isStatic = (node: SdsClassMember): boolean => { | ||
if (isSdsClass(node) || isSdsEnum(node)) { | ||
return true; | ||
} else if (isSdsAttribute(node)) { | ||
return node.isStatic; | ||
} else if (isSdsFunction(node)) { | ||
return node.isStatic; | ||
} else { | ||
/* c8 ignore next 2 */ | ||
return false; | ||
} | ||
}; | ||
|
||
// ------------------------------------------------------------------------------------------------- | ||
// Accessors for list elements | ||
// ------------------------------------------------------------------------------------------------- | ||
|
||
export const annotationCallsOrEmpty = (node: SdsAnnotatedObject | undefined): SdsAnnotationCall[] => { | ||
if (!node) { | ||
/* c8 ignore next 2 */ | ||
return []; | ||
} | ||
|
||
if (isSdsDeclaration(node)) { | ||
return node?.annotationCallList?.annotationCalls ?? node?.annotationCalls ?? []; | ||
} else { | ||
/* c8 ignore next 2 */ | ||
return node?.annotationCalls ?? []; | ||
} | ||
}; | ||
export const argumentsOrEmpty = (node: SdsAbstractCall | undefined): SdsArgument[] => { | ||
return node?.argumentList?.arguments ?? []; | ||
}; | ||
export const assigneesOrEmpty = (node: SdsAssignment | undefined): SdsAssignee[] => { | ||
return node?.assigneeList?.assignees ?? []; | ||
}; | ||
export const blockLambdaResultsOrEmpty = (node: SdsBlockLambda | undefined): SdsBlockLambdaResult[] => { | ||
return stream(statementsOrEmpty(node?.body)) | ||
.filter(isSdsAssignment) | ||
.flatMap(assigneesOrEmpty) | ||
.filter(isSdsBlockLambdaResult) | ||
.toArray(); | ||
}; | ||
export const importedDeclarationsOrEmpty = (node: SdsQualifiedImport | undefined): SdsImportedDeclaration[] => { | ||
return node?.importedDeclarationList?.importedDeclarations ?? []; | ||
}; | ||
|
||
export const literalsOrEmpty = (node: SdsLiteralType | undefined): SdsLiteral[] => { | ||
return node?.literalList?.literals ?? []; | ||
}; | ||
export const classMembersOrEmpty = ( | ||
node: SdsClass | undefined, | ||
filterFunction: (member: SdsClassMember) => boolean = () => true, | ||
): SdsClassMember[] => { | ||
return node?.body?.members?.filter(filterFunction) ?? []; | ||
}; | ||
|
||
export const enumVariantsOrEmpty = (node: SdsEnum | undefined): SdsEnumVariant[] => { | ||
return node?.body?.variants ?? []; | ||
}; | ||
|
||
export const importsOrEmpty = (node: SdsModule | undefined): SdsImport[] => { | ||
return node?.imports ?? []; | ||
}; | ||
|
||
export const moduleMembersOrEmpty = (node: SdsModule | undefined): SdsModuleMember[] => { | ||
return node?.members?.filter(isSdsModuleMember) ?? []; | ||
}; | ||
|
||
export const packageNameOrUndefined = (node: AstNode | undefined): string | undefined => { | ||
return getContainerOfType(node, isSdsModule)?.name; | ||
}; | ||
|
||
export const parametersOrEmpty = (node: SdsCallable | undefined): SdsParameter[] => { | ||
return node?.parameterList?.parameters ?? []; | ||
}; | ||
|
||
export const placeholdersOrEmpty = (node: SdsBlock | undefined): SdsPlaceholder[] => { | ||
return stream(statementsOrEmpty(node)) | ||
.filter(isSdsAssignment) | ||
.flatMap(assigneesOrEmpty) | ||
.filter(isSdsPlaceholder) | ||
.toArray(); | ||
}; | ||
|
||
export const resultsOrEmpty = (node: SdsResultList | undefined): SdsResult[] => { | ||
return node?.results ?? []; | ||
}; | ||
|
||
export const statementsOrEmpty = (node: SdsBlock | undefined): SdsStatement[] => { | ||
return node?.statements ?? []; | ||
}; | ||
|
||
export const typeArgumentsOrEmpty = (node: SdsTypeArgumentList | undefined): SdsTypeArgument[] => { | ||
return node?.typeArguments ?? []; | ||
}; | ||
|
||
export const typeParametersOrEmpty = ( | ||
node: SdsTypeParameterList | SdsNamedTypeDeclaration | undefined, | ||
): SdsTypeParameter[] => { | ||
if (!node) { | ||
return []; | ||
} | ||
|
||
if (isSdsTypeParameterList(node)) { | ||
return node.typeParameters; | ||
} else if (isSdsClass(node)) { | ||
return typeParametersOrEmpty(node.typeParameterList); | ||
} else if (isSdsEnumVariant(node)) { | ||
return typeParametersOrEmpty(node.typeParameterList); | ||
} /* c8 ignore start */ else { | ||
return []; | ||
} /* c8 ignore stop */ | ||
}; |
Oops, something went wrong.