Skip to content

Debugging and Analysis

Mick edited this page Jul 7, 2026 · 8 revisions

Debugging and Analysis

RuleScript exposes strict analysis, best-effort editor analysis, formatting, navigation, runtime events, breakpoints, and debug sessions. In v1.10.0 the same debug model can also create Host Trigger runtimes.

Strict Static Analysis

Analyze parses complete source and returns symbols without executing it. Syntax errors throw SyntaxException; semantic errors are returned in Diagnostics.

var engine = new RuleScriptEngine();
RuleScriptAnalysisResult symbols = engine.Analyze(script);

foreach (var function in symbols.Functions)
{
    Console.WriteLine($"{function.Kind}: {function.Signature}");
}

foreach (var diagnostic in symbols.Diagnostics)
{
    Console.WriteLine($"{diagnostic.Severity} {diagnostic.Code}: {diagnostic.Message}");
}

For cursor-aware completion, call Analyze(script, line, column) and read VisibleVariables or VisibleVariableNames. Lines and columns are 1-based.

Best-Effort Analysis for Editors

TryAnalyze does not throw for recoverable syntax errors. It returns partial symbols and diagnostics, which makes it suitable for text being edited.

RuleScriptAnalysisAttempt attempt = engine.TryAnalyze(editorText, cursorLine, cursorColumn);

foreach (var diagnostic in attempt.Diagnostics)
{
    Console.WriteLine(
        $"{diagnostic.Severity} {diagnostic.Code} " +
        $"at {diagnostic.Line}:{diagnostic.Column}: {diagnostic.Message}");
}

IReadOnlyList<RuleScriptVariableSymbol> completionVariables =
    attempt.Symbols.VisibleVariables;

Success is false when at least one diagnostic has Error severity. Warnings are allowed.

Host-Provided Analysis Variables

Use SetKnownVariable when a value will be supplied by the host at execution time. This suppresses undefined-variable warnings and lets the analyzer check its type.

engine.SetKnownVariable("Distance", RuleScriptValueType.Number);
engine.SetKnownVariable("CustomerName", RuleScriptValueType.String);

RuleScriptAnalysisAttempt analysis = engine.TryAnalyze("""
    var label = CustomerName + ": " + ToString(Distance);
    var alert = Distance > 500;
    """);

Known variables configure analysis only; the runtime context still needs actual values.

Function and Host Trigger Symbols

Use RuleScriptAnalysisResult.Functions as the primary function metadata source. It includes user, imported, host, and built-in functions.

foreach (var symbol in symbols.Functions)
{
    Console.WriteLine(symbol.Kind);
    Console.WriteLine(symbol.Signature);
    Console.WriteLine(symbol.Documentation);
}

Host Trigger functions also appear in HostTriggers:

foreach (var trigger in symbols.HostTriggers)
{
    Console.WriteLine(trigger.HostTriggerMetadata?.Name);
    Console.WriteLine(trigger.Signature);
}

Formatting, Regions, and Documentation

using RuleScript.Core.Formatting;

var formatted = RuleScriptFormatter.Format(source);

RuleScriptFormatter.Format(source) normalizes valid source with four-space indentation, canonical spacing, normalized block layout, and preserved comments/directives. It also normalizes @HostTrigger(...) function and @HostTrigger(...) export function into the canonical multi-line attribute form.

RuleScriptLanguageService.GetRegions(source) returns named, nested source ranges for #region and #endregion editor folding.

RuleScriptLanguageService.GetFunctionDocumentation(source, functionName) returns the normalized /// documentation attached to a user function.

Navigation API

Use RuleScriptLanguageService to query one-based source positions:

var definition = RuleScriptLanguageService.GetDefinition(engine, source, line: 4, column: 1);
var references = RuleScriptLanguageService.FindReferences(engine, source, line: 4, column: 1);

GetDefinition returns RuleScriptDefinitionInfo?. FindReferences returns IReadOnlyList<RuleScriptReferenceInfo>. Both can resolve local variables, parameters, functions, imported functions, registered host functions, and built-ins.

Reusable Document Analysis

Editor integrations can parse and analyze once, then reuse the result:

var document = RuleScriptLanguageService.AnalyzeDocument(engine, source);

var definition = RuleScriptLanguageService.GetDefinition(document, line: 4, column: 1);
var references = RuleScriptLanguageService.FindReferences(document, line: 4, column: 1);

Use RuleScriptAnalysisCache when an editor keeps multiple open documents:

var cache = new RuleScriptAnalysisCache(engine);
var document = cache.GetOrAnalyze("main.rules", source);

The cache replaces a document when its source text changes.

Breakpoints

var engine = new RuleScriptEngine
{
    WorkingDirectory = @"C:\rules"
};

engine.AddBreakpoint("main.rules", 3);
engine.AddBreakpoint("main.rules", 5, "score > 80");

var session = new RuleScriptDebugSession(engine);
Task<RuntimeContext> runTask = session.RunFileAsync("main.rules");

RuleScriptRuntimeEvent pause = await session.WaitForPauseAsync();
Console.WriteLine($"Paused at {pause.Location.File}:{pause.Location.Line}");

session.StepOver();
pause = await session.WaitForPauseAsync();

session.Continue();
RuntimeContext context = await runTask;

At a pause point, CurrentSnapshot exposes source location, globals, current function locals, and call stack.

Debugging Host Trigger Runtimes

var engine = new RuleScriptEngine
{
    WorkingDirectory = @"C:\rules",
    ExecutionTimeoutEnabled = false
};

engine.AddBreakpoint("main.rules", 12);

var session = new RuleScriptDebugSession(engine);
var runtime = session.CreateRuntimeFromFile("main.rules");

var running = runtime.StartAsync();
await runtime.TriggerAsync("LotArrived", ["LOT-001"]);

var pause = await session.WaitForPauseAsync();
Console.WriteLine(pause.Location.Line);

session.Continue();
await runtime.StopAsync();
await running;

Debug-created runtimes preserve breakpoint, stepping, pause, continue, runtime-event inspection, and snapshot behavior while the runtime is executing.

Runtime Limits

var engine = new RuleScriptEngine
{
    ExecutionTimeout = TimeSpan.FromSeconds(10),
    ExecutionTimeoutEnabled = true,
    MaxCallDepth = 50,
    CallDepthLimitEnabled = true,
    MaxExecutedStatements = 100_000,
    StatementExecutionLimitEnabled = true,
    MaxLoopIterations = 10_000,
    LoopIterationLimitEnabled = true
};
Limit Scope Default
ExecutionTimeout Elapsed time for one run, checked at statement boundaries. 30 seconds
MaxCallDepth Active user function calls. 100
MaxExecutedStatements Total statements across the complete run. 1,000,000
MaxLoopIterations Iterations of each individual while or foreach. Engine default

Long-running Host Trigger runtimes commonly disable ExecutionTimeoutEnabled; the host should still use cancellation and StopAsync.

Diagnostics

RuleScriptDiagnostic provides message, severity, code, token text, source file, source location, and optional half-open range. Use codes for program logic and messages for display.

Common diagnostics include syntax errors, undefined variables, undefined functions, type mismatches, duplicate declarations, invalid assignments, invalid property/index access, parallel-task errors, duplicate overload signatures, no matching overload, ambiguous overloads, and invalid return paths.