Skip to content

Commit

Permalink
Merge pull request #557 from smeup/bugfix/LS24003185/comptime-ds-extname
Browse files Browse the repository at this point in the history
Bugfix/ls24003185/comptime ds extname
  • Loading branch information
lanarimarco authored Jul 4, 2024
2 parents 2afa798 + bd8b258 commit 214fe64
Show file tree
Hide file tree
Showing 5 changed files with 636 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ object CommonCompileTimeInterpreter : BaseCompileTimeInterpreter(emptyList())

class InjectableCompileTimeInterpreter(
knownDataDefinitions: List<DataDefinition> = emptyList(),
fileDefinitions: Map<FileDefinition, List<DataDefinition>>? = null,
delegatedCompileTimeInterpreter: CompileTimeInterpreter? = null
) : BaseCompileTimeInterpreter(knownDataDefinitions, delegatedCompileTimeInterpreter) {
) : BaseCompileTimeInterpreter(knownDataDefinitions, fileDefinitions, delegatedCompileTimeInterpreter) {
override fun evaluateNumberOfElementsOf(rContext: RpgParser.RContext, declName: String): Int {
return mockedDecls[declName]?.numberOfElements() ?: super.evaluateNumberOfElementsOf(rContext, declName)
}
Expand All @@ -68,6 +69,7 @@ class NotFoundAtCompileTimeException(declName: String) : ParseTreeToAstError("Un

open class BaseCompileTimeInterpreter(
private val knownDataDefinitions: List<DataDefinition>,
private val fileDefinitions: Map<FileDefinition, List<DataDefinition>>? = null,
private val delegatedCompileTimeInterpreter: CompileTimeInterpreter? = null
) : CompileTimeInterpreter {

Expand Down Expand Up @@ -118,7 +120,11 @@ open class BaseCompileTimeInterpreter(
it.dspec() != null -> {
val name = it.dspec().ds_name().text
if (name == declName) {
return it.dspec().toAst(conf = conf, knownDataDefinitions = listOf()).let { dataDefinition ->
return it.dspec().toAst(
conf = conf,
knownDataDefinitions = knownDataDefinitions,
fileDefinitions = fileDefinitions
).let { dataDefinition ->
if (dataDefinition.type is ArrayType) {
dataDefinition.numberOfElements()
} else throw it.dspec().ds_name().error("D spec is not an array", conf = conf)
Expand Down Expand Up @@ -174,8 +180,9 @@ open class BaseCompileTimeInterpreter(
}
it.dcl_ds() != null -> {
val name = it.dcl_ds().name
val fields = fileDefinitions?.let { defs -> it.dcl_ds().getExtnameFields(defs, conf) } ?: emptyList()
if (name == declName) {
return it.dcl_ds().elementSizeOf(knownDataDefinitions)
return it.dcl_ds().elementSizeOf(knownDataDefinitions, fields)
}
}
it.block() != null -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ private fun inferDsSizeFromFieldLines(fieldsList: FieldsList): Int {
return maxEnd
}

internal fun RpgParser.Dcl_dsContext.elementSizeOf(knownDataDefinitions: Collection<DataDefinition>): Int {
val fieldsList = this.calculateFieldInfos(knownDataDefinitions)
internal fun RpgParser.Dcl_dsContext.elementSizeOf(
knownDataDefinitions: Collection<DataDefinition>,
fields: List<FieldInfo> = emptyList()
): Int {
val fieldsList = this.calculateFieldInfos(knownDataDefinitions, fields)
val toPosition = if (this.nameIsInFirstLine) {
this.TO_POSITION().text
} else {
Expand Down Expand Up @@ -159,7 +162,10 @@ internal fun RpgParser.Parm_fixedContext.toAst(
conf: ToAstConfiguration = ToAstConfiguration(),
knownDataDefinitions: List<DataDefinition>
): DataDefinition {
val compileTimeInterpreter = InjectableCompileTimeInterpreter(knownDataDefinitions, conf.compileTimeInterpreter)
val compileTimeInterpreter = InjectableCompileTimeInterpreter(
knownDataDefinitions = knownDataDefinitions,
delegatedCompileTimeInterpreter = conf.compileTimeInterpreter
)

// A Character (Fixed or Variable-length format)
// B Numeric (Binary format)
Expand Down Expand Up @@ -290,7 +296,8 @@ internal fun RpgParser.Parm_fixedContext.toAst(
internal fun RpgParser.DspecContext.toAst(
conf: ToAstConfiguration = ToAstConfiguration(),
knownDataDefinitions: List<DataDefinition>,
parentDataDefinitions: List<DataDefinition>? = null
parentDataDefinitions: List<DataDefinition>? = null,
fileDefinitions: Map<FileDefinition, List<DataDefinition>>? = null
): DataDefinition {

if (dspecConstant() != null) return dspecConstant().toAst(conf = conf)
Expand All @@ -299,10 +306,12 @@ internal fun RpgParser.DspecContext.toAst(
// If we have a parent data definition we can use it to resolve the variable through the delegate
delegatedCompileTimeInterpreter = parentDataDefinitions?.let {
InjectableCompileTimeInterpreter(
it,
conf.compileTimeInterpreter
knownDataDefinitions = it,
fileDefinitions = fileDefinitions,
delegatedCompileTimeInterpreter = conf.compileTimeInterpreter
)
} ?: conf.compileTimeInterpreter
} ?: conf.compileTimeInterpreter,
fileDefinitions = fileDefinitions
)
// A Character (Fixed or Variable-length format)
// B Numeric (Binary format)
Expand Down Expand Up @@ -578,8 +587,8 @@ internal fun RpgParser.Keyword_occursContext.evaluate(conf: ToAstConfiguration =
this.numeric_constant != null -> numeric_constant?.getChild(0)?.text?.toInt()
this.expr != null -> {
val injectableCompileTimeInterpreter = InjectableCompileTimeInterpreter(
KnownDataDefinition.getInstance().values.toList(),
conf.compileTimeInterpreter
knownDataDefinitions = KnownDataDefinition.getInstance().values.toList(),
delegatedCompileTimeInterpreter = conf.compileTimeInterpreter
)
injectableCompileTimeInterpreter.evaluate(rContext(), this.expr.toAst(conf)).asInt().value.toInt()
}
Expand Down Expand Up @@ -672,8 +681,8 @@ data class FieldInfo(
internal fun RpgParser.Parm_fixedContext.arraySizeDeclared(conf: ToAstConfiguration): Int? {
if (this.keyword().any { it.keyword_dim() != null }) {
val compileTimeInterpreter = InjectableCompileTimeInterpreter(
KnownDataDefinition.getInstance().values.toList(),
conf.compileTimeInterpreter
knownDataDefinitions = KnownDataDefinition.getInstance().values.toList(),
delegatedCompileTimeInterpreter = conf.compileTimeInterpreter
)
val dims = this.keyword().mapNotNull { it.keyword_dim() }
require(dims.size == 1)
Expand Down Expand Up @@ -873,7 +882,10 @@ private fun RpgParser.Parm_fixedContext.toFieldInfo(conf: ToAstConfiguration = T
?: knownDataDefinitions.firstOrNull { it.name.equals(varName, ignoreCase = true) }?.type
?: knownDataDefinitions.flatMap { it.fields }.firstOrNull { fe -> fe.name.equals(varName, ignoreCase = true) }?.type
?: like?.let {
InjectableCompileTimeInterpreter(knownDataDefinitions.toList(), conf.compileTimeInterpreter).evaluateTypeOf(this.rContext(), it, conf)
InjectableCompileTimeInterpreter(
knownDataDefinitions = knownDataDefinitions.toList(),
delegatedCompileTimeInterpreter = conf.compileTimeInterpreter
).evaluateTypeOf(this.rContext(), it, conf)
}

return FieldInfo(this.name, overlayInfo = overlayInfo,
Expand Down Expand Up @@ -1107,35 +1119,12 @@ internal fun RpgParser.Dcl_dsContext.toAst(
val size = this.declaredSize()

// Using `EXTNAME`
var fieldsFile: List<FieldInfo> = listOf()
if (this.keyword().any { it.keyword_extname() != null }) {
val keywordExtName = getKeywordExtName()
val keywordPrefix = getKeywordPrefix()

val extName = keywordExtName.getExtName()
val prefixName = keywordPrefix?.getPrefixName()

val fileDataDefinitions =
fileDefinitions?.filter {
val nameMatches = it.key.name.uppercase() == extName.uppercase()
val prefixIsNull = keywordPrefix == null && it.key.prefix == null
val prefixIsValid = keywordPrefix != null && it.key.prefix != null && it.key.prefix is Prefix
val prefixMatches = prefixIsValid && it.key.prefix?.prefix == prefixName

nameMatches && (prefixIsNull || prefixMatches)
}?.values?.flatten()

if (fileDataDefinitions.isNullOrEmpty()) {
return null
}

fieldsFile = extractFieldsFromFile(fileDataDefinitions, conf)
}
val fieldsFile = fileDefinitions?.let { getExtnameFields(fileDefinitions, conf) } ?: emptyList()

// Calculating information about the DS and its fields is full of interdependecies, therefore we do that in steps.
val fieldsList = calculateFieldInfos(knownDataDefinitions, fieldsFile)
val type: Type = this.type(size, fieldsList, conf)
val inz = this.keyword().asSequence().firstOrNull { it.keyword_inz() != null }
val inz = this.keyword().firstOrNull { it.keyword_inz() != null }

val dataDefinition = DataDefinition(
this.name,
Expand Down Expand Up @@ -1233,6 +1222,33 @@ internal fun RpgParser.Dcl_dsContext.getKeywordPrefix() = this.keyword().firstOr

internal fun RpgParser.Keyword_prefixContext.getPrefixName() = prefix.text

internal fun RpgParser.Dcl_dsContext.hasExtname() = this.keyword().any { it.keyword_extname() != null }

internal fun RpgParser.Dcl_dsContext.getExtnameFields(
fileDefinitions: Map<FileDefinition, List<DataDefinition>>,
conf: ToAstConfiguration
): List<FieldInfo> {
if (!hasExtname()) return emptyList()
val keywordExtName = getKeywordExtName()
val keywordPrefix = getKeywordPrefix()

val extName = keywordExtName.getExtName()
val prefixName = keywordPrefix?.getPrefixName()

val fileDataDefinitions =
fileDefinitions.filter {
val nameMatches = it.key.name.uppercase() == extName.uppercase()
val prefixIsNull = keywordPrefix == null && it.key.prefix == null
val prefixIsValid = keywordPrefix != null && it.key.prefix != null && it.key.prefix is Prefix
val prefixMatches = prefixIsValid && it.key.prefix?.prefix == prefixName

nameMatches && (prefixIsNull || prefixMatches)
}.values.flatten()

if (fileDataDefinitions.isEmpty()) return emptyList()
return extractFieldsFromFile(fileDataDefinitions, conf)
}

fun RpgParser.Parm_fixedContext.explicitStartOffset(): Int? {
val text = this.FROM_POSITION().text.trim()
return if (text.isBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,16 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() {
assertEquals(expected, "smeup/MUDRNRAPU00222".outputOf(configuration = smeupConfig))
}

/**
* Comptime DS with EXTNAME resolution
* @see #LS24003185
*/
@Test
fun executeMUDRNRAPU00223() {
val expected = listOf("ok")
assertEquals(expected, "smeup/MUDRNRAPU00223".outputOf(configuration = smeupConfig))
}

/**
* Access to an array detected as a function call by parser
* @see #LS24003149
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
D £DBG_Str S 2
D B£SLOT E DS EXTNAME(B£SLOT0F)

D CSDS DS
D CSNR 5S 0

D CSLOTS S LIKE(B£SLOT) DIM(200)
D CSLOTADS DS

D CSLOTA LIKE(CSDS)
D DIM(%ELEM(CSLOTS)) ASCEND

C EVAL £DBG_Str='ok'
C £DBG_Str DSPLY
Loading

0 comments on commit 214fe64

Please sign in to comment.