From 7f982e8591d157c700a13499949acb4047b528ef Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 10 Oct 2023 18:13:37 +0200 Subject: [PATCH 01/56] feat: Begin of generator work --- src/cli/generator.ts | 401 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 393 insertions(+), 8 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index c1de486d2..b850056db 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,11 +1,96 @@ import fs from 'fs'; -import { CompositeGeneratorNode, toString } from 'langium'; +import { + CompositeGeneratorNode, + expandToString, + expandToStringWithNL, + findLocalReferences, + streamAllContents, + toString, + getDocument, +} from 'langium'; import path from 'path'; -import { SdsModule } from '../language/generated/ast.js'; +import { + isQualifiedName, + isSdsAssignee, + isSdsAssignment, + isSdsBlockLambda, + isSdsBlockLambdaResult, + isSdsCall, + isSdsCallable, + isSdsCallableType, + isSdsExpressionLambda, + isSdsExpressionStatement, + isSdsIndexedAccess, + isSdsInfixOperation, + isSdsList, + isSdsLiteral, + isSdsLiteralType, + isSdsMap, + isSdsMemberAccess, + isSdsMemberType, + isSdsNamedType, + isSdsParenthesizedExpression, + isSdsPipeline, + isSdsPlaceholder, + isSdsPrefixOperation, + isSdsReference, + isSdsSegment, + isSdsTemplateString, + isSdsTemplateStringEnd, + isSdsTemplateStringInner, + isSdsTemplateStringPart, + isSdsTemplateStringStart, + isSdsUnionType, + isSdsWildcard, + isSdsYield, + SdsAnnotatedObject, + SdsArgument, + SdsArgumentList, + SdsAssignee, + SdsAssignment, + SdsBlock, + SdsCall, + SdsCallable, + SdsExpression, + SdsExpressionLambda, + SdsExpressionStatement, + SdsList, + SdsMap, + SdsModule, + SdsNamedType, + SdsParameter, + SdsParameterList, + SdsPipeline, + SdsPlaceholder, + SdsPrefixOperation, + SdsReference, + SdsResultList, + SdsSegment, + SdsStatement, + SdsType, + SdsYield, +} from '../language/generated/ast.js'; import { extractAstNode, extractDestinationAndName } from './cli-util.js'; import chalk from 'chalk'; import { createSafeDsServices } from '../language/safe-ds-module.js'; import { NodeFileSystem } from 'langium/node'; +import { toConstantExpressionOrUndefined } from '../language/partialEvaluation/toConstantExpressionOrUndefined.js'; +import { + SdsConstantBoolean, + SdsConstantEnumVariant, + SdsConstantFloat, + SdsConstantInt, + SdsConstantNull, + SdsConstantString, +} from '../language/partialEvaluation/model.js'; +import { + abstractResultsOrEmpty, + annotationCallsOrEmpty, + assigneesOrEmpty, + parametersOrEmpty, + resultsOrEmpty, + statementsOrEmpty, +} from '../language/helpers/nodeProperties.js'; /* c8 ignore start */ export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { @@ -17,25 +102,325 @@ export const generateAction = async (fileName: string, opts: GenerateOptions): P }; /* c8 ignore stop */ +const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; +const PYTHON_INDENT = ' '; + export type GenerateOptions = { destination?: string; }; +const getPythonName = function (annotatedObject: SdsAnnotatedObject) { + if (annotatedObject.annotationCalls === undefined) { + return undefined; + } + // TODO python name + for (const annotation of annotationCallsOrEmpty(annotatedObject)) { + if (annotation.annotation.ref !== undefined && annotation.annotation.ref.name === 'PythonName') { + return JSON.stringify(annotation.annotation.ref); + } + } + return undefined; +}; + +const generateAssignee = function (assignee: SdsAssignee): string { + switch (true) { + case isSdsBlockLambdaResult(assignee): + return ''; + case isSdsPlaceholder(assignee): + return (assignee as SdsPlaceholder).name; + case isSdsWildcard(assignee): + return '_'; + case isSdsYield(assignee): + // TODO handle (assignee as SdsYield). + return ''; + } +}; + +const generateAssignment = function (assignment: SdsAssignment): string { + const requiredAssignees = isSdsCallable(assignment.expression) + ? abstractResultsOrEmpty(assignment.expression as SdsCallable).length + : 1; + const actualAssignees = assigneesOrEmpty(assignment).map(generateAssignee); + return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); +}; + +const generateStatement = function (statement: SdsStatement): string { + switch (true) { + case isSdsAssignment(statement): + return generateAssignment(statement as SdsAssignment); + case isSdsExpressionStatement(statement): + return generateExpression((statement as SdsExpressionStatement).expression); + default: + throw new Error(`Unknown SdsStatement: ${statement}`); + } +}; + +const generateBlock = function (block: SdsBlock): string { + let statements = statementsOrEmpty(block); + if (statements.length === 0) { + return 'pass'; + } + return expandToString`${statements.map(generateStatement).join('\n')}`; +}; + +const generateType = function (type: SdsType | undefined): string | null { + if (type === undefined) { + // TODO do something + return ''; + } + switch (true) { + case isSdsCallableType(type): + // TODO do something + return ''; + case isSdsLiteralType(type): + // TODO do something + return ''; + case isSdsMemberType(type): + // TODO do something + return ''; + case isSdsNamedType(type): + const namedType = type as SdsNamedType; + if (namedType.typeArgumentList === undefined) { + return null; + } + // TODO do something + return ''; + case isSdsUnionType(type): + // TODO do something + return ''; + default: + throw new Error(`Unknown SdsType: ${type}`); + } +}; + +const generateArgument = function (argument: SdsArgument) { + return expandToString`${ + argument.parameter !== undefined && argument.parameter.ref !== undefined + ? generateParameter(argument.parameter.ref) + '=' + : '' + }${generateExpression(argument.value)}`; +}; + +const generateExpression = function (expression: SdsExpression): string { + if (isSdsTemplateString(expression)) { + return expandToString`f'${expression.expressions.map(generateExpression).join('')}'`; + } + + if (isSdsTemplateStringPart(expression)) { + switch (true) { + case isSdsTemplateStringStart(expression): + return `${expression.value.replace('\n', '\\n')}{ `; + case isSdsTemplateStringInner(expression): + return ` }${expression.value.replace('\n', '\\n')}{ `; + case isSdsTemplateStringEnd(expression): + return ` }${expression.value.replace('\n', '\\n')}`; + } + } + + const potentialConstantExpression = toConstantExpressionOrUndefined(expression); + if (potentialConstantExpression !== null) { + switch (true) { + case potentialConstantExpression instanceof SdsConstantBoolean: + return expandToString`${(potentialConstantExpression as SdsConstantBoolean).value ? 'True' : 'False'}`; + case potentialConstantExpression instanceof SdsConstantFloat: + return expandToString`${(potentialConstantExpression as SdsConstantFloat).value}`; + case potentialConstantExpression instanceof SdsConstantInt: + return expandToString`${(potentialConstantExpression as SdsConstantInt).value}`; + case potentialConstantExpression instanceof SdsConstantNull: + return expandToString`None`; + case potentialConstantExpression instanceof SdsConstantString: + return expandToString`${(potentialConstantExpression as SdsConstantString).value.replace('\n', '\\n')}`; + case potentialConstantExpression instanceof SdsConstantEnumVariant: + return expandToString`${(potentialConstantExpression as SdsConstantEnumVariant).value}`; // TODO SdsConstantEnumVariant?? generate something useful + default: + throw new Error(`Unknown SdsLiteral: ${expression}`); + } + } + + if (isSdsLiteral(expression)) { + switch (true) { + // These should be handled by ConstantExpression + /*case isSdsBoolean(expression): + return expandToString`${(expression as SdsBoolean).value ? 'True' : 'False'}`; + case isSdsFloat(expression): + return expandToString`${(expression as SdsFloat).value}`; + case isSdsInt(expression): + return expandToString`${(expression as SdsInt).value}`; + case isSdsNull(expression): + return expandToString`None`; + case isSdsString(expression): + return expandToString`'${(expression as SdsString).value}'`;*/ + case isSdsMap(expression): + return expandToString`'${expression as SdsMap}'`; // TODO SdsMap?? + case isSdsList(expression): + return expandToString`'${expression as SdsList}'`; // TODO SdsList?? + default: + throw new Error(`Unknown SdsLiteral: ${expression}`); + } + } + if (isSdsBlockLambda(expression)) { + // TODO do something + return ''; + } + if (isSdsCall(expression)) { + return expandToString`${generateExpression(expression.receiver)}(${expression.argumentList.arguments + .map(generateArgument) + .join(', ')})`; + } + if (isSdsExpressionLambda(expression)) { + return expandToString`lambda ${generateParameters(expression.parameterList)}: ${generateExpression( + expression.result, + )}`; + } + if (isSdsInfixOperation(expression)) { + const leftOperand = generateExpression(expression.leftOperand); + const rightOperand = generateExpression(expression.rightOperand); + // TODO import codegen somehow + switch (expression.operator) { + case 'or': + return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_or(${leftOperand}, ${rightOperand})`; + case 'and': + return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_and(${leftOperand}, ${rightOperand})`; + case '?:': + return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_elvis(${leftOperand}, ${rightOperand})`; + case '===': + return expandToString`(${leftOperand}) is (${rightOperand})`; + case '!==': + return expandToString`(${leftOperand}) is not (${rightOperand})`; + default: + return expandToString`(${leftOperand}) ${expression.operator} (${rightOperand})`; + } + } + if (isSdsIndexedAccess(expression)) { + return expandToString`${generateExpression(expression.receiver)}[${generateExpression(expression.index)}]`; + } + if (isSdsMemberAccess(expression)) { + // TODO return memberaccess?? + return ''; + } + if (isSdsParenthesizedExpression(expression)) { + return expandToString`(${generateExpression(expression.expression)})`; + } + if (isSdsPrefixOperation(expression)) { + const operand = generateExpression(expression.operand); + switch (expression.operator) { + case 'not': + return expandToString`not (${operand})`; + case '-': + return expandToString`-(${operand})`; + default: + throw new Error('Unknown Prefix Operation Expression'); + } + } + if (isSdsReference(expression)) { + const currentDocument = getDocument(expression); + // TODO generate reference + // TODO import + const declaration = (expression as SdsReference).target.ref; + //return ''; + if (declaration === undefined) { + return ''; + } + return declaration.name; + } + // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' + return ``; +}; + +const generateParameter = function (parameter: SdsParameter | undefined, asNamedAssignment: boolean = false): string { + // TODO annotations? annotationCalls annotationCallList + // TODO isConstant? + if (parameter === undefined) { + return ''; + } + if (!asNamedAssignment) { + return expandToString`${parameter.name}`; + } + const parameterType = generateType(parameter.type); + return expandToString`${parameter.name}${parameterType !== null ? `: ${parameterType}` : ''}${ + parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue) : '' + }`; // TODO correspondingPythonName??? +}; + +const generateParameters = function (parameters: SdsParameterList | undefined): string { + if (parameters === undefined) { + return ''; + } + const result = parameters.parameters.map(generateParameter); + return result.join(', '); +}; + +const generateResults = function (result: SdsResultList | undefined): string { + if (result === undefined || result.results.length === 0) { + return ''; + } + // TODO do something + return ''; +}; + +const generateSegment = function (segment: SdsSegment): string { + // TODO annotations PythonName + return expandToString`def ${getPythonName(segment) || segment.name}(${generateParameters(segment.parameterList)})${ + segment.resultList !== undefined && segment.resultList.results.length !== 0 + ? ' -> ' + generateResults(segment.resultList) + : '' + }:\n${PYTHON_INDENT}${generateBlock(segment.body)}`; +}; + +const generatePipeline = function (pipeline: SdsPipeline): string { + // TODO annotations PythonName + return expandToString`def ${getPythonName(pipeline) || pipeline.name}():\n${PYTHON_INDENT}${generateBlock( + pipeline.body, + )}`; +}; + +const generateModule = function (module: SdsModule): string { + const segments = streamAllContents(module).filter(isSdsSegment).map(generateSegment).toArray(); + const pipelines = streamAllContents(module).filter(isSdsPipeline).map(generatePipeline).toArray(); + const output: string[] = []; + if (segments.length > 0) { + output.push( + expandToString`# Steps ------------------------------------------------------------------------\n\n${segments.join( + '\n\n', + )}`, + ); + } + if (pipelines.length > 0) { + output.push( + expandToString`# Pipelines --------------------------------------------------------------------\n\n${pipelines.join( + '\n\n', + )}`, + ); + } + return expandToStringWithNL`${output.join('\n')}`; +}; + export const generatePython = function ( module: SdsModule, filePath: string, destination: string | undefined, ): string[] { const data = extractDestinationAndName(filePath, destination); - const generatedFilePath = `${path.join(data.destination, data.name)}.py`; + const packagePath = module.name.split('.'); - const fileNode = new CompositeGeneratorNode(); + const parentDirectoryPath = path.join(data.destination, ...packagePath); + + const generatedFiles = new Map(); + generatedFiles.set(`${path.join(parentDirectoryPath, `gen_${data.name}`)}.py`, generateModule(module)); + for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { + const entryPointFilename = `${path.join(parentDirectoryPath, `gen_${data.name}_${pipeline.name}`)}.py`; // TODO python name? + const entryPointContent = expandToStringWithNL`from gen_${data.name} import ${pipeline.name}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${pipeline.name}()`; + generatedFiles.set(entryPointFilename, entryPointContent); + } + // const fileNode = new CompositeGeneratorNode(); // fileNode.append('"use strict";', NL, NL); // model.greetings.forEach(greeting => fileNode.append(`console.log('Hello, ${greeting.person.ref?.name}!');`, NL)); - if (!fs.existsSync(data.destination)) { - fs.mkdirSync(data.destination, { recursive: true }); + if (!fs.existsSync(parentDirectoryPath)) { + fs.mkdirSync(parentDirectoryPath, { recursive: true }); + } + for (const [generatedFilePath, generatedFileContent] of generatedFiles.entries()) { + fs.writeFileSync(generatedFilePath, generatedFileContent); } - fs.writeFileSync(generatedFilePath, toString(fileNode)); - return [generatedFilePath]; + return [...generatedFiles.keys()]; }; From 2c585cbeb09e61ee6e44918182ffed0e6af83347 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Wed, 11 Oct 2023 15:48:11 +0200 Subject: [PATCH 02/56] feat: More Expression generation, blocklambda management, import management fix: for constant expressions, treat ints actually as ints fix: read stubs first, then read tests, to avoid wrongly broken references --- src/cli/generator.ts | 327 ++++++++++++++---- .../toConstantExpressionOrUndefined.ts | 4 +- tests/language/generation/creator.ts | 8 +- 3 files changed, 273 insertions(+), 66 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index b850056db..ce1a2956f 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -18,6 +18,7 @@ import { isSdsCall, isSdsCallable, isSdsCallableType, + isSdsEnumVariant, isSdsExpressionLambda, isSdsExpressionStatement, isSdsIndexedAccess, @@ -34,6 +35,7 @@ import { isSdsPlaceholder, isSdsPrefixOperation, isSdsReference, + isSdsResult, isSdsSegment, isSdsTemplateString, isSdsTemplateStringEnd, @@ -49,13 +51,17 @@ import { SdsAssignee, SdsAssignment, SdsBlock, + SdsBlockLambda, + SdsBlockLambdaResult, SdsCall, SdsCallable, + SdsEnumVariant, SdsExpression, SdsExpressionLambda, SdsExpressionStatement, SdsList, SdsMap, + SdsMemberAccess, SdsModule, SdsNamedType, SdsParameter, @@ -87,10 +93,12 @@ import { abstractResultsOrEmpty, annotationCallsOrEmpty, assigneesOrEmpty, + blockLambdaResultsOrEmpty, parametersOrEmpty, resultsOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; +import { group } from 'radash'; /* c8 ignore start */ export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { @@ -105,6 +113,62 @@ export const generateAction = async (fileName: string, opts: GenerateOptions): P const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; const PYTHON_INDENT = ' '; +class GenerationInfoFrame { + blockLambdaManager: BlockLambdaIdManager; + importSet: Set; + + constructor(importSet: Set = new Set()) { + this.blockLambdaManager = new BlockLambdaIdManager(); + this.importSet = importSet; + } + + addImport(importData: ImportData) { + this.importSet.add(importData); + } + + getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { + return this.blockLambdaManager.getUniqueName(lambda); + } +} + +class BlockLambdaIdManager { + currentId: number; + registeredLambdas: Map; + + constructor(sequenceIdStart: number = 0) { + this.currentId = sequenceIdStart; + this.registeredLambdas = new Map(); + } + + getId(lambda: SdsBlockLambda): number { + if (!this.registeredLambdas.has(lambda)) { + this.registeredLambdas.set(lambda, this.currentId); + this.currentId++; + } + return this.registeredLambdas.get(lambda); + } + + getUniqueName(lambda: SdsBlockLambda): string { + return `__block_lambda_${this.getId(lambda)}`; + } +} + +class ImportData { + importPath: string; + declarationName: string | undefined; + alias: string | undefined; + + constructor( + importPath: string, + declarationName: string | undefined = undefined, + alias: string | undefined = undefined, + ) { + this.importPath = importPath; + this.declarationName = declarationName; + this.alias = alias; + } +} + export type GenerateOptions = { destination?: string; }; @@ -125,7 +189,7 @@ const getPythonName = function (annotatedObject: SdsAnnotatedObject) { const generateAssignee = function (assignee: SdsAssignee): string { switch (true) { case isSdsBlockLambdaResult(assignee): - return ''; + return (assignee as SdsBlockLambdaResult).name; case isSdsPlaceholder(assignee): return (assignee as SdsPlaceholder).name; case isSdsWildcard(assignee): @@ -133,40 +197,80 @@ const generateAssignee = function (assignee: SdsAssignee): string { case isSdsYield(assignee): // TODO handle (assignee as SdsYield). return ''; + default: + // TODO more assignees + throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); } }; -const generateAssignment = function (assignment: SdsAssignment): string { +const generateAssignment = function (assignment: SdsAssignment, frame: GenerationInfoFrame): string { const requiredAssignees = isSdsCallable(assignment.expression) ? abstractResultsOrEmpty(assignment.expression as SdsCallable).length : 1; const actualAssignees = assigneesOrEmpty(assignment).map(generateAssignee); - return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); + if (requiredAssignees === actualAssignees.length) { + return actualAssignees.join(', '); + } + if (assignment.expression !== undefined) { + console.info('Required: ' + requiredAssignees); + console.info('Actual: ' + actualAssignees.length); + console.info('Assignees: ' + actualAssignees.join(', ')); + console.info( + 'Expected: ' + + abstractResultsOrEmpty(assignment.expression as SdsCallable) + .map((a) => a.$type + ' - ' + a.name) + .join(', '), + ); + console.info('Expr: ' + assignment.expression.$type); + return `${actualAssignees + .concat(Array(requiredAssignees - actualAssignees.length).fill('_')) + .join(', ')} = ${generateExpression(assignment.expression, frame)}`; + } else { + return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); + } }; -const generateStatement = function (statement: SdsStatement): string { +const generateStatement = function (statement: SdsStatement, frame: GenerationInfoFrame): string { switch (true) { case isSdsAssignment(statement): - return generateAssignment(statement as SdsAssignment); + return generateAssignment(statement as SdsAssignment, frame); case isSdsExpressionStatement(statement): - return generateExpression((statement as SdsExpressionStatement).expression); + const expressionStatement = statement as SdsExpressionStatement; + const blockLambdaCode: string[] = []; + for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { + blockLambdaCode.push(generateBlockLambda(lambda, frame)); + } + blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); + return expandToString`${blockLambdaCode.join('\n')}`; default: throw new Error(`Unknown SdsStatement: ${statement}`); } }; -const generateBlock = function (block: SdsBlock): string { +const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: GenerationInfoFrame): string { + const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); + let lambdaBlock = generateBlock(blockLambda.body, frame); + if (lambdaResult.length !== 0) { + lambdaBlock += `\nreturn ${lambdaResult.map((result) => result.name).join(', ')}`; + } + return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( + blockLambda.parameterList, + frame, + )}):\n${PYTHON_INDENT}${lambdaBlock}`; +}; + +const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): string { + // TODO filter withEffect let statements = statementsOrEmpty(block); if (statements.length === 0) { return 'pass'; } - return expandToString`${statements.map(generateStatement).join('\n')}`; + return expandToString`${statements.map((stmt) => generateStatement(stmt, frame)).join('\n')}`; }; const generateType = function (type: SdsType | undefined): string | null { if (type === undefined) { - // TODO do something - return ''; + return null; } switch (true) { case isSdsCallableType(type): @@ -193,27 +297,28 @@ const generateType = function (type: SdsType | undefined): string | null { } }; -const generateArgument = function (argument: SdsArgument) { +const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { return expandToString`${ argument.parameter !== undefined && argument.parameter.ref !== undefined - ? generateParameter(argument.parameter.ref) + '=' + ? generateParameter(argument.parameter.ref, frame, false) + '=' : '' - }${generateExpression(argument.value)}`; + }${generateExpression(argument.value, frame)}`; }; -const generateExpression = function (expression: SdsExpression): string { +const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { if (isSdsTemplateString(expression)) { - return expandToString`f'${expression.expressions.map(generateExpression).join('')}'`; + return `f'${expression.expressions.map((expr) => generateExpression(expr, frame)).join('')}'`; } if (isSdsTemplateStringPart(expression)) { + // TODO: really replace both line endings? switch (true) { case isSdsTemplateStringStart(expression): - return `${expression.value.replace('\n', '\\n')}{ `; + return `${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}{ `; case isSdsTemplateStringInner(expression): - return ` }${expression.value.replace('\n', '\\n')}{ `; + return ` }${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}{ `; case isSdsTemplateStringEnd(expression): - return ` }${expression.value.replace('\n', '\\n')}`; + return ` }${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}`; } } @@ -221,17 +326,20 @@ const generateExpression = function (expression: SdsExpression): string { if (potentialConstantExpression !== null) { switch (true) { case potentialConstantExpression instanceof SdsConstantBoolean: - return expandToString`${(potentialConstantExpression as SdsConstantBoolean).value ? 'True' : 'False'}`; - case potentialConstantExpression instanceof SdsConstantFloat: - return expandToString`${(potentialConstantExpression as SdsConstantFloat).value}`; + return (potentialConstantExpression as SdsConstantBoolean).value ? 'True' : 'False'; case potentialConstantExpression instanceof SdsConstantInt: - return expandToString`${(potentialConstantExpression as SdsConstantInt).value}`; + return String((potentialConstantExpression as SdsConstantInt).value); + case potentialConstantExpression instanceof SdsConstantFloat: + const floatValue = (potentialConstantExpression as SdsConstantFloat).value; + return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); case potentialConstantExpression instanceof SdsConstantNull: - return expandToString`None`; + return 'None'; case potentialConstantExpression instanceof SdsConstantString: - return expandToString`${(potentialConstantExpression as SdsConstantString).value.replace('\n', '\\n')}`; + return `'${(potentialConstantExpression as SdsConstantString).value + .replaceAll('\r\n', '\\n') + .replaceAll('\n', '\\n')}'`; case potentialConstantExpression instanceof SdsConstantEnumVariant: - return expandToString`${(potentialConstantExpression as SdsConstantEnumVariant).value}`; // TODO SdsConstantEnumVariant?? generate something useful + return String((potentialConstantExpression as SdsConstantEnumVariant).value); // TODO SdsConstantEnumVariant?? generate something useful default: throw new Error(`Unknown SdsLiteral: ${expression}`); } @@ -251,58 +359,91 @@ const generateExpression = function (expression: SdsExpression): string { case isSdsString(expression): return expandToString`'${(expression as SdsString).value}'`;*/ case isSdsMap(expression): - return expandToString`'${expression as SdsMap}'`; // TODO SdsMap?? + return `'${expression as SdsMap}'`; // TODO SdsMap?? case isSdsList(expression): - return expandToString`'${expression as SdsList}'`; // TODO SdsList?? + return `'${expression as SdsList}'`; // TODO SdsList?? default: throw new Error(`Unknown SdsLiteral: ${expression}`); } } if (isSdsBlockLambda(expression)) { - // TODO do something - return ''; + const blockLambda = expression as SdsBlockLambda; + return frame.getUniqueLambdaBlockName(blockLambda); } if (isSdsCall(expression)) { - return expandToString`${generateExpression(expression.receiver)}(${expression.argumentList.arguments - .map(generateArgument) + return expandToString`${generateExpression(expression.receiver, frame)}(${expression.argumentList.arguments + .map((arg) => generateArgument(arg, frame)) .join(', ')})`; } if (isSdsExpressionLambda(expression)) { - return expandToString`lambda ${generateParameters(expression.parameterList)}: ${generateExpression( + return `lambda ${generateParameters(expression.parameterList, frame)}: ${generateExpression( expression.result, + frame, )}`; } if (isSdsInfixOperation(expression)) { - const leftOperand = generateExpression(expression.leftOperand); - const rightOperand = generateExpression(expression.rightOperand); + const leftOperand = generateExpression(expression.leftOperand, frame); + const rightOperand = generateExpression(expression.rightOperand, frame); // TODO import codegen somehow switch (expression.operator) { case 'or': - return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_or(${leftOperand}, ${rightOperand})`; + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.eager_or(${leftOperand}, ${rightOperand})`; case 'and': - return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_and(${leftOperand}, ${rightOperand})`; + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.eager_and(${leftOperand}, ${rightOperand})`; case '?:': - return expandToString`${RUNNER_CODEGEN_PACKAGE}.eager_elvis(${leftOperand}, ${rightOperand})`; + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.eager_elvis(${leftOperand}, ${rightOperand})`; case '===': - return expandToString`(${leftOperand}) is (${rightOperand})`; + return `(${leftOperand}) is (${rightOperand})`; case '!==': - return expandToString`(${leftOperand}) is not (${rightOperand})`; + return `(${leftOperand}) is not (${rightOperand})`; default: - return expandToString`(${leftOperand}) ${expression.operator} (${rightOperand})`; + return `(${leftOperand}) ${expression.operator} (${rightOperand})`; } } if (isSdsIndexedAccess(expression)) { - return expandToString`${generateExpression(expression.receiver)}[${generateExpression(expression.index)}]`; + return expandToString`${generateExpression(expression.receiver, frame)}[${generateExpression( + expression.index, + frame, + )}]`; } if (isSdsMemberAccess(expression)) { - // TODO return memberaccess?? + let memberAccess = expression as SdsMemberAccess; + const member = memberAccess.member.target.ref; + const receiver = generateExpression(memberAccess.receiver, frame); + switch (true) { + case isSdsBlockLambdaResult(member): + let res = member as SdsBlockLambdaResult; + return ''; + case isSdsEnumVariant(member): + const enumMember = generateExpression(memberAccess.member, frame); + const suffix = isSdsCall(expression.$container) ? '' : '()'; + if (expression.isNullSafe) { + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${enumMember}')${suffix}`; + } else { + return `${receiver}.${enumMember}${suffix}`; + } + case isSdsResult(member): + return ''; + default: + const memberExpression = generateExpression(memberAccess.member, frame); + if (expression.isNullSafe) { + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; + } else { + return `${receiver}.${memberExpression}`; + } + } return ''; } if (isSdsParenthesizedExpression(expression)) { - return expandToString`(${generateExpression(expression.expression)})`; + return expandToString`${generateExpression(expression.expression, frame)}`; } if (isSdsPrefixOperation(expression)) { - const operand = generateExpression(expression.operand); + const operand = generateExpression(expression.operand, frame); switch (expression.operator) { case 'not': return expandToString`not (${operand})`; @@ -321,32 +462,34 @@ const generateExpression = function (expression: SdsExpression): string { if (declaration === undefined) { return ''; } - return declaration.name; + return declaration.name; // TODO python name, may be fixed with reference / import } // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' return ``; }; -const generateParameter = function (parameter: SdsParameter | undefined, asNamedAssignment: boolean = false): string { +const generateParameter = function ( + parameter: SdsParameter | undefined, + frame: GenerationInfoFrame, + defaultValue: boolean = true, +): string { // TODO annotations? annotationCalls annotationCallList // TODO isConstant? if (parameter === undefined) { return ''; } - if (!asNamedAssignment) { - return expandToString`${parameter.name}`; - } - const parameterType = generateType(parameter.type); - return expandToString`${parameter.name}${parameterType !== null ? `: ${parameterType}` : ''}${ - parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue) : '' + return expandToString`${parameter.name}${ + defaultValue && parameter.defaultValue !== undefined + ? '=' + generateExpression(parameter.defaultValue, frame) + : '' }`; // TODO correspondingPythonName??? }; -const generateParameters = function (parameters: SdsParameterList | undefined): string { +const generateParameters = function (parameters: SdsParameterList | undefined, frame: GenerationInfoFrame): string { if (parameters === undefined) { return ''; } - const result = parameters.parameters.map(generateParameter); + const result = parameters.parameters.map((param) => generateParameter(param, frame)); return result.join(', '); }; @@ -358,36 +501,96 @@ const generateResults = function (result: SdsResultList | undefined): string { return ''; }; -const generateSegment = function (segment: SdsSegment): string { +const generateSegment = function (segment: SdsSegment, importSet: Set): string { // TODO annotations PythonName - return expandToString`def ${getPythonName(segment) || segment.name}(${generateParameters(segment.parameterList)})${ + const infoFrame = new GenerationInfoFrame(importSet); + return expandToString`def ${getPythonName(segment) || segment.name}(${generateParameters( + segment.parameterList, + infoFrame, + )})${ segment.resultList !== undefined && segment.resultList.results.length !== 0 ? ' -> ' + generateResults(segment.resultList) : '' - }:\n${PYTHON_INDENT}${generateBlock(segment.body)}`; + }:\n${PYTHON_INDENT}${generateBlock(segment.body, infoFrame)}`; }; -const generatePipeline = function (pipeline: SdsPipeline): string { +const generatePipeline = function (pipeline: SdsPipeline, importSet: Set): string { // TODO annotations PythonName + const infoFrame = new GenerationInfoFrame(importSet); return expandToString`def ${getPythonName(pipeline) || pipeline.name}():\n${PYTHON_INDENT}${generateBlock( pipeline.body, + infoFrame, )}`; }; +const generateImport = function (importStmt: ImportData): string { + if (importStmt.declarationName === undefined && importStmt.alias === undefined) { + return `import ${importStmt.importPath}`; + } else if (importStmt.declarationName === undefined && importStmt.alias !== undefined) { + return `import ${importStmt.importPath} as ${importStmt.alias}`; + } else if (importStmt.declarationName !== undefined && importStmt.alias === undefined) { + return `from ${importStmt.importPath} import ${importStmt.declarationName}`; + } else { + return `from ${importStmt.importPath} import ${importStmt.declarationName} as ${importStmt.alias}`; + } +}; + +const generateImports = function (importSet: Set): string[] { + const qualifiedImports = Array.from(importSet) + .filter((importStmt) => importStmt.declarationName === undefined) + .sort((a, b) => a.importPath.localeCompare(b.importPath)) + .map(generateImport); + const groupedImports = Object.entries( + group( + Array.from(importSet).filter((importStmt) => importStmt.declarationName !== undefined), + (importStmt) => importStmt.importPath, + ), + ).sort(([key1, _value1], [key2, _value2]) => key1.localeCompare(key2)); + const declaredImports: string[] = []; + for (const [key, value] of groupedImports) { + const importedDecls = + value + ?.filter((importData) => importData !== undefined) + .sort((a, b) => (a.declarationName).localeCompare(b.declarationName)) + .map((localValue) => + localValue.alias !== undefined + ? `${localValue.declarationName} as ${localValue.alias}` + : localValue.declarationName, + ) || []; + declaredImports.push(`from ${key} import ${[...new Set(importedDecls)].join(', ')}`); + } + return [...new Set(qualifiedImports), ...new Set(declaredImports)]; +}; + const generateModule = function (module: SdsModule): string { - const segments = streamAllContents(module).filter(isSdsSegment).map(generateSegment).toArray(); - const pipelines = streamAllContents(module).filter(isSdsPipeline).map(generatePipeline).toArray(); + const importSet = new Set(); + const segments = streamAllContents(module) + .filter(isSdsSegment) + .map((segment) => generateSegment(segment, importSet)) + .toArray(); + const pipelines = streamAllContents(module) + .filter(isSdsPipeline) + .map((pipeline) => generatePipeline(pipeline, importSet)) + .toArray(); + const imports = generateImports(importSet); const output: string[] = []; + if (imports.length > 0) { + output.push( + expandToStringWithNL`# Imports ----------------------------------------------------------------------\n\n${imports.join( + '\n', + )}`, + ); + } if (segments.length > 0) { output.push( - expandToString`# Steps ------------------------------------------------------------------------\n\n${segments.join( + expandToStringWithNL`# Steps ------------------------------------------------------------------------\n\n${segments.join( '\n\n', )}`, ); } if (pipelines.length > 0) { output.push( - expandToString`# Pipelines --------------------------------------------------------------------\n\n${pipelines.join( + expandToStringWithNL`# Pipelines --------------------------------------------------------------------\n\n${pipelines.join( '\n\n', )}`, ); diff --git a/src/language/partialEvaluation/toConstantExpressionOrUndefined.ts b/src/language/partialEvaluation/toConstantExpressionOrUndefined.ts index fbd55dc2f..9e0cdd33a 100644 --- a/src/language/partialEvaluation/toConstantExpressionOrUndefined.ts +++ b/src/language/partialEvaluation/toConstantExpressionOrUndefined.ts @@ -2,7 +2,7 @@ import { ParameterSubstitutions, SdsConstantBoolean, SdsConstantExpression, - SdsConstantFloat, + SdsConstantFloat, SdsConstantInt, SdsConstantNull, SdsConstantString, SdsIntermediateBlockLambda, @@ -74,7 +74,7 @@ const simplify = (node: AstNode, substitutions: ParameterSubstitutions): SdsSimp } else if (isSdsFloat(node)) { return new SdsConstantFloat(node.value); } else if (isSdsInt(node)) { - return new SdsConstantFloat(node.value); + return new SdsConstantInt(BigInt(node.value)); } else if (isSdsNull(node)) { return new SdsConstantNull(); } else if (isSdsString(node)) { diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 88a40584a..eada08ee8 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -30,8 +30,12 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const actualOutputRoot = URI.file(path.join(parentDirectory.fsPath, 'generated')); const expectedOutputFiles = readExpectedOutputFiles(expectedOutputRoot, actualOutputRoot); let runUntil: Location | undefined; - - for (const uri of inputUris) { + // First read all stubs, then read all the other files; This should avoid broken references + const sortedInputUris = inputUris + .filter((uri) => uri.fsPath.endsWith('sdsstub')) + .sort() + .concat(...inputUris.filter((uri) => !uri.fsPath.endsWith('sdsstub')).sort()); + for (const uri of sortedInputUris) { const code = fs.readFileSync(uri.fsPath).toString(); // File must not contain any errors From c5887db8f42f52398e875e1b403a5aed364d0bcd Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Wed, 11 Oct 2023 19:47:23 +0200 Subject: [PATCH 03/56] feat: Use python names for generation, when available feat: More correctly generate lambda block content --- src/cli/generator.ts | 77 +++++++++++++++++++------- src/language/helpers/nodeProperties.ts | 15 +++++ 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index ce1a2956f..24e4770c1 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -37,6 +37,7 @@ import { isSdsReference, isSdsResult, isSdsSegment, + isSdsString, isSdsTemplateString, isSdsTemplateStringEnd, isSdsTemplateStringInner, @@ -55,6 +56,7 @@ import { SdsBlockLambdaResult, SdsCall, SdsCallable, + SdsDeclaration, SdsEnumVariant, SdsExpression, SdsExpressionLambda, @@ -92,8 +94,10 @@ import { import { abstractResultsOrEmpty, annotationCallsOrEmpty, + argumentsOrEmpty, assigneesOrEmpty, blockLambdaResultsOrEmpty, + callResultsOrEmpty, parametersOrEmpty, resultsOrEmpty, statementsOrEmpty, @@ -173,14 +177,40 @@ export type GenerateOptions = { destination?: string; }; +const getPythonNameOrDefault = function (object: SdsPipeline | SdsSegment | SdsParameter | SdsDeclaration) { + return getPythonName(object) || object.name; +}; const getPythonName = function (annotatedObject: SdsAnnotatedObject) { - if (annotatedObject.annotationCalls === undefined) { + let annotationCalls = annotationCallsOrEmpty(annotatedObject); + if (annotationCalls.length === 0) { + return undefined; + } + for (const annotationCall of annotationCalls) { + if (annotationCall.annotation.ref !== undefined && annotationCall.annotation.ref.name === 'PythonName') { + const argumentsArray = argumentsOrEmpty(annotationCall); + if (argumentsArray.length !== 0) { + if (isSdsString(argumentsArray[0].value)) { + return argumentsArray[0].value.value; + } + } + } + } + return undefined; +}; + +const getPythonModuleName = function (annotatedObject: SdsAnnotatedObject): string | undefined { + let annotationCalls = annotationCallsOrEmpty(annotatedObject); + if (annotationCalls.length === 0) { return undefined; } - // TODO python name - for (const annotation of annotationCallsOrEmpty(annotatedObject)) { - if (annotation.annotation.ref !== undefined && annotation.annotation.ref.name === 'PythonName') { - return JSON.stringify(annotation.annotation.ref); + for (const annotationCall of annotationCalls) { + if (annotationCall.annotation.ref !== undefined && annotationCall.annotation.ref.name === 'PythonModule') { + const argumentsArray = argumentsOrEmpty(annotationCall); + if (argumentsArray.length !== 0) { + if (isSdsString(argumentsArray[0].value)) { + return argumentsArray[0].value.value; + } + } } } return undefined; @@ -204,11 +234,15 @@ const generateAssignee = function (assignee: SdsAssignee): string { }; const generateAssignment = function (assignment: SdsAssignment, frame: GenerationInfoFrame): string { - const requiredAssignees = isSdsCallable(assignment.expression) - ? abstractResultsOrEmpty(assignment.expression as SdsCallable).length - : 1; + const requiredAssignees = + isSdsCallable(assignment.expression) || isSdsCall(assignment.expression) + ? callResultsOrEmpty(assignment.expression as SdsCallable).length + : 1; const actualAssignees = assigneesOrEmpty(assignment).map(generateAssignee); if (requiredAssignees === actualAssignees.length) { + if (assignment.expression !== undefined) { + return `${actualAssignees.join(', ')} = ${generateExpression(assignment.expression, frame)}`; + } return actualAssignees.join(', '); } if (assignment.expression !== undefined) { @@ -384,7 +418,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (isSdsInfixOperation(expression)) { const leftOperand = generateExpression(expression.leftOperand, frame); const rightOperand = generateExpression(expression.rightOperand, frame); - // TODO import codegen somehow switch (expression.operator) { case 'or': frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); @@ -462,7 +495,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (declaration === undefined) { return ''; } - return declaration.name; // TODO python name, may be fixed with reference / import + return getPythonNameOrDefault(declaration); // TODO python name, may be fixed with reference / import } // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' return ``; @@ -473,16 +506,15 @@ const generateParameter = function ( frame: GenerationInfoFrame, defaultValue: boolean = true, ): string { - // TODO annotations? annotationCalls annotationCallList // TODO isConstant? if (parameter === undefined) { return ''; } - return expandToString`${parameter.name}${ + return expandToString`${getPythonNameOrDefault(parameter)}${ defaultValue && parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue, frame) : '' - }`; // TODO correspondingPythonName??? + }`; }; const generateParameters = function (parameters: SdsParameterList | undefined, frame: GenerationInfoFrame): string { @@ -502,9 +534,8 @@ const generateResults = function (result: SdsResultList | undefined): string { }; const generateSegment = function (segment: SdsSegment, importSet: Set): string { - // TODO annotations PythonName const infoFrame = new GenerationInfoFrame(importSet); - return expandToString`def ${getPythonName(segment) || segment.name}(${generateParameters( + return expandToString`def ${getPythonNameOrDefault(segment)}(${generateParameters( segment.parameterList, infoFrame, )})${ @@ -515,9 +546,8 @@ const generateSegment = function (segment: SdsSegment, importSet: Set): string { - // TODO annotations PythonName const infoFrame = new GenerationInfoFrame(importSet); - return expandToString`def ${getPythonName(pipeline) || pipeline.name}():\n${PYTHON_INDENT}${generateBlock( + return expandToString`def ${getPythonNameOrDefault(pipeline)}():\n${PYTHON_INDENT}${generateBlock( pipeline.body, infoFrame, )}`; @@ -604,15 +634,20 @@ export const generatePython = function ( destination: string | undefined, ): string[] { const data = extractDestinationAndName(filePath, destination); - const packagePath = module.name.split('.'); - + const pythonModuleName = getPythonModuleName(module); + const packagePath = pythonModuleName === undefined ? module.name.split('.') : [pythonModuleName]; const parentDirectoryPath = path.join(data.destination, ...packagePath); const generatedFiles = new Map(); generatedFiles.set(`${path.join(parentDirectoryPath, `gen_${data.name}`)}.py`, generateModule(module)); for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { - const entryPointFilename = `${path.join(parentDirectoryPath, `gen_${data.name}_${pipeline.name}`)}.py`; // TODO python name? - const entryPointContent = expandToStringWithNL`from gen_${data.name} import ${pipeline.name}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${pipeline.name}()`; + const entryPointFilename = `${path.join( + parentDirectoryPath, + `gen_${data.name}_${getPythonNameOrDefault(pipeline)}`, + )}.py`; + const entryPointContent = expandToStringWithNL`from gen_${data.name} import ${getPythonNameOrDefault( + pipeline, + )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(pipeline)}()`; generatedFiles.set(entryPointFilename, entryPointContent); } // const fileNode = new CompositeGeneratorNode(); diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index fe08f4268..d114ad65c 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -3,6 +3,8 @@ import { isSdsAttribute, isSdsBlockLambda, isSdsBlockLambdaResult, + isSdsCall, + isSdsCallable, isSdsCallableType, isSdsClass, isSdsDeclaration, @@ -12,6 +14,7 @@ import { isSdsModule, isSdsModuleMember, isSdsPlaceholder, + isSdsReference, isSdsSegment, isSdsTypeParameterList, SdsAbstractCall, @@ -24,6 +27,7 @@ import { SdsBlock, SdsBlockLambda, SdsBlockLambdaResult, + SdsCall, SdsCallable, SdsClass, SdsClassMember, @@ -86,6 +90,17 @@ export const isStatic = (node: SdsClassMember): boolean => { // ------------------------------------------------------------------------------------------------- // Accessors for list elements // ------------------------------------------------------------------------------------------------- +export const callResultsOrEmpty = (node: SdsCall | SdsCallable | undefined): SdsAbstractResult[] => { + if (node && isSdsCall(node)) { + if (isSdsReference(node.receiver)) { + if (isSdsCallable(node.receiver.target.ref)) { + return abstractResultsOrEmpty(node.receiver.target.ref); + } + } + return []; + } + return abstractResultsOrEmpty(node); +}; export const abstractResultsOrEmpty = (node: SdsCallable | undefined): SdsAbstractResult[] => { if (!node) { From f48a1cbbe3acaae41daf7a8da2453319517f9e34 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 12 Oct 2023 11:49:01 +0200 Subject: [PATCH 04/56] fix: when loading a generation test case, load all files first before diagnosing errors feat: incomplete argument sorting --- src/cli/generator.ts | 25 +++++++++++++++++++++++-- src/language/helpers/nodeProperties.ts | 11 +++++++++++ tests/helpers/testResources.ts | 14 +++++++++++++- tests/language/generation/creator.ts | 10 ++++------ 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 24e4770c1..c935ba3cc 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -7,6 +7,7 @@ import { streamAllContents, toString, getDocument, + Reference, } from 'langium'; import path from 'path'; import { @@ -97,7 +98,9 @@ import { argumentsOrEmpty, assigneesOrEmpty, blockLambdaResultsOrEmpty, + callParametersOrEmpty, callResultsOrEmpty, + isRequiredParameter, parametersOrEmpty, resultsOrEmpty, statementsOrEmpty, @@ -333,12 +336,28 @@ const generateType = function (type: SdsType | undefined): string | null { const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { return expandToString`${ - argument.parameter !== undefined && argument.parameter.ref !== undefined + argument.parameter !== undefined && + argument.parameter.ref !== undefined && + !isRequiredParameter(argument.parameter.ref) ? generateParameter(argument.parameter.ref, frame, false) + '=' : '' }${generateExpression(argument.value, frame)}`; }; +const sortArguments = function (argumentList: SdsArgument[], call: SdsCall): SdsArgument[] { + // $containerIndex contains the index of the parameter in the receivers parameter list + const parameters = callParametersOrEmpty(call); + const sortedArgs = argumentList + .slice() + .filter((value) => value.parameter !== undefined && value.parameter.ref !== undefined) + .sort( + (a, b) => + parameters.indexOf((>b.parameter).ref) - + parameters.indexOf((>a.parameter).ref), + ); + return sortedArgs.length === 0 ? argumentList : sortedArgs; +}; + const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { if (isSdsTemplateString(expression)) { return `f'${expression.expressions.map((expr) => generateExpression(expr, frame)).join('')}'`; @@ -405,7 +424,9 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return frame.getUniqueLambdaBlockName(blockLambda); } if (isSdsCall(expression)) { - return expandToString`${generateExpression(expression.receiver, frame)}(${expression.argumentList.arguments + // TODO sort arguments to target order + const sortedArgs = sortArguments(expression.argumentList.arguments, expression); + return expandToString`${generateExpression(expression.receiver, frame)}(${sortedArgs .map((arg) => generateArgument(arg, frame)) .join(', ')})`; } diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index d114ad65c..82257e50a 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -102,6 +102,17 @@ export const callResultsOrEmpty = (node: SdsCall | SdsCallable | undefined): Sds return abstractResultsOrEmpty(node); }; +export const callParametersOrEmpty = (node: SdsCall | undefined): SdsParameter[] => { + if (node) { + if (isSdsReference(node.receiver)) { + if (isSdsCallable(node.receiver.target.ref)) { + return parametersOrEmpty(node.receiver.target.ref); + } + } + } + return []; +}; + export const abstractResultsOrEmpty = (node: SdsCallable | undefined): SdsAbstractResult[] => { if (!node) { return []; diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index 67dfcd847..4397ac6a6 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -2,7 +2,8 @@ import path from 'path'; import { globSync } from 'glob'; import { SAFE_DS_FILE_EXTENSIONS } from '../../src/language/helpers/fileExtensions.js'; import { group } from 'radash'; -import { URI } from 'langium'; +import { LangiumDocument, URI } from 'langium'; +import { SafeDsServices } from '../../src/language/safe-ds-module.js'; const TEST_RESOURCES_PATH = path.join(__dirname, '..', 'resources'); @@ -92,3 +93,14 @@ const isNotSkipped = (pathRelativeToResources: string) => { const segments = pathRelativeToResources.split(path.sep); return !segments.some((segment) => segment.startsWith('skip')); }; + +/** + * For a list of given URIs, load all files into the current workspace. + * + * @returns List of loaded documents + */ +export const loadAllDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { + const documents = uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); + await services.shared.workspace.DocumentBuilder.build(documents); + return documents; +}; diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index eada08ee8..52d9876c9 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -1,6 +1,6 @@ import { listTestPythonFiles, - listTestSafeDsFilesGroupedByParentDirectory, + listTestSafeDsFilesGroupedByParentDirectory, loadAllDocuments, uriToShortenedTestResourceName, } from '../../helpers/testResources.js'; import path from 'path'; @@ -31,11 +31,9 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const expectedOutputFiles = readExpectedOutputFiles(expectedOutputRoot, actualOutputRoot); let runUntil: Location | undefined; // First read all stubs, then read all the other files; This should avoid broken references - const sortedInputUris = inputUris - .filter((uri) => uri.fsPath.endsWith('sdsstub')) - .sort() - .concat(...inputUris.filter((uri) => !uri.fsPath.endsWith('sdsstub')).sort()); - for (const uri of sortedInputUris) { + await loadAllDocuments(services, inputUris); + + for (const uri of inputUris) { const code = fs.readFileSync(uri.fsPath).toString(); // File must not contain any errors From e48802f269288ba59db7e6d33ac5653cb6dd7bd1 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 12 Oct 2023 11:57:15 +0200 Subject: [PATCH 05/56] refactor: use IdManager instead of own BlockLambdaIdManager --- src/cli/generator.ts | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index c935ba3cc..fa816ce38 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -106,6 +106,7 @@ import { statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; import { group } from 'radash'; +import { IdManager } from '../language/helpers/idManager.js'; /* c8 ignore start */ export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { @@ -121,11 +122,11 @@ const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; const PYTHON_INDENT = ' '; class GenerationInfoFrame { - blockLambdaManager: BlockLambdaIdManager; + blockLambdaManager: IdManager; importSet: Set; constructor(importSet: Set = new Set()) { - this.blockLambdaManager = new BlockLambdaIdManager(); + this.blockLambdaManager = new IdManager(); this.importSet = importSet; } @@ -134,29 +135,7 @@ class GenerationInfoFrame { } getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { - return this.blockLambdaManager.getUniqueName(lambda); - } -} - -class BlockLambdaIdManager { - currentId: number; - registeredLambdas: Map; - - constructor(sequenceIdStart: number = 0) { - this.currentId = sequenceIdStart; - this.registeredLambdas = new Map(); - } - - getId(lambda: SdsBlockLambda): number { - if (!this.registeredLambdas.has(lambda)) { - this.registeredLambdas.set(lambda, this.currentId); - this.currentId++; - } - return this.registeredLambdas.get(lambda); - } - - getUniqueName(lambda: SdsBlockLambda): string { - return `__block_lambda_${this.getId(lambda)}`; + return `__block_lambda_${this.blockLambdaManager.assignId(lambda)}`; } } From bcfebe4df665085cc41ed7a0a80f996d65668ac7 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 12 Oct 2023 12:04:45 +0200 Subject: [PATCH 06/56] test: enable generation test that pass --- .../empty pipeline/input.sdstest | 0 .../output/tests/generator/emptyPipeline/gen_input.py | 0 .../output/tests/generator/emptyPipeline/gen_input_test.py | 0 .../empty step/input.sdstest | 0 .../output/tests/generator/emptyStep/gen_input.py | 0 .../pipeline with python name/input.sdstest | 0 .../tests/generator/pipelineWithPythonName/gen_input.py | 0 .../pipelineWithPythonName/gen_input_test_pipeline.py | 0 .../skip-parameter with python name}/input.sdstest | 0 .../tests/generator/parameterWithPythonName/gen_input.py | 0 .../two steps => declarations/skip-two steps}/input.sdstest | 0 .../output/tests/generator/twoSteps/gen_input.py | 0 .../step with python name/input.sdstest | 0 .../output/tests/generator/stepWithPythonName/gen_input.py | 0 .../two pipelines/input.sdstest | 0 .../output/tests/generator/twoPipelines/gen_input.py | 0 .../output/tests/generator/twoPipelines/gen_input_test1.py | 0 .../output/tests/generator/twoPipelines/gen_input_test2.py | 0 tests/resources/generation/dummy/output/test.py | 0 tests/resources/generation/dummy/test.sdstest | 6 ------ .../enum variant call/input.sdstest | 0 .../output/tests/generator/enumVariantCall/gen_input.py | 0 .../tests/generator/enumVariantCall}/gen_input_test.py | 0 .../infix operation/input.sdstest | 0 .../output/tests/generator/infixOperation/gen_input.py | 0 .../tests/generator/infixOperation}/gen_input_test.py | 0 .../literals/input.sdstest | 0 .../literals/output/tests/generator/literals/gen_input.py | 0 .../output/tests/generator/literals}/gen_input_test.py | 0 .../parenthesized expression/input.sdstest | 0 .../tests/generator/parenthesizedExpression/gen_input.py | 0 .../generator/parenthesizedExpression}/gen_input_test.py | 0 .../prefix operation/input.sdstest | 0 .../output/tests/generator/prefixOperation/gen_input.py | 0 .../tests/generator/prefixOperation}/gen_input_test.py | 0 .../reference/input.sdstest | 0 .../reference/output/tests/generator/reference/gen_input.py | 0 .../output/tests/generator/reference}/gen_input_test.py | 0 .../skip-block lambda}/input.sdstest | 0 .../output/tests/generator/blockLambda/gen_input.py | 0 .../output/tests/generator/blockLambda}/gen_input_test.py | 0 .../call => expressions/skip-call}/input.sdstest | 0 .../skip-call}/output/tests/generator/call/gen_input.py | 0 .../output/tests/generator/call}/gen_input_test.py | 0 .../constant => expressions/skip-constant}/input.sdstest | 0 .../output/tests/generator/constant/gen_input.py | 0 .../output/tests/generator/constant}/gen_input_test.py | 0 .../skip-expression lambda}/input.sdstest | 0 .../output/tests/generator/expressionLambda/gen_input.py | 0 .../tests/generator/expressionLambda}/gen_input_test.py | 0 .../skip-indexed access}/input.sdstest | 0 .../output/tests/generator/indexedAccess/gen_input.py | 0 .../skip-member access}/input.sdstest | 0 .../output/tests/generator/memberAccess/gen_input.py | 0 .../output/tests/generator/memberAccess}/gen_input_test.py | 0 .../template string/input.sdstest | 0 .../output/tests/generator/templateString/gen_input.py | 0 .../output/tests/generator/templateString/gen_input_test.py | 0 .../{skip-python module => python module}/input.sdstest | 0 .../output/special_module/gen_input.py | 0 .../expression statement/input.sdstest | 0 .../output/tests/generator/expressionStatement/gen_input.py | 0 .../expressionStatement}/gen_input_testPipeline.py | 0 .../assignment => statements/skip-assignment}/input.sdstest | 0 .../output/tests/generator/assignment/gen_input.py | 0 .../tests/generator/assignment}/gen_input_testPipeline.py | 0 .../skip-statement without effect}/input.sdstest | 0 .../tests/generator/statementWithoutEffect/gen_input.py | 0 .../statementWithoutEffect/gen_input_testPipeline.py | 0 69 files changed, 6 deletions(-) rename tests/resources/generation/{skip-declarations => declarations}/empty pipeline/input.sdstest (100%) rename tests/resources/generation/{skip-declarations => declarations}/empty pipeline/output/tests/generator/emptyPipeline/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/empty pipeline/output/tests/generator/emptyPipeline/gen_input_test.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/empty step/input.sdstest (100%) rename tests/resources/generation/{skip-declarations => declarations}/empty step/output/tests/generator/emptyStep/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/pipeline with python name/input.sdstest (100%) rename tests/resources/generation/{skip-declarations => declarations}/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input_test_pipeline.py (100%) rename tests/resources/generation/{skip-declarations/parameter with python name => declarations/skip-parameter with python name}/input.sdstest (100%) rename tests/resources/generation/{skip-declarations/parameter with python name => declarations/skip-parameter with python name}/output/tests/generator/parameterWithPythonName/gen_input.py (100%) rename tests/resources/generation/{skip-declarations/two steps => declarations/skip-two steps}/input.sdstest (100%) rename tests/resources/generation/{skip-declarations/two steps => declarations/skip-two steps}/output/tests/generator/twoSteps/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/step with python name/input.sdstest (100%) rename tests/resources/generation/{skip-declarations => declarations}/step with python name/output/tests/generator/stepWithPythonName/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/two pipelines/input.sdstest (100%) rename tests/resources/generation/{skip-declarations => declarations}/two pipelines/output/tests/generator/twoPipelines/gen_input.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/two pipelines/output/tests/generator/twoPipelines/gen_input_test1.py (100%) rename tests/resources/generation/{skip-declarations => declarations}/two pipelines/output/tests/generator/twoPipelines/gen_input_test2.py (100%) delete mode 100644 tests/resources/generation/dummy/output/test.py delete mode 100644 tests/resources/generation/dummy/test.sdstest rename tests/resources/generation/{skip-expressions => expressions}/enum variant call/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/enum variant call/output/tests/generator/enumVariantCall/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/block lambda/output/tests/generator/blockLambda => expressions/enum variant call/output/tests/generator/enumVariantCall}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/infix operation/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/infix operation/output/tests/generator/infixOperation/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/call/output/tests/generator/call => expressions/infix operation/output/tests/generator/infixOperation}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/literals/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/literals/output/tests/generator/literals/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/constant/output/tests/generator/constant => expressions/literals/output/tests/generator/literals}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/parenthesized expression/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/enum variant call/output/tests/generator/enumVariantCall => expressions/parenthesized expression/output/tests/generator/parenthesizedExpression}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/prefix operation/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/prefix operation/output/tests/generator/prefixOperation/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/expression lambda/output/tests/generator/expressionLambda => expressions/prefix operation/output/tests/generator/prefixOperation}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/reference/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/reference/output/tests/generator/reference/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/infix operation/output/tests/generator/infixOperation => expressions/reference/output/tests/generator/reference}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions/block lambda => expressions/skip-block lambda}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/block lambda => expressions/skip-block lambda}/output/tests/generator/blockLambda/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/literals/output/tests/generator/literals => expressions/skip-block lambda/output/tests/generator/blockLambda}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions/call => expressions/skip-call}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/call => expressions/skip-call}/output/tests/generator/call/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/member access/output/tests/generator/memberAccess => expressions/skip-call/output/tests/generator/call}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions/constant => expressions/skip-constant}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/constant => expressions/skip-constant}/output/tests/generator/constant/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/parenthesized expression/output/tests/generator/parenthesizedExpression => expressions/skip-constant/output/tests/generator/constant}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions/expression lambda => expressions/skip-expression lambda}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/expression lambda => expressions/skip-expression lambda}/output/tests/generator/expressionLambda/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/prefix operation/output/tests/generator/prefixOperation => expressions/skip-expression lambda/output/tests/generator/expressionLambda}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions/indexed access => expressions/skip-indexed access}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/indexed access => expressions/skip-indexed access}/output/tests/generator/indexedAccess/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/member access => expressions/skip-member access}/input.sdstest (100%) rename tests/resources/generation/{skip-expressions/member access => expressions/skip-member access}/output/tests/generator/memberAccess/gen_input.py (100%) rename tests/resources/generation/{skip-expressions/reference/output/tests/generator/reference => expressions/skip-member access/output/tests/generator/memberAccess}/gen_input_test.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/template string/input.sdstest (100%) rename tests/resources/generation/{skip-expressions => expressions}/template string/output/tests/generator/templateString/gen_input.py (100%) rename tests/resources/generation/{skip-expressions => expressions}/template string/output/tests/generator/templateString/gen_input_test.py (100%) rename tests/resources/generation/{skip-python module => python module}/input.sdstest (100%) rename tests/resources/generation/{skip-python module => python module}/output/special_module/gen_input.py (100%) rename tests/resources/generation/{skip-statements => statements}/expression statement/input.sdstest (100%) rename tests/resources/generation/{skip-statements => statements}/expression statement/output/tests/generator/expressionStatement/gen_input.py (100%) rename tests/resources/generation/{skip-statements/assignment/output/tests/generator/assignment => statements/expression statement/output/tests/generator/expressionStatement}/gen_input_testPipeline.py (100%) rename tests/resources/generation/{skip-statements/assignment => statements/skip-assignment}/input.sdstest (100%) rename tests/resources/generation/{skip-statements/assignment => statements/skip-assignment}/output/tests/generator/assignment/gen_input.py (100%) rename tests/resources/generation/{skip-statements/expression statement/output/tests/generator/expressionStatement => statements/skip-assignment/output/tests/generator/assignment}/gen_input_testPipeline.py (100%) rename tests/resources/generation/{skip-statements/statement without effect => statements/skip-statement without effect}/input.sdstest (100%) rename tests/resources/generation/{skip-statements/statement without effect => statements/skip-statement without effect}/output/tests/generator/statementWithoutEffect/gen_input.py (100%) rename tests/resources/generation/{skip-statements/statement without effect => statements/skip-statement without effect}/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py (100%) diff --git a/tests/resources/generation/skip-declarations/empty pipeline/input.sdstest b/tests/resources/generation/declarations/empty pipeline/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/empty pipeline/input.sdstest rename to tests/resources/generation/declarations/empty pipeline/input.sdstest diff --git a/tests/resources/generation/skip-declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input.py b/tests/resources/generation/declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input.py rename to tests/resources/generation/declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input.py diff --git a/tests/resources/generation/skip-declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input_test.py b/tests/resources/generation/declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input_test.py rename to tests/resources/generation/declarations/empty pipeline/output/tests/generator/emptyPipeline/gen_input_test.py diff --git a/tests/resources/generation/skip-declarations/empty step/input.sdstest b/tests/resources/generation/declarations/empty step/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/empty step/input.sdstest rename to tests/resources/generation/declarations/empty step/input.sdstest diff --git a/tests/resources/generation/skip-declarations/empty step/output/tests/generator/emptyStep/gen_input.py b/tests/resources/generation/declarations/empty step/output/tests/generator/emptyStep/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/empty step/output/tests/generator/emptyStep/gen_input.py rename to tests/resources/generation/declarations/empty step/output/tests/generator/emptyStep/gen_input.py diff --git a/tests/resources/generation/skip-declarations/pipeline with python name/input.sdstest b/tests/resources/generation/declarations/pipeline with python name/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/pipeline with python name/input.sdstest rename to tests/resources/generation/declarations/pipeline with python name/input.sdstest diff --git a/tests/resources/generation/skip-declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input.py b/tests/resources/generation/declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input.py rename to tests/resources/generation/declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input.py diff --git a/tests/resources/generation/skip-declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input_test_pipeline.py b/tests/resources/generation/declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input_test_pipeline.py similarity index 100% rename from tests/resources/generation/skip-declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input_test_pipeline.py rename to tests/resources/generation/declarations/pipeline with python name/output/tests/generator/pipelineWithPythonName/gen_input_test_pipeline.py diff --git a/tests/resources/generation/skip-declarations/parameter with python name/input.sdstest b/tests/resources/generation/declarations/skip-parameter with python name/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/parameter with python name/input.sdstest rename to tests/resources/generation/declarations/skip-parameter with python name/input.sdstest diff --git a/tests/resources/generation/skip-declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py rename to tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py diff --git a/tests/resources/generation/skip-declarations/two steps/input.sdstest b/tests/resources/generation/declarations/skip-two steps/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/two steps/input.sdstest rename to tests/resources/generation/declarations/skip-two steps/input.sdstest diff --git a/tests/resources/generation/skip-declarations/two steps/output/tests/generator/twoSteps/gen_input.py b/tests/resources/generation/declarations/skip-two steps/output/tests/generator/twoSteps/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/two steps/output/tests/generator/twoSteps/gen_input.py rename to tests/resources/generation/declarations/skip-two steps/output/tests/generator/twoSteps/gen_input.py diff --git a/tests/resources/generation/skip-declarations/step with python name/input.sdstest b/tests/resources/generation/declarations/step with python name/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/step with python name/input.sdstest rename to tests/resources/generation/declarations/step with python name/input.sdstest diff --git a/tests/resources/generation/skip-declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py b/tests/resources/generation/declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py rename to tests/resources/generation/declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py diff --git a/tests/resources/generation/skip-declarations/two pipelines/input.sdstest b/tests/resources/generation/declarations/two pipelines/input.sdstest similarity index 100% rename from tests/resources/generation/skip-declarations/two pipelines/input.sdstest rename to tests/resources/generation/declarations/two pipelines/input.sdstest diff --git a/tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input.py b/tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input.py similarity index 100% rename from tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input.py rename to tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input.py diff --git a/tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test1.py b/tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test1.py similarity index 100% rename from tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test1.py rename to tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test1.py diff --git a/tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test2.py b/tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test2.py similarity index 100% rename from tests/resources/generation/skip-declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test2.py rename to tests/resources/generation/declarations/two pipelines/output/tests/generator/twoPipelines/gen_input_test2.py diff --git a/tests/resources/generation/dummy/output/test.py b/tests/resources/generation/dummy/output/test.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/resources/generation/dummy/test.sdstest b/tests/resources/generation/dummy/test.sdstest deleted file mode 100644 index 8e2774107..000000000 --- a/tests/resources/generation/dummy/test.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.generation.dummy - -pipeline myPipeline { - // $TEST$ run_until - val »a« = 1; -} diff --git a/tests/resources/generation/skip-expressions/enum variant call/input.sdstest b/tests/resources/generation/expressions/enum variant call/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/enum variant call/input.sdstest rename to tests/resources/generation/expressions/enum variant call/input.sdstest diff --git a/tests/resources/generation/skip-expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py rename to tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py diff --git a/tests/resources/generation/skip-expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py rename to tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/infix operation/input.sdstest b/tests/resources/generation/expressions/infix operation/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/infix operation/input.sdstest rename to tests/resources/generation/expressions/infix operation/input.sdstest diff --git a/tests/resources/generation/skip-expressions/infix operation/output/tests/generator/infixOperation/gen_input.py b/tests/resources/generation/expressions/infix operation/output/tests/generator/infixOperation/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/infix operation/output/tests/generator/infixOperation/gen_input.py rename to tests/resources/generation/expressions/infix operation/output/tests/generator/infixOperation/gen_input.py diff --git a/tests/resources/generation/skip-expressions/call/output/tests/generator/call/gen_input_test.py b/tests/resources/generation/expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/call/output/tests/generator/call/gen_input_test.py rename to tests/resources/generation/expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/literals/input.sdstest b/tests/resources/generation/expressions/literals/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/literals/input.sdstest rename to tests/resources/generation/expressions/literals/input.sdstest diff --git a/tests/resources/generation/skip-expressions/literals/output/tests/generator/literals/gen_input.py b/tests/resources/generation/expressions/literals/output/tests/generator/literals/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/literals/output/tests/generator/literals/gen_input.py rename to tests/resources/generation/expressions/literals/output/tests/generator/literals/gen_input.py diff --git a/tests/resources/generation/skip-expressions/constant/output/tests/generator/constant/gen_input_test.py b/tests/resources/generation/expressions/literals/output/tests/generator/literals/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/constant/output/tests/generator/constant/gen_input_test.py rename to tests/resources/generation/expressions/literals/output/tests/generator/literals/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/parenthesized expression/input.sdstest b/tests/resources/generation/expressions/parenthesized expression/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/parenthesized expression/input.sdstest rename to tests/resources/generation/expressions/parenthesized expression/input.sdstest diff --git a/tests/resources/generation/skip-expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py b/tests/resources/generation/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py rename to tests/resources/generation/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py diff --git a/tests/resources/generation/skip-expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py b/tests/resources/generation/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py rename to tests/resources/generation/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/prefix operation/input.sdstest b/tests/resources/generation/expressions/prefix operation/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/prefix operation/input.sdstest rename to tests/resources/generation/expressions/prefix operation/input.sdstest diff --git a/tests/resources/generation/skip-expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py b/tests/resources/generation/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py rename to tests/resources/generation/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py diff --git a/tests/resources/generation/skip-expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py b/tests/resources/generation/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py rename to tests/resources/generation/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/reference/input.sdstest b/tests/resources/generation/expressions/reference/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/reference/input.sdstest rename to tests/resources/generation/expressions/reference/input.sdstest diff --git a/tests/resources/generation/skip-expressions/reference/output/tests/generator/reference/gen_input.py b/tests/resources/generation/expressions/reference/output/tests/generator/reference/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/reference/output/tests/generator/reference/gen_input.py rename to tests/resources/generation/expressions/reference/output/tests/generator/reference/gen_input.py diff --git a/tests/resources/generation/skip-expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py b/tests/resources/generation/expressions/reference/output/tests/generator/reference/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py rename to tests/resources/generation/expressions/reference/output/tests/generator/reference/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/block lambda/input.sdstest b/tests/resources/generation/expressions/skip-block lambda/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/block lambda/input.sdstest rename to tests/resources/generation/expressions/skip-block lambda/input.sdstest diff --git a/tests/resources/generation/skip-expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/block lambda/output/tests/generator/blockLambda/gen_input.py rename to tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input.py diff --git a/tests/resources/generation/skip-expressions/literals/output/tests/generator/literals/gen_input_test.py b/tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/literals/output/tests/generator/literals/gen_input_test.py rename to tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/call/input.sdstest b/tests/resources/generation/expressions/skip-call/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/call/input.sdstest rename to tests/resources/generation/expressions/skip-call/input.sdstest diff --git a/tests/resources/generation/skip-expressions/call/output/tests/generator/call/gen_input.py b/tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/call/output/tests/generator/call/gen_input.py rename to tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input.py diff --git a/tests/resources/generation/skip-expressions/member access/output/tests/generator/memberAccess/gen_input_test.py b/tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/member access/output/tests/generator/memberAccess/gen_input_test.py rename to tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/constant/input.sdstest b/tests/resources/generation/expressions/skip-constant/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/constant/input.sdstest rename to tests/resources/generation/expressions/skip-constant/input.sdstest diff --git a/tests/resources/generation/skip-expressions/constant/output/tests/generator/constant/gen_input.py b/tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/constant/output/tests/generator/constant/gen_input.py rename to tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input.py diff --git a/tests/resources/generation/skip-expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py b/tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py rename to tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/expression lambda/input.sdstest b/tests/resources/generation/expressions/skip-expression lambda/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/expression lambda/input.sdstest rename to tests/resources/generation/expressions/skip-expression lambda/input.sdstest diff --git a/tests/resources/generation/skip-expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py b/tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py rename to tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input.py diff --git a/tests/resources/generation/skip-expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py b/tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py rename to tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/indexed access/input.sdstest b/tests/resources/generation/expressions/skip-indexed access/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/indexed access/input.sdstest rename to tests/resources/generation/expressions/skip-indexed access/input.sdstest diff --git a/tests/resources/generation/skip-expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py b/tests/resources/generation/expressions/skip-indexed access/output/tests/generator/indexedAccess/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py rename to tests/resources/generation/expressions/skip-indexed access/output/tests/generator/indexedAccess/gen_input.py diff --git a/tests/resources/generation/skip-expressions/member access/input.sdstest b/tests/resources/generation/expressions/skip-member access/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/member access/input.sdstest rename to tests/resources/generation/expressions/skip-member access/input.sdstest diff --git a/tests/resources/generation/skip-expressions/member access/output/tests/generator/memberAccess/gen_input.py b/tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/member access/output/tests/generator/memberAccess/gen_input.py rename to tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input.py diff --git a/tests/resources/generation/skip-expressions/reference/output/tests/generator/reference/gen_input_test.py b/tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/reference/output/tests/generator/reference/gen_input_test.py rename to tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input_test.py diff --git a/tests/resources/generation/skip-expressions/template string/input.sdstest b/tests/resources/generation/expressions/template string/input.sdstest similarity index 100% rename from tests/resources/generation/skip-expressions/template string/input.sdstest rename to tests/resources/generation/expressions/template string/input.sdstest diff --git a/tests/resources/generation/skip-expressions/template string/output/tests/generator/templateString/gen_input.py b/tests/resources/generation/expressions/template string/output/tests/generator/templateString/gen_input.py similarity index 100% rename from tests/resources/generation/skip-expressions/template string/output/tests/generator/templateString/gen_input.py rename to tests/resources/generation/expressions/template string/output/tests/generator/templateString/gen_input.py diff --git a/tests/resources/generation/skip-expressions/template string/output/tests/generator/templateString/gen_input_test.py b/tests/resources/generation/expressions/template string/output/tests/generator/templateString/gen_input_test.py similarity index 100% rename from tests/resources/generation/skip-expressions/template string/output/tests/generator/templateString/gen_input_test.py rename to tests/resources/generation/expressions/template string/output/tests/generator/templateString/gen_input_test.py diff --git a/tests/resources/generation/skip-python module/input.sdstest b/tests/resources/generation/python module/input.sdstest similarity index 100% rename from tests/resources/generation/skip-python module/input.sdstest rename to tests/resources/generation/python module/input.sdstest diff --git a/tests/resources/generation/skip-python module/output/special_module/gen_input.py b/tests/resources/generation/python module/output/special_module/gen_input.py similarity index 100% rename from tests/resources/generation/skip-python module/output/special_module/gen_input.py rename to tests/resources/generation/python module/output/special_module/gen_input.py diff --git a/tests/resources/generation/skip-statements/expression statement/input.sdstest b/tests/resources/generation/statements/expression statement/input.sdstest similarity index 100% rename from tests/resources/generation/skip-statements/expression statement/input.sdstest rename to tests/resources/generation/statements/expression statement/input.sdstest diff --git a/tests/resources/generation/skip-statements/expression statement/output/tests/generator/expressionStatement/gen_input.py b/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py similarity index 100% rename from tests/resources/generation/skip-statements/expression statement/output/tests/generator/expressionStatement/gen_input.py rename to tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py diff --git a/tests/resources/generation/skip-statements/assignment/output/tests/generator/assignment/gen_input_testPipeline.py b/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input_testPipeline.py similarity index 100% rename from tests/resources/generation/skip-statements/assignment/output/tests/generator/assignment/gen_input_testPipeline.py rename to tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input_testPipeline.py diff --git a/tests/resources/generation/skip-statements/assignment/input.sdstest b/tests/resources/generation/statements/skip-assignment/input.sdstest similarity index 100% rename from tests/resources/generation/skip-statements/assignment/input.sdstest rename to tests/resources/generation/statements/skip-assignment/input.sdstest diff --git a/tests/resources/generation/skip-statements/assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input.py similarity index 100% rename from tests/resources/generation/skip-statements/assignment/output/tests/generator/assignment/gen_input.py rename to tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input.py diff --git a/tests/resources/generation/skip-statements/expression statement/output/tests/generator/expressionStatement/gen_input_testPipeline.py b/tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input_testPipeline.py similarity index 100% rename from tests/resources/generation/skip-statements/expression statement/output/tests/generator/expressionStatement/gen_input_testPipeline.py rename to tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input_testPipeline.py diff --git a/tests/resources/generation/skip-statements/statement without effect/input.sdstest b/tests/resources/generation/statements/skip-statement without effect/input.sdstest similarity index 100% rename from tests/resources/generation/skip-statements/statement without effect/input.sdstest rename to tests/resources/generation/statements/skip-statement without effect/input.sdstest diff --git a/tests/resources/generation/skip-statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py b/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py similarity index 100% rename from tests/resources/generation/skip-statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py rename to tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py diff --git a/tests/resources/generation/skip-statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py b/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py similarity index 100% rename from tests/resources/generation/skip-statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py rename to tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testPipeline.py From 8dd99185e1cd56e87df1bd8dfaae30a3b13d080b Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Mon, 16 Oct 2023 17:19:57 +0200 Subject: [PATCH 07/56] chore: convert imports to a map with unique string keys for each import feat: wildcard assignments are now removed, if the assignment only contains wildcards test: enable generation/"member access" test that passes --- src/cli/generator.ts | 101 +++++++++--------- .../input.sdstest | 0 .../tests/generator/memberAccess/gen_input.py | 0 .../generator/memberAccess/gen_input_test.py | 0 4 files changed, 53 insertions(+), 48 deletions(-) rename tests/resources/generation/expressions/{skip-member access => member access}/input.sdstest (100%) rename tests/resources/generation/expressions/{skip-member access => member access}/output/tests/generator/memberAccess/gen_input.py (100%) rename tests/resources/generation/expressions/{skip-member access => member access}/output/tests/generator/memberAccess/gen_input_test.py (100%) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index fa816ce38..4d3d842e2 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -73,6 +73,7 @@ import { SdsPlaceholder, SdsPrefixOperation, SdsReference, + SdsResult, SdsResultList, SdsSegment, SdsStatement, @@ -123,15 +124,18 @@ const PYTHON_INDENT = ' '; class GenerationInfoFrame { blockLambdaManager: IdManager; - importSet: Set; + importSet: Map; - constructor(importSet: Set = new Set()) { + constructor(importSet: Map = new Map()) { this.blockLambdaManager = new IdManager(); this.importSet = importSet; } addImport(importData: ImportData) { - this.importSet.add(importData); + const hashKey = JSON.stringify(importData); + if (!this.importSet.has(hashKey)) { + this.importSet.set(hashKey, importData); + } } getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { @@ -207,10 +211,12 @@ const generateAssignee = function (assignee: SdsAssignee): string { case isSdsWildcard(assignee): return '_'; case isSdsYield(assignee): - // TODO handle (assignee as SdsYield). - return ''; + const yieldResultName = (assignee).result?.ref?.name; + if (yieldResultName === undefined) { + throw new Error('The result of a yield should not be undefined'); + } + return yieldResultName; default: - // TODO more assignees throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); } }; @@ -220,29 +226,30 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio isSdsCallable(assignment.expression) || isSdsCall(assignment.expression) ? callResultsOrEmpty(assignment.expression as SdsCallable).length : 1; - const actualAssignees = assigneesOrEmpty(assignment).map(generateAssignee); - if (requiredAssignees === actualAssignees.length) { - if (assignment.expression !== undefined) { - return `${actualAssignees.join(', ')} = ${generateExpression(assignment.expression, frame)}`; + const assignees = assigneesOrEmpty(assignment); + if (assignees.some((value) => !isSdsWildcard(value))) { + const actualAssignees = assignees.map(generateAssignee); + if (requiredAssignees === actualAssignees.length) { + if (assignment.expression !== undefined) { + return `${actualAssignees.join(', ')} = ${generateExpression(assignment.expression, frame)}`; + } + return actualAssignees.join(', '); + } else { + // Add wildcards to match given results + if (assignment.expression !== undefined) { + return `${actualAssignees + .concat(Array(requiredAssignees - actualAssignees.length).fill('_')) + .join(', ')} = ${generateExpression(assignment.expression, frame)}`; + } else { + return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); + } } - return actualAssignees.join(', '); - } - if (assignment.expression !== undefined) { - console.info('Required: ' + requiredAssignees); - console.info('Actual: ' + actualAssignees.length); - console.info('Assignees: ' + actualAssignees.join(', ')); - console.info( - 'Expected: ' + - abstractResultsOrEmpty(assignment.expression as SdsCallable) - .map((a) => a.$type + ' - ' + a.name) - .join(', '), - ); - console.info('Expr: ' + assignment.expression.$type); - return `${actualAssignees - .concat(Array(requiredAssignees - actualAssignees.length).fill('_')) - .join(', ')} = ${generateExpression(assignment.expression, frame)}`; } else { - return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); + if (assignment.expression !== undefined) { + return generateExpression(assignment.expression, frame); + } + // Only wildcard and no expression + return ''; } }; @@ -266,7 +273,7 @@ const generateStatement = function (statement: SdsStatement, frame: GenerationIn const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: GenerationInfoFrame): string { const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); let lambdaBlock = generateBlock(blockLambda.body, frame); - if (lambdaResult.length !== 0) { + if (lambdaResult.length !== 0 && lambdaBlock !== 'pass') { lambdaBlock += `\nreturn ${lambdaResult.map((result) => result.name).join(', ')}`; } return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( @@ -460,7 +467,12 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return `${receiver}.${enumMember}${suffix}`; } case isSdsResult(member): - return ''; + const resultList = (member).$container.results; + if (resultList.length === 1) { + return receiver; + } + const currentIndex = resultList.indexOf(member); + return `${receiver}[${currentIndex}]`; default: const memberExpression = generateExpression(memberAccess.member, frame); if (expression.isNullSafe) { @@ -470,7 +482,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return `${receiver}.${memberExpression}`; } } - return ''; } if (isSdsParenthesizedExpression(expression)) { return expandToString`${generateExpression(expression.expression, frame)}`; @@ -525,27 +536,21 @@ const generateParameters = function (parameters: SdsParameterList | undefined, f return result.join(', '); }; -const generateResults = function (result: SdsResultList | undefined): string { - if (result === undefined || result.results.length === 0) { - return ''; - } - // TODO do something - return ''; -}; - -const generateSegment = function (segment: SdsSegment, importSet: Set): string { +const generateSegment = function (segment: SdsSegment, importSet: Map): string { const infoFrame = new GenerationInfoFrame(importSet); + const segmentResult = segment.resultList?.results || []; + let segmentBlock = generateBlock(segment.body, infoFrame); + if (segmentResult.length !== 0) { + // Segment should always have results + segmentBlock += `\nreturn ${segmentResult.map((result) => result.name).join(', ')}`; + } return expandToString`def ${getPythonNameOrDefault(segment)}(${generateParameters( segment.parameterList, infoFrame, - )})${ - segment.resultList !== undefined && segment.resultList.results.length !== 0 - ? ' -> ' + generateResults(segment.resultList) - : '' - }:\n${PYTHON_INDENT}${generateBlock(segment.body, infoFrame)}`; + )}):\n${PYTHON_INDENT}${segmentBlock}`; }; -const generatePipeline = function (pipeline: SdsPipeline, importSet: Set): string { +const generatePipeline = function (pipeline: SdsPipeline, importSet: Map): string { const infoFrame = new GenerationInfoFrame(importSet); return expandToString`def ${getPythonNameOrDefault(pipeline)}():\n${PYTHON_INDENT}${generateBlock( pipeline.body, @@ -565,7 +570,7 @@ const generateImport = function (importStmt: ImportData): string { } }; -const generateImports = function (importSet: Set): string[] { +const generateImports = function (importSet: ImportData[]): string[] { const qualifiedImports = Array.from(importSet) .filter((importStmt) => importStmt.declarationName === undefined) .sort((a, b) => a.importPath.localeCompare(b.importPath)) @@ -593,7 +598,7 @@ const generateImports = function (importSet: Set): string[] { }; const generateModule = function (module: SdsModule): string { - const importSet = new Set(); + const importSet = new Map(); const segments = streamAllContents(module) .filter(isSdsSegment) .map((segment) => generateSegment(segment, importSet)) @@ -602,7 +607,7 @@ const generateModule = function (module: SdsModule): string { .filter(isSdsPipeline) .map((pipeline) => generatePipeline(pipeline, importSet)) .toArray(); - const imports = generateImports(importSet); + const imports = generateImports(Array.from(importSet.values())); const output: string[] = []; if (imports.length > 0) { output.push( diff --git a/tests/resources/generation/expressions/skip-member access/input.sdstest b/tests/resources/generation/expressions/member access/input.sdstest similarity index 100% rename from tests/resources/generation/expressions/skip-member access/input.sdstest rename to tests/resources/generation/expressions/member access/input.sdstest diff --git a/tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input.py b/tests/resources/generation/expressions/member access/output/tests/generator/memberAccess/gen_input.py similarity index 100% rename from tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input.py rename to tests/resources/generation/expressions/member access/output/tests/generator/memberAccess/gen_input.py diff --git a/tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input_test.py b/tests/resources/generation/expressions/member access/output/tests/generator/memberAccess/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/skip-member access/output/tests/generator/memberAccess/gen_input_test.py rename to tests/resources/generation/expressions/member access/output/tests/generator/memberAccess/gen_input_test.py From c0aa7c7c3375f5bb4db1afb7225b9fa9f0d64f18 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Mon, 16 Oct 2023 20:59:23 +0200 Subject: [PATCH 08/56] fix: Pass services to generation functions, to get correct annotation information fix: Temporarily revert loading all documents for tests, as duplicate definitions will occur --- src/cli/generator.ts | 96 ++++++++----------- tests/language/generation/creator.ts | 7 +- .../generation/testGeneration.test.ts | 2 +- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 4d3d842e2..39b90daf3 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -82,7 +82,7 @@ import { } from '../language/generated/ast.js'; import { extractAstNode, extractDestinationAndName } from './cli-util.js'; import chalk from 'chalk'; -import { createSafeDsServices } from '../language/safe-ds-module.js'; +import { createSafeDsServices, SafeDsServices } from '../language/safe-ds-module.js'; import { NodeFileSystem } from 'langium/node'; import { toConstantExpressionOrUndefined } from '../language/partialEvaluation/toConstantExpressionOrUndefined.js'; import { @@ -113,7 +113,7 @@ import { IdManager } from '../language/helpers/idManager.js'; export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { const services = createSafeDsServices(NodeFileSystem).SafeDs; const module = await extractAstNode(fileName, services); - const generatedFilePath = generatePython(module, fileName, opts.destination); + const generatedFilePath = generatePython(services, module, fileName, opts.destination); // eslint-disable-next-line no-console console.log(chalk.green(`Python code generated successfully: ${generatedFilePath}`)); }; @@ -123,10 +123,12 @@ const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; const PYTHON_INDENT = ' '; class GenerationInfoFrame { + services: SafeDsServices; blockLambdaManager: IdManager; importSet: Map; - constructor(importSet: Map = new Map()) { + constructor(services: SafeDsServices, importSet: Map = new Map()) { + this.services = services; this.blockLambdaManager = new IdManager(); this.importSet = importSet; } @@ -141,6 +143,10 @@ class GenerationInfoFrame { getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { return `__block_lambda_${this.blockLambdaManager.assignId(lambda)}`; } + + getServices(): SafeDsServices { + return this.services; + } } class ImportData { @@ -163,43 +169,11 @@ export type GenerateOptions = { destination?: string; }; -const getPythonNameOrDefault = function (object: SdsPipeline | SdsSegment | SdsParameter | SdsDeclaration) { - return getPythonName(object) || object.name; -}; -const getPythonName = function (annotatedObject: SdsAnnotatedObject) { - let annotationCalls = annotationCallsOrEmpty(annotatedObject); - if (annotationCalls.length === 0) { - return undefined; - } - for (const annotationCall of annotationCalls) { - if (annotationCall.annotation.ref !== undefined && annotationCall.annotation.ref.name === 'PythonName') { - const argumentsArray = argumentsOrEmpty(annotationCall); - if (argumentsArray.length !== 0) { - if (isSdsString(argumentsArray[0].value)) { - return argumentsArray[0].value.value; - } - } - } - } - return undefined; -}; - -const getPythonModuleName = function (annotatedObject: SdsAnnotatedObject): string | undefined { - let annotationCalls = annotationCallsOrEmpty(annotatedObject); - if (annotationCalls.length === 0) { - return undefined; - } - for (const annotationCall of annotationCalls) { - if (annotationCall.annotation.ref !== undefined && annotationCall.annotation.ref.name === 'PythonModule') { - const argumentsArray = argumentsOrEmpty(annotationCall); - if (argumentsArray.length !== 0) { - if (isSdsString(argumentsArray[0].value)) { - return argumentsArray[0].value.value; - } - } - } - } - return undefined; +const getPythonNameOrDefault = function ( + services: SafeDsServices, + object: SdsPipeline | SdsSegment | SdsParameter | SdsDeclaration, +) { + return services.builtins.Annotations.getPythonName(object) || object.name; }; const generateAssignee = function (assignee: SdsAssignee): string { @@ -379,8 +353,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio .replaceAll('\n', '\\n')}'`; case potentialConstantExpression instanceof SdsConstantEnumVariant: return String((potentialConstantExpression as SdsConstantEnumVariant).value); // TODO SdsConstantEnumVariant?? generate something useful - default: - throw new Error(`Unknown SdsLiteral: ${expression}`); } } @@ -506,7 +478,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (declaration === undefined) { return ''; } - return getPythonNameOrDefault(declaration); // TODO python name, may be fixed with reference / import + return getPythonNameOrDefault(frame.getServices(), declaration); // TODO python name, may be fixed with reference / import } // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' return ``; @@ -521,7 +493,7 @@ const generateParameter = function ( if (parameter === undefined) { return ''; } - return expandToString`${getPythonNameOrDefault(parameter)}${ + return expandToString`${getPythonNameOrDefault(frame.getServices(), parameter)}${ defaultValue && parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue, frame) : '' @@ -536,23 +508,31 @@ const generateParameters = function (parameters: SdsParameterList | undefined, f return result.join(', '); }; -const generateSegment = function (segment: SdsSegment, importSet: Map): string { - const infoFrame = new GenerationInfoFrame(importSet); +const generateSegment = function ( + services: SafeDsServices, + segment: SdsSegment, + importSet: Map, +): string { + const infoFrame = new GenerationInfoFrame(services, importSet); const segmentResult = segment.resultList?.results || []; let segmentBlock = generateBlock(segment.body, infoFrame); if (segmentResult.length !== 0) { // Segment should always have results segmentBlock += `\nreturn ${segmentResult.map((result) => result.name).join(', ')}`; } - return expandToString`def ${getPythonNameOrDefault(segment)}(${generateParameters( + return expandToString`def ${getPythonNameOrDefault(services, segment)}(${generateParameters( segment.parameterList, infoFrame, )}):\n${PYTHON_INDENT}${segmentBlock}`; }; -const generatePipeline = function (pipeline: SdsPipeline, importSet: Map): string { - const infoFrame = new GenerationInfoFrame(importSet); - return expandToString`def ${getPythonNameOrDefault(pipeline)}():\n${PYTHON_INDENT}${generateBlock( +const generatePipeline = function ( + services: SafeDsServices, + pipeline: SdsPipeline, + importSet: Map, +): string { + const infoFrame = new GenerationInfoFrame(services, importSet); + return expandToString`def ${getPythonNameOrDefault(services, pipeline)}():\n${PYTHON_INDENT}${generateBlock( pipeline.body, infoFrame, )}`; @@ -597,15 +577,15 @@ const generateImports = function (importSet: ImportData[]): string[] { return [...new Set(qualifiedImports), ...new Set(declaredImports)]; }; -const generateModule = function (module: SdsModule): string { +const generateModule = function (services: SafeDsServices, module: SdsModule): string { const importSet = new Map(); const segments = streamAllContents(module) .filter(isSdsSegment) - .map((segment) => generateSegment(segment, importSet)) + .map((segment) => generateSegment(services, segment, importSet)) .toArray(); const pipelines = streamAllContents(module) .filter(isSdsPipeline) - .map((pipeline) => generatePipeline(pipeline, importSet)) + .map((pipeline) => generatePipeline(services, pipeline, importSet)) .toArray(); const imports = generateImports(Array.from(importSet.values())); const output: string[] = []; @@ -634,25 +614,27 @@ const generateModule = function (module: SdsModule): string { }; export const generatePython = function ( + services: SafeDsServices, module: SdsModule, filePath: string, destination: string | undefined, ): string[] { const data = extractDestinationAndName(filePath, destination); - const pythonModuleName = getPythonModuleName(module); + const pythonModuleName = services.builtins.Annotations.getPythonModule(module); const packagePath = pythonModuleName === undefined ? module.name.split('.') : [pythonModuleName]; const parentDirectoryPath = path.join(data.destination, ...packagePath); const generatedFiles = new Map(); - generatedFiles.set(`${path.join(parentDirectoryPath, `gen_${data.name}`)}.py`, generateModule(module)); + generatedFiles.set(`${path.join(parentDirectoryPath, `gen_${data.name}`)}.py`, generateModule(services, module)); for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { const entryPointFilename = `${path.join( parentDirectoryPath, - `gen_${data.name}_${getPythonNameOrDefault(pipeline)}`, + `gen_${data.name}_${getPythonNameOrDefault(services, pipeline)}`, )}.py`; const entryPointContent = expandToStringWithNL`from gen_${data.name} import ${getPythonNameOrDefault( + services, pipeline, - )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(pipeline)}()`; + )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(services, pipeline)}()`; generatedFiles.set(entryPointFilename, entryPointContent); } // const fileNode = new CompositeGeneratorNode(); diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 52d9876c9..209dc7e2f 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -31,9 +31,12 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const expectedOutputFiles = readExpectedOutputFiles(expectedOutputRoot, actualOutputRoot); let runUntil: Location | undefined; // First read all stubs, then read all the other files; This should avoid broken references - await loadAllDocuments(services, inputUris); + const sortedInputUris = inputUris + .filter((uri) => uri.fsPath.endsWith('sdsstub')) + .sort() + .concat(...inputUris.filter((uri) => !uri.fsPath.endsWith('sdsstub')).sort()); - for (const uri of inputUris) { + for (const uri of sortedInputUris) { const code = fs.readFileSync(uri.fsPath).toString(); // File must not contain any errors diff --git a/tests/language/generation/testGeneration.test.ts b/tests/language/generation/testGeneration.test.ts index 2587d37db..940c14fb6 100644 --- a/tests/language/generation/testGeneration.test.ts +++ b/tests/language/generation/testGeneration.test.ts @@ -38,7 +38,7 @@ describe('generation', async () => { for (const document of documents) { const module = document.parseResult.value as SdsModule; const fileName = document.uri.fsPath; - const generatedFilePaths = generatePython(module, fileName, test.actualOutputRoot.fsPath); + const generatedFilePaths = generatePython(services, module, fileName, test.actualOutputRoot.fsPath); actualOutputPaths.push(...generatedFilePaths); } From 7325e10f0cca8fd4ca492d9d797b407dbe64f01f Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Mon, 16 Oct 2023 21:31:13 +0200 Subject: [PATCH 09/56] fix: Use more general functions to get parameters / results from a call --- src/cli/generator.ts | 78 +++----------------------- src/language/helpers/nodeProperties.ts | 26 --------- 2 files changed, 9 insertions(+), 95 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 39b90daf3..7c63370b8 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,24 +1,11 @@ import fs from 'fs'; -import { - CompositeGeneratorNode, - expandToString, - expandToStringWithNL, - findLocalReferences, - streamAllContents, - toString, - getDocument, - Reference, -} from 'langium'; +import { expandToString, expandToStringWithNL, getDocument, Reference, streamAllContents } from 'langium'; import path from 'path'; import { - isQualifiedName, - isSdsAssignee, isSdsAssignment, isSdsBlockLambda, isSdsBlockLambdaResult, isSdsCall, - isSdsCallable, - isSdsCallableType, isSdsEnumVariant, isSdsExpressionLambda, isSdsExpressionStatement, @@ -26,11 +13,8 @@ import { isSdsInfixOperation, isSdsList, isSdsLiteral, - isSdsLiteralType, isSdsMap, isSdsMemberAccess, - isSdsMemberType, - isSdsNamedType, isSdsParenthesizedExpression, isSdsPipeline, isSdsPlaceholder, @@ -38,46 +22,35 @@ import { isSdsReference, isSdsResult, isSdsSegment, - isSdsString, isSdsTemplateString, isSdsTemplateStringEnd, isSdsTemplateStringInner, isSdsTemplateStringPart, isSdsTemplateStringStart, - isSdsUnionType, isSdsWildcard, isSdsYield, - SdsAnnotatedObject, SdsArgument, - SdsArgumentList, SdsAssignee, SdsAssignment, SdsBlock, SdsBlockLambda, SdsBlockLambdaResult, SdsCall, - SdsCallable, SdsDeclaration, - SdsEnumVariant, SdsExpression, - SdsExpressionLambda, SdsExpressionStatement, SdsList, SdsMap, SdsMemberAccess, SdsModule, - SdsNamedType, SdsParameter, SdsParameterList, SdsPipeline, SdsPlaceholder, - SdsPrefixOperation, SdsReference, SdsResult, - SdsResultList, SdsSegment, SdsStatement, - SdsType, SdsYield, } from '../language/generated/ast.js'; import { extractAstNode, extractDestinationAndName } from './cli-util.js'; @@ -95,15 +68,10 @@ import { } from '../language/partialEvaluation/model.js'; import { abstractResultsOrEmpty, - annotationCallsOrEmpty, - argumentsOrEmpty, assigneesOrEmpty, blockLambdaResultsOrEmpty, - callParametersOrEmpty, - callResultsOrEmpty, isRequiredParameter, parametersOrEmpty, - resultsOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; import { group } from 'radash'; @@ -196,10 +164,11 @@ const generateAssignee = function (assignee: SdsAssignee): string { }; const generateAssignment = function (assignment: SdsAssignment, frame: GenerationInfoFrame): string { - const requiredAssignees = - isSdsCallable(assignment.expression) || isSdsCall(assignment.expression) - ? callResultsOrEmpty(assignment.expression as SdsCallable).length - : 1; + const requiredAssignees = isSdsCall(assignment.expression) + ? abstractResultsOrEmpty( + frame.getServices().helpers.NodeMapper.callToCallableOrUndefined(assignment.expression), + ).length + : 1; const assignees = assigneesOrEmpty(assignment); if (assignees.some((value) => !isSdsWildcard(value))) { const actualAssignees = assignees.map(generateAssignee); @@ -265,35 +234,6 @@ const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): st return expandToString`${statements.map((stmt) => generateStatement(stmt, frame)).join('\n')}`; }; -const generateType = function (type: SdsType | undefined): string | null { - if (type === undefined) { - return null; - } - switch (true) { - case isSdsCallableType(type): - // TODO do something - return ''; - case isSdsLiteralType(type): - // TODO do something - return ''; - case isSdsMemberType(type): - // TODO do something - return ''; - case isSdsNamedType(type): - const namedType = type as SdsNamedType; - if (namedType.typeArgumentList === undefined) { - return null; - } - // TODO do something - return ''; - case isSdsUnionType(type): - // TODO do something - return ''; - default: - throw new Error(`Unknown SdsType: ${type}`); - } -}; - const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { return expandToString`${ argument.parameter !== undefined && @@ -304,9 +244,9 @@ const generateArgument = function (argument: SdsArgument, frame: GenerationInfoF }${generateExpression(argument.value, frame)}`; }; -const sortArguments = function (argumentList: SdsArgument[], call: SdsCall): SdsArgument[] { +const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[], call: SdsCall): SdsArgument[] { // $containerIndex contains the index of the parameter in the receivers parameter list - const parameters = callParametersOrEmpty(call); + const parameters = parametersOrEmpty(services.helpers.NodeMapper.callToCallableOrUndefined(call)); const sortedArgs = argumentList .slice() .filter((value) => value.parameter !== undefined && value.parameter.ref !== undefined) @@ -383,7 +323,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } if (isSdsCall(expression)) { // TODO sort arguments to target order - const sortedArgs = sortArguments(expression.argumentList.arguments, expression); + const sortedArgs = sortArguments(frame.getServices(), expression.argumentList.arguments, expression); return expandToString`${generateExpression(expression.receiver, frame)}(${sortedArgs .map((arg) => generateArgument(arg, frame)) .join(', ')})`; diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index 59c6ceb26..a0340cfb3 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -3,8 +3,6 @@ import { isSdsAttribute, isSdsBlockLambda, isSdsBlockLambdaResult, - isSdsCall, - isSdsCallable, isSdsCallableType, isSdsClass, isSdsDeclaration, @@ -14,7 +12,6 @@ import { isSdsModule, isSdsModuleMember, isSdsPlaceholder, - isSdsReference, isSdsSegment, isSdsTypeParameterList, SdsAbstractCall, @@ -28,7 +25,6 @@ import { SdsBlock, SdsBlockLambda, SdsBlockLambdaResult, - SdsCall, SdsCallable, SdsClass, SdsClassMember, @@ -104,28 +100,6 @@ export const isStatic = (node: SdsClassMember): boolean => { // ------------------------------------------------------------------------------------------------- // Accessors for list elements // ------------------------------------------------------------------------------------------------- -export const callResultsOrEmpty = (node: SdsCall | SdsCallable | undefined): SdsAbstractResult[] => { - if (node && isSdsCall(node)) { - if (isSdsReference(node.receiver)) { - if (isSdsCallable(node.receiver.target.ref)) { - return abstractResultsOrEmpty(node.receiver.target.ref); - } - } - return []; - } - return abstractResultsOrEmpty(node); -}; - -export const callParametersOrEmpty = (node: SdsCall | undefined): SdsParameter[] => { - if (node) { - if (isSdsReference(node.receiver)) { - if (isSdsCallable(node.receiver.target.ref)) { - return parametersOrEmpty(node.receiver.target.ref); - } - } - } - return []; -}; export const abstractResultsOrEmpty = (node: SdsCallable | undefined): SdsAbstractResult[] => { if (!node) { From af753b3e5d33b2cd6f7be2e436e5fb7e9579c35e Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 01:14:30 +0200 Subject: [PATCH 10/56] feat: generate references (and imports) correctly --- src/cli/generator.ts | 138 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 21 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 7c63370b8..a5792cfac 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import { expandToString, expandToStringWithNL, getDocument, Reference, streamAllContents } from 'langium'; +import {expandToString, expandToStringWithNL, findRootNode, getDocument, Reference, streamAllContents} from 'langium'; import path from 'path'; import { isSdsAssignment, @@ -15,10 +15,12 @@ import { isSdsLiteral, isSdsMap, isSdsMemberAccess, + isSdsModule, isSdsParenthesizedExpression, isSdsPipeline, isSdsPlaceholder, isSdsPrefixOperation, + isSdsQualifiedImport, isSdsReference, isSdsResult, isSdsSegment, @@ -28,6 +30,7 @@ import { isSdsTemplateStringPart, isSdsTemplateStringStart, isSdsWildcard, + isSdsWildcardImport, isSdsYield, SdsArgument, SdsAssignee, @@ -53,11 +56,11 @@ import { SdsStatement, SdsYield, } from '../language/generated/ast.js'; -import { extractAstNode, extractDestinationAndName } from './cli-util.js'; +import {extractAstNode, extractDestinationAndName} from './cli-util.js'; import chalk from 'chalk'; -import { createSafeDsServices, SafeDsServices } from '../language/safe-ds-module.js'; -import { NodeFileSystem } from 'langium/node'; -import { toConstantExpressionOrUndefined } from '../language/partialEvaluation/toConstantExpressionOrUndefined.js'; +import {createSafeDsServices, SafeDsServices} from '../language/safe-ds-module.js'; +import {NodeFileSystem} from 'langium/node'; +import {toConstantExpressionOrUndefined} from '../language/partialEvaluation/toConstantExpressionOrUndefined.js'; import { SdsConstantBoolean, SdsConstantEnumVariant, @@ -70,12 +73,15 @@ import { abstractResultsOrEmpty, assigneesOrEmpty, blockLambdaResultsOrEmpty, + importedDeclarationsOrEmpty, + importsOrEmpty, isRequiredParameter, parametersOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; -import { group } from 'radash'; -import { IdManager } from '../language/helpers/idManager.js'; +import {group} from 'radash'; +import {IdManager} from '../language/helpers/idManager.js'; +import {isInStubFile} from '../language/helpers/fileExtensions.js'; /* c8 ignore start */ export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { @@ -101,10 +107,12 @@ class GenerationInfoFrame { this.importSet = importSet; } - addImport(importData: ImportData) { - const hashKey = JSON.stringify(importData); - if (!this.importSet.has(hashKey)) { - this.importSet.set(hashKey, importData); + addImport(importData: ImportData | undefined) { + if (importData) { + const hashKey = JSON.stringify(importData); + if (!this.importSet.has(hashKey)) { + this.importSet.set(hashKey, importData); + } } } @@ -410,18 +418,93 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } if (isSdsReference(expression)) { - const currentDocument = getDocument(expression); - // TODO generate reference - // TODO import const declaration = (expression as SdsReference).target.ref; - //return ''; if (declaration === undefined) { - return ''; + throw new Error('Unknown corresponding declaration to reference'); } - return getPythonNameOrDefault(frame.getServices(), declaration); // TODO python name, may be fixed with reference / import + const declarationQualifiedName = getReferenceDeclarationQualifiedName(declaration); + const referenceImport = + getExternalReferenceNeededImport(frame.getServices(), expression, declaration) || + getInternalReferenceNeededImport(frame.getServices(), expression, declaration); + frame.addImport(referenceImport); + return referenceImport?.alias || getPythonNameOrDefault(frame.getServices(), declaration); } // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' - return ``; + throw new Error(`Unknown expression type: ${expression.$type}`); +}; + +const getExternalReferenceNeededImport = function ( + services: SafeDsServices, + expression: SdsExpression, + declaration: SdsDeclaration, +): ImportData | undefined { + const currentModule = findRootNode(expression); + const targetModule = findRootNode(declaration); + for (const value of importsOrEmpty(currentModule)) { + // Verify same package + if (value.package !== targetModule.name) { + continue; + } + if (isSdsQualifiedImport(value)) { + const importedDeclarations = importedDeclarationsOrEmpty(value); + for (const importedDeclaration of importedDeclarations) { + if (declaration === importedDeclaration.declaration.ref) { + if (importedDeclaration.alias !== undefined) { + return new ImportData( + services.builtins.Annotations.getPythonModule(targetModule) || value.package, + importedDeclaration.declaration?.ref?.name, + importedDeclaration.alias.alias, + ); + } else { + return new ImportData( + services.builtins.Annotations.getPythonModule(targetModule) || value.package, + importedDeclaration.declaration?.ref?.name, + ); + } + } + } + } + if (isSdsWildcardImport(value)) { + // TODO wildcard import? + throw new Error('Wildcard imports are not yet handled'); + } + } + return undefined; +}; + +const getInternalReferenceNeededImport = function ( + services: SafeDsServices, + expression: SdsExpression, + declaration: SdsDeclaration, +): ImportData | undefined { + const currentModule = findRootNode(expression); + const targetModule = findRootNode(declaration); + if (currentModule !== targetModule && !isInStubFile(targetModule)) { + // TODO __skip__ in file name? + return new ImportData( + `${ + services.builtins.Annotations.getPythonModule(targetModule) || targetModule.name + }.${formatGeneratedFileName(getModuleFileBaseName(targetModule))}`, + getPythonNameOrDefault(services, declaration), + ); + } + return undefined; +}; + +const getModuleFileBaseName = function (module: SdsModule): string { + const filePath = getDocument(module).uri.fsPath; + return path.basename(filePath, path.extname(filePath)); +}; + +const getReferenceDeclarationQualifiedName = function (declaration: SdsDeclaration | undefined): string | undefined { + if (declaration === undefined) { + return undefined; + } + const rootModule = findRootNode(declaration); + if (isSdsModule(rootModule)) { + return `${rootModule.name}.${declaration.name}`; + } + return undefined; }; const generateParameter = function ( @@ -553,25 +636,38 @@ const generateModule = function (services: SafeDsServices, module: SdsModule): s return expandToStringWithNL`${output.join('\n')}`; }; +const formatGeneratedFileName = function (baseName: string): string { + return `gen_${baseName.replaceAll('%2520', '_').replaceAll(/[ .-]/gu, '_').replaceAll(/\\W/gu, '')}`; +}; + export const generatePython = function ( services: SafeDsServices, module: SdsModule, filePath: string, destination: string | undefined, ): string[] { + // Do not generate stub files + if (isInStubFile(module)) { + return []; + } const data = extractDestinationAndName(filePath, destination); const pythonModuleName = services.builtins.Annotations.getPythonModule(module); const packagePath = pythonModuleName === undefined ? module.name.split('.') : [pythonModuleName]; const parentDirectoryPath = path.join(data.destination, ...packagePath); const generatedFiles = new Map(); - generatedFiles.set(`${path.join(parentDirectoryPath, `gen_${data.name}`)}.py`, generateModule(services, module)); + generatedFiles.set( + `${path.join(parentDirectoryPath, formatGeneratedFileName(data.name))}.py`, + generateModule(services, module), + ); for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { const entryPointFilename = `${path.join( parentDirectoryPath, - `gen_${data.name}_${getPythonNameOrDefault(services, pipeline)}`, + `${formatGeneratedFileName(data.name)}_${getPythonNameOrDefault(services, pipeline)}`, )}.py`; - const entryPointContent = expandToStringWithNL`from gen_${data.name} import ${getPythonNameOrDefault( + const entryPointContent = expandToStringWithNL`from ${formatGeneratedFileName( + data.name, + )} import ${getPythonNameOrDefault( services, pipeline, )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(services, pipeline)}()`; From 54452abe037d2de7ce3769311862d0040fffa687 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 01:47:28 +0200 Subject: [PATCH 11/56] feat: sorting parameters + treating optional parameters correctly --- src/cli/generator.ts | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index a5792cfac..6c18d3b20 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import {expandToString, expandToStringWithNL, findRootNode, getDocument, Reference, streamAllContents} from 'langium'; +import {expandToString, expandToStringWithNL, findRootNode, getDocument, streamAllContents} from 'langium'; import path from 'path'; import { isSdsAssignment, @@ -38,7 +38,6 @@ import { SdsBlock, SdsBlockLambda, SdsBlockLambdaResult, - SdsCall, SdsDeclaration, SdsExpression, SdsExpressionStatement, @@ -76,7 +75,6 @@ import { importedDeclarationsOrEmpty, importsOrEmpty, isRequiredParameter, - parametersOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; import {group} from 'radash'; @@ -243,27 +241,27 @@ const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): st }; const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { + const parameter = frame.getServices().helpers.NodeMapper.argumentToParameterOrUndefined(argument); + const req = parameter !== undefined && !isRequiredParameter(parameter); return expandToString`${ - argument.parameter !== undefined && - argument.parameter.ref !== undefined && - !isRequiredParameter(argument.parameter.ref) - ? generateParameter(argument.parameter.ref, frame, false) + '=' + parameter !== undefined && !isRequiredParameter(parameter) + ? generateParameter(parameter, frame, false) + '=' : '' }${generateExpression(argument.value, frame)}`; }; -const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[], call: SdsCall): SdsArgument[] { +const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[]): SdsArgument[] { // $containerIndex contains the index of the parameter in the receivers parameter list - const parameters = parametersOrEmpty(services.helpers.NodeMapper.callToCallableOrUndefined(call)); - const sortedArgs = argumentList + + const parameters = argumentList.map((argument) => { + return { par: services.helpers.NodeMapper.argumentToParameterOrUndefined(argument), arg: argument }; + }); + const sortedArgs = parameters .slice() - .filter((value) => value.parameter !== undefined && value.parameter.ref !== undefined) - .sort( - (a, b) => - parameters.indexOf((>b.parameter).ref) - - parameters.indexOf((>a.parameter).ref), - ); - return sortedArgs.length === 0 ? argumentList : sortedArgs; + .filter((value) => value.par !== undefined) + .sort((a, b) => a.par?.$containerIndex - b.par?.$containerIndex) + .map((value) => value.arg); + return sortedArgs; }; const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { @@ -330,8 +328,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return frame.getUniqueLambdaBlockName(blockLambda); } if (isSdsCall(expression)) { - // TODO sort arguments to target order - const sortedArgs = sortArguments(frame.getServices(), expression.argumentList.arguments, expression); + const sortedArgs = sortArguments(frame.getServices(), expression.argumentList.arguments); return expandToString`${generateExpression(expression.receiver, frame)}(${sortedArgs .map((arg) => generateArgument(arg, frame)) .join(', ')})`; From f4af467a181fa7452dc209eb7a24d6c7e4e7697d Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 01:56:38 +0200 Subject: [PATCH 12/56] chore: cleanup unneeded code --- src/cli/generator.ts | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 6c18d3b20..92763fa0c 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -15,7 +15,6 @@ import { isSdsLiteral, isSdsMap, isSdsMemberAccess, - isSdsModule, isSdsParenthesizedExpression, isSdsPipeline, isSdsPlaceholder, @@ -242,7 +241,6 @@ const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): st const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { const parameter = frame.getServices().helpers.NodeMapper.argumentToParameterOrUndefined(argument); - const req = parameter !== undefined && !isRequiredParameter(parameter); return expandToString`${ parameter !== undefined && !isRequiredParameter(parameter) ? generateParameter(parameter, frame, false) + '=' @@ -252,16 +250,16 @@ const generateArgument = function (argument: SdsArgument, frame: GenerationInfoF const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[]): SdsArgument[] { // $containerIndex contains the index of the parameter in the receivers parameter list - const parameters = argumentList.map((argument) => { return { par: services.helpers.NodeMapper.argumentToParameterOrUndefined(argument), arg: argument }; }); - const sortedArgs = parameters + return parameters .slice() .filter((value) => value.par !== undefined) - .sort((a, b) => a.par?.$containerIndex - b.par?.$containerIndex) + .sort((a, b) => + a.par !== undefined && b.par !== undefined ? a.par.$containerIndex! - b.par.$containerIndex! : 0, + ) .map((value) => value.arg); - return sortedArgs; }; const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { @@ -419,7 +417,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (declaration === undefined) { throw new Error('Unknown corresponding declaration to reference'); } - const declarationQualifiedName = getReferenceDeclarationQualifiedName(declaration); const referenceImport = getExternalReferenceNeededImport(frame.getServices(), expression, declaration) || getInternalReferenceNeededImport(frame.getServices(), expression, declaration); @@ -462,7 +459,7 @@ const getExternalReferenceNeededImport = function ( } } if (isSdsWildcardImport(value)) { - // TODO wildcard import? + // TODO wildcard import? collisions? throw new Error('Wildcard imports are not yet handled'); } } @@ -493,17 +490,6 @@ const getModuleFileBaseName = function (module: SdsModule): string { return path.basename(filePath, path.extname(filePath)); }; -const getReferenceDeclarationQualifiedName = function (declaration: SdsDeclaration | undefined): string | undefined { - if (declaration === undefined) { - return undefined; - } - const rootModule = findRootNode(declaration); - if (isSdsModule(rootModule)) { - return `${rootModule.name}.${declaration.name}`; - } - return undefined; -}; - const generateParameter = function ( parameter: SdsParameter | undefined, frame: GenerationInfoFrame, From a2969c8220d8da9f68a12d2d5ebae895965f4b76 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:02:58 +0200 Subject: [PATCH 13/56] test: remove varargs from test "expressions/expression lambda" and enable --- .../input.sdstest | 4 ++-- .../output/tests/generator/expressionLambda/gen_input.py | 2 +- .../output/tests/generator/expressionLambda/gen_input_test.py | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/resources/generation/expressions/{skip-expression lambda => expression lambda}/input.sdstest (62%) rename tests/resources/generation/expressions/{skip-expression lambda => expression lambda}/output/tests/generator/expressionLambda/gen_input.py (83%) rename tests/resources/generation/expressions/{skip-expression lambda => expression lambda}/output/tests/generator/expressionLambda/gen_input_test.py (100%) diff --git a/tests/resources/generation/expressions/skip-expression lambda/input.sdstest b/tests/resources/generation/expressions/expression lambda/input.sdstest similarity index 62% rename from tests/resources/generation/expressions/skip-expression lambda/input.sdstest rename to tests/resources/generation/expressions/expression lambda/input.sdstest index 6d49a6ce7..a9d56bb6b 100644 --- a/tests/resources/generation/expressions/skip-expression lambda/input.sdstest +++ b/tests/resources/generation/expressions/expression lambda/input.sdstest @@ -1,9 +1,9 @@ package tests.generator.expressionLambda fun f1(param: (a: Int, b: Int) -> r: Int) -fun f2(param: (a: Int, vararg c: Int) -> r: Int) +fun f2(param: (a: Int, c: Int) -> r: Int) pipeline test { f1((a, b = 2) -> 1); - f2((a, vararg c) -> 1); + f2((a, c) -> 1); } diff --git a/tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input.py b/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py similarity index 83% rename from tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input.py rename to tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py index 9399f7d79..ed2990afd 100644 --- a/tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input.py +++ b/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py @@ -2,4 +2,4 @@ def test(): f1(lambda a, b=2: 1) - f2(lambda a, *c: 1) + f2(lambda a, c: 1) diff --git a/tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input_test.py b/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/skip-expression lambda/output/tests/generator/expressionLambda/gen_input_test.py rename to tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py From 75196cbefbdcf223fd77823c37c50859a947a870 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:05:23 +0200 Subject: [PATCH 14/56] test: remove varargs from test "expressions/block lambda" and enable --- .../{skip-block lambda => block lambda}/input.sdstest | 4 ++-- .../output/tests/generator/blockLambda/gen_input.py | 2 +- .../output/tests/generator/blockLambda/gen_input_test.py | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/resources/generation/expressions/{skip-block lambda => block lambda}/input.sdstest (75%) rename tests/resources/generation/expressions/{skip-block lambda => block lambda}/output/tests/generator/blockLambda/gen_input.py (90%) rename tests/resources/generation/expressions/{skip-block lambda => block lambda}/output/tests/generator/blockLambda/gen_input_test.py (100%) diff --git a/tests/resources/generation/expressions/skip-block lambda/input.sdstest b/tests/resources/generation/expressions/block lambda/input.sdstest similarity index 75% rename from tests/resources/generation/expressions/skip-block lambda/input.sdstest rename to tests/resources/generation/expressions/block lambda/input.sdstest index 6105b6ad0..6f7f3073c 100644 --- a/tests/resources/generation/expressions/skip-block lambda/input.sdstest +++ b/tests/resources/generation/expressions/block lambda/input.sdstest @@ -1,7 +1,7 @@ package tests.generator.blockLambda fun f1(param: (a: Int, b: Int) -> r: Int) -fun f2(param: (a: Int, vararg b: Int) -> r: Int) +fun f2(param: (a: Int, b: Int) -> r: Int) fun f3(param: () -> ()) fun g() -> a: Int @@ -10,7 +10,7 @@ pipeline test { f1((a: Int, b: Int = 2) { yield d = g(); }); - f2((a: Int, vararg c: Int) { + f2((a: Int, c: Int) { yield d = g(); }); f3(() {}); diff --git a/tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py similarity index 90% rename from tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input.py rename to tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py index 57e2b1fcf..7f122eb63 100644 --- a/tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input.py +++ b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -5,7 +5,7 @@ def __block_lambda_0(a, b=2): d = g() return d f1(__block_lambda_0) - def __block_lambda_1(a, *c): + def __block_lambda_1(a, c): d = g() return d f2(__block_lambda_1) diff --git a/tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input_test.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/skip-block lambda/output/tests/generator/blockLambda/gen_input_test.py rename to tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py From b9c44ec11299199f67437064dac46b2d83b44ce6 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:07:07 +0200 Subject: [PATCH 15/56] test: remove varargs from test "expressions/call" and enable --- .../expressions/{skip-call => call}/input.sdstest | 8 ++++---- .../output/tests/generator/call/gen_input.py | 4 ++-- .../output/tests/generator/call/gen_input_test.py | 0 3 files changed, 6 insertions(+), 6 deletions(-) rename tests/resources/generation/expressions/{skip-call => call}/input.sdstest (80%) rename tests/resources/generation/expressions/{skip-call => call}/output/tests/generator/call/gen_input.py (83%) rename tests/resources/generation/expressions/{skip-call => call}/output/tests/generator/call/gen_input_test.py (100%) diff --git a/tests/resources/generation/expressions/skip-call/input.sdstest b/tests/resources/generation/expressions/call/input.sdstest similarity index 80% rename from tests/resources/generation/expressions/skip-call/input.sdstest rename to tests/resources/generation/expressions/call/input.sdstest index 655b2e2a7..0b3fe12ba 100644 --- a/tests/resources/generation/expressions/skip-call/input.sdstest +++ b/tests/resources/generation/expressions/call/input.sdstest @@ -9,7 +9,7 @@ fun g1( fun g2( param1: Int, - vararg param3: Int + param3: Int ) -> result: Boolean fun h1( @@ -19,14 +19,14 @@ fun h1( fun h2( @PythonName("param_1") param1: Int, - @PythonName("param_3") vararg param3: Int + @PythonName("param_3") param3: Int ) -> result: Boolean pipeline test { f((g1(1, 2))); f((g1(param2 = 1, param1 = 2))); - f((g2(2, 3, 4))); + f((g2(2, 3))); f((h1(1, 2))); f((h1(param2 = 1, param1 = 2))); - f((h2(2, 3, 4))); + f((h2(2, 3))); } diff --git a/tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input.py b/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py similarity index 83% rename from tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input.py rename to tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py index 37634f44a..3a46418ff 100644 --- a/tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input.py +++ b/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py @@ -3,7 +3,7 @@ def test(): f(g1(1, param2=2)) f(g1(2, param2=1)) - f(g2(2, 3, 4)) + f(g2(2, 3)) f(h1(1, param_2=2)) f(h1(2, param_2=1)) - f(h2(2, 3, 4)) + f(h2(2, 3)) diff --git a/tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input_test.py b/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/skip-call/output/tests/generator/call/gen_input_test.py rename to tests/resources/generation/expressions/call/output/tests/generator/call/gen_input_test.py From 0f3587d4626febe1f8601e01d29ae3ce07d4ca86 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:09:10 +0200 Subject: [PATCH 16/56] test: remove varargs from test "declarations/two steps" and enable --- .../declarations/{skip-two steps => two steps}/input.sdstest | 2 +- .../output/tests/generator/twoSteps/gen_input.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/resources/generation/declarations/{skip-two steps => two steps}/input.sdstest (72%) rename tests/resources/generation/declarations/{skip-two steps => two steps}/output/tests/generator/twoSteps/gen_input.py (86%) diff --git a/tests/resources/generation/declarations/skip-two steps/input.sdstest b/tests/resources/generation/declarations/two steps/input.sdstest similarity index 72% rename from tests/resources/generation/declarations/skip-two steps/input.sdstest rename to tests/resources/generation/declarations/two steps/input.sdstest index 8c21e4602..d265f8262 100644 --- a/tests/resources/generation/declarations/skip-two steps/input.sdstest +++ b/tests/resources/generation/declarations/two steps/input.sdstest @@ -6,6 +6,6 @@ segment test1(a: Int, b: Int = 0) { f(); } -segment test2(a: Int, vararg c: Int) { +segment test2(a: Int, c: Int) { f(); } diff --git a/tests/resources/generation/declarations/skip-two steps/output/tests/generator/twoSteps/gen_input.py b/tests/resources/generation/declarations/two steps/output/tests/generator/twoSteps/gen_input.py similarity index 86% rename from tests/resources/generation/declarations/skip-two steps/output/tests/generator/twoSteps/gen_input.py rename to tests/resources/generation/declarations/two steps/output/tests/generator/twoSteps/gen_input.py index bb0c1f51b..15d4deb31 100644 --- a/tests/resources/generation/declarations/skip-two steps/output/tests/generator/twoSteps/gen_input.py +++ b/tests/resources/generation/declarations/two steps/output/tests/generator/twoSteps/gen_input.py @@ -3,5 +3,5 @@ def test1(a, b=0): f() -def test2(a, *c): +def test2(a, c): f() From d928b05296f878168e64bc1cfc8246bb452a461e Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:11:34 +0200 Subject: [PATCH 17/56] test: remove varargs and lambda parameter annotations from test "declarations/parameter with python name" and enable --- .../parameter with python name/input.sdstest | 14 ++++++++++++++ .../generator/parameterWithPythonName/gen_input.py | 13 +++++++++++++ .../skip-parameter with python name/input.sdstest | 14 -------------- .../generator/parameterWithPythonName/gen_input.py | 13 ------------- 4 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 tests/resources/generation/declarations/parameter with python name/input.sdstest create mode 100644 tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py delete mode 100644 tests/resources/generation/declarations/skip-parameter with python name/input.sdstest delete mode 100644 tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py diff --git a/tests/resources/generation/declarations/parameter with python name/input.sdstest b/tests/resources/generation/declarations/parameter with python name/input.sdstest new file mode 100644 index 000000000..a05862f73 --- /dev/null +++ b/tests/resources/generation/declarations/parameter with python name/input.sdstest @@ -0,0 +1,14 @@ +package tests.generator.parameterWithPythonName + +fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) +fun f2(param: (a: Int, b: Int, c: Int) -> ()) + +segment test1(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { + f1((param1: Int, param2: Int, param3: Int = 0) -> 1); + f2((param1: Int, param2: Int, param3: Int = 0) {}); +} + +segment test2(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") param4: Int) { + f1((param1: Int, param2: Int, param4: Int) -> 1); + f2((param1: Int, param2: Int, param4: Int) {}); +} diff --git a/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py new file mode 100644 index 000000000..a1030c309 --- /dev/null +++ b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py @@ -0,0 +1,13 @@ +# Steps ------------------------------------------------------------------------ + +def test1(param1, param_2, param_3=0): + f1(lambda param1, param2, param3=0: 1) + def __block_lambda_0(param1, param2, param3=0): + pass + f2(__block_lambda_0) + +def test2(param1, param_2, param_4): + f1(lambda param1, param2, param4: 1) + def __block_lambda_0(param1, param2, param4): + pass + f2(__block_lambda_0) diff --git a/tests/resources/generation/declarations/skip-parameter with python name/input.sdstest b/tests/resources/generation/declarations/skip-parameter with python name/input.sdstest deleted file mode 100644 index 860e98ece..000000000 --- a/tests/resources/generation/declarations/skip-parameter with python name/input.sdstest +++ /dev/null @@ -1,14 +0,0 @@ -package tests.generator.parameterWithPythonName - -fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) -fun f2(param: (a: Int, b: Int, c: Int) -> ()) - -segment test1(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { - f1((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) -> 1); - f2((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) {}); -} - -segment test2(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) { - f1((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) -> 1); - f2((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) {}); -} diff --git a/tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py deleted file mode 100644 index 5a04fef8f..000000000 --- a/tests/resources/generation/declarations/skip-parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py +++ /dev/null @@ -1,13 +0,0 @@ -# Steps ------------------------------------------------------------------------ - -def test1(param1, param_2, param_3=0): - f1(lambda param1, param_2, param_3=0: 1) - def __block_lambda_0(param1, param_2, param_3=0): - pass - f2(__block_lambda_0) - -def test2(param1, param_2, *param_4): - f1(lambda param1, param_2, *param_4: 1) - def __block_lambda_0(param1, param_2, *param_4): - pass - f2(__block_lambda_0) From dc72b55f4a4440c8d23b68d956d1b8973a29e56c Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:16:52 +0200 Subject: [PATCH 18/56] test: remove placeholder saving from test "statements/assignment" and enable (as this will be implemented with memoization) --- .../{skip-assignment => assignment}/input.sdstest | 0 .../output/tests/generator/assignment/gen_input.py | 6 ------ .../tests/generator/assignment/gen_input_testPipeline.py | 0 3 files changed, 6 deletions(-) rename tests/resources/generation/statements/{skip-assignment => assignment}/input.sdstest (100%) rename tests/resources/generation/statements/{skip-assignment => assignment}/output/tests/generator/assignment/gen_input.py (72%) rename tests/resources/generation/statements/{skip-assignment => assignment}/output/tests/generator/assignment/gen_input_testPipeline.py (100%) diff --git a/tests/resources/generation/statements/skip-assignment/input.sdstest b/tests/resources/generation/statements/assignment/input.sdstest similarity index 100% rename from tests/resources/generation/statements/skip-assignment/input.sdstest rename to tests/resources/generation/statements/assignment/input.sdstest diff --git a/tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py similarity index 72% rename from tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input.py rename to tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py index 8c24c2309..f68fc3c51 100644 --- a/tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input.py +++ b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -1,7 +1,3 @@ -# Imports ---------------------------------------------------------------------- - -import runtimeBridge - # Steps ------------------------------------------------------------------------ def testStep(): @@ -17,9 +13,7 @@ def testStep(): def testPipeline(): g() a, _, _ = g() - runtimeBridge.save_placeholder('a', a) x, _, _ = g() - runtimeBridge.save_placeholder('x', x) f1(a) f1(x) def __block_lambda_0(): diff --git a/tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input_testPipeline.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input_testPipeline.py similarity index 100% rename from tests/resources/generation/statements/skip-assignment/output/tests/generator/assignment/gen_input_testPipeline.py rename to tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input_testPipeline.py From ce2485093624bf60c62600ab5cb6743f527129b9 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 02:25:52 +0200 Subject: [PATCH 19/56] test: convert varargs to list in test "expression/indexed access" and enable --- .../{skip-indexed access => indexed access}/input.sdstest | 2 +- .../output/tests/generator/indexedAccess/gen_input.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/resources/generation/expressions/{skip-indexed access => indexed access}/input.sdstest (69%) rename tests/resources/generation/expressions/{skip-indexed access => indexed access}/output/tests/generator/indexedAccess/gen_input.py (83%) diff --git a/tests/resources/generation/expressions/skip-indexed access/input.sdstest b/tests/resources/generation/expressions/indexed access/input.sdstest similarity index 69% rename from tests/resources/generation/expressions/skip-indexed access/input.sdstest rename to tests/resources/generation/expressions/indexed access/input.sdstest index 4532ca536..4b9c2a608 100644 --- a/tests/resources/generation/expressions/skip-indexed access/input.sdstest +++ b/tests/resources/generation/expressions/indexed access/input.sdstest @@ -2,6 +2,6 @@ package tests.generator.indexedAccess fun f(param: Any?) -segment test(vararg params: Int) { +segment test(params: List) { f(params[0]); } diff --git a/tests/resources/generation/expressions/skip-indexed access/output/tests/generator/indexedAccess/gen_input.py b/tests/resources/generation/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py similarity index 83% rename from tests/resources/generation/expressions/skip-indexed access/output/tests/generator/indexedAccess/gen_input.py rename to tests/resources/generation/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py index 2673de5a6..56096ed17 100644 --- a/tests/resources/generation/expressions/skip-indexed access/output/tests/generator/indexedAccess/gen_input.py +++ b/tests/resources/generation/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py @@ -1,4 +1,4 @@ # Steps ------------------------------------------------------------------------ -def test(*params): +def test(params): f(params[0]) From 4502b6ba5b78ea279134d5d33b8185127cb7a81e Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 14:01:19 +0200 Subject: [PATCH 20/56] feat: generate code for lists and maps test: test generation of lists and maps --- src/cli/generator.ts | 43 ++++++++----------- .../complex literals/input.sdstest | 15 +++++++ .../generator/complexLiterals/gen_input.py | 8 ++++ .../tests/generator/imports/gen_input_test.py | 4 ++ 4 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 tests/resources/generation/expressions/complex literals/input.sdstest create mode 100644 tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py create mode 100644 tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 92763fa0c..a38d7ef3b 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -279,6 +279,22 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } + // TODO move down again, when supported as constant expression + if (isSdsLiteral(expression) && (isSdsMap(expression) || isSdsList(expression))) { + switch (true) { + case isSdsMap(expression): + const map = expression as SdsMap; + const mapContent = map.entries.map( + (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, + ); + return `{${mapContent.join(', ')}}`; + case isSdsList(expression): + const list = expression as SdsList; + const listContent = list.elements.map((value) => generateExpression(value, frame)); + return `[${listContent.join(', ')}]`; + } + } + const potentialConstantExpression = toConstantExpressionOrUndefined(expression); if (potentialConstantExpression !== null) { switch (true) { @@ -300,27 +316,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } - if (isSdsLiteral(expression)) { - switch (true) { - // These should be handled by ConstantExpression - /*case isSdsBoolean(expression): - return expandToString`${(expression as SdsBoolean).value ? 'True' : 'False'}`; - case isSdsFloat(expression): - return expandToString`${(expression as SdsFloat).value}`; - case isSdsInt(expression): - return expandToString`${(expression as SdsInt).value}`; - case isSdsNull(expression): - return expandToString`None`; - case isSdsString(expression): - return expandToString`'${(expression as SdsString).value}'`;*/ - case isSdsMap(expression): - return `'${expression as SdsMap}'`; // TODO SdsMap?? - case isSdsList(expression): - return `'${expression as SdsList}'`; // TODO SdsList?? - default: - throw new Error(`Unknown SdsLiteral: ${expression}`); - } - } if (isSdsBlockLambda(expression)) { const blockLambda = expression as SdsBlockLambda; return frame.getUniqueLambdaBlockName(blockLambda); @@ -369,9 +364,9 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio const member = memberAccess.member.target.ref; const receiver = generateExpression(memberAccess.receiver, frame); switch (true) { - case isSdsBlockLambdaResult(member): - let res = member as SdsBlockLambdaResult; - return ''; + // TODO this should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else + /*case isSdsBlockLambdaResult(member): + return '';*/ case isSdsEnumVariant(member): const enumMember = generateExpression(memberAccess.member, frame); const suffix = isSdsCall(expression.$container) ? '' : '()'; diff --git a/tests/resources/generation/expressions/complex literals/input.sdstest b/tests/resources/generation/expressions/complex literals/input.sdstest new file mode 100644 index 000000000..1a261d93a --- /dev/null +++ b/tests/resources/generation/expressions/complex literals/input.sdstest @@ -0,0 +1,15 @@ +package tests.generator.complexLiterals + +fun f(param: List) + +fun g(param: Map) + +fun g2(param: Map) + +pipeline test { + f([]); + f([1, 2, 3]); + g({}); + g({"a": 1.2, "b": 1.0}); + g2({1.2: "a", 1.0: "b"}); +} diff --git a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py b/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py new file mode 100644 index 000000000..ff3f0ad98 --- /dev/null +++ b/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py @@ -0,0 +1,8 @@ +# Pipelines -------------------------------------------------------------------- + +def test(): + f([]) + f([1, 2, 3]) + g({}) + g({'a': 1.2, 'b': 1.0}) + g2({1.2: 'a', 1.0: 'b'}) diff --git a/tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py b/tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() From 3e2ecebbbd49ef231a58df11d5ba7fe713c199e9 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 14:44:47 +0200 Subject: [PATCH 21/56] test: use expressions for list and map literals in complex literal test --- .../expressions/complex literals/input.sdstest | 9 +++++++++ .../output/tests/generator/complexLiterals/gen_input.py | 3 +++ 2 files changed, 12 insertions(+) diff --git a/tests/resources/generation/expressions/complex literals/input.sdstest b/tests/resources/generation/expressions/complex literals/input.sdstest index 1a261d93a..3f0fc66b6 100644 --- a/tests/resources/generation/expressions/complex literals/input.sdstest +++ b/tests/resources/generation/expressions/complex literals/input.sdstest @@ -6,10 +6,19 @@ fun g(param: Map) fun g2(param: Map) +fun h1() -> result: Int + +fun h2() -> result: Float + +fun h3() -> result: String + pipeline test { f([]); f([1, 2, 3]); + f([1, h1(), h1() + 5]); g({}); g({"a": 1.2, "b": 1.0}); + g({h3(): -0.5, "b": h2()}); g2({1.2: "a", 1.0: "b"}); + g2({5.6: "c", h2(): h3()}); } diff --git a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py b/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py index ff3f0ad98..b0b2d5feb 100644 --- a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py +++ b/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py @@ -3,6 +3,9 @@ def test(): f([]) f([1, 2, 3]) + f([1, h1(), (h1()) + (5)]) g({}) g({'a': 1.2, 'b': 1.0}) + g({h3(): -0.5, 'b': h2()}) g2({1.2: 'a', 1.0: 'b'}) + g2({5.6: 'c', h2(): h3()}) From 68857e9da3c371c9d157547cb992a7766907e564 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 17:23:53 +0200 Subject: [PATCH 22/56] chore: refactor swich(true) to if-else, assignments should always contain expressions, use null-assertion instead of null-checking, where null is not possible --- src/cli/generator.ts | 205 ++++++++++++++++++------------------------- 1 file changed, 86 insertions(+), 119 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 4c6ab4e1d..75f856b53 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -150,22 +150,16 @@ const getPythonNameOrDefault = function ( }; const generateAssignee = function (assignee: SdsAssignee): string { - switch (true) { - case isSdsBlockLambdaResult(assignee): - return (assignee as SdsBlockLambdaResult).name; - case isSdsPlaceholder(assignee): - return (assignee as SdsPlaceholder).name; - case isSdsWildcard(assignee): - return '_'; - case isSdsYield(assignee): - const yieldResultName = (assignee).result?.ref?.name; - if (yieldResultName === undefined) { - throw new Error('The result of a yield should not be undefined'); - } - return yieldResultName; - default: - throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); - } + if (isSdsBlockLambdaResult(assignee)) { + return (assignee as SdsBlockLambdaResult).name; + } else if (isSdsPlaceholder(assignee)) { + return (assignee as SdsPlaceholder).name; + } else if (isSdsWildcard(assignee)) { + return '_'; + } else if (isSdsYield(assignee)) { + return (assignee).result?.ref?.name!; + } + throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); }; const generateAssignment = function (assignment: SdsAssignment, frame: GenerationInfoFrame): string { @@ -178,44 +172,31 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio if (assignees.some((value) => !isSdsWildcard(value))) { const actualAssignees = assignees.map(generateAssignee); if (requiredAssignees === actualAssignees.length) { - if (assignment.expression !== undefined) { - return `${actualAssignees.join(', ')} = ${generateExpression(assignment.expression, frame)}`; - } - return actualAssignees.join(', '); + return `${actualAssignees.join(', ')} = ${generateExpression(assignment.expression!, frame)}`; } else { // Add wildcards to match given results - if (assignment.expression !== undefined) { - return `${actualAssignees - .concat(Array(requiredAssignees - actualAssignees.length).fill('_')) - .join(', ')} = ${generateExpression(assignment.expression, frame)}`; - } else { - return actualAssignees.concat(Array(requiredAssignees - actualAssignees.length).fill('_')).join(', '); - } + return `${actualAssignees + .concat(Array(requiredAssignees - actualAssignees.length).fill('_')) + .join(', ')} = ${generateExpression(assignment.expression!, frame)}`; } } else { - if (assignment.expression !== undefined) { - return generateExpression(assignment.expression, frame); - } - // Only wildcard and no expression - return ''; + return generateExpression(assignment.expression!, frame); } }; const generateStatement = function (statement: SdsStatement, frame: GenerationInfoFrame): string { - switch (true) { - case isSdsAssignment(statement): - return generateAssignment(statement as SdsAssignment, frame); - case isSdsExpressionStatement(statement): - const expressionStatement = statement as SdsExpressionStatement; - const blockLambdaCode: string[] = []; - for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { - blockLambdaCode.push(generateBlockLambda(lambda, frame)); - } - blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); - return expandToString`${blockLambdaCode.join('\n')}`; - default: - throw new Error(`Unknown SdsStatement: ${statement}`); + if (isSdsAssignment(statement)) { + return generateAssignment(statement as SdsAssignment, frame); + } else if (isSdsExpressionStatement(statement)) { + const expressionStatement = statement as SdsExpressionStatement; + const blockLambdaCode: string[] = []; + for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { + blockLambdaCode.push(generateBlockLambda(lambda, frame)); + } + blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); + return expandToString`${blockLambdaCode.join('\n')}`; } + throw new Error(`Unknown SdsStatement: ${statement}`); }; const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: GenerationInfoFrame): string { @@ -268,51 +249,45 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } if (isSdsTemplateStringPart(expression)) { - // TODO: really replace both line endings? - switch (true) { - case isSdsTemplateStringStart(expression): - return `${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}{ `; - case isSdsTemplateStringInner(expression): - return ` }${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}{ `; - case isSdsTemplateStringEnd(expression): - return ` }${expression.value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n')}`; + if (isSdsTemplateStringStart(expression)) { + return `${formatStringSingleLine(expression.value)}{ `; + } else if (isSdsTemplateStringInner(expression)) { + return ` }${formatStringSingleLine(expression.value)}{ `; + } else if (isSdsTemplateStringEnd(expression)) { + return ` }${formatStringSingleLine(expression.value)}`; } } // TODO move down again, when supported as constant expression if (isSdsLiteral(expression) && (isSdsMap(expression) || isSdsList(expression))) { - switch (true) { - case isSdsMap(expression): - const map = expression as SdsMap; - const mapContent = map.entries.map( - (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, - ); - return `{${mapContent.join(', ')}}`; - case isSdsList(expression): - const list = expression as SdsList; - const listContent = list.elements.map((value) => generateExpression(value, frame)); - return `[${listContent.join(', ')}]`; + if (isSdsMap(expression)) { + const map = expression as SdsMap; + const mapContent = map.entries.map( + (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, + ); + return `{${mapContent.join(', ')}}`; + } else if (isSdsList(expression)) { + const list = expression as SdsList; + const listContent = list.elements.map((value) => generateExpression(value, frame)); + return `[${listContent.join(', ')}]`; } } const potentialConstantExpression = toConstantExpression(expression); if (potentialConstantExpression !== null) { - switch (true) { - case potentialConstantExpression instanceof ConstantBoolean: - return (potentialConstantExpression as ConstantBoolean).value ? 'True' : 'False'; - case potentialConstantExpression instanceof ConstantInt: - return String((potentialConstantExpression as ConstantInt).value); - case potentialConstantExpression instanceof ConstantFloat: - const floatValue = (potentialConstantExpression as ConstantFloat).value; - return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); - case potentialConstantExpression === ConstantNull: - return 'None'; - case potentialConstantExpression instanceof ConstantString: - return `'${(potentialConstantExpression as ConstantString).value - .replaceAll('\r\n', '\\n') - .replaceAll('\n', '\\n')}'`; - case potentialConstantExpression instanceof ConstantEnumVariant: - return String((potentialConstantExpression as ConstantEnumVariant).value); // TODO SdsConstantEnumVariant?? generate something useful + if (potentialConstantExpression instanceof ConstantBoolean) { + return (potentialConstantExpression as ConstantBoolean).value ? 'True' : 'False'; + } else if (potentialConstantExpression instanceof ConstantInt) { + return String((potentialConstantExpression as ConstantInt).value); + } else if (potentialConstantExpression instanceof ConstantFloat) { + const floatValue = (potentialConstantExpression as ConstantFloat).value; + return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); + } else if (potentialConstantExpression === ConstantNull) { + return 'None'; + } else if (potentialConstantExpression instanceof ConstantString) { + return `'${formatStringSingleLine((potentialConstantExpression as ConstantString).value)}'`; + } else if (potentialConstantExpression instanceof ConstantEnumVariant) { + return (potentialConstantExpression as ConstantEnumVariant).value.name; // TODO correct interpretation of SdsEnumVariant / SdsConstantEnumVariant? } } @@ -363,34 +338,31 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio let memberAccess = expression as SdsMemberAccess; const member = memberAccess.member.target.ref; const receiver = generateExpression(memberAccess.receiver, frame); - switch (true) { - // TODO this should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else - /*case isSdsBlockLambdaResult(member): - return '';*/ - case isSdsEnumVariant(member): - const enumMember = generateExpression(memberAccess.member, frame); - const suffix = isSdsCall(expression.$container) ? '' : '()'; - if (expression.isNullSafe) { - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); - return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${enumMember}')${suffix}`; - } else { - return `${receiver}.${enumMember}${suffix}`; - } - case isSdsResult(member): - const resultList = (member).$container.results; - if (resultList.length === 1) { - return receiver; - } - const currentIndex = resultList.indexOf(member); - return `${receiver}[${currentIndex}]`; - default: - const memberExpression = generateExpression(memberAccess.member, frame); - if (expression.isNullSafe) { - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); - return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; - } else { - return `${receiver}.${memberExpression}`; - } + // TODO SdsBlockLambdaResult should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else + if (isSdsEnumVariant(member)) { + const enumMember = generateExpression(memberAccess.member, frame); + const suffix = isSdsCall(expression.$container) ? '' : '()'; + if (expression.isNullSafe) { + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${enumMember}')${suffix}`; + } else { + return `${receiver}.${enumMember}${suffix}`; + } + } else if (isSdsResult(member)) { + const resultList = (member).$container.results; + if (resultList.length === 1) { + return receiver; + } + const currentIndex = resultList.indexOf(member); + return `${receiver}[${currentIndex}]`; + } else { + const memberExpression = generateExpression(memberAccess.member, frame); + if (expression.isNullSafe) { + frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; + } else { + return `${receiver}.${memberExpression}`; + } } } if (isSdsParenthesizedExpression(expression)) { @@ -403,22 +375,16 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return expandToString`not (${operand})`; case '-': return expandToString`-(${operand})`; - default: - throw new Error('Unknown Prefix Operation Expression'); } } if (isSdsReference(expression)) { - const declaration = (expression as SdsReference).target.ref; - if (declaration === undefined) { - throw new Error('Unknown corresponding declaration to reference'); - } + const declaration = (expression as SdsReference).target.ref!; const referenceImport = getExternalReferenceNeededImport(frame.getServices(), expression, declaration) || getInternalReferenceNeededImport(frame.getServices(), expression, declaration); frame.addImport(referenceImport); return referenceImport?.alias || getPythonNameOrDefault(frame.getServices(), declaration); } - // SdsArgument' | 'SdsChainedExpression' | 'SdsLambda' throw new Error(`Unknown expression type: ${expression.$type}`); }; @@ -486,14 +452,11 @@ const getModuleFileBaseName = function (module: SdsModule): string { }; const generateParameter = function ( - parameter: SdsParameter | undefined, + parameter: SdsParameter, frame: GenerationInfoFrame, defaultValue: boolean = true, ): string { // TODO isConstant? - if (parameter === undefined) { - return ''; - } return expandToString`${getPythonNameOrDefault(frame.getServices(), parameter)}${ defaultValue && parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue, frame) @@ -618,6 +581,10 @@ const formatGeneratedFileName = function (baseName: string): string { return `gen_${baseName.replaceAll('%2520', '_').replaceAll(/[ .-]/gu, '_').replaceAll(/\\W/gu, '')}`; }; +const formatStringSingleLine = function (value: string): string { + return value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n'); +}; + export const generatePython = function ( services: SafeDsServices, module: SdsModule, From b64e2693a371ed4ede0e22779918f49cb24b4fd5 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 17:32:55 +0200 Subject: [PATCH 23/56] fix: member expressions may be undefined in ast, but should be defined when generating --- src/cli/generator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 75f856b53..4f79d7442 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -336,11 +336,11 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } if (isSdsMemberAccess(expression)) { let memberAccess = expression as SdsMemberAccess; - const member = memberAccess.member.target.ref; + const member = memberAccess.member?.target.ref!; const receiver = generateExpression(memberAccess.receiver, frame); // TODO SdsBlockLambdaResult should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else if (isSdsEnumVariant(member)) { - const enumMember = generateExpression(memberAccess.member, frame); + const enumMember = generateExpression(memberAccess.member!, frame); const suffix = isSdsCall(expression.$container) ? '' : '()'; if (expression.isNullSafe) { frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); @@ -356,7 +356,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio const currentIndex = resultList.indexOf(member); return `${receiver}[${currentIndex}]`; } else { - const memberExpression = generateExpression(memberAccess.member, frame); + const memberExpression = generateExpression(memberAccess.member!, frame); if (expression.isNullSafe) { frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; From 6c00c4479097d3109a8880d43f432458dab2666a Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Tue, 17 Oct 2023 23:47:45 +0200 Subject: [PATCH 24/56] chore: removed unneeded type assertions; optimize getting pipelines and segments Co-authored-by: Lars Reimann --- src/cli/generator.ts | 70 ++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 4f79d7442..820900fdd 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -36,23 +36,14 @@ import { SdsAssignment, SdsBlock, SdsBlockLambda, - SdsBlockLambdaResult, SdsDeclaration, SdsExpression, - SdsExpressionStatement, - SdsList, - SdsMap, - SdsMemberAccess, SdsModule, SdsParameter, SdsParameterList, SdsPipeline, - SdsPlaceholder, - SdsReference, - SdsResult, SdsSegment, SdsStatement, - SdsYield, } from '../language/generated/ast.js'; import {extractAstNode, extractDestinationAndName} from './cli-util.js'; import chalk from 'chalk'; @@ -74,6 +65,7 @@ import { importedDeclarationsOrEmpty, importsOrEmpty, isRequiredParameter, + moduleMembersOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; import {group} from 'radash'; @@ -151,13 +143,13 @@ const getPythonNameOrDefault = function ( const generateAssignee = function (assignee: SdsAssignee): string { if (isSdsBlockLambdaResult(assignee)) { - return (assignee as SdsBlockLambdaResult).name; + return assignee.name; } else if (isSdsPlaceholder(assignee)) { - return (assignee as SdsPlaceholder).name; + return assignee.name; } else if (isSdsWildcard(assignee)) { return '_'; } else if (isSdsYield(assignee)) { - return (assignee).result?.ref?.name!; + return assignee.result?.ref?.name!; } throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); }; @@ -186,9 +178,9 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio const generateStatement = function (statement: SdsStatement, frame: GenerationInfoFrame): string { if (isSdsAssignment(statement)) { - return generateAssignment(statement as SdsAssignment, frame); + return generateAssignment(statement, frame); } else if (isSdsExpressionStatement(statement)) { - const expressionStatement = statement as SdsExpressionStatement; + const expressionStatement = statement; const blockLambdaCode: string[] = []; for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { blockLambdaCode.push(generateBlockLambda(lambda, frame)); @@ -261,14 +253,12 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio // TODO move down again, when supported as constant expression if (isSdsLiteral(expression) && (isSdsMap(expression) || isSdsList(expression))) { if (isSdsMap(expression)) { - const map = expression as SdsMap; - const mapContent = map.entries.map( + const mapContent = expression.entries.map( (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, ); return `{${mapContent.join(', ')}}`; } else if (isSdsList(expression)) { - const list = expression as SdsList; - const listContent = list.elements.map((value) => generateExpression(value, frame)); + const listContent = expression.elements.map((value) => generateExpression(value, frame)); return `[${listContent.join(', ')}]`; } } @@ -276,24 +266,23 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio const potentialConstantExpression = toConstantExpression(expression); if (potentialConstantExpression !== null) { if (potentialConstantExpression instanceof ConstantBoolean) { - return (potentialConstantExpression as ConstantBoolean).value ? 'True' : 'False'; + return potentialConstantExpression.value ? 'True' : 'False'; } else if (potentialConstantExpression instanceof ConstantInt) { - return String((potentialConstantExpression as ConstantInt).value); + return String(potentialConstantExpression.value); } else if (potentialConstantExpression instanceof ConstantFloat) { - const floatValue = (potentialConstantExpression as ConstantFloat).value; + const floatValue = potentialConstantExpression.value; return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); } else if (potentialConstantExpression === ConstantNull) { return 'None'; } else if (potentialConstantExpression instanceof ConstantString) { - return `'${formatStringSingleLine((potentialConstantExpression as ConstantString).value)}'`; + return `'${formatStringSingleLine(potentialConstantExpression.value)}'`; } else if (potentialConstantExpression instanceof ConstantEnumVariant) { - return (potentialConstantExpression as ConstantEnumVariant).value.name; // TODO correct interpretation of SdsEnumVariant / SdsConstantEnumVariant? + return potentialConstantExpression.value.name; // TODO correct interpretation of SdsEnumVariant / SdsConstantEnumVariant? } } if (isSdsBlockLambda(expression)) { - const blockLambda = expression as SdsBlockLambda; - return frame.getUniqueLambdaBlockName(blockLambda); + return frame.getUniqueLambdaBlockName(expression); } if (isSdsCall(expression)) { const sortedArgs = sortArguments(frame.getServices(), expression.argumentList.arguments); @@ -335,12 +324,11 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio )}]`; } if (isSdsMemberAccess(expression)) { - let memberAccess = expression as SdsMemberAccess; - const member = memberAccess.member?.target.ref!; - const receiver = generateExpression(memberAccess.receiver, frame); + const member = expression.member?.target.ref!; + const receiver = generateExpression(expression.receiver, frame); // TODO SdsBlockLambdaResult should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else if (isSdsEnumVariant(member)) { - const enumMember = generateExpression(memberAccess.member!, frame); + const enumMember = generateExpression(expression.member!, frame); const suffix = isSdsCall(expression.$container) ? '' : '()'; if (expression.isNullSafe) { frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); @@ -349,14 +337,14 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return `${receiver}.${enumMember}${suffix}`; } } else if (isSdsResult(member)) { - const resultList = (member).$container.results; + const resultList = member.$container.results; if (resultList.length === 1) { return receiver; } - const currentIndex = resultList.indexOf(member); + const currentIndex = resultList.indexOf(member); return `${receiver}[${currentIndex}]`; } else { - const memberExpression = generateExpression(memberAccess.member!, frame); + const memberExpression = generateExpression(expression.member!, frame); if (expression.isNullSafe) { frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; @@ -378,7 +366,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } if (isSdsReference(expression)) { - const declaration = (expression as SdsReference).target.ref!; + const declaration = expression.target.ref!; const referenceImport = getExternalReferenceNeededImport(frame.getServices(), expression, declaration) || getInternalReferenceNeededImport(frame.getServices(), expression, declaration); @@ -393,6 +381,7 @@ const getExternalReferenceNeededImport = function ( expression: SdsExpression, declaration: SdsDeclaration, ): ImportData | undefined { + // Root Node is always a module. const currentModule = findRootNode(expression); const targetModule = findRootNode(declaration); for (const value of importsOrEmpty(currentModule)) { @@ -432,6 +421,7 @@ const getInternalReferenceNeededImport = function ( expression: SdsExpression, declaration: SdsDeclaration, ): ImportData | undefined { + // Root Node is always a module. const currentModule = findRootNode(expression); const targetModule = findRootNode(declaration); if (currentModule !== targetModule && !isInStubFile(targetModule)) { @@ -530,11 +520,11 @@ const generateImports = function (importSet: ImportData[]): string[] { const importedDecls = value ?.filter((importData) => importData !== undefined) - .sort((a, b) => (a.declarationName).localeCompare(b.declarationName)) + .sort((a, b) => a.declarationName!.localeCompare(b.declarationName!)) .map((localValue) => localValue.alias !== undefined ? `${localValue.declarationName} as ${localValue.alias}` - : localValue.declarationName, + : localValue.declarationName!, ) || []; declaredImports.push(`from ${key} import ${[...new Set(importedDecls)].join(', ')}`); } @@ -543,14 +533,12 @@ const generateImports = function (importSet: ImportData[]): string[] { const generateModule = function (services: SafeDsServices, module: SdsModule): string { const importSet = new Map(); - const segments = streamAllContents(module) + const segments = moduleMembersOrEmpty(module) .filter(isSdsSegment) - .map((segment) => generateSegment(services, segment, importSet)) - .toArray(); - const pipelines = streamAllContents(module) + .map((segment) => generateSegment(services, segment, importSet)); + const pipelines = moduleMembersOrEmpty(module) .filter(isSdsPipeline) - .map((pipeline) => generatePipeline(services, pipeline, importSet)) - .toArray(); + .map((pipeline) => generatePipeline(services, pipeline, importSet)); const imports = generateImports(Array.from(importSet.values())); const output: string[] = []; if (imports.length > 0) { From ac6d7cc03b2c828980ae1b4872b065fa7b67f885 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Wed, 18 Oct 2023 00:01:53 +0200 Subject: [PATCH 25/56] chore: reorder functions to be more readable --- src/cli/generator.ts | 530 +++++++++++++++++++++---------------------- 1 file changed, 263 insertions(+), 267 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 820900fdd..49dda0c0d 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import {expandToString, expandToStringWithNL, findRootNode, getDocument, streamAllContents} from 'langium'; +import { expandToString, expandToStringWithNL, findRootNode, getDocument, streamAllContents } from 'langium'; import path from 'path'; import { isSdsAssignment, @@ -45,11 +45,11 @@ import { SdsSegment, SdsStatement, } from '../language/generated/ast.js'; -import {extractAstNode, extractDestinationAndName} from './cli-util.js'; +import { extractAstNode, extractDestinationAndName } from './cli-util.js'; import chalk from 'chalk'; -import {createSafeDsServices, SafeDsServices} from '../language/safe-ds-module.js'; -import {NodeFileSystem} from 'langium/node'; -import {toConstantExpression} from '../language/partialEvaluation/toConstantExpression.js'; +import { createSafeDsServices, SafeDsServices } from '../language/safe-ds-module.js'; +import { NodeFileSystem } from 'langium/node'; +import { toConstantExpression } from '../language/partialEvaluation/toConstantExpression.js'; import { ConstantBoolean, ConstantEnumVariant, @@ -68,9 +68,12 @@ import { moduleMembersOrEmpty, statementsOrEmpty, } from '../language/helpers/nodeProperties.js'; -import {group} from 'radash'; -import {IdManager} from '../language/helpers/idManager.js'; -import {isInStubFile} from '../language/helpers/fileExtensions.js'; +import { group } from 'radash'; +import { IdManager } from '../language/helpers/idManager.js'; +import { isInStubFile } from '../language/helpers/fileExtensions.js'; + +const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; +const PYTHON_INDENT = ' '; /* c8 ignore start */ export const generateAction = async (fileName: string, opts: GenerateOptions): Promise => { @@ -82,76 +85,205 @@ export const generateAction = async (fileName: string, opts: GenerateOptions): P }; /* c8 ignore stop */ -const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; -const PYTHON_INDENT = ' '; - -class GenerationInfoFrame { - services: SafeDsServices; - blockLambdaManager: IdManager; - importSet: Map; +export const generatePython = function ( + services: SafeDsServices, + module: SdsModule, + filePath: string, + destination: string | undefined, +): string[] { + // Do not generate stub files + if (isInStubFile(module)) { + return []; + } + const data = extractDestinationAndName(filePath, destination); + const pythonModuleName = services.builtins.Annotations.getPythonModule(module); + const packagePath = pythonModuleName === undefined ? module.name.split('.') : [pythonModuleName]; + const parentDirectoryPath = path.join(data.destination, ...packagePath); - constructor(services: SafeDsServices, importSet: Map = new Map()) { - this.services = services; - this.blockLambdaManager = new IdManager(); - this.importSet = importSet; + const generatedFiles = new Map(); + generatedFiles.set( + `${path.join(parentDirectoryPath, formatGeneratedFileName(data.name))}.py`, + generateModule(services, module), + ); + for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { + const entryPointFilename = `${path.join( + parentDirectoryPath, + `${formatGeneratedFileName(data.name)}_${getPythonNameOrDefault(services, pipeline)}`, + )}.py`; + const entryPointContent = expandToStringWithNL`from ${formatGeneratedFileName( + data.name, + )} import ${getPythonNameOrDefault( + services, + pipeline, + )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(services, pipeline)}()`; + generatedFiles.set(entryPointFilename, entryPointContent); + } + if (!fs.existsSync(parentDirectoryPath)) { + fs.mkdirSync(parentDirectoryPath, { recursive: true }); } + for (const [generatedFilePath, generatedFileContent] of generatedFiles.entries()) { + fs.writeFileSync(generatedFilePath, generatedFileContent); + } + return [...generatedFiles.keys()]; +}; - addImport(importData: ImportData | undefined) { - if (importData) { - const hashKey = JSON.stringify(importData); - if (!this.importSet.has(hashKey)) { - this.importSet.set(hashKey, importData); - } - } +const getPythonNameOrDefault = function ( + services: SafeDsServices, + object: SdsPipeline | SdsSegment | SdsParameter | SdsDeclaration, +) { + return services.builtins.Annotations.getPythonName(object) || object.name; +}; + +const formatGeneratedFileName = function (baseName: string): string { + return `gen_${baseName.replaceAll('%2520', '_').replaceAll(/[ .-]/gu, '_').replaceAll(/\\W/gu, '')}`; +}; + +const generateModule = function (services: SafeDsServices, module: SdsModule): string { + const importSet = new Map(); + const segments = moduleMembersOrEmpty(module) + .filter(isSdsSegment) + .map((segment) => generateSegment(services, segment, importSet)); + const pipelines = moduleMembersOrEmpty(module) + .filter(isSdsPipeline) + .map((pipeline) => generatePipeline(services, pipeline, importSet)); + const imports = generateImports(Array.from(importSet.values())); + const output: string[] = []; + if (imports.length > 0) { + output.push( + expandToStringWithNL`# Imports ----------------------------------------------------------------------\n\n${imports.join( + '\n', + )}`, + ); + } + if (segments.length > 0) { + output.push( + expandToStringWithNL`# Steps ------------------------------------------------------------------------\n\n${segments.join( + '\n\n', + )}`, + ); + } + if (pipelines.length > 0) { + output.push( + expandToStringWithNL`# Pipelines --------------------------------------------------------------------\n\n${pipelines.join( + '\n\n', + )}`, + ); } + return expandToStringWithNL`${output.join('\n')}`; +}; - getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { - return `__block_lambda_${this.blockLambdaManager.assignId(lambda)}`; +const generateSegment = function ( + services: SafeDsServices, + segment: SdsSegment, + importSet: Map, +): string { + const infoFrame = new GenerationInfoFrame(services, importSet); + const segmentResult = segment.resultList?.results || []; + let segmentBlock = generateBlock(segment.body, infoFrame); + if (segmentResult.length !== 0) { + // Segment should always have results + segmentBlock += `\nreturn ${segmentResult.map((result) => result.name).join(', ')}`; } + return expandToString`def ${getPythonNameOrDefault(services, segment)}(${generateParameters( + segment.parameterList, + infoFrame, + )}):\n${PYTHON_INDENT}${segmentBlock}`; +}; - getServices(): SafeDsServices { - return this.services; +const generateParameters = function (parameters: SdsParameterList | undefined, frame: GenerationInfoFrame): string { + if (parameters === undefined) { + return ''; } -} + const result = parameters.parameters.map((param) => generateParameter(param, frame)); + return result.join(', '); +}; -class ImportData { - importPath: string; - declarationName: string | undefined; - alias: string | undefined; +const generateParameter = function ( + parameter: SdsParameter, + frame: GenerationInfoFrame, + defaultValue: boolean = true, +): string { + // TODO isConstant? + return expandToString`${getPythonNameOrDefault(frame.getServices(), parameter)}${ + defaultValue && parameter.defaultValue !== undefined + ? '=' + generateExpression(parameter.defaultValue, frame) + : '' + }`; +}; - constructor( - importPath: string, - declarationName: string | undefined = undefined, - alias: string | undefined = undefined, - ) { - this.importPath = importPath; - this.declarationName = declarationName; - this.alias = alias; +const generatePipeline = function ( + services: SafeDsServices, + pipeline: SdsPipeline, + importSet: Map, +): string { + const infoFrame = new GenerationInfoFrame(services, importSet); + return expandToString`def ${getPythonNameOrDefault(services, pipeline)}():\n${PYTHON_INDENT}${generateBlock( + pipeline.body, + infoFrame, + )}`; +}; + +const generateImports = function (importSet: ImportData[]): string[] { + const qualifiedImports = Array.from(importSet) + .filter((importStmt) => importStmt.declarationName === undefined) + .sort((a, b) => a.importPath.localeCompare(b.importPath)) + .map(generateImport); + const groupedImports = Object.entries( + group( + Array.from(importSet).filter((importStmt) => importStmt.declarationName !== undefined), + (importStmt) => importStmt.importPath, + ), + ).sort(([key1, _value1], [key2, _value2]) => key1.localeCompare(key2)); + const declaredImports: string[] = []; + for (const [key, value] of groupedImports) { + const importedDecls = + value + ?.filter((importData) => importData !== undefined) + .sort((a, b) => a.declarationName!.localeCompare(b.declarationName!)) + .map((localValue) => + localValue.alias !== undefined + ? `${localValue.declarationName} as ${localValue.alias}` + : localValue.declarationName!, + ) || []; + declaredImports.push(`from ${key} import ${[...new Set(importedDecls)].join(', ')}`); } -} + return [...new Set(qualifiedImports), ...new Set(declaredImports)]; +}; -export type GenerateOptions = { - destination?: string; +const generateImport = function (importStmt: ImportData): string { + if (importStmt.declarationName === undefined && importStmt.alias === undefined) { + return `import ${importStmt.importPath}`; + } else if (importStmt.declarationName === undefined && importStmt.alias !== undefined) { + return `import ${importStmt.importPath} as ${importStmt.alias}`; + } else if (importStmt.declarationName !== undefined && importStmt.alias === undefined) { + return `from ${importStmt.importPath} import ${importStmt.declarationName}`; + } else { + return `from ${importStmt.importPath} import ${importStmt.declarationName} as ${importStmt.alias}`; + } }; -const getPythonNameOrDefault = function ( - services: SafeDsServices, - object: SdsPipeline | SdsSegment | SdsParameter | SdsDeclaration, -) { - return services.builtins.Annotations.getPythonName(object) || object.name; +const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): string { + // TODO filter withEffect + let statements = statementsOrEmpty(block); + if (statements.length === 0) { + return 'pass'; + } + return expandToString`${statements.map((stmt) => generateStatement(stmt, frame)).join('\n')}`; }; -const generateAssignee = function (assignee: SdsAssignee): string { - if (isSdsBlockLambdaResult(assignee)) { - return assignee.name; - } else if (isSdsPlaceholder(assignee)) { - return assignee.name; - } else if (isSdsWildcard(assignee)) { - return '_'; - } else if (isSdsYield(assignee)) { - return assignee.result?.ref?.name!; +const generateStatement = function (statement: SdsStatement, frame: GenerationInfoFrame): string { + if (isSdsAssignment(statement)) { + return generateAssignment(statement, frame); + } else if (isSdsExpressionStatement(statement)) { + const expressionStatement = statement; + const blockLambdaCode: string[] = []; + for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { + blockLambdaCode.push(generateBlockLambda(lambda, frame)); + } + blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); + return expandToString`${blockLambdaCode.join('\n')}`; } - throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); + throw new Error(`Unknown SdsStatement: ${statement}`); }; const generateAssignment = function (assignment: SdsAssignment, frame: GenerationInfoFrame): string { @@ -176,19 +308,17 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio } }; -const generateStatement = function (statement: SdsStatement, frame: GenerationInfoFrame): string { - if (isSdsAssignment(statement)) { - return generateAssignment(statement, frame); - } else if (isSdsExpressionStatement(statement)) { - const expressionStatement = statement; - const blockLambdaCode: string[] = []; - for (const lambda of streamAllContents(expressionStatement.expression).filter(isSdsBlockLambda)) { - blockLambdaCode.push(generateBlockLambda(lambda, frame)); - } - blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); - return expandToString`${blockLambdaCode.join('\n')}`; +const generateAssignee = function (assignee: SdsAssignee): string { + if (isSdsBlockLambdaResult(assignee)) { + return assignee.name; + } else if (isSdsPlaceholder(assignee)) { + return assignee.name; + } else if (isSdsWildcard(assignee)) { + return '_'; + } else if (isSdsYield(assignee)) { + return assignee.result?.ref?.name!; } - throw new Error(`Unknown SdsStatement: ${statement}`); + throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); }; const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: GenerationInfoFrame): string { @@ -203,38 +333,6 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera )}):\n${PYTHON_INDENT}${lambdaBlock}`; }; -const generateBlock = function (block: SdsBlock, frame: GenerationInfoFrame): string { - // TODO filter withEffect - let statements = statementsOrEmpty(block); - if (statements.length === 0) { - return 'pass'; - } - return expandToString`${statements.map((stmt) => generateStatement(stmt, frame)).join('\n')}`; -}; - -const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { - const parameter = frame.getServices().helpers.NodeMapper.argumentToParameterOrUndefined(argument); - return expandToString`${ - parameter !== undefined && !isRequiredParameter(parameter) - ? generateParameter(parameter, frame, false) + '=' - : '' - }${generateExpression(argument.value, frame)}`; -}; - -const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[]): SdsArgument[] { - // $containerIndex contains the index of the parameter in the receivers parameter list - const parameters = argumentList.map((argument) => { - return { par: services.helpers.NodeMapper.argumentToParameterOrUndefined(argument), arg: argument }; - }); - return parameters - .slice() - .filter((value) => value.par !== undefined) - .sort((a, b) => - a.par !== undefined && b.par !== undefined ? a.par.$containerIndex! - b.par.$containerIndex! : 0, - ) - .map((value) => value.arg); -}; - const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { if (isSdsTemplateString(expression)) { return `f'${expression.expressions.map((expr) => generateExpression(expr, frame)).join('')}'`; @@ -376,6 +474,29 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio throw new Error(`Unknown expression type: ${expression.$type}`); }; +const sortArguments = function (services: SafeDsServices, argumentList: SdsArgument[]): SdsArgument[] { + // $containerIndex contains the index of the parameter in the receivers parameter list + const parameters = argumentList.map((argument) => { + return { par: services.helpers.NodeMapper.argumentToParameterOrUndefined(argument), arg: argument }; + }); + return parameters + .slice() + .filter((value) => value.par !== undefined) + .sort((a, b) => + a.par !== undefined && b.par !== undefined ? a.par.$containerIndex! - b.par.$containerIndex! : 0, + ) + .map((value) => value.arg); +}; + +const generateArgument = function (argument: SdsArgument, frame: GenerationInfoFrame) { + const parameter = frame.getServices().helpers.NodeMapper.argumentToParameterOrUndefined(argument); + return expandToString`${ + parameter !== undefined && !isRequiredParameter(parameter) + ? generateParameter(parameter, frame, false) + '=' + : '' + }${generateExpression(argument.value, frame)}`; +}; + const getExternalReferenceNeededImport = function ( services: SafeDsServices, expression: SdsExpression, @@ -441,180 +562,55 @@ const getModuleFileBaseName = function (module: SdsModule): string { return path.basename(filePath, path.extname(filePath)); }; -const generateParameter = function ( - parameter: SdsParameter, - frame: GenerationInfoFrame, - defaultValue: boolean = true, -): string { - // TODO isConstant? - return expandToString`${getPythonNameOrDefault(frame.getServices(), parameter)}${ - defaultValue && parameter.defaultValue !== undefined - ? '=' + generateExpression(parameter.defaultValue, frame) - : '' - }`; +const formatStringSingleLine = function (value: string): string { + return value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n'); }; -const generateParameters = function (parameters: SdsParameterList | undefined, frame: GenerationInfoFrame): string { - if (parameters === undefined) { - return ''; - } - const result = parameters.parameters.map((param) => generateParameter(param, frame)); - return result.join(', '); -}; +class ImportData { + importPath: string; + declarationName: string | undefined; + alias: string | undefined; -const generateSegment = function ( - services: SafeDsServices, - segment: SdsSegment, - importSet: Map, -): string { - const infoFrame = new GenerationInfoFrame(services, importSet); - const segmentResult = segment.resultList?.results || []; - let segmentBlock = generateBlock(segment.body, infoFrame); - if (segmentResult.length !== 0) { - // Segment should always have results - segmentBlock += `\nreturn ${segmentResult.map((result) => result.name).join(', ')}`; + constructor( + importPath: string, + declarationName: string | undefined = undefined, + alias: string | undefined = undefined, + ) { + this.importPath = importPath; + this.declarationName = declarationName; + this.alias = alias; } - return expandToString`def ${getPythonNameOrDefault(services, segment)}(${generateParameters( - segment.parameterList, - infoFrame, - )}):\n${PYTHON_INDENT}${segmentBlock}`; -}; - -const generatePipeline = function ( - services: SafeDsServices, - pipeline: SdsPipeline, - importSet: Map, -): string { - const infoFrame = new GenerationInfoFrame(services, importSet); - return expandToString`def ${getPythonNameOrDefault(services, pipeline)}():\n${PYTHON_INDENT}${generateBlock( - pipeline.body, - infoFrame, - )}`; -}; +} -const generateImport = function (importStmt: ImportData): string { - if (importStmt.declarationName === undefined && importStmt.alias === undefined) { - return `import ${importStmt.importPath}`; - } else if (importStmt.declarationName === undefined && importStmt.alias !== undefined) { - return `import ${importStmt.importPath} as ${importStmt.alias}`; - } else if (importStmt.declarationName !== undefined && importStmt.alias === undefined) { - return `from ${importStmt.importPath} import ${importStmt.declarationName}`; - } else { - return `from ${importStmt.importPath} import ${importStmt.declarationName} as ${importStmt.alias}`; - } -}; +class GenerationInfoFrame { + services: SafeDsServices; + blockLambdaManager: IdManager; + importSet: Map; -const generateImports = function (importSet: ImportData[]): string[] { - const qualifiedImports = Array.from(importSet) - .filter((importStmt) => importStmt.declarationName === undefined) - .sort((a, b) => a.importPath.localeCompare(b.importPath)) - .map(generateImport); - const groupedImports = Object.entries( - group( - Array.from(importSet).filter((importStmt) => importStmt.declarationName !== undefined), - (importStmt) => importStmt.importPath, - ), - ).sort(([key1, _value1], [key2, _value2]) => key1.localeCompare(key2)); - const declaredImports: string[] = []; - for (const [key, value] of groupedImports) { - const importedDecls = - value - ?.filter((importData) => importData !== undefined) - .sort((a, b) => a.declarationName!.localeCompare(b.declarationName!)) - .map((localValue) => - localValue.alias !== undefined - ? `${localValue.declarationName} as ${localValue.alias}` - : localValue.declarationName!, - ) || []; - declaredImports.push(`from ${key} import ${[...new Set(importedDecls)].join(', ')}`); + constructor(services: SafeDsServices, importSet: Map = new Map()) { + this.services = services; + this.blockLambdaManager = new IdManager(); + this.importSet = importSet; } - return [...new Set(qualifiedImports), ...new Set(declaredImports)]; -}; -const generateModule = function (services: SafeDsServices, module: SdsModule): string { - const importSet = new Map(); - const segments = moduleMembersOrEmpty(module) - .filter(isSdsSegment) - .map((segment) => generateSegment(services, segment, importSet)); - const pipelines = moduleMembersOrEmpty(module) - .filter(isSdsPipeline) - .map((pipeline) => generatePipeline(services, pipeline, importSet)); - const imports = generateImports(Array.from(importSet.values())); - const output: string[] = []; - if (imports.length > 0) { - output.push( - expandToStringWithNL`# Imports ----------------------------------------------------------------------\n\n${imports.join( - '\n', - )}`, - ); - } - if (segments.length > 0) { - output.push( - expandToStringWithNL`# Steps ------------------------------------------------------------------------\n\n${segments.join( - '\n\n', - )}`, - ); - } - if (pipelines.length > 0) { - output.push( - expandToStringWithNL`# Pipelines --------------------------------------------------------------------\n\n${pipelines.join( - '\n\n', - )}`, - ); + addImport(importData: ImportData | undefined) { + if (importData) { + const hashKey = JSON.stringify(importData); + if (!this.importSet.has(hashKey)) { + this.importSet.set(hashKey, importData); + } + } } - return expandToStringWithNL`${output.join('\n')}`; -}; - -const formatGeneratedFileName = function (baseName: string): string { - return `gen_${baseName.replaceAll('%2520', '_').replaceAll(/[ .-]/gu, '_').replaceAll(/\\W/gu, '')}`; -}; - -const formatStringSingleLine = function (value: string): string { - return value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n'); -}; -export const generatePython = function ( - services: SafeDsServices, - module: SdsModule, - filePath: string, - destination: string | undefined, -): string[] { - // Do not generate stub files - if (isInStubFile(module)) { - return []; + getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { + return `__block_lambda_${this.blockLambdaManager.assignId(lambda)}`; } - const data = extractDestinationAndName(filePath, destination); - const pythonModuleName = services.builtins.Annotations.getPythonModule(module); - const packagePath = pythonModuleName === undefined ? module.name.split('.') : [pythonModuleName]; - const parentDirectoryPath = path.join(data.destination, ...packagePath); - const generatedFiles = new Map(); - generatedFiles.set( - `${path.join(parentDirectoryPath, formatGeneratedFileName(data.name))}.py`, - generateModule(services, module), - ); - for (const pipeline of streamAllContents(module).filter(isSdsPipeline)) { - const entryPointFilename = `${path.join( - parentDirectoryPath, - `${formatGeneratedFileName(data.name)}_${getPythonNameOrDefault(services, pipeline)}`, - )}.py`; - const entryPointContent = expandToStringWithNL`from ${formatGeneratedFileName( - data.name, - )} import ${getPythonNameOrDefault( - services, - pipeline, - )}\n\nif __name__ == '__main__':\n${PYTHON_INDENT}${getPythonNameOrDefault(services, pipeline)}()`; - generatedFiles.set(entryPointFilename, entryPointContent); + getServices(): SafeDsServices { + return this.services; } - // const fileNode = new CompositeGeneratorNode(); - // fileNode.append('"use strict";', NL, NL); - // model.greetings.forEach(greeting => fileNode.append(`console.log('Hello, ${greeting.person.ref?.name}!');`, NL)); +} - if (!fs.existsSync(parentDirectoryPath)) { - fs.mkdirSync(parentDirectoryPath, { recursive: true }); - } - for (const [generatedFilePath, generatedFileContent] of generatedFiles.entries()) { - fs.writeFileSync(generatedFilePath, generatedFileContent); - } - return [...generatedFiles.keys()]; +export type GenerateOptions = { + destination?: string; }; From 0bbf5fe42b5915870d93bcd11ce7783135208d03 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Wed, 18 Oct 2023 18:27:25 +0200 Subject: [PATCH 26/56] fix: Files for tests are now all read first, then diagnostics are run --- tests/helpers/diagnostics.ts | 24 ++++++++++++++++++++++++ tests/helpers/testResources.ts | 13 ++++++++++++- tests/language/generation/creator.ts | 18 ++++++++---------- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/tests/helpers/diagnostics.ts b/tests/helpers/diagnostics.ts index 07bc5d3ce..a6c466078 100644 --- a/tests/helpers/diagnostics.ts +++ b/tests/helpers/diagnostics.ts @@ -41,6 +41,18 @@ export const getErrors = async (services: LangiumServices, code: string): Promis return diagnostics.filter((d) => d.severity === DiagnosticSeverity.Error); }; +/** + * Get all errors from a loaded document. + * + * @param services The language services. + * @param uri The URI of the code snippet to check. + * @returns The errors. + */ +export const getErrorsByURI = (services: LangiumServices, uri: URI): Diagnostic[] => { + const diagnostics = getDiagnosticsByURI(services, uri); + return diagnostics.filter((d) => d.severity === DiagnosticSeverity.Error); +}; + /** * Get all diagnostics from a code snippet. * @@ -57,6 +69,18 @@ const getDiagnostics = async (services: LangiumServices, code: string): Promise< return document.diagnostics ?? []; }; +/** + * Get all diagnostics from a loaded document. + * + * @param services The language services. + * @param uri The URI of the code snippet to check. + * @returns The diagnostics. + */ +const getDiagnosticsByURI = (services: LangiumServices, uri: URI): Diagnostic[] => { + const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri); + return document.diagnostics ?? []; +}; + /** * The code contains syntax errors. */ diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index 4397ac6a6..781f91e18 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -3,7 +3,9 @@ import { globSync } from 'glob'; import { SAFE_DS_FILE_EXTENSIONS } from '../../src/language/helpers/fileExtensions.js'; import { group } from 'radash'; import { LangiumDocument, URI } from 'langium'; +import { parseHelper } from 'langium/test'; import { SafeDsServices } from '../../src/language/safe-ds-module.js'; +import fs from 'fs'; const TEST_RESOURCES_PATH = path.join(__dirname, '..', 'resources'); @@ -100,7 +102,16 @@ const isNotSkipped = (pathRelativeToResources: string) => { * @returns List of loaded documents */ export const loadAllDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { - const documents = uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); + const documents = await Promise.all(uris.map(async (uri) => await loadDocumentWithDiagnostics(services, uri))); await services.shared.workspace.DocumentBuilder.build(documents); return documents; }; + +const loadDocumentWithDiagnostics = async function (services: SafeDsServices, uri: URI) { + const parse = parseHelper(services); + const code = fs.readFileSync(uri.fsPath).toString(); + return await parse(code, { + documentUri: uri.toString(), + validation: true, + }); +}; diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 209dc7e2f..90ac5cf81 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -1,12 +1,13 @@ import { listTestPythonFiles, - listTestSafeDsFilesGroupedByParentDirectory, loadAllDocuments, + listTestSafeDsFilesGroupedByParentDirectory, + loadAllDocuments, uriToShortenedTestResourceName, } from '../../helpers/testResources.js'; import path from 'path'; import fs from 'fs'; import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; -import { ErrorsInCodeError, getErrors } from '../../helpers/diagnostics.js'; +import { ErrorsInCodeError, getErrorsByURI } from '../../helpers/diagnostics.js'; import { findTestChecks } from '../../helpers/testChecks.js'; import { Location } from 'vscode-languageserver'; import { NodeFileSystem } from 'langium/node'; @@ -30,17 +31,14 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const actualOutputRoot = URI.file(path.join(parentDirectory.fsPath, 'generated')); const expectedOutputFiles = readExpectedOutputFiles(expectedOutputRoot, actualOutputRoot); let runUntil: Location | undefined; - // First read all stubs, then read all the other files; This should avoid broken references - const sortedInputUris = inputUris - .filter((uri) => uri.fsPath.endsWith('sdsstub')) - .sort() - .concat(...inputUris.filter((uri) => !uri.fsPath.endsWith('sdsstub')).sort()); + // First read all files; Then check diagnostics + await loadAllDocuments(services, inputUris); - for (const uri of sortedInputUris) { - const code = fs.readFileSync(uri.fsPath).toString(); + for (const uri of inputUris) { + const code = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri).textDocument.getText(); // File must not contain any errors - const errors = await getErrors(services, code); + const errors = getErrorsByURI(services, uri); if (errors.length > 0) { return invalidTest('FILE', new ErrorsInCodeError(errors, uri)); } From 63f0f663f9af3c07f93b4216d304019064ae0d89 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Wed, 18 Oct 2023 18:38:28 +0200 Subject: [PATCH 27/56] fix: Linter --- tests/helpers/testResources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index 781f91e18..21301036f 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -110,7 +110,7 @@ export const loadAllDocuments = async (services: SafeDsServices, uris: URI[]): P const loadDocumentWithDiagnostics = async function (services: SafeDsServices, uri: URI) { const parse = parseHelper(services); const code = fs.readFileSync(uri.fsPath).toString(); - return await parse(code, { + return parse(code, { documentUri: uri.toString(), validation: true, }); From 9644f8de3c7520ec604988b1ae68b1c1f61b6b95 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:40:47 +0000 Subject: [PATCH 28/56] style: apply automated linter fixes --- tests/helpers/testResources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index 21301036f..2ca8bfe16 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -102,7 +102,7 @@ const isNotSkipped = (pathRelativeToResources: string) => { * @returns List of loaded documents */ export const loadAllDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { - const documents = await Promise.all(uris.map(async (uri) => await loadDocumentWithDiagnostics(services, uri))); + const documents = await Promise.all(uris.map(async (uri) => loadDocumentWithDiagnostics(services, uri))); await services.shared.workspace.DocumentBuilder.build(documents); return documents; }; From 564896a239b8222b78811c7cd215a35a35abd20c Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 10:36:06 +0200 Subject: [PATCH 29/56] test: rename gen__skip__context_same_package.py to gen_context_same_package.py for test "imports" and enable --- .../context different package.sdsstub | 0 .../context package with python module.sdsstub | 0 .../{skip-imports => imports}/context same package.sdstest | 0 .../generation/{skip-imports => imports}/input.sdstest | 0 .../tests/generator/imports/gen_context_same_package.py} | 0 .../output/tests/generator/imports/gen_input.py | 2 +- .../output/tests/generator/imports/gen_input_test.py | 4 ---- 7 files changed, 1 insertion(+), 5 deletions(-) rename tests/resources/generation/{skip-imports => imports}/context different package.sdsstub (100%) rename tests/resources/generation/{skip-imports => imports}/context package with python module.sdsstub (100%) rename tests/resources/generation/{skip-imports => imports}/context same package.sdstest (100%) rename tests/resources/generation/{skip-imports => imports}/input.sdstest (100%) rename tests/resources/generation/{skip-imports/output/tests/generator/imports/gen__skip__context_same_package.py => imports/output/tests/generator/imports/gen_context_same_package.py} (100%) rename tests/resources/generation/{skip-imports => imports}/output/tests/generator/imports/gen_input.py (87%) delete mode 100644 tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input_test.py diff --git a/tests/resources/generation/skip-imports/context different package.sdsstub b/tests/resources/generation/imports/context different package.sdsstub similarity index 100% rename from tests/resources/generation/skip-imports/context different package.sdsstub rename to tests/resources/generation/imports/context different package.sdsstub diff --git a/tests/resources/generation/skip-imports/context package with python module.sdsstub b/tests/resources/generation/imports/context package with python module.sdsstub similarity index 100% rename from tests/resources/generation/skip-imports/context package with python module.sdsstub rename to tests/resources/generation/imports/context package with python module.sdsstub diff --git a/tests/resources/generation/skip-imports/context same package.sdstest b/tests/resources/generation/imports/context same package.sdstest similarity index 100% rename from tests/resources/generation/skip-imports/context same package.sdstest rename to tests/resources/generation/imports/context same package.sdstest diff --git a/tests/resources/generation/skip-imports/input.sdstest b/tests/resources/generation/imports/input.sdstest similarity index 100% rename from tests/resources/generation/skip-imports/input.sdstest rename to tests/resources/generation/imports/input.sdstest diff --git a/tests/resources/generation/skip-imports/output/tests/generator/imports/gen__skip__context_same_package.py b/tests/resources/generation/imports/output/tests/generator/imports/gen_context_same_package.py similarity index 100% rename from tests/resources/generation/skip-imports/output/tests/generator/imports/gen__skip__context_same_package.py rename to tests/resources/generation/imports/output/tests/generator/imports/gen_context_same_package.py diff --git a/tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input.py b/tests/resources/generation/imports/output/tests/generator/imports/gen_input.py similarity index 87% rename from tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input.py rename to tests/resources/generation/imports/output/tests/generator/imports/gen_input.py index 1010e1898..895f4da5d 100644 --- a/tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input.py +++ b/tests/resources/generation/imports/output/tests/generator/imports/gen_input.py @@ -2,7 +2,7 @@ from special_location import function1InCompilationUnitWithPythonModule, function2InCompilationUnitWithPythonModule as h from tests.generator.differentPackage import function1InDifferentPackage, function2InDifferentPackage as g -from tests.generator.imports.gen__skip__context_same_package import step1InSamePackage, step2InSamePackage +from tests.generator.imports.gen_context_same_package import step1InSamePackage, step2InSamePackage # Pipelines -------------------------------------------------------------------- diff --git a/tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input_test.py b/tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input_test.py deleted file mode 100644 index 30abe031d..000000000 --- a/tests/resources/generation/skip-imports/output/tests/generator/imports/gen_input_test.py +++ /dev/null @@ -1,4 +0,0 @@ -from gen_input import test - -if __name__ == '__main__': - test() From bcf9fb55c65a074b0efc77cc5a47a37a9e9e1da6 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 11:01:03 +0200 Subject: [PATCH 30/56] fix: yielded placeholders in block lambdas are now assigned a prefix fix: return correct code for enum variant expression fix: handle BlockLambdaResult in SdsMemberAccess test: updated "expressions/block lambda" and "assignment" tests --- src/cli/generator.ts | 26 ++++++++++++------- .../tests/generator/blockLambda/gen_input.py | 8 +++--- .../tests/generator/assignment/gen_input.py | 4 +-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 49dda0c0d..0daf71ac8 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -1,11 +1,21 @@ import fs from 'fs'; -import { expandToString, expandToStringWithNL, findRootNode, getDocument, streamAllContents } from 'langium'; +import { + expandToString, + expandToStringWithNL, + findRootNode, + getContainerOfType, + getDocument, + streamAllContents, +} from 'langium'; import path from 'path'; import { + isSdsAbstractResult, isSdsAssignment, isSdsBlockLambda, isSdsBlockLambdaResult, isSdsCall, + isSdsCallable, + isSdsEnum, isSdsEnumVariant, isSdsExpressionLambda, isSdsExpressionStatement, @@ -21,7 +31,6 @@ import { isSdsPrefixOperation, isSdsQualifiedImport, isSdsReference, - isSdsResult, isSdsSegment, isSdsTemplateString, isSdsTemplateStringEnd, @@ -203,7 +212,6 @@ const generateParameter = function ( frame: GenerationInfoFrame, defaultValue: boolean = true, ): string { - // TODO isConstant? return expandToString`${getPythonNameOrDefault(frame.getServices(), parameter)}${ defaultValue && parameter.defaultValue !== undefined ? '=' + generateExpression(parameter.defaultValue, frame) @@ -310,7 +318,7 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio const generateAssignee = function (assignee: SdsAssignee): string { if (isSdsBlockLambdaResult(assignee)) { - return assignee.name; + return `__block_lambda_${assignee.name}`; } else if (isSdsPlaceholder(assignee)) { return assignee.name; } else if (isSdsWildcard(assignee)) { @@ -325,7 +333,7 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); let lambdaBlock = generateBlock(blockLambda.body, frame); if (lambdaResult.length !== 0 && lambdaBlock !== 'pass') { - lambdaBlock += `\nreturn ${lambdaResult.map((result) => result.name).join(', ')}`; + lambdaBlock += `\nreturn ${lambdaResult.map((result) => `__block_lambda_${result.name}`).join(', ')}`; } return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( blockLambda.parameterList, @@ -375,7 +383,8 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } else if (potentialConstantExpression instanceof ConstantString) { return `'${formatStringSingleLine(potentialConstantExpression.value)}'`; } else if (potentialConstantExpression instanceof ConstantEnumVariant) { - return potentialConstantExpression.value.name; // TODO correct interpretation of SdsEnumVariant / SdsConstantEnumVariant? + const enumType = getContainerOfType(potentialConstantExpression.value, isSdsEnum); + return `${enumType!.name}.${potentialConstantExpression.value.name}`; } } @@ -424,7 +433,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (isSdsMemberAccess(expression)) { const member = expression.member?.target.ref!; const receiver = generateExpression(expression.receiver, frame); - // TODO SdsBlockLambdaResult should not be possible? Grammar defines SdsBlockLambdaResult as only part of an assignment. There is no way to parse it anywhere else if (isSdsEnumVariant(member)) { const enumMember = generateExpression(expression.member!, frame); const suffix = isSdsCall(expression.$container) ? '' : '()'; @@ -434,8 +442,8 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } else { return `${receiver}.${enumMember}${suffix}`; } - } else if (isSdsResult(member)) { - const resultList = member.$container.results; + } else if (isSdsAbstractResult(member)) { + const resultList = abstractResultsOrEmpty(getContainerOfType(member, isSdsCallable)); if (resultList.length === 1) { return receiver; } diff --git a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py index 7f122eb63..f1532a670 100644 --- a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py +++ b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -2,12 +2,12 @@ def test(): def __block_lambda_0(a, b=2): - d = g() - return d + __block_lambda_d = g() + return __block_lambda_d f1(__block_lambda_0) def __block_lambda_1(a, c): - d = g() - return d + __block_lambda_d = g() + return __block_lambda_d f2(__block_lambda_1) def __block_lambda_2(): pass diff --git a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py index f68fc3c51..b728ffd8e 100644 --- a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py +++ b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -18,9 +18,9 @@ def testPipeline(): f1(x) def __block_lambda_0(): g() - a, _, c = g() + a, _, __block_lambda_c = g() x, _, _ = g() f1(a) f1(x) - return c + return __block_lambda_c f2(__block_lambda_0) From 493f8b5a98fa5ea90b4cc75207afa24c57e712c4 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 11:34:47 +0200 Subject: [PATCH 31/56] feat: add wildcard imports test: add test for wildcard imports --- src/cli/generator.ts | 6 ++++-- .../context different package.sdsstub | 0 .../context package with python module.sdsstub | 0 .../{ => general}/context same package.sdstest | 0 .../imports/{ => general}/input.sdstest | 0 .../imports/gen_context_same_package.py | 0 .../tests/generator/imports/gen_input.py | 0 .../tests/generator/imports/gen_input_test.py | 0 .../wildcard/context different package.sdsstub | 4 ++++ .../context package with python module.sdsstub | 6 ++++++ .../generation/imports/wildcard/input.sdstest | 18 ++++++++++++++++++ .../tests/generator/wildcard/gen_input.py | 16 ++++++++++++++++ .../tests/generator/wildcard/gen_input_test.py | 4 ++++ 13 files changed, 52 insertions(+), 2 deletions(-) rename tests/resources/generation/imports/{ => general}/context different package.sdsstub (100%) rename tests/resources/generation/imports/{ => general}/context package with python module.sdsstub (100%) rename tests/resources/generation/imports/{ => general}/context same package.sdstest (100%) rename tests/resources/generation/imports/{ => general}/input.sdstest (100%) rename tests/resources/generation/imports/{ => general}/output/tests/generator/imports/gen_context_same_package.py (100%) rename tests/resources/generation/imports/{ => general}/output/tests/generator/imports/gen_input.py (100%) rename tests/resources/generation/imports/{ => general}/output/tests/generator/imports/gen_input_test.py (100%) create mode 100644 tests/resources/generation/imports/wildcard/context different package.sdsstub create mode 100644 tests/resources/generation/imports/wildcard/context package with python module.sdsstub create mode 100644 tests/resources/generation/imports/wildcard/input.sdstest create mode 100644 tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input.py create mode 100644 tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input_test.py diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 0daf71ac8..e310b9415 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -538,8 +538,10 @@ const getExternalReferenceNeededImport = function ( } } if (isSdsWildcardImport(value)) { - // TODO wildcard import? collisions? - throw new Error('Wildcard imports are not yet handled'); + return new ImportData( + services.builtins.Annotations.getPythonModule(targetModule) || value.package, + declaration.name, + ); } } return undefined; diff --git a/tests/resources/generation/imports/context different package.sdsstub b/tests/resources/generation/imports/general/context different package.sdsstub similarity index 100% rename from tests/resources/generation/imports/context different package.sdsstub rename to tests/resources/generation/imports/general/context different package.sdsstub diff --git a/tests/resources/generation/imports/context package with python module.sdsstub b/tests/resources/generation/imports/general/context package with python module.sdsstub similarity index 100% rename from tests/resources/generation/imports/context package with python module.sdsstub rename to tests/resources/generation/imports/general/context package with python module.sdsstub diff --git a/tests/resources/generation/imports/context same package.sdstest b/tests/resources/generation/imports/general/context same package.sdstest similarity index 100% rename from tests/resources/generation/imports/context same package.sdstest rename to tests/resources/generation/imports/general/context same package.sdstest diff --git a/tests/resources/generation/imports/input.sdstest b/tests/resources/generation/imports/general/input.sdstest similarity index 100% rename from tests/resources/generation/imports/input.sdstest rename to tests/resources/generation/imports/general/input.sdstest diff --git a/tests/resources/generation/imports/output/tests/generator/imports/gen_context_same_package.py b/tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py similarity index 100% rename from tests/resources/generation/imports/output/tests/generator/imports/gen_context_same_package.py rename to tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py diff --git a/tests/resources/generation/imports/output/tests/generator/imports/gen_input.py b/tests/resources/generation/imports/general/output/tests/generator/imports/gen_input.py similarity index 100% rename from tests/resources/generation/imports/output/tests/generator/imports/gen_input.py rename to tests/resources/generation/imports/general/output/tests/generator/imports/gen_input.py diff --git a/tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py b/tests/resources/generation/imports/general/output/tests/generator/imports/gen_input_test.py similarity index 100% rename from tests/resources/generation/imports/output/tests/generator/imports/gen_input_test.py rename to tests/resources/generation/imports/general/output/tests/generator/imports/gen_input_test.py diff --git a/tests/resources/generation/imports/wildcard/context different package.sdsstub b/tests/resources/generation/imports/wildcard/context different package.sdsstub new file mode 100644 index 000000000..1bd918cce --- /dev/null +++ b/tests/resources/generation/imports/wildcard/context different package.sdsstub @@ -0,0 +1,4 @@ +package tests.generator.differentPackageWildcard + +fun function1InDifferentPackage() -> result: Int +fun function2InDifferentPackage() -> result: Int diff --git a/tests/resources/generation/imports/wildcard/context package with python module.sdsstub b/tests/resources/generation/imports/wildcard/context package with python module.sdsstub new file mode 100644 index 000000000..d274d7c98 --- /dev/null +++ b/tests/resources/generation/imports/wildcard/context package with python module.sdsstub @@ -0,0 +1,6 @@ +@PythonModule("special_location") + +package tests.generator.withPythonModuleWildcard + +fun function1InCompilationUnitWithPythonModule() -> result: Int +fun function2InCompilationUnitWithPythonModule() -> result: Int diff --git a/tests/resources/generation/imports/wildcard/input.sdstest b/tests/resources/generation/imports/wildcard/input.sdstest new file mode 100644 index 000000000..a0bfad653 --- /dev/null +++ b/tests/resources/generation/imports/wildcard/input.sdstest @@ -0,0 +1,18 @@ +package tests.generator.wildcard + +from tests.generator.differentPackageWildcard import * +from tests.generator.withPythonModuleWildcard import * + +fun f(param: Any?) + +pipeline test { + f(function1InDifferentPackage()); + f(function1InDifferentPackage()); + f(function2InDifferentPackage()); + f(function2InDifferentPackage()); + + f(function1InCompilationUnitWithPythonModule()); + f(function1InCompilationUnitWithPythonModule()); + f(function2InCompilationUnitWithPythonModule()); + f(function2InCompilationUnitWithPythonModule()); +} diff --git a/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input.py b/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input.py new file mode 100644 index 000000000..4283f3f54 --- /dev/null +++ b/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input.py @@ -0,0 +1,16 @@ +# Imports ---------------------------------------------------------------------- + +from special_location import function1InCompilationUnitWithPythonModule, function2InCompilationUnitWithPythonModule +from tests.generator.differentPackageWildcard import function1InDifferentPackage, function2InDifferentPackage + +# Pipelines -------------------------------------------------------------------- + +def test(): + f(function1InDifferentPackage()) + f(function1InDifferentPackage()) + f(function2InDifferentPackage()) + f(function2InDifferentPackage()) + f(function1InCompilationUnitWithPythonModule()) + f(function1InCompilationUnitWithPythonModule()) + f(function2InCompilationUnitWithPythonModule()) + f(function2InCompilationUnitWithPythonModule()) diff --git a/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input_test.py b/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/tests/resources/generation/imports/wildcard/output/tests/generator/wildcard/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() From d3da29998b2ffa5e0373ba9e824a70de85f51bbd Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 11:48:15 +0200 Subject: [PATCH 32/56] chore: simplified import generation --- src/cli/generator.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index e310b9415..ab371ff63 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -235,7 +235,7 @@ const generateImports = function (importSet: ImportData[]): string[] { const qualifiedImports = Array.from(importSet) .filter((importStmt) => importStmt.declarationName === undefined) .sort((a, b) => a.importPath.localeCompare(b.importPath)) - .map(generateImport); + .map(generateQualifiedImport); const groupedImports = Object.entries( group( Array.from(importSet).filter((importStmt) => importStmt.declarationName !== undefined), @@ -258,15 +258,11 @@ const generateImports = function (importSet: ImportData[]): string[] { return [...new Set(qualifiedImports), ...new Set(declaredImports)]; }; -const generateImport = function (importStmt: ImportData): string { - if (importStmt.declarationName === undefined && importStmt.alias === undefined) { +const generateQualifiedImport = function (importStmt: ImportData): string { + if (importStmt.alias === undefined) { return `import ${importStmt.importPath}`; - } else if (importStmt.declarationName === undefined && importStmt.alias !== undefined) { - return `import ${importStmt.importPath} as ${importStmt.alias}`; - } else if (importStmt.declarationName !== undefined && importStmt.alias === undefined) { - return `from ${importStmt.importPath} import ${importStmt.declarationName}`; } else { - return `from ${importStmt.importPath} import ${importStmt.declarationName} as ${importStmt.alias}`; + return `import ${importStmt.importPath} as ${importStmt.alias}`; } }; From 600d8848a082696d626f3e7f9a0cbc625b14fd39 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 12:11:33 +0200 Subject: [PATCH 33/56] test: added test "expression/block lambda result" --- .../block lambda result/input.sdstest | 26 +++++++++++++++++++ .../generator/blockLambdaResult/gen_input.py | 21 +++++++++++++++ .../blockLambdaResult/gen_input_test.py | 4 +++ 3 files changed, 51 insertions(+) create mode 100644 tests/resources/generation/expressions/block lambda result/input.sdstest create mode 100644 tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py create mode 100644 tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input_test.py diff --git a/tests/resources/generation/expressions/block lambda result/input.sdstest b/tests/resources/generation/expressions/block lambda result/input.sdstest new file mode 100644 index 000000000..28fc9b248 --- /dev/null +++ b/tests/resources/generation/expressions/block lambda result/input.sdstest @@ -0,0 +1,26 @@ +package tests.generator.blockLambdaResult + +fun g() -> a: Int + +fun h(a: Int) + +segment f1(l: (a: Int, b: Int) -> d: Int) { + h(l(1, 2).d); +} + +segment f2(l: (a: Int, b: Int) -> (d1: Int, e1: Int)) { + h(l(1, 2).e1); + h(l(1, 2).d1); +} + +pipeline test { + + f1((a: Int, b: Int) { + yield d = g(); + }); + f2((a: Int, b: Int) { + yield d = g(); + yield e = g(); + }); + +} diff --git a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py new file mode 100644 index 000000000..492773617 --- /dev/null +++ b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py @@ -0,0 +1,21 @@ +# Steps ------------------------------------------------------------------------ + +def f1(l): + h(l(1, 2)) + +def f2(l): + h(l(1, 2)[1]) + h(l(1, 2)[0]) + +# Pipelines -------------------------------------------------------------------- + +def test(): + def __block_lambda_0(a, b): + __block_lambda_d = g() + return __block_lambda_d + f1(__block_lambda_0) + def __block_lambda_1(a, b): + __block_lambda_d = g() + __block_lambda_e = g() + return __block_lambda_d, __block_lambda_e + f2(__block_lambda_1) diff --git a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input_test.py b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() From 16c1aa3bd1e86f4996383379d367c0b6fb608117 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Thu, 19 Oct 2023 12:34:53 +0200 Subject: [PATCH 34/56] test: added test of null-safe enum variant calls to "expression/enum variant call" chore: don't handle enum variant call, list and map as constant expression --- src/cli/generator.ts | 32 +++++++------------ .../enum variant call/input.sdstest | 3 ++ .../generator/enumVariantCall/gen_input.py | 7 ++++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index ab371ff63..44b987ef9 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -15,14 +15,12 @@ import { isSdsBlockLambdaResult, isSdsCall, isSdsCallable, - isSdsEnum, isSdsEnumVariant, isSdsExpressionLambda, isSdsExpressionStatement, isSdsIndexedAccess, isSdsInfixOperation, isSdsList, - isSdsLiteral, isSdsMap, isSdsMemberAccess, isSdsParenthesizedExpression, @@ -61,7 +59,6 @@ import { NodeFileSystem } from 'langium/node'; import { toConstantExpression } from '../language/partialEvaluation/toConstantExpression.js'; import { ConstantBoolean, - ConstantEnumVariant, ConstantFloat, ConstantInt, ConstantNull, @@ -352,19 +349,6 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } - // TODO move down again, when supported as constant expression - if (isSdsLiteral(expression) && (isSdsMap(expression) || isSdsList(expression))) { - if (isSdsMap(expression)) { - const mapContent = expression.entries.map( - (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, - ); - return `{${mapContent.join(', ')}}`; - } else if (isSdsList(expression)) { - const listContent = expression.elements.map((value) => generateExpression(value, frame)); - return `[${listContent.join(', ')}]`; - } - } - const potentialConstantExpression = toConstantExpression(expression); if (potentialConstantExpression !== null) { if (potentialConstantExpression instanceof ConstantBoolean) { @@ -378,10 +362,19 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio return 'None'; } else if (potentialConstantExpression instanceof ConstantString) { return `'${formatStringSingleLine(potentialConstantExpression.value)}'`; - } else if (potentialConstantExpression instanceof ConstantEnumVariant) { - const enumType = getContainerOfType(potentialConstantExpression.value, isSdsEnum); - return `${enumType!.name}.${potentialConstantExpression.value.name}`; } + // Handled after constant expressions: EnumVariant, List, Map + } + + if (isSdsMap(expression)) { + const mapContent = expression.entries.map( + (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, + ); + return `{${mapContent.join(', ')}}`; + } + if (isSdsList(expression)) { + const listContent = expression.elements.map((value) => generateExpression(value, frame)); + return `[${listContent.join(', ')}]`; } if (isSdsBlockLambda(expression)) { @@ -552,7 +545,6 @@ const getInternalReferenceNeededImport = function ( const currentModule = findRootNode(expression); const targetModule = findRootNode(declaration); if (currentModule !== targetModule && !isInStubFile(targetModule)) { - // TODO __skip__ in file name? return new ImportData( `${ services.builtins.Annotations.getPythonModule(targetModule) || targetModule.name diff --git a/tests/resources/generation/expressions/enum variant call/input.sdstest b/tests/resources/generation/expressions/enum variant call/input.sdstest index d113a4972..a6d06f263 100644 --- a/tests/resources/generation/expressions/enum variant call/input.sdstest +++ b/tests/resources/generation/expressions/enum variant call/input.sdstest @@ -11,6 +11,9 @@ enum MyEnum { pipeline test { f(MyEnum.Variant1); + f(MyEnum?.Variant1); f(MyEnum.Variant1()); + f(MyEnum?.Variant1()); f(MyEnum.Variant2(1)); + f(MyEnum?.Variant2(1)); } diff --git a/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py index 1a9a4f9d8..6a5e22226 100644 --- a/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py +++ b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py @@ -1,6 +1,13 @@ +# Imports ---------------------------------------------------------------------- + +import safeds_runner.codegen + # Pipelines -------------------------------------------------------------------- def test(): f(MyEnum.Variant1()) + f(safeds_runner.codegen.safe_access(MyEnum, 'Variant1')()) f(MyEnum.Variant1()) + f(safeds_runner.codegen.safe_access(MyEnum, 'Variant1')()) f(MyEnum.Variant2(1)) + f(safeds_runner.codegen.safe_access(MyEnum, 'Variant2')(1)) From 8773e233cdfb2c203b839cf439397b60fdaedbe9 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Fri, 20 Oct 2023 16:24:08 +0200 Subject: [PATCH 35/56] chore: adapt to new partial evaluator service --- src/cli/generator.ts | 45 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 44b987ef9..d672d3362 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -56,14 +56,6 @@ import { extractAstNode, extractDestinationAndName } from './cli-util.js'; import chalk from 'chalk'; import { createSafeDsServices, SafeDsServices } from '../language/safe-ds-module.js'; import { NodeFileSystem } from 'langium/node'; -import { toConstantExpression } from '../language/partialEvaluation/toConstantExpression.js'; -import { - ConstantBoolean, - ConstantFloat, - ConstantInt, - ConstantNull, - ConstantString, -} from '../language/partialEvaluation/model.js'; import { abstractResultsOrEmpty, assigneesOrEmpty, @@ -77,6 +69,13 @@ import { import { group } from 'radash'; import { IdManager } from '../language/helpers/idManager.js'; import { isInStubFile } from '../language/helpers/fileExtensions.js'; +import { + BooleanConstant, + FloatConstant, + IntConstant, + NullConstant, + StringConstant, +} from '../language/partialEvaluation/model.js'; const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; const PYTHON_INDENT = ' '; @@ -349,22 +348,20 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } } - const potentialConstantExpression = toConstantExpression(expression); - if (potentialConstantExpression !== null) { - if (potentialConstantExpression instanceof ConstantBoolean) { - return potentialConstantExpression.value ? 'True' : 'False'; - } else if (potentialConstantExpression instanceof ConstantInt) { - return String(potentialConstantExpression.value); - } else if (potentialConstantExpression instanceof ConstantFloat) { - const floatValue = potentialConstantExpression.value; - return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); - } else if (potentialConstantExpression === ConstantNull) { - return 'None'; - } else if (potentialConstantExpression instanceof ConstantString) { - return `'${formatStringSingleLine(potentialConstantExpression.value)}'`; - } - // Handled after constant expressions: EnumVariant, List, Map - } + const partiallyEvaluatedNode = frame.getServices().evaluation.PartialEvaluator.evaluate(expression); + if (partiallyEvaluatedNode instanceof BooleanConstant) { + return partiallyEvaluatedNode.value ? 'True' : 'False'; + } else if (partiallyEvaluatedNode instanceof IntConstant) { + return String(partiallyEvaluatedNode.value); + } else if (partiallyEvaluatedNode instanceof FloatConstant) { + const floatValue = partiallyEvaluatedNode.value; + return Number.isInteger(floatValue) ? `${floatValue}.0` : String(floatValue); + } else if (partiallyEvaluatedNode === NullConstant) { + return 'None'; + } else if (partiallyEvaluatedNode instanceof StringConstant) { + return `'${formatStringSingleLine(partiallyEvaluatedNode.value)}'`; + } + // Handled after constant expressions: EnumVariant, List, Map if (isSdsMap(expression)) { const mapContent = expression.entries.map( From 579048723be3e09f003d9c92379f770fc5490858 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Fri, 20 Oct 2023 16:28:42 +0200 Subject: [PATCH 36/56] fix: if a template string can be partially evaluated, use the result test: enable "expressions/constant" --- src/cli/generator.ts | 8 ++++---- .../expressions/{skip-constant => constant}/input.sdstest | 0 .../output/tests/generator/constant/gen_input.py | 0 .../output/tests/generator/constant/gen_input_test.py | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename tests/resources/generation/expressions/{skip-constant => constant}/input.sdstest (100%) rename tests/resources/generation/expressions/{skip-constant => constant}/output/tests/generator/constant/gen_input.py (100%) rename tests/resources/generation/expressions/{skip-constant => constant}/output/tests/generator/constant/gen_input_test.py (100%) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index d672d3362..e5a29e019 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -334,10 +334,6 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera }; const generateExpression = function (expression: SdsExpression, frame: GenerationInfoFrame): string { - if (isSdsTemplateString(expression)) { - return `f'${expression.expressions.map((expr) => generateExpression(expr, frame)).join('')}'`; - } - if (isSdsTemplateStringPart(expression)) { if (isSdsTemplateStringStart(expression)) { return `${formatStringSingleLine(expression.value)}{ `; @@ -363,6 +359,10 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } // Handled after constant expressions: EnumVariant, List, Map + if (isSdsTemplateString(expression)) { + return `f'${expression.expressions.map((expr) => generateExpression(expr, frame)).join('')}'`; + } + if (isSdsMap(expression)) { const mapContent = expression.entries.map( (entry) => `${generateExpression(entry.key, frame)}: ${generateExpression(entry.value, frame)}`, diff --git a/tests/resources/generation/expressions/skip-constant/input.sdstest b/tests/resources/generation/expressions/constant/input.sdstest similarity index 100% rename from tests/resources/generation/expressions/skip-constant/input.sdstest rename to tests/resources/generation/expressions/constant/input.sdstest diff --git a/tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input.py b/tests/resources/generation/expressions/constant/output/tests/generator/constant/gen_input.py similarity index 100% rename from tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input.py rename to tests/resources/generation/expressions/constant/output/tests/generator/constant/gen_input.py diff --git a/tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input_test.py b/tests/resources/generation/expressions/constant/output/tests/generator/constant/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/skip-constant/output/tests/generator/constant/gen_input_test.py rename to tests/resources/generation/expressions/constant/output/tests/generator/constant/gen_input_test.py From 9d44e8f38bfd114862679a1ff7761aff73260890 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Fri, 20 Oct 2023 16:47:20 +0200 Subject: [PATCH 37/56] chore: don't treat empty parameter lists specially when generating parameters --- src/cli/generator.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index e5a29e019..d907e4bf2 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -196,10 +196,7 @@ const generateSegment = function ( }; const generateParameters = function (parameters: SdsParameterList | undefined, frame: GenerationInfoFrame): string { - if (parameters === undefined) { - return ''; - } - const result = parameters.parameters.map((param) => generateParameter(param, frame)); + const result = (parameters?.parameters || []).map((param) => generateParameter(param, frame)); return result.join(', '); }; From 497a4dc7d5c42fc9a813e279a873f0f7251de866 Mon Sep 17 00:00:00 2001 From: WinPlay02 Date: Sat, 21 Oct 2023 18:40:35 +0200 Subject: [PATCH 38/56] chore: ignore unreachable branches in coverage calculation for now --- src/cli/generator.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index d907e4bf2..0a747bed1 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -255,6 +255,7 @@ const generateQualifiedImport = function (importStmt: ImportData): string { if (importStmt.alias === undefined) { return `import ${importStmt.importPath}`; } else { + /* c8 ignore next 2 */ return `import ${importStmt.importPath} as ${importStmt.alias}`; } }; @@ -280,6 +281,7 @@ const generateStatement = function (statement: SdsStatement, frame: GenerationIn blockLambdaCode.push(generateExpression(expressionStatement.expression, frame)); return expandToString`${blockLambdaCode.join('\n')}`; } + /* c8 ignore next 2 */ throw new Error(`Unknown SdsStatement: ${statement}`); }; @@ -288,6 +290,7 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio ? abstractResultsOrEmpty( frame.getServices().helpers.NodeMapper.callToCallableOrUndefined(assignment.expression), ).length + /* c8 ignore next */ : 1; const assignees = assigneesOrEmpty(assignment); if (assignees.some((value) => !isSdsWildcard(value))) { @@ -315,6 +318,7 @@ const generateAssignee = function (assignee: SdsAssignee): string { } else if (isSdsYield(assignee)) { return assignee.result?.ref?.name!; } + /* c8 ignore next 2 */ throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); }; @@ -462,6 +466,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio frame.addImport(referenceImport); return referenceImport?.alias || getPythonNameOrDefault(frame.getServices(), declaration); } + /* c8 ignore next 2 */ throw new Error(`Unknown expression type: ${expression.$type}`); }; From a9afb9536c8b8b442a576d3506c7050c410be3c3 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:43:23 +0000 Subject: [PATCH 39/56] style: apply automated linter fixes --- src/cli/generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 0a747bed1..31abd848f 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -290,8 +290,8 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio ? abstractResultsOrEmpty( frame.getServices().helpers.NodeMapper.callToCallableOrUndefined(assignment.expression), ).length - /* c8 ignore next */ - : 1; + : /* c8 ignore next */ + 1; const assignees = assigneesOrEmpty(assignment); if (assignees.some((value) => !isSdsWildcard(value))) { const actualAssignees = assignees.map(generateAssignee); From 926cdb22a898fba0d5fda66a6da7239f4d08feec Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 20:30:51 +0200 Subject: [PATCH 40/56] test: remove unneeded code --- .../generation/expressions/block lambda/input.sdstest | 7 +++---- .../output/tests/generator/blockLambda/gen_input.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/resources/generation/expressions/block lambda/input.sdstest b/tests/resources/generation/expressions/block lambda/input.sdstest index 6f7f3073c..bdfcd497c 100644 --- a/tests/resources/generation/expressions/block lambda/input.sdstest +++ b/tests/resources/generation/expressions/block lambda/input.sdstest @@ -1,8 +1,7 @@ package tests.generator.blockLambda fun f1(param: (a: Int, b: Int) -> r: Int) -fun f2(param: (a: Int, b: Int) -> r: Int) -fun f3(param: () -> ()) +fun f2(param: () -> ()) fun g() -> a: Int @@ -10,8 +9,8 @@ pipeline test { f1((a: Int, b: Int = 2) { yield d = g(); }); - f2((a: Int, c: Int) { + f1((a: Int, c: Int) { yield d = g(); }); - f3(() {}); + f2(() {}); } diff --git a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py index f1532a670..81f20bb9c 100644 --- a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py +++ b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -8,7 +8,7 @@ def __block_lambda_0(a, b=2): def __block_lambda_1(a, c): __block_lambda_d = g() return __block_lambda_d - f2(__block_lambda_1) + f1(__block_lambda_1) def __block_lambda_2(): pass - f3(__block_lambda_2) + f2(__block_lambda_2) From a114bd1375598301dd091f97c2d73023eed3d5af Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 20:38:39 +0200 Subject: [PATCH 41/56] fix: remove unneeded safe access to enum variants --- src/cli/generator.ts | 7 +------ .../expressions/enum variant call/input.sdstest | 2 -- .../tests/generator/enumVariantCall/gen_input.py | 10 +++------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 31abd848f..5f5466cbc 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -423,12 +423,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio if (isSdsEnumVariant(member)) { const enumMember = generateExpression(expression.member!, frame); const suffix = isSdsCall(expression.$container) ? '' : '()'; - if (expression.isNullSafe) { - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); - return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${enumMember}')${suffix}`; - } else { - return `${receiver}.${enumMember}${suffix}`; - } + return `${receiver}.${enumMember}${suffix}`; } else if (isSdsAbstractResult(member)) { const resultList = abstractResultsOrEmpty(getContainerOfType(member, isSdsCallable)); if (resultList.length === 1) { diff --git a/tests/resources/generation/expressions/enum variant call/input.sdstest b/tests/resources/generation/expressions/enum variant call/input.sdstest index a6d06f263..62431f4b4 100644 --- a/tests/resources/generation/expressions/enum variant call/input.sdstest +++ b/tests/resources/generation/expressions/enum variant call/input.sdstest @@ -1,5 +1,3 @@ -// Related to https://github.com/lars-reimann/Safe-DS/issues/118 - package tests.generator.enumVariantCall fun f(p: Any) diff --git a/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py index 6a5e22226..60d9dfc46 100644 --- a/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py +++ b/tests/resources/generation/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py @@ -1,13 +1,9 @@ -# Imports ---------------------------------------------------------------------- - -import safeds_runner.codegen - # Pipelines -------------------------------------------------------------------- def test(): f(MyEnum.Variant1()) - f(safeds_runner.codegen.safe_access(MyEnum, 'Variant1')()) f(MyEnum.Variant1()) - f(safeds_runner.codegen.safe_access(MyEnum, 'Variant1')()) + f(MyEnum.Variant1()) + f(MyEnum.Variant1()) + f(MyEnum.Variant2(1)) f(MyEnum.Variant2(1)) - f(safeds_runner.codegen.safe_access(MyEnum, 'Variant2')(1)) From 3554d22b59309ae61a5d61f35269d2d082d3cb43 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 20:40:51 +0200 Subject: [PATCH 42/56] test: remove unneeded code --- .../generation/expressions/expression lambda/input.sdstest | 7 +++---- .../output/tests/generator/expressionLambda/gen_input.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/resources/generation/expressions/expression lambda/input.sdstest b/tests/resources/generation/expressions/expression lambda/input.sdstest index a9d56bb6b..6f39b97ec 100644 --- a/tests/resources/generation/expressions/expression lambda/input.sdstest +++ b/tests/resources/generation/expressions/expression lambda/input.sdstest @@ -1,9 +1,8 @@ package tests.generator.expressionLambda -fun f1(param: (a: Int, b: Int) -> r: Int) -fun f2(param: (a: Int, c: Int) -> r: Int) +fun f(param: (a: Int, b: Int) -> r: Int) pipeline test { - f1((a, b = 2) -> 1); - f2((a, c) -> 1); + f((a, b = 2) -> 1); + f((a, c) -> 1); } diff --git a/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py b/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py index ed2990afd..7f130276a 100644 --- a/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py +++ b/tests/resources/generation/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py @@ -1,5 +1,5 @@ # Pipelines -------------------------------------------------------------------- def test(): - f1(lambda a, b=2: 1) - f2(lambda a, c: 1) + f(lambda a, b=2: 1) + f(lambda a, c: 1) From 447bdc1c843c09a33bfe767ea8dfd3334ebe2849 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 20:42:55 +0200 Subject: [PATCH 43/56] test: remove unneeded code --- .../declarations/parameter with python name/input.sdstest | 7 +------ .../tests/generator/parameterWithPythonName/gen_input.py | 8 +------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/resources/generation/declarations/parameter with python name/input.sdstest b/tests/resources/generation/declarations/parameter with python name/input.sdstest index a05862f73..c6b48eed9 100644 --- a/tests/resources/generation/declarations/parameter with python name/input.sdstest +++ b/tests/resources/generation/declarations/parameter with python name/input.sdstest @@ -3,12 +3,7 @@ package tests.generator.parameterWithPythonName fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) fun f2(param: (a: Int, b: Int, c: Int) -> ()) -segment test1(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { +segment test(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { f1((param1: Int, param2: Int, param3: Int = 0) -> 1); f2((param1: Int, param2: Int, param3: Int = 0) {}); } - -segment test2(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") param4: Int) { - f1((param1: Int, param2: Int, param4: Int) -> 1); - f2((param1: Int, param2: Int, param4: Int) {}); -} diff --git a/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py index a1030c309..327d25b7b 100644 --- a/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py +++ b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py @@ -1,13 +1,7 @@ # Steps ------------------------------------------------------------------------ -def test1(param1, param_2, param_3=0): +def test(param1, param_2, param_3=0): f1(lambda param1, param2, param3=0: 1) def __block_lambda_0(param1, param2, param3=0): pass f2(__block_lambda_0) - -def test2(param1, param_2, param_4): - f1(lambda param1, param2, param4: 1) - def __block_lambda_0(param1, param2, param4): - pass - f2(__block_lambda_0) From 29f057e53174c46dcf88dbeb79bf65522d6b1ef1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 20:52:55 +0200 Subject: [PATCH 44/56] feat: unique prefix for block lambda results --- src/cli/generator.ts | 4 ++-- .../tests/generator/blockLambdaResult/gen_input.py | 10 +++++----- .../output/tests/generator/blockLambda/gen_input.py | 8 ++++---- .../output/tests/generator/assignment/gen_input.py | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index 5f5466cbc..a17f05e4f 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -310,7 +310,7 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio const generateAssignee = function (assignee: SdsAssignee): string { if (isSdsBlockLambdaResult(assignee)) { - return `__block_lambda_${assignee.name}`; + return `__block_lambda_result_${assignee.name}`; } else if (isSdsPlaceholder(assignee)) { return assignee.name; } else if (isSdsWildcard(assignee)) { @@ -326,7 +326,7 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); let lambdaBlock = generateBlock(blockLambda.body, frame); if (lambdaResult.length !== 0 && lambdaBlock !== 'pass') { - lambdaBlock += `\nreturn ${lambdaResult.map((result) => `__block_lambda_${result.name}`).join(', ')}`; + lambdaBlock += `\nreturn ${lambdaResult.map((result) => `__block_lambda_result_${result.name}`).join(', ')}`; } return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( blockLambda.parameterList, diff --git a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py index 492773617..1ced19a7a 100644 --- a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py +++ b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py @@ -11,11 +11,11 @@ def f2(l): def test(): def __block_lambda_0(a, b): - __block_lambda_d = g() - return __block_lambda_d + __block_lambda_result_d = g() + return __block_lambda_result_d f1(__block_lambda_0) def __block_lambda_1(a, b): - __block_lambda_d = g() - __block_lambda_e = g() - return __block_lambda_d, __block_lambda_e + __block_lambda_result_d = g() + __block_lambda_result_e = g() + return __block_lambda_result_d, __block_lambda_result_e f2(__block_lambda_1) diff --git a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py index 81f20bb9c..e5b176019 100644 --- a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py +++ b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -2,12 +2,12 @@ def test(): def __block_lambda_0(a, b=2): - __block_lambda_d = g() - return __block_lambda_d + __block_lambda_result_d = g() + return __block_lambda_result_d f1(__block_lambda_0) def __block_lambda_1(a, c): - __block_lambda_d = g() - return __block_lambda_d + __block_lambda_result_d = g() + return __block_lambda_result_d f1(__block_lambda_1) def __block_lambda_2(): pass diff --git a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py index b728ffd8e..44a19ef88 100644 --- a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py +++ b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -18,9 +18,9 @@ def testPipeline(): f1(x) def __block_lambda_0(): g() - a, _, __block_lambda_c = g() + a, _, __block_lambda_result_c = g() x, _, _ = g() f1(a) f1(x) - return __block_lambda_c + return __block_lambda_result_c f2(__block_lambda_0) From f89751cfa9110e442492644998c433fa4f460c71 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:04:10 +0200 Subject: [PATCH 45/56] feat: reserve prefix `__gen_` instead of `__block_lambda_` --- src/language/validation/names.ts | 14 +++++++------- src/language/validation/safe-ds-validator.ts | 4 ++-- .../__block_lambda_ prefix/annotations.sdstest | 7 ------- .../__block_lambda_ prefix/attributes.sdstest | 9 --------- .../block lambda results.sdstest | 11 ----------- .../names/__block_lambda_ prefix/classes.sdstest | 7 ------- .../__block_lambda_ prefix/enum variants.sdstest | 9 --------- .../names/__block_lambda_ prefix/enums.sdstest | 7 ------- .../names/__block_lambda_ prefix/functions.sdstest | 7 ------- .../__block_lambda_ prefix/no package name.sdstest | 1 - .../package name with block lambda prefix.sdstest | 2 -- ...ackage name without block lambda prefix.sdstest | 2 -- .../__block_lambda_ prefix/parameters.sdstest | 9 --------- .../names/__block_lambda_ prefix/pipelines.sdstest | 7 ------- .../__block_lambda_ prefix/placeholders.sdstest | 9 --------- .../names/__block_lambda_ prefix/results.sdstest | 9 --------- .../names/__block_lambda_ prefix/schemas.sdstest | 7 ------- .../names/__block_lambda_ prefix/segments.sdstest | 7 ------- .../__block_lambda_ prefix/type parameters.sdstest | 9 --------- .../names/codegen prefix/annotations.sdstest | 7 +++++++ .../names/codegen prefix/attributes.sdstest | 9 +++++++++ .../codegen prefix/block lambda results.sdstest | 11 +++++++++++ .../names/codegen prefix/classes.sdstest | 7 +++++++ .../names/codegen prefix/enum variants.sdstest | 9 +++++++++ .../validation/names/codegen prefix/enums.sdstest | 7 +++++++ .../names/codegen prefix/functions.sdstest | 7 +++++++ .../names/codegen prefix/no package name.sdstest | 1 + .../package name with block lambda prefix.sdstest | 2 ++ ...ackage name without block lambda prefix.sdstest | 2 ++ .../names/codegen prefix/parameters.sdstest | 9 +++++++++ .../names/codegen prefix/pipelines.sdstest | 7 +++++++ .../names/codegen prefix/placeholders.sdstest | 9 +++++++++ .../names/codegen prefix/results.sdstest | 9 +++++++++ .../names/codegen prefix/schemas.sdstest | 7 +++++++ .../names/codegen prefix/segments.sdstest | 7 +++++++ .../names/codegen prefix/type parameters.sdstest | 9 +++++++++ 36 files changed, 128 insertions(+), 128 deletions(-) delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/annotations.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/attributes.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/block lambda results.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/classes.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/enum variants.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/enums.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/functions.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/no package name.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/package name with block lambda prefix.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/package name without block lambda prefix.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/parameters.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/pipelines.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/placeholders.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/results.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/schemas.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/segments.sdstest delete mode 100644 tests/resources/validation/names/__block_lambda_ prefix/type parameters.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/annotations.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/attributes.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/block lambda results.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/classes.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/enum variants.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/enums.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/functions.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/no package name.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/package name with block lambda prefix.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/package name without block lambda prefix.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/parameters.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/pipelines.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/placeholders.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/results.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/schemas.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/segments.sdstest create mode 100644 tests/resources/validation/names/codegen prefix/type parameters.sdstest diff --git a/src/language/validation/names.ts b/src/language/validation/names.ts index dc2bb7349..6a0e83938 100644 --- a/src/language/validation/names.ts +++ b/src/language/validation/names.ts @@ -36,26 +36,26 @@ import { isInPipelineFile, isInStubFile, isInTestFile } from '../helpers/fileExt import { declarationIsAllowedInPipelineFile, declarationIsAllowedInStubFile } from './other/modules.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { listBuiltinFiles } from '../builtins/fileFinder.js'; +import { CODEGEN_PREFIX } from '../../cli/generator.js'; -export const CODE_NAME_BLOCK_LAMBDA_PREFIX = 'name/block-lambda-prefix'; +export const CODE_NAME_CODEGEN_PREFIX = 'name/codegen-prefix'; export const CODE_NAME_CASING = 'name/casing'; export const CODE_NAME_DUPLICATE = 'name/duplicate'; // ----------------------------------------------------------------------------- -// Block lambda prefix +// Codegen prefix // ----------------------------------------------------------------------------- -export const nameMustNotStartWithBlockLambdaPrefix = (node: SdsDeclaration, accept: ValidationAcceptor) => { +export const nameMustNotStartWithCodegenPrefix = (node: SdsDeclaration, accept: ValidationAcceptor) => { const name = node.name ?? ''; - const blockLambdaPrefix = '__block_lambda_'; - if (name.startsWith(blockLambdaPrefix)) { + if (name.startsWith(CODEGEN_PREFIX)) { accept( 'error', - "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas.", + `Names of declarations must not start with '${CODEGEN_PREFIX}'. This is reserved for code generation.`, { node, property: 'name', - code: CODE_NAME_BLOCK_LAMBDA_PREFIX, + code: CODE_NAME_CODEGEN_PREFIX, }, ); } diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 15fd46a26..618b96196 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -12,7 +12,7 @@ import { functionMustContainUniqueNames, moduleMemberMustHaveNameThatIsUniqueInPackage, moduleMustContainUniqueNames, - nameMustNotStartWithBlockLambdaPrefix, + nameMustNotStartWithCodegenPrefix, nameShouldHaveCorrectCasing, pipelineMustContainUniqueNames, schemaMustContainUniqueNames, @@ -188,7 +188,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsClassBody: [classBodyShouldNotBeEmpty], SdsConstraintList: [constraintListShouldNotBeEmpty], SdsDeclaration: [ - nameMustNotStartWithBlockLambdaPrefix, + nameMustNotStartWithCodegenPrefix, nameShouldHaveCorrectCasing, pythonNameShouldDifferFromSafeDsName(services), singleUseAnnotationsMustNotBeRepeated(services), diff --git a/tests/resources/validation/names/__block_lambda_ prefix/annotations.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/annotations.sdstest deleted file mode 100644 index d6e72e88b..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/annotations.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -annotation »__block_lambda_0« - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -annotation »_block_lambda_1« diff --git a/tests/resources/validation/names/__block_lambda_ prefix/attributes.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/attributes.sdstest deleted file mode 100644 index c00524d6f..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/attributes.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -class MyClass { - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - attr »__block_lambda_0«: Int - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - attr »_block_lambda_1«: Int -} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/block lambda results.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/block lambda results.sdstest deleted file mode 100644 index 35cc49624..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/block lambda results.sdstest +++ /dev/null @@ -1,11 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -pipeline myPipeline2 { - () { - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - yield »__block_lambda_0« = 1; - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - yield »_block_lambda_1« = 1; - }; -} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/classes.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/classes.sdstest deleted file mode 100644 index b1a513e91..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/classes.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -class »__block_lambda_0« - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -class »_block_lambda_1« diff --git a/tests/resources/validation/names/__block_lambda_ prefix/enum variants.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/enum variants.sdstest deleted file mode 100644 index 096504548..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/enum variants.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -enum MyEnum { - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »__block_lambda_0« - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »_block_lambda_1« -} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/enums.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/enums.sdstest deleted file mode 100644 index fe820ae01..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/enums.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -enum »__block_lambda_0« - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -enum »_block_lambda_1« diff --git a/tests/resources/validation/names/__block_lambda_ prefix/functions.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/functions.sdstest deleted file mode 100644 index 9c99c5a4b..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/functions.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -fun »__block_lambda_0«() - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -fun »_block_lambda_1«() diff --git a/tests/resources/validation/names/__block_lambda_ prefix/no package name.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/no package name.sdstest deleted file mode 100644 index ef31d98d5..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/no package name.sdstest +++ /dev/null @@ -1 +0,0 @@ -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." diff --git a/tests/resources/validation/names/__block_lambda_ prefix/package name with block lambda prefix.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/package name with block lambda prefix.sdstest deleted file mode 100644 index 4c00c1f81..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/package name with block lambda prefix.sdstest +++ /dev/null @@ -1,2 +0,0 @@ -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -package »__block_lambda_0« diff --git a/tests/resources/validation/names/__block_lambda_ prefix/package name without block lambda prefix.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/package name without block lambda prefix.sdstest deleted file mode 100644 index d09e0f930..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/package name without block lambda prefix.sdstest +++ /dev/null @@ -1,2 +0,0 @@ -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -package »_block_lambda_1« diff --git a/tests/resources/validation/names/__block_lambda_ prefix/parameters.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/parameters.sdstest deleted file mode 100644 index 9fbf21acd..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/parameters.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -fun myFunction1( - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »__block_lambda_0«: Int, - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »_block_lambda_1«: Int, -) diff --git a/tests/resources/validation/names/__block_lambda_ prefix/pipelines.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/pipelines.sdstest deleted file mode 100644 index fc10c0a35..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/pipelines.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -pipeline »__block_lambda_0« {} - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -pipeline »_block_lambda_1« {} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/placeholders.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/placeholders.sdstest deleted file mode 100644 index 92fd70ff9..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/placeholders.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -pipeline myPipeline1 { - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - val »__block_lambda_0« = 1; - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - val »_block_lambda_1« = 1; -} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/results.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/results.sdstest deleted file mode 100644 index 82d3f06c3..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/results.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -fun myFunction2() -> ( - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »__block_lambda_0«: Int, - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »_block_lambda_1«: Int, -) diff --git a/tests/resources/validation/names/__block_lambda_ prefix/schemas.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/schemas.sdstest deleted file mode 100644 index b39f5072d..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/schemas.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -schema »__block_lambda_0« {} - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -schema »_block_lambda_1« {} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/segments.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/segments.sdstest deleted file mode 100644 index 51e23ce62..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/segments.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -// $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -segment »__block_lambda_0«() {} - -// $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." -segment »_block_lambda_1«() {} diff --git a/tests/resources/validation/names/__block_lambda_ prefix/type parameters.sdstest b/tests/resources/validation/names/__block_lambda_ prefix/type parameters.sdstest deleted file mode 100644 index 5503e2cf3..000000000 --- a/tests/resources/validation/names/__block_lambda_ prefix/type parameters.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.validation.names.blockLambdaPrefix - -fun myFunction3< - // $TEST$ error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »__block_lambda_0«, - - // $TEST$ no error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." - »_block_lambda_1«, ->() diff --git a/tests/resources/validation/names/codegen prefix/annotations.sdstest b/tests/resources/validation/names/codegen prefix/annotations.sdstest new file mode 100644 index 000000000..2f1ed5138 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/annotations.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +annotation »__gen_0« + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +annotation »_gen_1« diff --git a/tests/resources/validation/names/codegen prefix/attributes.sdstest b/tests/resources/validation/names/codegen prefix/attributes.sdstest new file mode 100644 index 000000000..410cd1004 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/attributes.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +class MyClass { + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + attr »__gen_0«: Int + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + attr »_gen_1«: Int +} diff --git a/tests/resources/validation/names/codegen prefix/block lambda results.sdstest b/tests/resources/validation/names/codegen prefix/block lambda results.sdstest new file mode 100644 index 000000000..dcb4646b5 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/block lambda results.sdstest @@ -0,0 +1,11 @@ +package tests.validation.names.blockLambdaPrefix + +pipeline myPipeline2 { + () { + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + yield »__gen_0« = 1; + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + yield »_gen_1« = 1; + }; +} diff --git a/tests/resources/validation/names/codegen prefix/classes.sdstest b/tests/resources/validation/names/codegen prefix/classes.sdstest new file mode 100644 index 000000000..d7e77027e --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/classes.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +class »__gen_0« + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +class »_gen_1« diff --git a/tests/resources/validation/names/codegen prefix/enum variants.sdstest b/tests/resources/validation/names/codegen prefix/enum variants.sdstest new file mode 100644 index 000000000..d69569bd0 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/enum variants.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +enum MyEnum { + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »__gen_0« + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »_gen_1« +} diff --git a/tests/resources/validation/names/codegen prefix/enums.sdstest b/tests/resources/validation/names/codegen prefix/enums.sdstest new file mode 100644 index 000000000..c549ec75f --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/enums.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +enum »__gen_0« + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +enum »_gen_1« diff --git a/tests/resources/validation/names/codegen prefix/functions.sdstest b/tests/resources/validation/names/codegen prefix/functions.sdstest new file mode 100644 index 000000000..bdd235e7e --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/functions.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +fun »__gen_0«() + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +fun »_gen_1«() diff --git a/tests/resources/validation/names/codegen prefix/no package name.sdstest b/tests/resources/validation/names/codegen prefix/no package name.sdstest new file mode 100644 index 000000000..961d17e5d --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/no package name.sdstest @@ -0,0 +1 @@ +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." diff --git a/tests/resources/validation/names/codegen prefix/package name with block lambda prefix.sdstest b/tests/resources/validation/names/codegen prefix/package name with block lambda prefix.sdstest new file mode 100644 index 000000000..72430e42b --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/package name with block lambda prefix.sdstest @@ -0,0 +1,2 @@ +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +package »__gen_0« diff --git a/tests/resources/validation/names/codegen prefix/package name without block lambda prefix.sdstest b/tests/resources/validation/names/codegen prefix/package name without block lambda prefix.sdstest new file mode 100644 index 000000000..56bc1da44 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/package name without block lambda prefix.sdstest @@ -0,0 +1,2 @@ +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +package »_gen_1« diff --git a/tests/resources/validation/names/codegen prefix/parameters.sdstest b/tests/resources/validation/names/codegen prefix/parameters.sdstest new file mode 100644 index 000000000..6fc81e57d --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/parameters.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +fun myFunction1( + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »__gen_0«: Int, + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »_gen_1«: Int, +) diff --git a/tests/resources/validation/names/codegen prefix/pipelines.sdstest b/tests/resources/validation/names/codegen prefix/pipelines.sdstest new file mode 100644 index 000000000..e5da70f5b --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/pipelines.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +pipeline »__gen_0« {} + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +pipeline »_gen_1« {} diff --git a/tests/resources/validation/names/codegen prefix/placeholders.sdstest b/tests/resources/validation/names/codegen prefix/placeholders.sdstest new file mode 100644 index 000000000..7bb4abb1d --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/placeholders.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +pipeline myPipeline1 { + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + val »__gen_0« = 1; + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + val »_gen_1« = 1; +} diff --git a/tests/resources/validation/names/codegen prefix/results.sdstest b/tests/resources/validation/names/codegen prefix/results.sdstest new file mode 100644 index 000000000..654e3798d --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/results.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +fun myFunction2() -> ( + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »__gen_0«: Int, + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »_gen_1«: Int, +) diff --git a/tests/resources/validation/names/codegen prefix/schemas.sdstest b/tests/resources/validation/names/codegen prefix/schemas.sdstest new file mode 100644 index 000000000..7ec0a8ad0 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/schemas.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +schema »__gen_0« {} + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +schema »_gen_1« {} diff --git a/tests/resources/validation/names/codegen prefix/segments.sdstest b/tests/resources/validation/names/codegen prefix/segments.sdstest new file mode 100644 index 000000000..fe7c64e41 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/segments.sdstest @@ -0,0 +1,7 @@ +package tests.validation.names.blockLambdaPrefix + +// $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +segment »__gen_0«() {} + +// $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." +segment »_gen_1«() {} diff --git a/tests/resources/validation/names/codegen prefix/type parameters.sdstest b/tests/resources/validation/names/codegen prefix/type parameters.sdstest new file mode 100644 index 000000000..71f700288 --- /dev/null +++ b/tests/resources/validation/names/codegen prefix/type parameters.sdstest @@ -0,0 +1,9 @@ +package tests.validation.names.blockLambdaPrefix + +fun myFunction3< + // $TEST$ error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »__gen_0«, + + // $TEST$ no error "Names of declarations must not start with '__gen_'. This is reserved for code generation." + »_gen_1«, +>() From 8eb38817224f41301976e4fb7bd7cbb8eab502c7 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:10:50 +0200 Subject: [PATCH 46/56] feat: use new reserved prefix for block lambdas and their results --- src/cli/generator.ts | 11 +++++++--- .../parameterWithPythonName/gen_input.py | 4 ++-- .../generator/blockLambdaResult/gen_input.py | 18 ++++++++--------- .../tests/generator/blockLambda/gen_input.py | 20 +++++++++---------- .../tests/generator/assignment/gen_input.py | 8 ++++---- .../expressionStatement/gen_input.py | 4 ++-- .../statementWithoutEffect/gen_input.py | 4 ++-- 7 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index a17f05e4f..dd34309bb 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -77,6 +77,11 @@ import { StringConstant, } from '../language/partialEvaluation/model.js'; +export const CODEGEN_PREFIX = '__gen_' +const BLOCK_LAMBDA_PREFIX = `${CODEGEN_PREFIX}block_lambda_`; +const BLOCK_LAMBDA_RESULT_PREFIX = `${CODEGEN_PREFIX}block_lambda_result_`; +const YIELD_PREFIX = `${CODEGEN_PREFIX}yield_`; + const RUNNER_CODEGEN_PACKAGE = 'safeds_runner.codegen'; const PYTHON_INDENT = ' '; @@ -310,7 +315,7 @@ const generateAssignment = function (assignment: SdsAssignment, frame: Generatio const generateAssignee = function (assignee: SdsAssignee): string { if (isSdsBlockLambdaResult(assignee)) { - return `__block_lambda_result_${assignee.name}`; + return `${BLOCK_LAMBDA_RESULT_PREFIX}${assignee.name}`; } else if (isSdsPlaceholder(assignee)) { return assignee.name; } else if (isSdsWildcard(assignee)) { @@ -326,7 +331,7 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); let lambdaBlock = generateBlock(blockLambda.body, frame); if (lambdaResult.length !== 0 && lambdaBlock !== 'pass') { - lambdaBlock += `\nreturn ${lambdaResult.map((result) => `__block_lambda_result_${result.name}`).join(', ')}`; + lambdaBlock += `\nreturn ${lambdaResult.map((result) => `${BLOCK_LAMBDA_RESULT_PREFIX}${result.name}`).join(', ')}`; } return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( blockLambda.parameterList, @@ -595,7 +600,7 @@ class GenerationInfoFrame { } getUniqueLambdaBlockName(lambda: SdsBlockLambda): string { - return `__block_lambda_${this.blockLambdaManager.assignId(lambda)}`; + return `${BLOCK_LAMBDA_PREFIX}${this.blockLambdaManager.assignId(lambda)}`; } getServices(): SafeDsServices { diff --git a/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py index 327d25b7b..4d9c8f8d5 100644 --- a/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py +++ b/tests/resources/generation/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py @@ -2,6 +2,6 @@ def test(param1, param_2, param_3=0): f1(lambda param1, param2, param3=0: 1) - def __block_lambda_0(param1, param2, param3=0): + def __gen_block_lambda_0(param1, param2, param3=0): pass - f2(__block_lambda_0) + f2(__gen_block_lambda_0) diff --git a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py index 1ced19a7a..3dfd67ece 100644 --- a/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py +++ b/tests/resources/generation/expressions/block lambda result/output/tests/generator/blockLambdaResult/gen_input.py @@ -10,12 +10,12 @@ def f2(l): # Pipelines -------------------------------------------------------------------- def test(): - def __block_lambda_0(a, b): - __block_lambda_result_d = g() - return __block_lambda_result_d - f1(__block_lambda_0) - def __block_lambda_1(a, b): - __block_lambda_result_d = g() - __block_lambda_result_e = g() - return __block_lambda_result_d, __block_lambda_result_e - f2(__block_lambda_1) + def __gen_block_lambda_0(a, b): + __gen_block_lambda_result_d = g() + return __gen_block_lambda_result_d + f1(__gen_block_lambda_0) + def __gen_block_lambda_1(a, b): + __gen_block_lambda_result_d = g() + __gen_block_lambda_result_e = g() + return __gen_block_lambda_result_d, __gen_block_lambda_result_e + f2(__gen_block_lambda_1) diff --git a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py index e5b176019..1f02920f7 100644 --- a/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py +++ b/tests/resources/generation/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -1,14 +1,14 @@ # Pipelines -------------------------------------------------------------------- def test(): - def __block_lambda_0(a, b=2): - __block_lambda_result_d = g() - return __block_lambda_result_d - f1(__block_lambda_0) - def __block_lambda_1(a, c): - __block_lambda_result_d = g() - return __block_lambda_result_d - f1(__block_lambda_1) - def __block_lambda_2(): + def __gen_block_lambda_0(a, b=2): + __gen_block_lambda_result_d = g() + return __gen_block_lambda_result_d + f1(__gen_block_lambda_0) + def __gen_block_lambda_1(a, c): + __gen_block_lambda_result_d = g() + return __gen_block_lambda_result_d + f1(__gen_block_lambda_1) + def __gen_block_lambda_2(): pass - f2(__block_lambda_2) + f2(__gen_block_lambda_2) diff --git a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py index 44a19ef88..798cf4135 100644 --- a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py +++ b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -16,11 +16,11 @@ def testPipeline(): x, _, _ = g() f1(a) f1(x) - def __block_lambda_0(): + def __gen_block_lambda_0(): g() - a, _, __block_lambda_result_c = g() + a, _, __gen_block_lambda_result_c = g() x, _, _ = g() f1(a) f1(x) - return __block_lambda_result_c - f2(__block_lambda_0) + return __gen_block_lambda_result_c + f2(__gen_block_lambda_0) diff --git a/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py b/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py index f77b73f00..117709000 100644 --- a/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py +++ b/tests/resources/generation/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py @@ -7,6 +7,6 @@ def testStep(): def testPipeline(): g() - def __block_lambda_0(): + def __gen_block_lambda_0(): g() - f(__block_lambda_0) + f(__gen_block_lambda_0) diff --git a/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py b/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py index 85e9e98ee..2cde3391a 100644 --- a/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py +++ b/tests/resources/generation/statements/skip-statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py @@ -6,6 +6,6 @@ def testStep(): # Pipelines -------------------------------------------------------------------- def testPipeline(): - def __block_lambda_0(): + def __gen_block_lambda_0(): pass - f(__block_lambda_0) + f(__gen_block_lambda_0) From 173b142e39db9a43cd75cfc773b6360bb6c4f605 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:17:33 +0200 Subject: [PATCH 47/56] fix: also add prefix to results of segments, so they don't clash with placeholders --- src/cli/generator.ts | 4 ++-- .../tests/generator/imports/gen_context_same_package.py | 8 ++++---- .../output/tests/generator/assignment/gen_input.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index dd34309bb..a7fd25284 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -192,7 +192,7 @@ const generateSegment = function ( let segmentBlock = generateBlock(segment.body, infoFrame); if (segmentResult.length !== 0) { // Segment should always have results - segmentBlock += `\nreturn ${segmentResult.map((result) => result.name).join(', ')}`; + segmentBlock += `\nreturn ${segmentResult.map((result) => `${YIELD_PREFIX}${result.name}`).join(', ')}`; } return expandToString`def ${getPythonNameOrDefault(services, segment)}(${generateParameters( segment.parameterList, @@ -321,7 +321,7 @@ const generateAssignee = function (assignee: SdsAssignee): string { } else if (isSdsWildcard(assignee)) { return '_'; } else if (isSdsYield(assignee)) { - return assignee.result?.ref?.name!; + return `${YIELD_PREFIX}${assignee.result?.ref?.name!}`; } /* c8 ignore next 2 */ throw new Error(`Unknown SdsAssignment: ${assignee.$type}`); diff --git a/tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py b/tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py index 4f69c70b4..2575a1ac1 100644 --- a/tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py +++ b/tests/resources/generation/imports/general/output/tests/generator/imports/gen_context_same_package.py @@ -1,9 +1,9 @@ # Steps ------------------------------------------------------------------------ def step1InSamePackage(): - result = impureFunction() - return result + __gen_yield_result = impureFunction() + return __gen_yield_result def step2InSamePackage(): - result = impureFunction() - return result + __gen_yield_result = impureFunction() + return __gen_yield_result diff --git a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py index 798cf4135..d98f68403 100644 --- a/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py +++ b/tests/resources/generation/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -2,11 +2,11 @@ def testStep(): g() - a, _, c = g() + a, _, __gen_yield_c = g() x, _, _ = g() f1(a) f1(x) - return c + return __gen_yield_c # Pipelines -------------------------------------------------------------------- From b1000496ae4d66769d759c6e1ddcc7882563cd22 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:21:26 +0000 Subject: [PATCH 48/56] style: apply automated linter fixes --- src/cli/generator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index a7fd25284..b2a9b5c1a 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -77,7 +77,7 @@ import { StringConstant, } from '../language/partialEvaluation/model.js'; -export const CODEGEN_PREFIX = '__gen_' +export const CODEGEN_PREFIX = '__gen_'; const BLOCK_LAMBDA_PREFIX = `${CODEGEN_PREFIX}block_lambda_`; const BLOCK_LAMBDA_RESULT_PREFIX = `${CODEGEN_PREFIX}block_lambda_result_`; const YIELD_PREFIX = `${CODEGEN_PREFIX}yield_`; @@ -331,7 +331,9 @@ const generateBlockLambda = function (blockLambda: SdsBlockLambda, frame: Genera const lambdaResult = blockLambdaResultsOrEmpty(blockLambda); let lambdaBlock = generateBlock(blockLambda.body, frame); if (lambdaResult.length !== 0 && lambdaBlock !== 'pass') { - lambdaBlock += `\nreturn ${lambdaResult.map((result) => `${BLOCK_LAMBDA_RESULT_PREFIX}${result.name}`).join(', ')}`; + lambdaBlock += `\nreturn ${lambdaResult + .map((result) => `${BLOCK_LAMBDA_RESULT_PREFIX}${result.name}`) + .join(', ')}`; } return expandToString`def ${frame.getUniqueLambdaBlockName(blockLambda)}(${generateParameters( blockLambda.parameterList, From b5b1dbe79effcac39f76e7dcf570b9c32a2c6c0e Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:35:09 +0200 Subject: [PATCH 49/56] test: remove unneeded code --- .../generation/expressions/call/input.sdstest | 24 +++++-------------- .../output/tests/generator/call/gen_input.py | 10 ++++---- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/tests/resources/generation/expressions/call/input.sdstest b/tests/resources/generation/expressions/call/input.sdstest index 0b3fe12ba..656a10dbe 100644 --- a/tests/resources/generation/expressions/call/input.sdstest +++ b/tests/resources/generation/expressions/call/input.sdstest @@ -2,31 +2,19 @@ package tests.generator.call fun f(param: Any?) -fun g1( +fun g( param1: Int, param2: Int = 0 ) -> result: Boolean -fun g2( - param1: Int, - param3: Int -) -> result: Boolean - -fun h1( +fun h( @PythonName("param_1") param1: Int, @PythonName("param_2") param2: Int = 0 ) -> result: Boolean -fun h2( - @PythonName("param_1") param1: Int, - @PythonName("param_3") param3: Int -) -> result: Boolean - pipeline test { - f((g1(1, 2))); - f((g1(param2 = 1, param1 = 2))); - f((g2(2, 3))); - f((h1(1, 2))); - f((h1(param2 = 1, param1 = 2))); - f((h2(2, 3))); + f((g(1, 2))); + f((g(param2 = 1, param1 = 2))); + f((h(1, 2))); + f((h(param2 = 1, param1 = 2))); } diff --git a/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py b/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py index 3a46418ff..e2968155b 100644 --- a/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py +++ b/tests/resources/generation/expressions/call/output/tests/generator/call/gen_input.py @@ -1,9 +1,7 @@ # Pipelines -------------------------------------------------------------------- def test(): - f(g1(1, param2=2)) - f(g1(2, param2=1)) - f(g2(2, 3)) - f(h1(1, param_2=2)) - f(h1(2, param_2=1)) - f(h2(2, 3)) + f(g(1, param2=2)) + f(g(2, param2=1)) + f(h(1, param_2=2)) + f(h(2, param_2=1)) From d1201f90bdde5dd651ad0fcd798cc79e87ecd1fa Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:40:41 +0200 Subject: [PATCH 50/56] test: split tests for code generation for lists and maps --- .../complex literals/input.sdstest | 24 ------------------- .../generator/complexLiterals/gen_input.py | 11 --------- .../expressions/lists/input.sdstest | 11 +++++++++ .../output/tests/generator/lists/gen_input.py | 6 +++++ .../tests/generator/lists}/gen_input_test.py | 0 .../generation/expressions/maps/input.sdstest | 17 +++++++++++++ .../output/tests/generator/maps/gen_input.py | 8 +++++++ .../tests/generator/maps/gen_input_test.py | 4 ++++ 8 files changed, 46 insertions(+), 35 deletions(-) delete mode 100644 tests/resources/generation/expressions/complex literals/input.sdstest delete mode 100644 tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py create mode 100644 tests/resources/generation/expressions/lists/input.sdstest create mode 100644 tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input.py rename tests/resources/generation/expressions/{complex literals/output/tests/generator/complexLiterals => lists/output/tests/generator/lists}/gen_input_test.py (100%) create mode 100644 tests/resources/generation/expressions/maps/input.sdstest create mode 100644 tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py create mode 100644 tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input_test.py diff --git a/tests/resources/generation/expressions/complex literals/input.sdstest b/tests/resources/generation/expressions/complex literals/input.sdstest deleted file mode 100644 index 3f0fc66b6..000000000 --- a/tests/resources/generation/expressions/complex literals/input.sdstest +++ /dev/null @@ -1,24 +0,0 @@ -package tests.generator.complexLiterals - -fun f(param: List) - -fun g(param: Map) - -fun g2(param: Map) - -fun h1() -> result: Int - -fun h2() -> result: Float - -fun h3() -> result: String - -pipeline test { - f([]); - f([1, 2, 3]); - f([1, h1(), h1() + 5]); - g({}); - g({"a": 1.2, "b": 1.0}); - g({h3(): -0.5, "b": h2()}); - g2({1.2: "a", 1.0: "b"}); - g2({5.6: "c", h2(): h3()}); -} diff --git a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py b/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py deleted file mode 100644 index b0b2d5feb..000000000 --- a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input.py +++ /dev/null @@ -1,11 +0,0 @@ -# Pipelines -------------------------------------------------------------------- - -def test(): - f([]) - f([1, 2, 3]) - f([1, h1(), (h1()) + (5)]) - g({}) - g({'a': 1.2, 'b': 1.0}) - g({h3(): -0.5, 'b': h2()}) - g2({1.2: 'a', 1.0: 'b'}) - g2({5.6: 'c', h2(): h3()}) diff --git a/tests/resources/generation/expressions/lists/input.sdstest b/tests/resources/generation/expressions/lists/input.sdstest new file mode 100644 index 000000000..efb5ca1f8 --- /dev/null +++ b/tests/resources/generation/expressions/lists/input.sdstest @@ -0,0 +1,11 @@ +package tests.generator.lists + +fun f(param: List) + +fun h() -> result: Int + +pipeline test { + f([]); + f([1, 2, 3]); + f([1, h(), h() + 5]); +} diff --git a/tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input.py b/tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input.py new file mode 100644 index 000000000..d5faf3b21 --- /dev/null +++ b/tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input.py @@ -0,0 +1,6 @@ +# Pipelines -------------------------------------------------------------------- + +def test(): + f([]) + f([1, 2, 3]) + f([1, h(), (h()) + (5)]) diff --git a/tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input_test.py b/tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input_test.py similarity index 100% rename from tests/resources/generation/expressions/complex literals/output/tests/generator/complexLiterals/gen_input_test.py rename to tests/resources/generation/expressions/lists/output/tests/generator/lists/gen_input_test.py diff --git a/tests/resources/generation/expressions/maps/input.sdstest b/tests/resources/generation/expressions/maps/input.sdstest new file mode 100644 index 000000000..981e6a274 --- /dev/null +++ b/tests/resources/generation/expressions/maps/input.sdstest @@ -0,0 +1,17 @@ +package tests.generator.maps + +fun g1(param: Map) + +fun g2(param: Map) + +fun h1() -> result: Float + +fun h2() -> result: String + +pipeline test { + g1({}); + g1({"a": 1.2, "b": 1.0}); + g1({h2(): -0.5, "b": h1()}); + g2({1.2: "a", 1.0: "b"}); + g2({5.6: "c", h1(): h2()}); +} diff --git a/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py b/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py new file mode 100644 index 000000000..9db46e6bf --- /dev/null +++ b/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py @@ -0,0 +1,8 @@ +# Pipelines -------------------------------------------------------------------- + +def test(): + g1({}) + g1({'a': 1.2, 'b': 1.0}) + g1({h2(): -0.5, 'b': h1()}) + g2({1.2: 'a', 1.0: 'b'}) + g2({5.6: 'c', h1(): h2()}) diff --git a/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input_test.py b/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() From b30d64931f1cc5b67cfba19aa8a56f2908091887 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 21:42:00 +0200 Subject: [PATCH 51/56] docs: remove incorrect comment --- src/cli/generator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index b2a9b5c1a..ea0ab43c4 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -191,7 +191,6 @@ const generateSegment = function ( const segmentResult = segment.resultList?.results || []; let segmentBlock = generateBlock(segment.body, infoFrame); if (segmentResult.length !== 0) { - // Segment should always have results segmentBlock += `\nreturn ${segmentResult.map((result) => `${YIELD_PREFIX}${result.name}`).join(', ')}`; } return expandToString`def ${getPythonNameOrDefault(services, segment)}(${generateParameters( From a969226bcc09b75e361406b54c1ec5ddf64a21c7 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 22:00:33 +0200 Subject: [PATCH 52/56] refactor: shorten method to load documents --- tests/helpers/testResources.ts | 19 +++++-------------- tests/language/generation/creator.ts | 7 ++++--- .../typing/{ => type computer}/creator.ts | 10 +++++----- .../{ => type computer}/testTyping.test.ts | 6 +++--- 4 files changed, 17 insertions(+), 25 deletions(-) rename tests/language/typing/{ => type computer}/creator.ts (94%) rename tests/language/typing/{ => type computer}/testTyping.test.ts (93%) diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index 2ca8bfe16..c9bebabba 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -97,21 +97,12 @@ const isNotSkipped = (pathRelativeToResources: string) => { }; /** - * For a list of given URIs, load all files into the current workspace. + * Load the documents at the specified URIs into the workspace managed by the given services. * - * @returns List of loaded documents + * @returns The loaded documents. */ -export const loadAllDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { - const documents = await Promise.all(uris.map(async (uri) => loadDocumentWithDiagnostics(services, uri))); - await services.shared.workspace.DocumentBuilder.build(documents); +export const loadDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { + const documents = uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); + await services.shared.workspace.DocumentBuilder.build(documents, { validation: true }); return documents; }; - -const loadDocumentWithDiagnostics = async function (services: SafeDsServices, uri: URI) { - const parse = parseHelper(services); - const code = fs.readFileSync(uri.fsPath).toString(); - return parse(code, { - documentUri: uri.toString(), - validation: true, - }); -}; diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 90ac5cf81..562c12f5c 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -1,7 +1,7 @@ import { listTestPythonFiles, listTestSafeDsFilesGroupedByParentDirectory, - loadAllDocuments, + loadDocuments, uriToShortenedTestResourceName, } from '../../helpers/testResources.js'; import path from 'path'; @@ -31,8 +31,9 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const actualOutputRoot = URI.file(path.join(parentDirectory.fsPath, 'generated')); const expectedOutputFiles = readExpectedOutputFiles(expectedOutputRoot, actualOutputRoot); let runUntil: Location | undefined; - // First read all files; Then check diagnostics - await loadAllDocuments(services, inputUris); + + // Load all files, so they get linked + await loadDocuments(services, inputUris); for (const uri of inputUris) { const code = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri).textDocument.getText(); diff --git a/tests/language/typing/creator.ts b/tests/language/typing/type computer/creator.ts similarity index 94% rename from tests/language/typing/creator.ts rename to tests/language/typing/type computer/creator.ts index 1bc690c74..d1908613b 100644 --- a/tests/language/typing/creator.ts +++ b/tests/language/typing/type computer/creator.ts @@ -1,14 +1,14 @@ import { listTestSafeDsFilesGroupedByParentDirectory, uriToShortenedTestResourceName, -} from '../../helpers/testResources.js'; +} from '../../../helpers/testResources.js'; import fs from 'fs'; -import { findTestChecks } from '../../helpers/testChecks.js'; +import { findTestChecks } from '../../../helpers/testChecks.js'; import { Location } from 'vscode-languageserver'; -import { getSyntaxErrors, SyntaxErrorsInCodeError } from '../../helpers/diagnostics.js'; +import { getSyntaxErrors, SyntaxErrorsInCodeError } from '../../../helpers/diagnostics.js'; import { EmptyFileSystem, URI } from 'langium'; -import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; -import { TestDescription, TestDescriptionError } from '../../helpers/testDescription.js'; +import { createSafeDsServices } from '../../../../src/language/safe-ds-module.js'; +import { TestDescription, TestDescriptionError } from '../../../helpers/testDescription.js'; const services = createSafeDsServices(EmptyFileSystem).SafeDs; const rootResourceName = 'typing'; diff --git a/tests/language/typing/testTyping.test.ts b/tests/language/typing/type computer/testTyping.test.ts similarity index 93% rename from tests/language/typing/testTyping.test.ts rename to tests/language/typing/type computer/testTyping.test.ts index b2975c05e..3d17815b0 100644 --- a/tests/language/typing/testTyping.test.ts +++ b/tests/language/typing/type computer/testTyping.test.ts @@ -1,11 +1,11 @@ import { afterEach, beforeEach, describe, it } from 'vitest'; -import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; +import { createSafeDsServices } from '../../../../src/language/safe-ds-module.js'; import { NodeFileSystem } from 'langium/node'; import { clearDocuments } from 'langium/test'; import { AssertionError } from 'assert'; -import { locationToString } from '../../helpers/location.js'; +import { locationToString } from '../../../helpers/location.js'; import { createTypingTests } from './creator.js'; -import { getNodeByLocation } from '../../helpers/nodeFinder.js'; +import { getNodeByLocation } from '../../../helpers/nodeFinder.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; const typeComputer = services.types.TypeComputer; From 7f2c924a88f3ebec544f215d8dc6bfba2573e65d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 22:17:45 +0200 Subject: [PATCH 53/56] refactor: use the `loadDocuments` method where applicable --- tests/helpers/testResources.ts | 15 ++++++++++----- tests/language/generation/creator.ts | 2 +- tests/language/generation/testGeneration.test.ts | 6 ++---- .../testPartialEvaluation.test.ts | 4 ++-- tests/language/scoping/testScoping.test.ts | 4 ++-- .../typing/type computer/testTyping.test.ts | 4 ++-- tests/language/validation/testValidation.test.ts | 4 ++-- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/helpers/testResources.ts b/tests/helpers/testResources.ts index c9bebabba..63a0daee4 100644 --- a/tests/helpers/testResources.ts +++ b/tests/helpers/testResources.ts @@ -2,10 +2,8 @@ import path from 'path'; import { globSync } from 'glob'; import { SAFE_DS_FILE_EXTENSIONS } from '../../src/language/helpers/fileExtensions.js'; import { group } from 'radash'; -import { LangiumDocument, URI } from 'langium'; -import { parseHelper } from 'langium/test'; +import { BuildOptions, LangiumDocument, URI } from 'langium'; import { SafeDsServices } from '../../src/language/safe-ds-module.js'; -import fs from 'fs'; const TEST_RESOURCES_PATH = path.join(__dirname, '..', 'resources'); @@ -99,10 +97,17 @@ const isNotSkipped = (pathRelativeToResources: string) => { /** * Load the documents at the specified URIs into the workspace managed by the given services. * + * @param services The language services. + * @param uris The URIs of the documents to load. + * @param options The build options. * @returns The loaded documents. */ -export const loadDocuments = async (services: SafeDsServices, uris: URI[]): Promise => { +export const loadDocuments = async ( + services: SafeDsServices, + uris: URI[], + options: BuildOptions = {}, +): Promise => { const documents = uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); - await services.shared.workspace.DocumentBuilder.build(documents, { validation: true }); + await services.shared.workspace.DocumentBuilder.build(documents, options); return documents; }; diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 562c12f5c..3605aea57 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -33,7 +33,7 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro let runUntil: Location | undefined; // Load all files, so they get linked - await loadDocuments(services, inputUris); + await loadDocuments(services, inputUris, { validation: true }); for (const uri of inputUris) { const code = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri).textDocument.getText(); diff --git a/tests/language/generation/testGeneration.test.ts b/tests/language/generation/testGeneration.test.ts index 940c14fb6..79ba59b58 100644 --- a/tests/language/generation/testGeneration.test.ts +++ b/tests/language/generation/testGeneration.test.ts @@ -6,6 +6,7 @@ import { createGenerationTests } from './creator.js'; import { SdsModule } from '../../../src/language/generated/ast.js'; import { generatePython } from '../../../src/cli/generator.js'; import fs from 'fs'; +import { loadDocuments } from '../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; const generationTests = createGenerationTests(); @@ -27,10 +28,7 @@ describe('generation', async () => { } // Load all documents - const documents = test.inputUris.map((uri) => - services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri), - ); - await services.shared.workspace.DocumentBuilder.build(documents); + const documents = await loadDocuments(services, test.inputUris); // Generate code for all documents const actualOutputPaths: string[] = []; diff --git a/tests/language/partialEvaluation/testPartialEvaluation.test.ts b/tests/language/partialEvaluation/testPartialEvaluation.test.ts index 533772466..2b5b8f64f 100644 --- a/tests/language/partialEvaluation/testPartialEvaluation.test.ts +++ b/tests/language/partialEvaluation/testPartialEvaluation.test.ts @@ -6,6 +6,7 @@ import { AssertionError } from 'assert'; import { locationToString } from '../../helpers/location.js'; import { createPartialEvaluationTests } from './creator.js'; import { getNodeByLocation } from '../../helpers/nodeFinder.js'; +import { loadDocuments } from '../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; const partialEvaluator = services.evaluation.PartialEvaluator; @@ -22,8 +23,7 @@ describe('partial evaluation', async () => { } // Load all documents - const documents = test.uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); - await services.shared.workspace.DocumentBuilder.build(documents); + await loadDocuments(services, test.uris); // Ensure that partially evaluating nodes in the same equivalence class yields the same result for (const equivalenceClassAssertion of test.equivalenceClassAssertions) { diff --git a/tests/language/scoping/testScoping.test.ts b/tests/language/scoping/testScoping.test.ts index 86a29987d..485ed0caf 100644 --- a/tests/language/scoping/testScoping.test.ts +++ b/tests/language/scoping/testScoping.test.ts @@ -7,6 +7,7 @@ import { AssertionError } from 'assert'; import { isLocationEqual, locationToString } from '../../helpers/location.js'; import { createScopingTests, ExpectedReference } from './creator.js'; import { Location } from 'vscode-languageserver'; +import { loadDocuments } from '../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; @@ -27,8 +28,7 @@ describe('scoping', async () => { } // Load all documents - const documents = test.uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); - await services.shared.workspace.DocumentBuilder.build(documents); + await loadDocuments(services, test.uris); // Ensure all expected references match for (const expectedReference of test.expectedReferences) { diff --git a/tests/language/typing/type computer/testTyping.test.ts b/tests/language/typing/type computer/testTyping.test.ts index 3d17815b0..4d6da34bb 100644 --- a/tests/language/typing/type computer/testTyping.test.ts +++ b/tests/language/typing/type computer/testTyping.test.ts @@ -6,6 +6,7 @@ import { AssertionError } from 'assert'; import { locationToString } from '../../../helpers/location.js'; import { createTypingTests } from './creator.js'; import { getNodeByLocation } from '../../../helpers/nodeFinder.js'; +import { loadDocuments } from '../../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; const typeComputer = services.types.TypeComputer; @@ -27,8 +28,7 @@ describe('typing', async () => { } // Load all documents - const documents = test.uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); - await services.shared.workspace.DocumentBuilder.build(documents); + await loadDocuments(services, test.uris) // Ensure all nodes in the equivalence class have the same type for (const equivalenceClassAssertion of test.equivalenceClassAssertions) { diff --git a/tests/language/validation/testValidation.test.ts b/tests/language/validation/testValidation.test.ts index 6d342dc07..81d6a7f4a 100644 --- a/tests/language/validation/testValidation.test.ts +++ b/tests/language/validation/testValidation.test.ts @@ -6,6 +6,7 @@ import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver'; import { AssertionError } from 'assert'; import { clearDocuments, isRangeEqual } from 'langium/test'; import { locationToString } from '../../helpers/location.js'; +import { loadDocuments } from '../../helpers/testResources.js'; const services = createSafeDsServices(NodeFileSystem).SafeDs; @@ -26,8 +27,7 @@ describe('validation', async () => { } // Load all documents - const documents = test.uris.map((uri) => services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri)); - await services.shared.workspace.DocumentBuilder.build(documents, { validation: true }); + await loadDocuments(services, test.uris, { validation: true }); // Ensure all expected issues match for (const expectedIssue of test.expectedIssues) { From 41fc1dd3a57ea098d42dbb1738a92f197b776860 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:20:59 +0000 Subject: [PATCH 54/56] style: apply automated linter fixes --- tests/language/typing/type computer/testTyping.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/language/typing/type computer/testTyping.test.ts b/tests/language/typing/type computer/testTyping.test.ts index 4d6da34bb..6f388838a 100644 --- a/tests/language/typing/type computer/testTyping.test.ts +++ b/tests/language/typing/type computer/testTyping.test.ts @@ -28,7 +28,7 @@ describe('typing', async () => { } // Load all documents - await loadDocuments(services, test.uris) + await loadDocuments(services, test.uris); // Ensure all nodes in the equivalence class have the same type for (const equivalenceClassAssertion of test.equivalenceClassAssertions) { From e029e77069e6443c81cfa1b0159938492a21fea5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 22:28:02 +0200 Subject: [PATCH 55/56] chore: minor changes --- tests/helpers/diagnostics.ts | 10 +++++----- tests/language/generation/creator.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/helpers/diagnostics.ts b/tests/helpers/diagnostics.ts index 322df7f2b..78b54f709 100644 --- a/tests/helpers/diagnostics.ts +++ b/tests/helpers/diagnostics.ts @@ -45,11 +45,11 @@ export const getErrors = async (services: LangiumServices, code: string): Promis * Get all errors from a loaded document. * * @param services The language services. - * @param uri The URI of the code snippet to check. + * @param uri The URI of the document to check. * @returns The errors. */ -export const getErrorsByURI = (services: LangiumServices, uri: URI): Diagnostic[] => { - const diagnostics = getDiagnosticsByURI(services, uri); +export const getErrorsAtURI = (services: LangiumServices, uri: URI): Diagnostic[] => { + const diagnostics = getDiagnosticsAtURI(services, uri); return diagnostics.filter((d) => d.severity === DiagnosticSeverity.Error); }; @@ -73,10 +73,10 @@ const getDiagnostics = async (services: LangiumServices, code: string): Promise< * Get all diagnostics from a loaded document. * * @param services The language services. - * @param uri The URI of the code snippet to check. + * @param uri The URI of the document to check. * @returns The diagnostics. */ -const getDiagnosticsByURI = (services: LangiumServices, uri: URI): Diagnostic[] => { +const getDiagnosticsAtURI = (services: LangiumServices, uri: URI): Diagnostic[] => { const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri); return document.diagnostics ?? []; }; diff --git a/tests/language/generation/creator.ts b/tests/language/generation/creator.ts index 3605aea57..87364e6b5 100644 --- a/tests/language/generation/creator.ts +++ b/tests/language/generation/creator.ts @@ -7,7 +7,7 @@ import { import path from 'path'; import fs from 'fs'; import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; -import { ErrorsInCodeError, getErrorsByURI } from '../../helpers/diagnostics.js'; +import { ErrorsInCodeError, getErrorsAtURI } from '../../helpers/diagnostics.js'; import { findTestChecks } from '../../helpers/testChecks.js'; import { Location } from 'vscode-languageserver'; import { NodeFileSystem } from 'langium/node'; @@ -39,7 +39,7 @@ const createGenerationTest = async (parentDirectory: URI, inputUris: URI[]): Pro const code = services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri).textDocument.getText(); // File must not contain any errors - const errors = getErrorsByURI(services, uri); + const errors = getErrorsAtURI(services, uri); if (errors.length > 0) { return invalidTest('FILE', new ErrorsInCodeError(errors, uri)); } From 28481d4205abc2f48edf774cd611820bfd75fee7 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 Oct 2023 22:38:22 +0200 Subject: [PATCH 56/56] refactor: use interface for import data --- src/cli/generator.ts | 64 +++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/src/cli/generator.ts b/src/cli/generator.ts index ea0ab43c4..b6a5849f4 100644 --- a/src/cli/generator.ts +++ b/src/cli/generator.ts @@ -401,13 +401,13 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio const rightOperand = generateExpression(expression.rightOperand, frame); switch (expression.operator) { case 'or': - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + frame.addImport({ importPath: RUNNER_CODEGEN_PACKAGE }); return `${RUNNER_CODEGEN_PACKAGE}.eager_or(${leftOperand}, ${rightOperand})`; case 'and': - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + frame.addImport({ importPath: RUNNER_CODEGEN_PACKAGE }); return `${RUNNER_CODEGEN_PACKAGE}.eager_and(${leftOperand}, ${rightOperand})`; case '?:': - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + frame.addImport({ importPath: RUNNER_CODEGEN_PACKAGE }); return `${RUNNER_CODEGEN_PACKAGE}.eager_elvis(${leftOperand}, ${rightOperand})`; case '===': return `(${leftOperand}) is (${rightOperand})`; @@ -440,7 +440,7 @@ const generateExpression = function (expression: SdsExpression, frame: Generatio } else { const memberExpression = generateExpression(expression.member!, frame); if (expression.isNullSafe) { - frame.addImport(new ImportData(RUNNER_CODEGEN_PACKAGE)); + frame.addImport({ importPath: RUNNER_CODEGEN_PACKAGE }); return `${RUNNER_CODEGEN_PACKAGE}.safe_access(${receiver}, '${memberExpression}')`; } else { return `${receiver}.${memberExpression}`; @@ -512,25 +512,25 @@ const getExternalReferenceNeededImport = function ( for (const importedDeclaration of importedDeclarations) { if (declaration === importedDeclaration.declaration.ref) { if (importedDeclaration.alias !== undefined) { - return new ImportData( - services.builtins.Annotations.getPythonModule(targetModule) || value.package, - importedDeclaration.declaration?.ref?.name, - importedDeclaration.alias.alias, - ); + return { + importPath: services.builtins.Annotations.getPythonModule(targetModule) || value.package, + declarationName: importedDeclaration.declaration?.ref?.name, + alias: importedDeclaration.alias.alias, + }; } else { - return new ImportData( - services.builtins.Annotations.getPythonModule(targetModule) || value.package, - importedDeclaration.declaration?.ref?.name, - ); + return { + importPath: services.builtins.Annotations.getPythonModule(targetModule) || value.package, + declarationName: importedDeclaration.declaration?.ref?.name, + }; } } } } if (isSdsWildcardImport(value)) { - return new ImportData( - services.builtins.Annotations.getPythonModule(targetModule) || value.package, - declaration.name, - ); + return { + importPath: services.builtins.Annotations.getPythonModule(targetModule) || value.package, + declarationName: declaration.name, + }; } } return undefined; @@ -545,12 +545,12 @@ const getInternalReferenceNeededImport = function ( const currentModule = findRootNode(expression); const targetModule = findRootNode(declaration); if (currentModule !== targetModule && !isInStubFile(targetModule)) { - return new ImportData( - `${ + return { + importPath: `${ services.builtins.Annotations.getPythonModule(targetModule) || targetModule.name }.${formatGeneratedFileName(getModuleFileBaseName(targetModule))}`, - getPythonNameOrDefault(services, declaration), - ); + declarationName: getPythonNameOrDefault(services, declaration), + }; } return undefined; }; @@ -564,20 +564,10 @@ const formatStringSingleLine = function (value: string): string { return value.replaceAll('\r\n', '\\n').replaceAll('\n', '\\n'); }; -class ImportData { - importPath: string; - declarationName: string | undefined; - alias: string | undefined; - - constructor( - importPath: string, - declarationName: string | undefined = undefined, - alias: string | undefined = undefined, - ) { - this.importPath = importPath; - this.declarationName = declarationName; - this.alias = alias; - } +interface ImportData { + readonly importPath: string; + readonly declarationName?: string; + readonly alias?: string; } class GenerationInfoFrame { @@ -609,6 +599,6 @@ class GenerationInfoFrame { } } -export type GenerateOptions = { +export interface GenerateOptions { destination?: string; -}; +}