From 1f4d986c8afd069a06a0f19d7c22e89ea72ff373 Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 3 Jul 2024 11:58:02 +0200 Subject: [PATCH 1/4] Add missing metadata --- .../smeup/metadata/B\302\243SLOT0F.json" | 549 ++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 "rpgJavaInterpreter-core/src/test/resources/smeup/metadata/B\302\243SLOT0F.json" diff --git "a/rpgJavaInterpreter-core/src/test/resources/smeup/metadata/B\302\243SLOT0F.json" "b/rpgJavaInterpreter-core/src/test/resources/smeup/metadata/B\302\243SLOT0F.json" new file mode 100644 index 000000000..58a41ef33 --- /dev/null +++ "b/rpgJavaInterpreter-core/src/test/resources/smeup/metadata/B\302\243SLOT0F.json" @@ -0,0 +1,549 @@ +{ + "name": "B£SLOT0F", + "tableName": "B£SLOT0F", + "recordFormat": "B£SLOTR", + "fields": [ + { + "fieldName": "B£TOGG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 2, + "varying": false + } + }, + { + "fieldName": "B£POGG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£TOGS", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 2, + "varying": false + } + }, + { + "fieldName": "B£POGS", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£AOGG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£DAOG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 35, + "varying": false + } + }, + { + "fieldName": "B£OGGA", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 50, + "varying": false + } + }, + { + "fieldName": "B£CLPR", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 2, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£CATE", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£DEVI", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£PGMC", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£PARC", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£CAMF", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£LUNG", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 5, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£DECI", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 2, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£OBBL", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£NCTP", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£NCCO", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£INOU", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£CASE", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 2, + "varying": false + } + }, + { + "fieldName": "B£TEFI", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£LUGR", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 5, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£LOGM", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£COD1", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD2", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD3", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD4", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD5", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD6", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD7", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD8", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD9", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£COD0", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 15, + "varying": false + } + }, + { + "fieldName": "B£NUM1", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 15, + "decimalDigits": 6, + "rpgType": "P" + } + }, + { + "fieldName": "B£NUM2", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 15, + "decimalDigits": 6, + "rpgType": "P" + } + }, + { + "fieldName": "B£NUM3", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 15, + "decimalDigits": 6, + "rpgType": "P" + } + }, + { + "fieldName": "B£NUM4", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 15, + "decimalDigits": 6, + "rpgType": "P" + } + }, + { + "fieldName": "B£NUM5", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 15, + "decimalDigits": 6, + "rpgType": "P" + } + }, + { + "fieldName": "B£FL01", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL02", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL03", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL04", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL05", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL06", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL07", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL08", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL09", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL10", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL11", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL12", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL13", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL14", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL15", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL16", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL17", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL18", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL19", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£FL20", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 1, + "varying": false + } + }, + { + "fieldName": "B£DTIN", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 8, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£ORIN", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 6, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£USIN", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£OGIN", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£DTAG", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 8, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£ORAG", + "type": { + "type": "com.smeup.rpgparser.interpreter.NumberType", + "entireDigits": 6, + "decimalDigits": 0, + "rpgType": "P" + } + }, + { + "fieldName": "B£USAG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + }, + { + "fieldName": "B£OGAG", + "type": { + "type": "com.smeup.rpgparser.interpreter.StringType", + "length": 10, + "varying": false + } + } + ], + "accessFields": [] +} From 8800c7a5b929926ff797ab57b8bfa79d7979294e Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 3 Jul 2024 11:58:13 +0200 Subject: [PATCH 2/4] Add rpgle test case --- .../src/test/resources/smeup/MUDRNRAPU00223.rpgle | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00223.rpgle diff --git a/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00223.rpgle b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00223.rpgle new file mode 100644 index 000000000..862730c26 --- /dev/null +++ b/rpgJavaInterpreter-core/src/test/resources/smeup/MUDRNRAPU00223.rpgle @@ -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 \ No newline at end of file From d65ecc12b3de66fd4b4dd9a19e3b1a0c1e3ff1a1 Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 3 Jul 2024 11:58:59 +0200 Subject: [PATCH 3/4] Add unit test case --- .../rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt index e273da003..4d035eefe 100644 --- a/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt +++ b/rpgJavaInterpreter-core/src/test/kotlin/com/smeup/rpgparser/smeup/MULANGT02ConstAndDSpecTest.kt @@ -362,4 +362,14 @@ open class MULANGT02ConstAndDSpecTest : MULANGTTest() { val expected = listOf("ok") assertEquals(expected, "smeup/MUDRNRAPU00221".outputOf(configuration = smeupConfig)) } + + /** + * Comptime DS with EXTNAME resolution + * @see #LS24003185 + */ + @Test + fun executeMUDRNRAPU00223() { + val expected = listOf("ok") + assertEquals(expected, "smeup/MUDRNRAPU00223".outputOf(configuration = smeupConfig)) + } } \ No newline at end of file From 3afd0c3e7c82b3d664eaf6142545d20f5457f1b0 Mon Sep 17 00:00:00 2001 From: Domenico Date: Wed, 3 Jul 2024 11:59:27 +0200 Subject: [PATCH 4/4] Add support for file definition in CompileTimeInterpreter --- .../interpreter/compile_time_interpreter.kt | 13 ++- .../parsetreetoast/data_definitions.kt | 90 +++++++++++-------- 2 files changed, 63 insertions(+), 40 deletions(-) diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt index 9f854827b..db5619b2c 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/interpreter/compile_time_interpreter.kt @@ -43,8 +43,9 @@ object CommonCompileTimeInterpreter : BaseCompileTimeInterpreter(emptyList()) class InjectableCompileTimeInterpreter( knownDataDefinitions: List = emptyList(), + fileDefinitions: Map>? = 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) } @@ -68,6 +69,7 @@ class NotFoundAtCompileTimeException(declName: String) : ParseTreeToAstError("Un open class BaseCompileTimeInterpreter( private val knownDataDefinitions: List, + private val fileDefinitions: Map>? = null, private val delegatedCompileTimeInterpreter: CompileTimeInterpreter? = null ) : CompileTimeInterpreter { @@ -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) @@ -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 -> { diff --git a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt index a1e86eca1..7126a78a8 100644 --- a/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt +++ b/rpgJavaInterpreter-core/src/main/kotlin/com/smeup/rpgparser/parsing/parsetreetoast/data_definitions.kt @@ -83,8 +83,11 @@ private fun inferDsSizeFromFieldLines(fieldsList: FieldsList): Int { return maxEnd } -internal fun RpgParser.Dcl_dsContext.elementSizeOf(knownDataDefinitions: Collection): Int { - val fieldsList = this.calculateFieldInfos(knownDataDefinitions) +internal fun RpgParser.Dcl_dsContext.elementSizeOf( + knownDataDefinitions: Collection, + fields: List = emptyList() +): Int { + val fieldsList = this.calculateFieldInfos(knownDataDefinitions, fields) val toPosition = if (this.nameIsInFirstLine) { this.TO_POSITION().text } else { @@ -159,7 +162,10 @@ internal fun RpgParser.Parm_fixedContext.toAst( conf: ToAstConfiguration = ToAstConfiguration(), knownDataDefinitions: List ): 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) @@ -290,7 +296,8 @@ internal fun RpgParser.Parm_fixedContext.toAst( internal fun RpgParser.DspecContext.toAst( conf: ToAstConfiguration = ToAstConfiguration(), knownDataDefinitions: List, - parentDataDefinitions: List? = null + parentDataDefinitions: List? = null, + fileDefinitions: Map>? = null ): DataDefinition { if (dspecConstant() != null) return dspecConstant().toAst(conf = conf) @@ -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) @@ -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() } @@ -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) @@ -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, @@ -1107,35 +1119,12 @@ internal fun RpgParser.Dcl_dsContext.toAst( val size = this.declaredSize() // Using `EXTNAME` - var fieldsFile: List = 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, @@ -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>, + conf: ToAstConfiguration +): List { + 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()) {