Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/ls24002977/i specs external fields renaming #547

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.smeup.rpgparser.parsing.parsetreetoast

import com.smeup.rpgparser.RpgParser
import com.strumenta.kolasu.mapping.toPosition
import com.strumenta.kolasu.model.Position
import kotlinx.serialization.Serializable

@Serializable
sealed class InputSpecification

@Serializable
data class FileNameInputSpecification(
val name: String,
val position: Position?
) : InputSpecification()

@Serializable
data class ExternalFieldInputSpecification(
val originalName: String,
val newName: String,
val controlLevelIndicator: String?,
val matchingFieldsIndicator: String?,
val position: Position?
) : InputSpecification()

/**
* Links the external definition to its specifications.
*/
data class InputSpecificationGroup(
val fileName: FileNameInputSpecification,
val specifications: List<InputSpecification>
)

fun RpgParser.ControlLevelIndicatorContext.toIndicator(): String? {
return when {
this.ControlLevelIndicator() != null -> this.ControlLevelIndicator().text
else -> null
}
}

fun RpgParser.MatchingFieldsIndicatorContext.toIndicator(): String? {
return when {
this.MatchingRecordIndicator() != null -> this.MatchingRecordIndicator().text
else -> null
}
}

fun RpgParser.Is_external_fieldContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): InputSpecification {
val originalName = this.IF_Name().text.trim()
val newName = this.IF_FieldName().text.trim()
val controlIndicator = this.controlLevelIndicator().toIndicator()
val matchField = this.matchingFieldsIndicator().toIndicator()

// TODO: Add result indicators
// val resultIndicators = this.resultIndicator()

return ExternalFieldInputSpecification(
originalName,
newName,
controlIndicator,
matchField,
toPosition(conf.considerPosition)
)
}

