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
- Kotlin
- Java
repositories {
mavenLocal()
mavenCentral()
flatDir {
dirs("deps")
}
}
dependencies {
implementation(files("deps/rpgparser-2.2.0-all.jar"))
}
repositories {
mavenLocal()
mavenCentral()
flatDir {
dirs("deps")
}
}
dependencies {
implementation(files("deps/rpgparser-2.2.0-all.jar"))
implementation "com.strumenta.kolasu:kolasu-javalib:1.5.107"
}
List F-spec file declarations
CompilationUnit.fileDescriptions holds the file specifications declared in the member.
- Kotlin
- Java
import com.strumenta.rpgparser.model.CompilationUnit
fun listFileSpecs(root: CompilationUnit) {
root.fileDescriptions.forEach { file ->
println("${file.name} @ ${file.position}")
}
}
import com.strumenta.rpgparser.model.CompilationUnit;
import com.strumenta.rpgparser.model.FileSpecification;
public class FileSpecs {
public static void listFileSpecs(CompilationUnit root) {
for (FileSpecification file : root.getFileDescriptions()) {
System.out.println(file.getName() + " @ " + file.getPosition());
}
}
}
Find I/O and EXFMT statements
Walk for the statement classes that correspond to common file and display operations:
- Kotlin
- Java
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
}
import com.strumenta.kolasu.javalib.Traversing;
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;
import java.util.ArrayList;
import java.util.List;
public class FileIo {
public record IoOp(String kind, String target, int line) {}
public static List<IoOp> collectIoOperations(CompilationUnit root) {
List<IoOp> ops = new ArrayList<>();
Traversing.walkDescendantsBreadthFirst(root, ReadRecordStatement.class).forEach(n ->
ops.add(new IoOp("READ", n.toString(), lineOf(n))));
Traversing.walkDescendantsBreadthFirst(root, WriteRecordStatement.class).forEach(n ->
ops.add(new IoOp("WRITE", n.toString(), lineOf(n))));
Traversing.walkDescendantsBreadthFirst(root, ChainStatement.class).forEach(n ->
ops.add(new IoOp("CHAIN", n.toString(), lineOf(n))));
Traversing.walkDescendantsBreadthFirst(root, UpdateRecordStatement.class).forEach(n ->
ops.add(new IoOp("UPDATE", n.toString(), lineOf(n))));
Traversing.walkDescendantsBreadthFirst(root, DeleteRecordStatement.class).forEach(n ->
ops.add(new IoOp("DELETE", n.toString(), lineOf(n))));
Traversing.walkDescendantsBreadthFirst(root, WriteThenReadStatement.class).forEach(n ->
ops.add(new IoOp("EXFMT", n.getFormatName().toString(), lineOf(n))));
return ops;
}
private static int lineOf(com.strumenta.kolasu.model.Node node) {
return node.getPosition() != null ? node.getPosition().getStart().getLine() : 0;
}
}
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)}…")
}