Handle Parsing Issues
Real RPG codebases mix fixed and free format, copybooks, DDS, and occasionally syntactically invalid members. A robust tool must inspect ParsingResult.issues, decide what severity levels matter, and often continue with a partial AST rather than aborting.
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"
}
Report issues by severity
Every parse returns a ParsingResult with a isCorrect flag and a list of Issue objects. Print them grouped by severity so operators can tell errors from warnings.
- Kotlin
- Java
import com.strumenta.kolasu.commercial.LicenseManager
import com.strumenta.kolasu.validation.IssueSeverity
import com.strumenta.rpgparser.RPGKolasuParser
import java.io.File
fun parseWithReporting(rpgFile: File, license: File) {
LicenseManager.registerLicense(license)
val result = RPGKolasuParser.parserFromExtension(rpgFile).parse(rpgFile)
if (result.isCorrect) {
println("Parsed ${rpgFile.name} with no issues.")
} else {
println("Parsed ${rpgFile.name} with ${result.issues.size} issue(s).")
}
result.issues.forEach { issue ->
val location = issue.position?.let { " @ $it" } ?: ""
when (issue.severity) {
IssueSeverity.INFO -> println("INFO: ${issue.message}$location")
IssueSeverity.WARNING -> System.err.println("WARNING: ${issue.message}$location")
IssueSeverity.ERROR -> System.err.println("ERROR: ${issue.message}$location")
}
}
val root = result.root
if (root == null) {
println("No AST produced — cannot continue analysis.")
return
}
println(
"AST root: ${root.mainStatements.size} main statement(s), " +
"${root.procedures.size} procedure(s), ${root.subroutines.size} subroutine(s)"
)
}
import com.strumenta.kolasu.commercial.LicenseManager;
import com.strumenta.kolasu.validation.Issue;
import com.strumenta.kolasu.validation.IssueSeverity;
import com.strumenta.kolasu.parsing.ParsingResult;
import com.strumenta.rpgparser.RPGKolasuParser;
import com.strumenta.rpgparser.model.CompilationUnit;
import java.io.File;
public class ParsingIssues {
public static void parseWithReporting(File rpgFile, File license) {
LicenseManager.INSTANCE.registerLicense(license);
ParsingResult<CompilationUnit> result =
RPGKolasuParser.parserFromExtension(rpgFile).parse(rpgFile);
if (result.isCorrect()) {
System.out.println("Parsed " + rpgFile.getName() + " with no issues.");
} else {
System.out.println("Parsed " + rpgFile.getName() + " with "
+ result.getIssues().size() + " issue(s).");
}
for (Issue issue : result.getIssues()) {
String location = issue.getPosition() != null
? " @ " + issue.getPosition()
: "";
switch (issue.getSeverity()) {
case INFO:
System.out.println("INFO: " + issue.getMessage() + location);
break;
case WARNING:
System.err.println("WARNING: " + issue.getMessage() + location);
break;
case ERROR:
System.err.println("ERROR: " + issue.getMessage() + location);
break;
}
}
CompilationUnit root = result.getRoot();
if (root == null) {
System.out.println("No AST produced — cannot continue analysis.");
return;
}
System.out.println("AST root: " + root.getMainStatements().size()
+ " main statement(s), " + root.getProcedures().size()
+ " procedure(s), " + root.getSubroutines().size() + " subroutine(s)");
}
}
Continue with a partial AST
Warnings and even some errors do not always prevent building an AST. Batch analyzers typically:
- Log all issues.
- Skip the file only when
rootisnull. - Optionally ignore specific severities for downstream passes.
- Kotlin
- Java
fun shouldAnalyze(result: com.strumenta.kolasu.parsing.ParsingResult<*>): Boolean {
if (result.root == null) return false
val blocking = result.issues.any { it.severity == IssueSeverity.ERROR }
if (blocking) {
println("File has errors but AST is available — proceeding with best-effort analysis.")
}
return true
}
import com.strumenta.kolasu.validation.IssueSeverity;
import com.strumenta.kolasu.parsing.ParsingResult;
public class PartialAst {
public static boolean shouldAnalyze(ParsingResult<?> result) {
if (result.getRoot() == null) {
return false;
}
boolean blocking = result.getIssues().stream()
.anyMatch(i -> i.getSeverity() == IssueSeverity.ERROR);
if (blocking) {
System.out.println(
"File has errors but AST is available — proceeding with best-effort analysis.");
}
return true;
}
}
Error nodes and error statements in the AST
Unparsed or recovered fragments may appear as Kolasu ErrorNode instances, and RPG also models some recovery points as ErrorStatement. Count both with a filtered walk:
- Kotlin
- Java
import com.strumenta.kolasu.model.ErrorNode
import com.strumenta.kolasu.traversing.walkDescendants
import com.strumenta.rpgparser.model.ErrorStatement
fun countRecoveryArtifacts(root: com.strumenta.kolasu.model.Node): Pair<Int, Int> {
val errorNodes = root.walkDescendants(ErrorNode::class).count()
val errorStatements = root.walkDescendants(ErrorStatement::class).count()
return errorNodes to errorStatements
}
import com.strumenta.kolasu.javalib.Traversing;
import com.strumenta.kolasu.model.ErrorNode;
import com.strumenta.kolasu.model.Node;
import com.strumenta.rpgparser.model.ErrorStatement;
public class ErrorNodes {
public static int countErrorNodes(Node root) {
return (int) Traversing.walkDescendantsBreadthFirst(root, ErrorNode.class).count();
}
public static int countErrorStatements(Node root) {
return (int) Traversing.walkDescendantsBreadthFirst(root, ErrorStatement.class).count();
}
}
Treat recovery-artifact counts as a quality signal: a high count often means inventory or dependency output will be incomplete for that region of the file. The CLI --stats / --error-stats flags are useful when scanning an entire library; see Usage.