Skip to main content

Program Inventory

Before refactoring or migrating RPG, teams need a high-level map of each member: procedures and subroutines, data definitions, file specifications, and DDS record formats. The parser turns source files into a structured inventory you can print, export to CSV, or feed into a dashboard.

Add Dependencies

repositories {
mavenLocal()
mavenCentral()
flatDir {
dirs("deps")
}
}

dependencies {
implementation(files("deps/rpgparser-2.2.0-all.jar"))
}

Collect structural entries

CompilationUnit exposes the main structural collections directly: procedures, subroutines / subroutinesIncludingMain, dataDefinitions, fileDescriptions, and dataDescriptions (DDS).

import com.strumenta.kolasu.commercial.LicenseManager
import com.strumenta.rpgparser.RPGKolasuParser
import com.strumenta.rpgparser.model.CompilationUnit
import com.strumenta.rpgparser.model.Constant
import com.strumenta.rpgparser.model.Prototype
import com.strumenta.rpgparser.model.RecordFormat
import com.strumenta.rpgparser.model.StandaloneField
import com.strumenta.rpgparser.model.StandardDataStructure
import java.io.File

data class InventoryEntry(val kind: String, val detail: String, val line: Int)

fun inventory(root: CompilationUnit): List<InventoryEntry> {
val entries = mutableListOf<InventoryEntry>()

root.procedures.forEach { proc ->
entries += InventoryEntry("PROCEDURE", proc.name, proc.position?.start?.line ?: 0)
}
root.subroutines.forEach { sr ->
entries += InventoryEntry("SUBROUTINE", sr.name, sr.position?.start?.line ?: 0)
}
root.fileDescriptions.forEach { file ->
entries += InventoryEntry("FILE", file.name, file.position?.start?.line ?: 0)
}
root.dataDefinitions.forEach { dd ->
val kind = when (dd) {
is StandaloneField -> "D-FIELD"
is StandardDataStructure -> "D-DS"
is Prototype -> "D-PR"
is Constant -> "D-CONST"
else -> dd.simpleNodeType
}
entries += InventoryEntry(kind, dd.name ?: "?", dd.position?.start?.line ?: 0)
}
root.dataDescriptions.filterIsInstance<RecordFormat>().forEach { rf ->
entries += InventoryEntry("DDS-FORMAT", rf.name ?: "?", rf.position?.start?.line ?: 0)
}
return entries
}

fun printInventory(rpgFile: File, license: File) {
LicenseManager.registerLicense(license)
val root = RPGKolasuParser.parserFromExtension(rpgFile).parse(rpgFile).root ?: return
inventory(root).forEach { entry ->
println("${entry.kind.padEnd(12)} ${entry.detail.padEnd(24)} line ${entry.line}")
}
}

Summarize a member at a glance

For dashboards, aggregate counts rather than listing every entry:

fun summarize(root: CompilationUnit): Map<String, Int> = mapOf(
"procedures" to root.procedures.size,
"subroutines" to root.subroutines.size,
"mainStatements" to root.mainStatements.size,
"dataDefinitions" to root.dataDefinitions.size,
"fileDescriptions" to root.fileDescriptions.size,
"ddsRecordFormats" to root.dataDescriptions.filterIsInstance<RecordFormat>().size,
)

For file I/O operations (READ, WRITE, CHAIN, EXFMT), see File I/O and display operations. For cross-member CALL / COPY graphs, see symbol resolution and the analyzer.