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: very basic type computer #596

Merged
merged 7 commits into from
Oct 1, 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
37 changes: 37 additions & 0 deletions src/language/builtins/fileFinder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import path from 'path';
import { URI } from 'langium';
import { SAFE_DS_FILE_EXTENSIONS } from '../helpers/fileExtensions.js';
import { globSync } from 'glob';

let builtinsPath: string;
if (__filename.endsWith('.ts')) {
// Before running ESBuild
builtinsPath = path.join(__dirname, '..', '..', 'resources', 'builtins');
} /* c8 ignore start */ else {
// After running ESBuild
builtinsPath = path.join(__dirname, '..', 'resources', 'builtins');
} /* c8 ignore stop */

/**
* Lists all Safe-DS files in `src/resources/builtins`.
*
* @return URIs of all discovered files.
*/
export const listBuiltinsFiles = (): URI[] => {
const pattern = `**/*.{${SAFE_DS_FILE_EXTENSIONS.join(',')}}`;
const relativePaths = globSync(pattern, { cwd: builtinsPath, nodir: true });
return relativePaths.map((relativePath) => {
const absolutePath = path.join(builtinsPath, relativePath);
return URI.file(absolutePath);
});
};

/**
* Resolves a relative path to a builtin file.
*
* @param relativePath
*/
export const resolveRelativePathToBuiltinFile = (relativePath: string): URI => {
const absolutePath = path.join(builtinsPath, relativePath);
return URI.file(absolutePath);
};
93 changes: 93 additions & 0 deletions src/language/builtins/safe-ds-core-classes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { SafeDsServices } from '../safe-ds-module.js';
import { resolveRelativePathToBuiltinFile } from './fileFinder.js';
import { isSdsClass, isSdsModule, SdsClass } from '../generated/ast.js';
import { LangiumDocuments } from 'langium';
import { moduleMembersOrEmpty } from '../helpers/shortcuts.js';

const CORE_CLASSES_URI = resolveRelativePathToBuiltinFile('safeds/lang/coreClasses.sdsstub');

export class SafeDsCoreClasses {
private readonly langiumDocuments: LangiumDocuments;

constructor(services: SafeDsServices) {
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
}

private cachedAny: SdsClass | undefined;

/* c8 ignore start */
get Any(): SdsClass | undefined {
if (!this.cachedAny) {
this.cachedAny = this.getClass('Any');
}
return this.cachedAny;
}

private cachedBoolean: SdsClass | undefined;
/* c8 ignore stop */

get Boolean(): SdsClass | undefined {
if (!this.cachedBoolean) {
this.cachedBoolean = this.getClass('Boolean');
}
return this.cachedBoolean;
}

private cachedFloat: SdsClass | undefined;

get Float(): SdsClass | undefined {
if (!this.cachedFloat) {
this.cachedFloat = this.getClass('Float');
}
return this.cachedFloat;
}

private cachedInt: SdsClass | undefined;

get Int(): SdsClass | undefined {
if (!this.cachedInt) {
this.cachedInt = this.getClass('Int');
}
return this.cachedInt;
}

private cachedNothing: SdsClass | undefined;

get Nothing(): SdsClass | undefined {
if (!this.cachedNothing) {
this.cachedNothing = this.getClass('Nothing');
}
return this.cachedNothing;
}

private cachedString: SdsClass | undefined;

get String(): SdsClass | undefined {
if (!this.cachedString) {
this.cachedString = this.getClass('String');
}
return this.cachedString;
}

private getClass(name: string): SdsClass | undefined {
if (!this.langiumDocuments.hasDocument(CORE_CLASSES_URI)) {
/* c8 ignore next 2 */
return undefined;
}

const document = this.langiumDocuments.getOrCreateDocument(CORE_CLASSES_URI);
const root = document.parseResult.value;
if (!isSdsModule(root)) {
/* c8 ignore next 2 */
return undefined;
}

const firstMatchingModuleMember = moduleMembersOrEmpty(root).find((m) => m.name === name);
if (!isSdsClass(firstMatchingModuleMember)) {
/* c8 ignore next 2 */
return undefined;
}

return firstMatchingModuleMember;
}
}
30 changes: 3 additions & 27 deletions src/language/builtins/safe-ds-workspace-manager.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { DefaultWorkspaceManager, LangiumDocument, LangiumDocumentFactory, LangiumSharedServices, URI } from 'langium';
import { DefaultWorkspaceManager, LangiumDocument, LangiumDocumentFactory, LangiumSharedServices } from 'langium';
import { WorkspaceFolder } from 'vscode-languageserver';
import { SAFE_DS_FILE_EXTENSIONS } from '../helpers/fileExtensions.js';
import { globSync } from 'glob';
import path from 'path';
import { listBuiltinsFiles } from './fileFinder.js';

export class SafeDsWorkspaceManager extends DefaultWorkspaceManager {
private documentFactory: LangiumDocumentFactory;
Expand All @@ -18,31 +16,9 @@ export class SafeDsWorkspaceManager extends DefaultWorkspaceManager {
): Promise<void> {
await super.loadAdditionalDocuments(folders, collector);

// Load builtin files
for (const uri of listBuiltinsFiles()) {
collector(this.documentFactory.create(uri));
}
}
}

let builtinsPath: string;
if (__filename.endsWith('.ts')) {
// Before running ESBuild
builtinsPath = path.join(__dirname, '..', '..', 'resources', 'builtins');
} /* c8 ignore start */ else {
// After running ESBuild
builtinsPath = path.join(__dirname, '..', 'resources', 'builtins');
} /* c8 ignore stop */

