Graph Query API

The Graph Query backend outputs the SPDG in a format that is understood by the paralegal-policy framework. Policies are written as Rust programs running graph traversals and queries on the SPDG using this framework. This engine tends to be much faster than the Forge backend. It is also easier to get started with, as only a familiarity with Rust is required. It also requires no additional setup and running a policy can be a simple cargo run command. This backend is however lower-level than Forge and we are actively working on a Paralegal specific DSL on top of it.

The low level query API paralegal-policy is a Rust library. For most up-to-date information you should consult it’s rustdocs, available online at

paralegal_policy - Rust

The primary API is Context, which offers partially index-memoized convenience queries. For some information you may have to access the raw graph (ProgramDescription), accessible via Context::desc().

Diagnostics

paralegal_policy::diagnostics - Rust

Policies communicate with the user in a similar mechanism as used by rustc: Diagnostics. Diagnostics are messages of varying severity that are emitted to the user. In Rust failure is typically handled with Result or panicking, which are “fail fast” approaches. Diagnostics on the other hand are a “fail slow” approach that tries to execute as much of the policy as possible to try an provide comprehensive feedback to the user. Instead of aborting upon encountering a failure, your policy should emit a message and then resume execution from the last non-failure state. Diagnostics are emitted with Context::emit_diagnostics, but most setups won’t need to call this as GraphLocation::with_context does so automatically.

Two levels of severity are currently available to policy implementors:

The trait Diagnostics defines analogous methods for emitting such messages. It is implemented for Context.

You can also use the assert!-style macros assert_error! and assert_warning! which take a context, a condition required to hold and an optional message.

In addition, our diagnostics mechanism allows the policy writer to programmatically accumulate additional context about where a message originates. The PolicyContext, CombinatorContext and ControllerContext structs wrap Context, overriding the error and warning methods to attach information about the current policy, combinator or controller respectively. They implement Deref to Context, so you can treat them the same as Context. They are created with named_policy, named_combinator and named_controller (or all_controllers) respectively.

As an example, this policy:

ctx.named_policy("cannot escape", |ctx| {
    let result_1 = ctx.named_combinator("collect something", |ctx| {
        /* actual computation */
        assert_error!(ctx, 1 + 2 == 4, "Oh oh, fail!");
        true
    });

would emit

[policy: cannot escape] [collect something] Oh oh, fail!