fun RpgParser.Ispec_fixedContext.toAst(conf: ToAstConfiguration = ToAstConfiguration()): InputSpecification {
return when {
this.is_external_field() != null -> this.is_external_field().toAst(conf)
this.IS_FileName() != null -> {
val fileName = this.IS_FileName().text.trim()

// TODO: Add support for indicators
FileNameInputSpecification(
fileName,
toPosition(conf.considerPosition)
)
}
else -> todo(conf = conf)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import java.util.*

enum class AstHandlingPhase {
FileDefinitionsCreation,
InputSpecificationsCreation,
DataDefinitionsCreation,
MainStatementsCreation,
SubroutinesCreation,
Expand Down Expand Up @@ -95,6 +96,7 @@ typealias KnownDataDefinitionInstance = MutableMap<String, DataDefinition>
private fun List<StatementContext?>.getDataDefinition(
conf: ToAstConfiguration = ToAstConfiguration(),
fileDefinitions: Map<FileDefinition, List<DataDefinition>>? = null,
inputSpecifications: List<InputSpecificationGroup> = emptyList(),
parentDataDefinitions: List<DataDefinition>? = null,
useKnownDataDefinitionInstance: Boolean = false
): Pair<MutableList<DataDefinitionProvider>, KnownDataDefinitionInstance> {
Expand All @@ -105,7 +107,8 @@ private fun List<StatementContext?>.getDataDefinition(
val knownDataDefinitions = if (useKnownDataDefinitionInstance) KnownDataDefinition.getInstance() else mutableMapOf()

fileDefinitions?.let {
it.values.flatten().toList().removeDuplicatedDataDefinition().forEach { def ->
val postProcessedFileDefinitions = it.processWithSpecifications(inputSpecifications)
postProcessedFileDefinitions.values.flatten().removeDuplicatedDataDefinition().forEach { def ->
dataDefinitionProviders.add(def.updateKnownDataDefinitionsAndGetHolder(knownDataDefinitions))
}
}
Expand Down Expand Up @@ -151,11 +154,13 @@ private fun List<StatementContext?>.getDataDefinition(

private fun RContext.getDataDefinitions(
conf: ToAstConfiguration = ToAstConfiguration(),
fileDefinitions: Map<FileDefinition, List<DataDefinition>>
fileDefinitions: Map<FileDefinition, List<DataDefinition>>,
inputSpecifications: List<InputSpecificationGroup> = emptyList()
): List<DataDefinition> {
val (providers) = this.statement().getDataDefinition(
conf = conf,
fileDefinitions = fileDefinitions,
inputSpecifications = inputSpecifications,
useKnownDataDefinitionInstance = true
)
return providers.mapNotNull { kotlin.runCatching { it.toDataDefinition() }.getOrNull() }
Expand Down Expand Up @@ -264,7 +269,12 @@ fun RContext.toAst(conf: ToAstConfiguration = ToAstConfiguration(), source: Stri
}.toMap()
checkAstCreationErrors(phase = AstHandlingPhase.FileDefinitionsCreation)

val dataDefinitions = getDataDefinitions(conf, fileDefinitions)
val inputSpecifications = this.findAllDescendants(Ispec_fixedContext::class).mapNotNull {
it.runParserRuleContext(conf) { context -> kotlin.runCatching { context.toAst(conf) }.getOrNull() }
}
checkAstCreationErrors(phase = AstHandlingPhase.InputSpecificationsCreation)

val dataDefinitions = getDataDefinitions(conf, fileDefinitions, inputSpecifications.grouped())
checkAstCreationErrors(phase = AstHandlingPhase.DataDefinitionsCreation)

val displayFiles = fileDefinitions.keys.toList().toDSPF()
Expand Down Expand Up @@ -2227,4 +2237,46 @@ private fun <T : AbstractDataDefinition> List<T>.removeUnnecessaryRecordFormat()
}
}

private fun List<DataDefinition>.renameFields(externalFieldSpecs: List<ExternalFieldInputSpecification>): List<DataDefinition> {
return this.map {
val match = externalFieldSpecs.find { spec -> spec.originalName == it.name }
match ?: return@map it
it.copy(name = match.newName)
}
}

private fun List<InputSpecification>.grouped(): List<InputSpecificationGroup> {
if (this.isEmpty()) return emptyList()
val files = this.withIndex().filter { it.value is FileNameInputSpecification }
val output: MutableList<InputSpecificationGroup> = ArrayList<InputSpecificationGroup>(files.size)

var lastFile = files.first()
fun addGroup(index: Int) {
val nestedSpecifications = this.slice(lastFile.index + 1 until index)
val group = InputSpecificationGroup(lastFile.value as FileNameInputSpecification, nestedSpecifications)
output.add(group)
}

files.slice(1..files.lastIndex).forEach {
addGroup(it.index)
lastFile = it
}

// Last pass if necessary
if (output.size < files.size) addGroup(this.size)

return output
}

private fun Map<FileDefinition, List<DataDefinition>>.processWithSpecifications(specifications: List<InputSpecificationGroup>): Map<FileDefinition, List<DataDefinition>> {
val buffer = this.toMutableMap()
specifications.forEach {
val target = buffer.entries.firstOrNull { entry -> (entry.key.internalFormatName ?: entry.key.name) == it.fileName.name }
target ?: error("No FileDefinition named ${it.fileName.name}")
val externalFieldSpecifications = it.specifications.filterIsInstance<ExternalFieldInputSpecification>()
buffer[target.key] = target.value.renameFields(externalFieldSpecifications)
}
return buffer
}

private fun String.isStringLiteral(): Boolean = startsWith('\'') && endsWith('\'')
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,14 @@ open class MULANGT50FileAccess1Test : MULANGTTest() {
val expected = listOf("Test")
assertEquals(expected, "smeup/MU500802".outputOf(configuration = smeupConfig))
}

/**
* I-Spec definitions on a renamed file definition
* @see #LS24002977
*/
@Test
fun executeMU500901() {
val expected = listOf("TSSYST(IBMI) TSLIBR() TSFILE() TATIPO(3) TBPROG(MULANGT12) AAAAAA(A03) BBBBBB(P01) CCCCCC()")
assertEquals(expected, "smeup/MU500901".outputOf(configuration = smeupConfig))
}
}
77 changes: 77 additions & 0 deletions rpgJavaInterpreter-core/src/test/resources/smeup/MU500901.rpgle
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
*====================================================================
* smeup V6R1.023DV
* Nome sorgente : MU500901
* Sorgente di origine : QTEMP/SRC(MU500901)
* Esportato il : 20240612 092228
*====================================================================
V* ==============================================================
V* MODIFICHE Ril. T Au Descrizione
V* gg/mm/aa nn.mm i xx Breve descrizione
V* ==============================================================
V* 29/05/24 MUTEST GUAGIA Creazione
V*=====================================================================
O * OBIETTIVO
O * Test atomico LIKE, OCCURS
V* ==============================================================
FMULANGTL IF E K DISK
F RENAME(MULANGR:MULANG3)
* -------------------------------------------------------------
/COPY QILEGEN,MULANG_D_D
/COPY QILEGEN,£TABB£1DS
/COPY QILEGEN,£PDS
IMULANG3
I MLSYST TSSYST
I MLLIBR TSLIBR
I MLFILE TSFILE
I MLTIPO TATIPO
I MLPROG TBPROG
I MLPSEZ AAAAAA
I MLPPAS BBBBBB
I MLPDES CCCCCC
*---------------------------------------------------------------------
RD* M A I N
*---------------------------------------------------------------------
C EVAL £DBG_Pgm = 'MU500901'
C EVAL £DBG_Sez = 'A09'
C EVAL £DBG_Fun = '*INZ'
C EXSR £DBG
C EXSR SEZ_A09
C EXSR £DBG
C EVAL £DBG_Fun = '*END'
C EXSR £DBG
C SETON LR
*---------------------------------------------------------------------
RD* Test atomico
*---------------------------------------------------------------------
C SEZ_A09 BEGSR
OA* A£.CDOP()
D* Test atomico specifiche I
C EVAL £DBG_Pas='P01'
*
C TSLANG5K KLIST
C KFLD TSSYST
C KFLD TATIPO
C KFLD TBPROG
C KFLD AAAAAA
C KFLD BBBBBB
C EVAL TSSYST='IBMI'
C EVAL TATIPO='3'
C EVAL TBPROG='MULANGT12'
C EVAL AAAAAA='A03'
C EVAL BBBBBB='P01'
*
C EVAL £DBG_Str=''
C TSLANG5K SETLL MULANGTL
C READ MULANG3
*
C EVAL £DBG_Str='TSSYST(' + %TRIM(TSSYST) +
C ') TSLIBR(' + %TRIM(TSLIBR) +
C ') TSFILE(' + %TRIM(TSFILE) +
C ') TATIPO(' + %TRIM(TATIPO) +
C ') TBPROG(' + %TRIM(TBPROG) +
C ') AAAAAA(' + %TRIM(AAAAAA) +
C ') BBBBBB(' + %TRIM(BBBBBB) +
C ') CCCCCC(' + %TRIM(CCCCCC) + ')'
C ENDSR
*---------------------------------------------------------------------
/COPY QILEGEN,MULANG_D_C