-
Notifications
You must be signed in to change notification settings - Fork 0
Debugging and Analysis
Add an unconditional breakpoint with a file and 1-based line number. A conditional breakpoint pauses only when its RuleScript expression evaluates to true.
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;Use session.Stop() to cancel a running or paused debug session. At a pause point, CurrentSnapshot exposes the source location, globals, current function locals, and call stack. CurrentPause contains the event that caused the pause.
RuleScriptDebugSnapshot? snapshot = session.CurrentSnapshot;
if (snapshot is not null)
{
foreach (var variable in snapshot.Globals)
{
Console.WriteLine($"global {variable.Key} = {variable.Value.Value}");
}
foreach (var frame in snapshot.CallStack)
{
Console.WriteLine(frame);
}
}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 variable in symbols.Variables)
{
Console.WriteLine($"{variable.Name}: {variable.Type}");
}
foreach (var function in symbols.UserFunctions)
{
Console.WriteLine($"function {function.Name} ({function.Parameters.Count} parameters)");
}
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.
RuleScriptAnalysisResult atCursor = engine.Analyze(script, line: 8, column: 12);
foreach (var name in atCursor.VisibleVariableNames)
{
Console.WriteLine(name);
}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, such as an unresolved runtime variable, do not make it false.
Use RuleScriptFormatter.Format(source) from RuleScript.Core.Formatting to normalize valid source with four-space indentation, canonical spacing, normalized block layout, preserved blank lines, and preserved comments/directives.
using RuleScript.Core.Formatting;
var formatted = RuleScriptFormatter.Format(source);RuleScriptLanguageService.GetRegions(source) returns named, nested source ranges for #region and #endregion editor folding. RuleScriptLanguageService.GetFunctionDocumentation(source, functionName) returns the normalized /// block attached to a user function. The same user-function documentation appears on RuleScriptFunctionSymbol.Documentation.
v1.2.0 reports undefined variables and functions, incompatible known types, duplicate declarations, and duplicate parameters. Use codes for program logic and messages for display.
var attempt = engine.TryAnalyze("var result = MissingFunction();");
RuleScriptDiagnostic diagnostic = attempt.Diagnostics.Single(
d => d.Code == RuleScriptDiagnosticCodes.UndefinedFunction);
Console.WriteLine(diagnostic.Severity); // Error
Console.WriteLine(diagnostic.TokenText); // MissingFunction
Console.WriteLine(diagnostic.Range); // half-open source range when availableDiagnostic code reference:
| Code | Constant | Default severity |
|---|---|---|
RS1000 |
SyntaxError |
Error |
RS2001 |
UndefinedVariable |
Warning |
RS2002 |
UndefinedFunction |
Error |
RS2003 |
TypeMismatch |
Error |
RS2004 |
DuplicateDeclaration |
Error |
RS2005 |
DuplicateParameter |
Error |
RS2006 |
DuplicateCase |
Error (since v1.3.0) |
RS2007 |
MissingDefaultBranch |
Warning (since v1.3.0) |
RS2008 |
PropertyNotFound |
Error (since v1.4.0) |
RS2009 |
InvalidAssignment |
Error (since v1.4.0) |
RS2010 |
CannotAssignToReadonly |
Error (since v1.4.0) |
RS2011 |
IndexTypeError |
Error (since v1.4.0) |
RS2012 |
NullAccess |
Error (since v1.4.0) |
RS2013 |
InvalidNullCoalescing |
Error (since v1.4.0) |
RS2014 |
DuplicateObjectProperty |
Error (since v1.4.0) |
RS3001 |
ParallelRequiresTask |
Error (since v1.5.0) |
RS3002 |
TaskOutsideParallel |
Error (since v1.5.0) |
RS3003 |
ParallelTaskFailed |
Error (since v1.5.0) |
RS3004 |
ParallelCancelled |
Error (since v1.5.0) |
RS3005 |
HostFunctionNotThreadSafe |
Error (since v1.5.0) |
RS3006 |
ParallelReturnTypeMismatch |
Error (since v1.5.0) |
RS3007 |
InvalidParallelBlock |
Error (since v1.5.0) |
RS3008 |
InvalidTaskBlock |
Error (since v1.5.0) |
Since v1.3.0, TypeMismatch also reports statically incompatible switch labels and known non-boolean when guards. A guarded duplicate constant is allowed as long as no earlier unguarded label with the same value makes it unreachable.
Use SetKnownVariable when a value will be supplied by the host at execution time. This suppresses the undefined-variable warning and lets the analyzer check its type.
engine.SetKnownVariable("Distance", RuleScriptValueType.Number);
engine.SetKnownVariable("CustomerName", RuleScriptValueType.String);
RuleScriptAnalysisAttempt analysis = engine.TryAnalyze("""
var label = CustomerName + ": " + Distance;
var alert = Distance > 500;
""");
var context = new RuntimeContext();
context.Set("Distance", 519);
context.Set("CustomerName", "North Plant");
engine.Execute("result = Distance > 500;", context);Known variables configure analysis only; the runtime context still needs actual values. KnownVariables returns the current sorted snapshot, RemoveKnownVariable removes one symbol, and ClearKnownVariables resets the schema.
var engine = new RuleScriptEngine
{
ExecutionTimeout = TimeSpan.FromSeconds(10),
ExecutionTimeoutEnabled = true,
MaxCallDepth = 50,
CallDepthLimitEnabled = true,
MaxExecutedStatements = 100_000,
StatementExecutionLimitEnabled = true,
MaxLoopIterations = 10_000,
LoopIterationLimitEnabled = true
};Each enable switch controls only its matching limit. Limit values must remain greater than zero even when a switch is disabled. The configuration applies consistently to synchronous, asynchronous, file, and debug-session execution.
| 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 |
An exceeded limit throws RuntimeException; the message identifies the limit and configured value. A caller can also pass a CancellationToken to async execution or call Stop() for explicit cancellation.
Every Analyze and TryAnalyze call reads the supplied script text and resolves current import content. Fixing editor text or changing an imported file is reflected on the next call; no source cache needs to be invalidated.
var first = engine.TryAnalyze("var value = MissingFunction();");
var second = engine.TryAnalyze("var value = 1;");
Console.WriteLine(first.Diagnostics.Count); // contains undefined-function error
Console.WriteLine(second.Diagnostics.Count); // 0See the API Reference by Class for the complete current API, or the v1.6.0 API Reference and Examples for formatting and editor metadata APIs.