Skip to content

Commit

Permalink
feat: allow schemas in pipeline files (#1080)
Browse files Browse the repository at this point in the history
Closes #1077

### Summary of Changes

Pipelines files can now contain schemas.
  • Loading branch information
lars-reimann authored Apr 22, 2024
1 parent e34b3ff commit 9508178
Show file tree
Hide file tree
Showing 11 changed files with 37 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import {
getQualifiedName,
getResults,
getTypeParameters,
isImplementedDeclaration,
isInternal,
isPrivate,
isStatic,
Expand All @@ -54,6 +53,7 @@ import { expandToStringLF } from 'langium/generate';
import { SafeDsClassHierarchy } from '../typing/safe-ds-class-hierarchy.js';
import { SafeDsClasses } from '../builtins/safe-ds-classes.js';
import { SafeDsPackageManager } from '../workspace/safe-ds-package-manager.js';
import { isInPipelineFile } from '../helpers/fileExtensions.js';

const INDENTATION = ' ';
const LIB = path.join('packages', 'safe-ds-lang', 'lib', 'resources');
Expand Down Expand Up @@ -695,7 +695,7 @@ export class SafeDsMarkdownGenerator {

const text = removeLinePrefix(cstNode.text, firstLineIndent);
const fileName = AstUtils.getDocument(node).uri.path.split('/').pop();
const kind = isImplementedDeclaration(node) ? 'Implementation' : 'Stub';
const kind = isInPipelineFile(node) ? 'Implementation' : 'Stub';

let result = `??? quote "${kind} code in \`${fileName}\`"\n\n`;
result += indent(`\`\`\`sds linenums="${startLine + 1}"\n${text}\n\`\`\``);
Expand Down
13 changes: 7 additions & 6 deletions packages/safe-ds-lang/src/language/helpers/nodeProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
isSdsPipeline,
isSdsPlaceholder,
isSdsQualifiedImport,
isSdsSchema,
isSdsSegment,
isSdsTypeArgumentList,
isSdsTypeParameter,
Expand Down Expand Up @@ -196,17 +197,17 @@ export namespace TypeParameter {
}

/**
* Checks whether the declaration has an implementation.
* Checks whether the declaration is valid in a pipeline file.
*/
export const isImplementedDeclaration = (declaration: SdsDeclaration): boolean => {
return isSdsPipeline(declaration) || isSdsSegment(declaration);
export const isValidPipelineDeclaration = (node: SdsDeclaration): boolean => {
return isSdsPipeline(node) || isSdsSchema(node) || isSdsSegment(node);
};

/**
* Checks whether the declaration is just a stub.
* Checks whether the declaration is valid in a stub file.
*/
export const isStubDeclaration = (declaration: SdsDeclaration): boolean => {
return !isSdsPipeline(declaration) && !isSdsSegment(declaration);
export const isValidStubDeclaration = (node: SdsDeclaration): boolean => {
return isSdsAnnotation(node) || isSdsClass(node) || isSdsEnum(node) || isSdsFunction(node) || isSdsSchema(node);
};

// -------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class SafeDsCompletionProvider extends DefaultCompletionProvider {
return true;
}

private illegalKeywordsInPipelineFile = new Set(['annotation', 'class', 'enum', 'fun', 'schema']);
private illegalKeywordsInPipelineFile = new Set(['annotation', 'class', 'enum', 'fun']);
private illegalKeywordsInStubFile = new Set(['pipeline', 'internal', 'private', 'segment']);

protected override filterKeyword(context: CompletionContext, keyword: Keyword): boolean {
Expand Down
8 changes: 4 additions & 4 deletions packages/safe-ds-lang/src/language/validation/names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ import {
getParameters,
getResults,
getTypeParameters,
isImplementedDeclaration,
isStatic,
isStubDeclaration,
isValidPipelineDeclaration,
isValidStubDeclaration,
streamBlockLambdaResults,
streamPlaceholders,
} from '../helpers/nodeProperties.js';
Expand Down Expand Up @@ -341,14 +341,14 @@ export const moduleMustContainUniqueNames = (node: SdsModule, accept: Validation
getModuleMembers(node),
(name) => `A declaration with name '${name}' exists already in this file.`,
accept,
isImplementedDeclaration,
isValidPipelineDeclaration,
);
} else if (isInStubFile(node)) {
namesMustBeUnique(
getModuleMembers(node),
(name) => `A declaration with name '${name}' exists already in this file.`,
accept,
isStubDeclaration,
isValidStubDeclaration,
);
} else if (isInDevFile(node)) {
namesMustBeUnique(
Expand Down
14 changes: 6 additions & 8 deletions packages/safe-ds-lang/src/language/validation/other/modules.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ValidationAcceptor } from 'langium';
import { isSdsDeclaration, SdsModule } from '../../generated/ast.js';
import { SdsModule } from '../../generated/ast.js';
import { isInPipelineFile, isInStubFile } from '../../helpers/fileExtensions.js';
import { getModuleMembers, isImplementedDeclaration, isStubDeclaration } from '../../helpers/nodeProperties.js';
import { getModuleMembers, isValidPipelineDeclaration, isValidStubDeclaration } from '../../helpers/nodeProperties.js';
import { BUILTINS_ROOT_PACKAGE } from '../../builtins/packageNames.js';

export const CODE_MODULE_FORBIDDEN_IN_PIPELINE_FILE = 'module/forbidden-in-pipeline-file';
Expand All @@ -10,11 +10,9 @@ export const CODE_MODULE_MISSING_PACKAGE = 'module/missing-package';
export const CODE_MODULE_PIPELINE_FILE_IN_BUILTIN_PACKAGE = 'module/pipeline-file-in-builtin-package';

export const moduleDeclarationsMustMatchFileKind = (node: SdsModule, accept: ValidationAcceptor): void => {
const declarations = node.members.filter(isSdsDeclaration);

if (isInPipelineFile(node)) {
for (const declaration of declarations) {
if (!isImplementedDeclaration(declaration)) {
for (const declaration of getModuleMembers(node)) {
if (!isValidPipelineDeclaration(declaration)) {
accept('error', 'A pipeline file must only declare pipelines and segments.', {
node: declaration,
property: 'name',
Expand All @@ -23,8 +21,8 @@ export const moduleDeclarationsMustMatchFileKind = (node: SdsModule, accept: Val
}
}
} else if (isInStubFile(node)) {
for (const declaration of declarations) {
if (!isStubDeclaration(declaration)) {
for (const declaration of getModuleMembers(node)) {
if (!isValidStubDeclaration(declaration)) {
accept('error', 'A stub file must not declare pipelines or segments.', {
node: declaration,
property: 'name',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('SafeDsCompletionProvider', async () => {
`,
uri: `file:///test1.sds`,
expectedLabels: {
shouldEqual: ['from', 'pipeline', 'internal', 'private', 'segment'],
shouldEqual: ['from', 'schema', 'pipeline', 'internal', 'private', 'segment'],
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fun »duplicateFunction«()
schema »UniqueSchema« {}
// $TEST$ no error r"A declaration with name '\w*' exists already in this file\."
schema »DuplicateSchema« {}
// $TEST$ no error r"A declaration with name '\w*' exists already in this file\."
// $TEST$ error r"A declaration with name '\w*' exists already in this file\."
schema »DuplicateSchema« {}


Expand All @@ -77,9 +77,9 @@ class »DuplicateDeclaration«
enum »DuplicateDeclaration«
// $TEST$ no error r"A declaration with name '\w*' exists already in this file\."
fun »DuplicateDeclaration«()
// $TEST$ no error r"A declaration with name '\w*' exists already in this file\."
schema »DuplicateDeclaration« {}
// $TEST$ error r"A declaration with name '\w*' exists already in this file\."
pipeline »DuplicateDeclaration« {}
// $TEST$ error r"A declaration with name '\w*' exists already in this file\."
schema »DuplicateDeclaration« {}
// $TEST$ error "A declaration with name 'DuplicateDeclaration' exists already in this file."
segment »DuplicateDeclaration«() {}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ enum »MyEnum« {
}
// $TEST$ error "A pipeline file must only declare pipelines and segments."
fun »myFunction«()
// $TEST$ error "A pipeline file must only declare pipelines and segments."
schema »MySchema« {}

// $TEST$ no error "A pipeline file must only declare pipelines and segments."
pipeline »myPipeline« {}
// $TEST$ no error "A pipeline file must only declare pipelines and segments."
schema »MySchema« {}
// $TEST$ no error "A pipeline file must only declare pipelines and segments."
segment »mySegment«() {}
10 changes: 5 additions & 5 deletions packages/safe-ds-vscode/snippets/safe-ds-dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@
],
"description": "A method."
},
"Schema": {
"prefix": ["schema"],
"body": ["schema ${1:MySchema} {", " $0", "}"],
"description": "A schema."
},
"Pipeline": {
"prefix": ["pipeline"],
"body": ["pipeline ${1:myPipeline} {", " $0", "}"],
"description": "A pipeline."
},
"Schema": {
"prefix": ["schema"],
"body": ["schema ${1:MySchema} {", " $0", "}"],
"description": "A schema."
},
"Segment": {
"prefix": ["segment"],
"body": ["${1|internal ,private |}segment ${2:mySegment}($3) ${4:-> ($5)} ${6:where {$7\\}} {", " $0", "}"],
Expand Down
5 changes: 5 additions & 0 deletions packages/safe-ds-vscode/snippets/safe-ds.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"body": ["pipeline ${1:myPipeline} {", " $0", "}"],
"description": "A pipeline."
},
"Schema": {
"prefix": ["schema"],
"body": ["schema ${1:MySchema} {", " $0", "}"],
"description": "A schema."
},
"Segment": {
"prefix": ["segment"],
"body": ["${1|internal ,private |}segment ${2:mySegment}($3) ${4:-> ($5)} ${6:where {$7\\}} {", " $0", "}"],
Expand Down
2 changes: 1 addition & 1 deletion packages/safe-ds-vscode/syntaxes/safe-ds.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

{
"name": "storage.type.safe-ds",
"match": "\\b(package|pipeline|segment|val)\\b"
"match": "\\b(package|pipeline|schema|segment|val)\\b"
},
{
"name": "storage.modifier.safe-ds",
Expand Down

0 comments on commit 9508178

Please sign in to comment.