/**
* Lists all Safe-DS files in `src/resources/builtins`.
*
* @return URIs of all discovered files.
*/
export const listBuiltinsFiles = (): URI[] => {
const pattern = `**/*.{${SAFE_DS_FILE_EXTENSIONS.join(',')}}`;
const relativePaths = globSync(pattern, { cwd: builtinsPath, nodir: true });
return relativePaths.map((relativePath) => {
const absolutePath = path.join(builtinsPath, relativePath);
return URI.file(absolutePath);
});
};
6 changes: 6 additions & 0 deletions src/language/helpers/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
isSdsBlockLambdaResult,
isSdsDeclaration,
isSdsModule,
isSdsModuleMember,
isSdsPlaceholder,
SdsAnnotatedObject,
SdsAnnotationCall,
Expand All @@ -19,6 +20,7 @@ import {
SdsLiteral,
SdsLiteralType,
SdsModule,
SdsModuleMember,
SdsParameter,
SdsParameterList,
SdsPlaceholder,
Expand Down Expand Up @@ -77,6 +79,10 @@ export const importsOrEmpty = function (node: SdsModule | undefined): SdsImport[
return node?.imports ?? [];
};

export const moduleMembersOrEmpty = function (node: SdsModule | undefined): SdsModuleMember[] {
return node?.members?.filter(isSdsModuleMember) ?? [];
};

export const packageNameOrNull = function (node: AstNode | undefined): string | null {
return getContainerOfType(node, isSdsModule)?.name ?? null;
};
Expand Down
18 changes: 13 additions & 5 deletions src/language/safe-ds-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ import {
PartialLangiumServices,
} from 'langium';
import { SafeDsGeneratedModule, SafeDsGeneratedSharedModule } from './generated/module.js';
import { registerValidationChecks, SafeDsValidator } from './validation/safe-ds-validator.js';
import { registerValidationChecks } from './validation/safe-ds-validator.js';
import { SafeDsFormatter } from './formatting/safe-ds-formatter.js';
import { SafeDsWorkspaceManager } from './builtins/safe-ds-workspace-manager.js';
import { SafeDsScopeComputation } from './scoping/safe-ds-scope-computation.js';
import { SafeDsScopeProvider } from './scoping/safe-ds-scope-provider.js';
import { SafeDsValueConverter } from './grammar/safe-ds-value-converter.js';
import { SafeDsTypeComputer } from './typing/safe-ds-type-computer.js';
import { SafeDsCoreClasses } from './builtins/safe-ds-core-classes.js';

/**
* Declaration of custom services - add your own service classes here.
*/
export type SafeDsAddedServices = {
validation: {
SafeDsValidator: SafeDsValidator;
builtins: {
CoreClasses: SafeDsCoreClasses;
};
types: {
TypeComputer: SafeDsTypeComputer;
};
};

Expand All @@ -38,6 +43,9 @@ export type SafeDsServices = LangiumServices & SafeDsAddedServices;
* selected services, while the custom services must be fully specified.
*/
export const SafeDsModule: Module<SafeDsServices, PartialLangiumServices & SafeDsAddedServices> = {
builtins: {
CoreClasses: (services) => new SafeDsCoreClasses(services),
},
lsp: {
Formatter: () => new SafeDsFormatter(),
},
Expand All @@ -48,8 +56,8 @@ export const SafeDsModule: Module<SafeDsServices, PartialLangiumServices & SafeD
ScopeComputation: (services) => new SafeDsScopeComputation(services),
ScopeProvider: (services) => new SafeDsScopeProvider(services),
},
validation: {
SafeDsValidator: () => new SafeDsValidator(),
types: {
TypeComputer: (services) => new SafeDsTypeComputer(services),
},
};

Expand Down
18 changes: 9 additions & 9 deletions src/language/scoping/safe-ds-scope-provider.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import {
AstNode,
AstNodeDescription,
AstNodeDescriptionProvider,
AstNodeLocator,
DefaultScopeProvider,
EMPTY_SCOPE,
getContainerOfType,
getDocument,
LangiumDocuments,
LangiumServices,
MultiMap,
ReferenceInfo,
Scope,
Expand Down Expand Up @@ -59,18 +57,20 @@ import {
} from '../helpers/shortcuts.js';
import { isContainedIn } from '../helpers/ast.js';
import { isStatic, isWildcardImport } from '../helpers/checks.js';
import { SafeDsServices } from '../safe-ds-module.js';
import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js';

export class SafeDsScopeProvider extends DefaultScopeProvider {
readonly documents: LangiumDocuments;
readonly astNodeDescriptionProvider: AstNodeDescriptionProvider;
readonly astNodeLocator: AstNodeLocator;
private readonly astNodeLocator: AstNodeLocator;
private readonly langiumDocuments: LangiumDocuments;
private readonly typeComputer: SafeDsTypeComputer;

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

this.documents = services.shared.workspace.LangiumDocuments;
this.astNodeDescriptionProvider = services.workspace.AstNodeDescriptionProvider;
this.astNodeLocator = services.workspace.AstNodeLocator;
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
this.typeComputer = services.types.TypeComputer;
}

override getScope(context: ReferenceInfo): Scope {
Expand Down Expand Up @@ -388,7 +388,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
/* c8 ignore next 2 */
return nodeDescription.node;
}
const document = this.documents.getOrCreateDocument(nodeDescription.documentUri);
const document = this.langiumDocuments.getOrCreateDocument(nodeDescription.documentUri);
return this.astNodeLocator.getAstNode(document.parseResult.value, nodeDescription.path);
}

Expand Down
Loading