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
- Kotlin
- Java
repositories {
mavenLocal()
mavenCentral()
flatDir {
dirs("deps")
}
}
dependencies {
implementation(files("deps/clparser-with-dependencies-1.0.1-all.jar"))
}
repositories {
mavenLocal()
mavenCentral()
flatDir {
dirs("deps")
}
}
dependencies {
implementation(files("deps/clparser-with-dependencies-1.0.1-all.jar"))
implementation "com.strumenta.kolasu:kolasu-javalib:1.5.107"
}
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.
- Kotlin
- Java
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)",
)
}
import com.strumenta.cl.model.CLCompilationUnit;
import com.strumenta.cl.parser.CLKolasuParser;
import com.strumenta.kolasu.commercial.LicenseManager;
import com.strumenta.kolasu.parsing.ParsingResult;
import com.strumenta.kolasu.validation.Issue;
import com.strumenta.kolasu.validation.IssueSeverity;
import java.io.File;
public class ParsingIssues {
public static void parseWithReporting(File clFile, File license) {
LicenseManager.INSTANCE.registerLicense(license);
ParsingResult<CLCompilationUnit> result = new CLKolasuParser().parse(clFile);
if (result.isCorrect()) {
System.out.println("Parsed " + clFile.getName() + " with no issues.");
} else {
System.out.println("Parsed " + clFile.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;
}
}
CLCompilationUnit root = result.getRoot();
if (root == null) {
System.out.println("No AST produced — cannot continue analysis.");
return;
}
System.out.println("AST root: " + root.getProcedures().size()
+ " procedure(s), " + root.getCommands().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:
- 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.parsing.ParsingResult;
import com.strumenta.kolasu.validation.IssueSeverity;
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 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.
- Kotlin
- Java
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
}
import com.strumenta.cl.model.GenericCommand;
import com.strumenta.kolasu.javalib.Traversing;
import com.strumenta.kolasu.model.ErrorNode;
import com.strumenta.kolasu.model.Node;
public class RecoveryArtifacts {
public static int[] countRecoveryArtifacts(Node root) {
int[] counts = {0, 0};
Traversing.walkDescendantsBreadthFirst(root, ErrorNode.class, n -> counts[0]++);
Traversing.walkDescendantsBreadthFirst(root, GenericCommand.class, n -> counts[1]++);
return counts; // [errorNodes, genericCommands]
}
}
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.