Skip to main content
Version: Next

Handle Parsing Issues

Real CL codebases are large and occasionally syntactically invalid. A robust tool must inspect ParsingResult.issues, decide what severity levels matter, and often continue with a partial AST rather than aborting. Unrecognized commands may also appear as GenericCommand nodes rather than hard failures.

Add Dependencies​

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

dependencies {
implementation(files("deps/clparser-with-dependencies-1.0.1-all.jar"))
}

Report issues by severity​

Every parse returns a ParsingResult with a correctness flag and a list of Issue objects. Print them grouped by severity so operators can tell errors from warnings.

import com.strumenta.cl.parser.CLKolasuParser
import com.strumenta.kolasu.commercial.LicenseManager
import com.strumenta.kolasu.validation.IssueSeverity
import java.io.File

fun parseWithReporting(clFile: File, license: File) {
LicenseManager.registerLicense(license)
val result = CLKolasuParser().parse(clFile)

if (result.isCorrect) {
println("Parsed ${clFile.name} with no issues.")
} else {
println("Parsed ${clFile.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.procedures.size} procedure(s), ${root.commands.size} top-level command(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 generic commands​

Unparsed fragments may appear as ErrorNode instances. Count them with a filtered walk. Also count GenericCommand nodes — they often represent vendor or custom commands that still carry a usable CommandName and parameter list.

import com.strumenta.cl.model.GenericCommand
import com.strumenta.kolasu.model.ErrorNode
import com.strumenta.kolasu.traversing.walkDescendants

fun countRecoveryArtifacts(root: com.strumenta.kolasu.model.Node): Pair<Int, Int> {
val errors = root.walkDescendants(ErrorNode::class).count()
val generics = root.walkDescendants(GenericCommand::class).count()
return errors to generics
}

Treat high ErrorNode counts as a quality signal: inventory and call-graph output may be incomplete for that region of the file. GenericCommand counts are often expected when a codebase uses custom CL commands.