Skip to main content
Version: 2.2.0

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.1-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)}…")
}