Skip to main content

File I/O and Display Operations

RPG programs are built around declared files (F-specs) and the statements that read, write, chain, update, delete, or display them. This recipe extracts file declarations and I/O / workstation operations such as READ, WRITE, CHAIN, and EXFMT (WriteThenReadStatement).

Add Dependencies

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

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

List F-spec file declarations

CompilationUnit.fileDescriptions holds the file specifications declared in the member.

import com.strumenta.rpgparser.model.CompilationUnit

fun listFileSpecs(root: CompilationUnit) {
root.fileDescriptions.forEach { file ->
println("${file.name} @ ${file.position}")
}
}

Find I/O and EXFMT statements

Walk for the statement classes that correspond to common file and display operations:

import com.strumenta.kolasu.traversing.walkDescendants
import com.strumenta.rpgparser.model.ChainStatement
import com.strumenta.rpgparser.model.CompilationUnit
import com.strumenta.rpgparser.model.DeleteRecordStatement
import com.strumenta.rpgparser.model.ReadRecordStatement
import com.strumenta.rpgparser.model.UpdateRecordStatement
import com.strumenta.rpgparser.model.WriteRecordStatement
import com.strumenta.rpgparser.model.WriteThenReadStatement

data class IoOp(val kind: String, val target: String, val line: Int)

fun collectIoOperations(root: CompilationUnit): List<IoOp> {
val ops = mutableListOf<IoOp>()

root.walkDescendants(ReadRecordStatement::class).forEach {
ops += IoOp("READ", it.toString(), it.position?.start?.line ?: 0)
}
root.walkDescendants(WriteRecordStatement::class).forEach {
ops += IoOp("WRITE", it.toString(), it.position?.start?.line ?: 0)
}
root.walkDescendants(ChainStatement::class).forEach {
ops += IoOp("CHAIN", it.toString(), it.position?.start?.line ?: 0)
}
root.walkDescendants(UpdateRecordStatement::class).forEach {
ops += IoOp("UPDATE", it.toString(), it.position?.start?.line ?: 0)
}
root.walkDescendants(DeleteRecordStatement::class).forEach {
ops += IoOp("DELETE", it.toString(), it.position?.start?.line ?: 0)
}
// EXFMT
root.walkDescendants(WriteThenReadStatement::class).forEach { exfmt ->
ops += IoOp("EXFMT", exfmt.formatName.toString(), exfmt.position?.start?.line ?: 0)
}
return ops
}

Exact property names on each I/O statement vary (file name, format, key expression). Inspect the AST reference for the statement class you care about, or dump properties as shown in Walk the AST.

Embedded SQL

ExecSqlStatement captures EXEC SQL blocks as an opaque sqlCode string — the SQL is not parsed into a SQL AST. You can still inventory SQL sites:

import com.strumenta.kolasu.traversing.walkDescendants
import com.strumenta.rpgparser.model.ExecSqlStatement

root.walkDescendants(ExecSqlStatement::class).forEach { sql ->
println("EXEC SQL @ ${sql.position}: ${sql.sqlCode.take(80)}…")
}