Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check @PythonName and @PythonModule #641

Merged
merged 5 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 61 additions & 13 deletions src/language/builtins/safe-ds-annotations.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,102 @@
import { isSdsAnnotation, SdsAnnotatedObject, SdsAnnotation, SdsParameter } from '../generated/ast.js';
import { annotationCallsOrEmpty } from '../helpers/nodeProperties.js';
import { isSdsAnnotation, SdsAnnotatedObject, SdsAnnotation, SdsModule, SdsParameter } from '../generated/ast.js';
import { argumentsOrEmpty, findFirstAnnotationCallOf, hasAnnotationCallOf } from '../helpers/nodeProperties.js';
import { SafeDsModuleMembers } from './safe-ds-module-members.js';
import { resourceNameToUri } from '../../helpers/resources.js';
import { URI } from 'langium';
import { SafeDsServices } from '../safe-ds-module.js';
import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js';
import { toConstantExpressionOrUndefined } from '../partialEvaluation/toConstantExpressionOrUndefined.js';
import { SdsConstantExpression, SdsConstantString } from '../partialEvaluation/model.js';

const ANNOTATION_USAGE_URI = resourceNameToUri('builtins/safeds/lang/annotationUsage.sdsstub');
const CODE_GENERATION_URI = resourceNameToUri('builtins/safeds/lang/codeGeneration.sdsstub');
const IDE_INTEGRATION_URI = resourceNameToUri('builtins/safeds/lang/ideIntegration.sdsstub');
const MATURITY_URI = resourceNameToUri('builtins/safeds/lang/maturity.sdsstub');

export class SafeDsAnnotations extends SafeDsModuleMembers<SdsAnnotation> {
private readonly nodeMapper: SafeDsNodeMapper;

constructor(services: SafeDsServices) {
super(services);

this.nodeMapper = services.helpers.NodeMapper;
}

isDeprecated(node: SdsAnnotatedObject | undefined): boolean {
return this.hasAnnotationCallOf(node, this.Deprecated);
return hasAnnotationCallOf(node, this.Deprecated);
}

private get Deprecated(): SdsAnnotation | undefined {
return this.getAnnotation(MATURITY_URI, 'Deprecated');
}

isExperimental(node: SdsAnnotatedObject | undefined): boolean {
return this.hasAnnotationCallOf(node, this.Experimental);
return hasAnnotationCallOf(node, this.Experimental);
}

private get Experimental(): SdsAnnotation | undefined {
return this.getAnnotation(MATURITY_URI, 'Experimental');
}

isExpert(node: SdsParameter | undefined): boolean {
return this.hasAnnotationCallOf(node, this.Expert);
return hasAnnotationCallOf(node, this.Expert);
}

private get Expert(): SdsAnnotation | undefined {
return this.getAnnotation(IDE_INTEGRATION_URI, 'Expert');
}

getPythonModule(node: SdsModule | undefined): string | undefined {
const value = this.getArgumentValue(node, this.PythonModule, 'qualifiedName');
if (value instanceof SdsConstantString) {
return value.value;
} else {
return undefined;
}
}

get PythonModule(): SdsAnnotation | undefined {
return this.getAnnotation(CODE_GENERATION_URI, 'PythonModule');
}

getPythonName(node: SdsAnnotatedObject | undefined): string | undefined {
const value = this.getArgumentValue(node, this.PythonName, 'name');
if (value instanceof SdsConstantString) {
return value.value;
} else {
return undefined;
}
}

get PythonName(): SdsAnnotation | undefined {
return this.getAnnotation(CODE_GENERATION_URI, 'PythonName');
}

isRepeatable(node: SdsAnnotation | undefined): boolean {
return this.hasAnnotationCallOf(node, this.Repeatable);
return hasAnnotationCallOf(node, this.Repeatable);
}

private get Repeatable(): SdsAnnotation | undefined {
return this.getAnnotation(ANNOTATION_USAGE_URI, 'Repeatable');
}

private hasAnnotationCallOf(node: SdsAnnotatedObject | undefined, expected: SdsAnnotation | undefined): boolean {
return annotationCallsOrEmpty(node).some((it) => {
const actual = it.annotation?.ref;
return actual === expected;
});
}

private getAnnotation(uri: URI, name: string): SdsAnnotation | undefined {
return this.getModuleMember(uri, name, isSdsAnnotation);
}

/**
* Finds the first call of the given annotation on the given node and returns the value that is assigned to the
* parameter with the given name.
*/
private getArgumentValue(
node: SdsAnnotatedObject | undefined,
annotation: SdsAnnotation | undefined,
parameterName: string,
): SdsConstantExpression | undefined {
const annotationCall = findFirstAnnotationCallOf(node, annotation);
const expression = argumentsOrEmpty(annotationCall).find(
(it) => this.nodeMapper.argumentToParameterOrUndefined(it)?.name === parameterName,
)?.value;
return toConstantExpressionOrUndefined(expression);
}
}
22 changes: 22 additions & 0 deletions src/language/helpers/nodeProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
SdsAbstractCall,
SdsAbstractResult,
SdsAnnotatedObject,
SdsAnnotation,
SdsAnnotationCall,
SdsArgument,
SdsAssignee,
Expand Down Expand Up @@ -57,6 +58,16 @@ import { AstNode, getContainerOfType, stream } from 'langium';
// Checks
// -------------------------------------------------------------------------------------------------

export const hasAnnotationCallOf = (
node: SdsAnnotatedObject | undefined,
expected: SdsAnnotation | undefined,
): boolean => {
return annotationCallsOrEmpty(node).some((it) => {
const actual = it.annotation?.ref;
return actual === expected;
});
};

export const isInternal = (node: SdsDeclaration): boolean => {
return isSdsSegment(node) && node.visibility === 'internal';
};
Expand Down Expand Up @@ -121,6 +132,17 @@ export const annotationCallsOrEmpty = (node: SdsAnnotatedObject | undefined): Sd
return node?.annotationCalls ?? [];
}
};

export const findFirstAnnotationCallOf = (
node: SdsAnnotatedObject | undefined,
expected: SdsAnnotation | undefined,
): SdsAnnotationCall | undefined => {
return annotationCallsOrEmpty(node).find((it) => {
const actual = it.annotation?.ref;
return actual === expected;
});
};

export const argumentsOrEmpty = (node: SdsAbstractCall | undefined): SdsArgument[] => {
return node?.argumentList?.arguments ?? [];
};
Expand Down
Loading