Skip to main content

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

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

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

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.

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)"
)
}

Continue with a partial AST

Warnings and even some errors do not always prevent building an AST. Batch analyzers typically:

  1. Log all issues.
  2. Skip the file only when root is null.
  3. Optionally ignore specific severities for downstream passes.
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
}

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:

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
}

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.