Chapter 5The interactive debugger

If you run XML Calabash with the --debugger option, the pipeline is loaded and then the debugger starts.

Note

This is a very new feature and there are a number of rough edges. There are discrepancies between atomic and compound steps that don’t really make any sense. The command line interpreter doesn’t have history or completion. Future releases will (hopefully) sand off some of the sharper edges!

The debugger prompt is > . When you start a pipeline in the debugger, the first prompt occurs just before the initial pipeline begins running. Subsequent prompts occur when you step through the running pipeline or when breakpoints are encountered.

The debugger is always operating in the context of a particular step. There may be a stack of open steps. Moving around in the stack changes the context.

Command summary

The available commands are:

  • base-uri, set or display the base URI.

  • breakpoint, set or display breakpoints.

  • catch, catch errors.

  • define, define a local variable.

  • down, move down the stack.

  • exit, exit the debugger and terminate the pipeline.

  • help, get help.

  • inputs, show step inputs.

  • models, show the pipeline models.

  • namespace, set or display namespace bindings.

  • options, show step options and local variables.

  • run, continue running the pipeline.

  • set, set the value of an option or local variable.

  • show, show the result of evaluating an expression.

  • , show the stack.

  • , run the current step.

  • subpipeline, show the subpipeline steps.

  • up, move up the stack.

base-uri

Set or display the value of the base URI.

base-uri ["uri"]

If no uri is specified, shows the current base URI. If a URI is provided, the base URI is changed to uri. (The URI can be delimited with single or double quotes.)

Setting the base URI is temporary; it is reset to the base URI of the context step whenever a step is run.

The command base is a synonym for base-uri.

breakpoint

Set or display breakpoints.

There are five forms of the breakpoint command:

breakpoint

If used with no arguments, the breakpoint command prints a summary of enabled breakpoints.

breakpoint on id [ when expression ]

Sets a breakpoint before the step id is executed. If an XPath expression is provided, the breakpoint is conditional on that expression. The breakpoint only occurs if the effective boolean value of the expression is true.

breakpoint on id [ [at] input port ] [ when expression ]

Sets a breakpoint before the step id is executed. This breakpoint is conditional on input arriving on port. If an XPath expression is provided, it will be evaluated once for each input on the specified port, with the input as the context item. The breakpoint only occurs if the effective boolean value of at least one expression is true.

Unlike breakpoints on output ports, the breakpoint occurs just before the step executes. An “input” breakpoint is just a special kind of conditional breakpoint.

breakpoint on id [ [at] output port ] [ when expression ]

Sets a breakpoint that occurs when the step id produces a document on its output port named port. If an XPath expression is provided, it will be evaluated with the document as the context item. The breakpoint only occurs if the effective boolean value of the expression is true.

When this breakpoint occurs, a variable named cx:document is added to the local variables. If the value of this variable is changed, the document that’s actually sent as output will be changed accordingly.

Finally,

breakpoint clear id

Will clear a breakpoint.

The commands break and b are synonyms for breakpoint and set is a synonym for on.

catch

Catch errors.

catch [ on id ] [ error code ]

If used with no arguments, the catch command prints a summary of existing catch points.

If an id is provided, the debugger will catch errors on the step id. If a code is provided, the debugger will only stop if the error code matches code. If only a code is provided, the debugger will stop at any step when that error occurs. Errors precolate up the stack until a p:try is encountered or the pipeline terminates. It is possible to catch the same error on several consecutive steps as you go “up” the stack.

If a code is provided, it is interpreted as a QName in the context of the step(s) on which it is being evaluated. It must be a valid QName in the context in order to match.

When an error is caught, the last stack frame is the step that has been aborted. You can inspect its options and inputs, but it has already run, so changing them can have no effect.

define

Define a local variable.

define $varname = expression

Creates a local variable named $varname and assigns to it the value that results from evaluating the expression. (If an error occurs evaluating the expression, the empty sequence will be assigned.)

You can only define variables that do not already exist. To change the value of an existing variable, use set.

Local variables shadow step options of the same name.

The command def is a synonym for define.

down

Move down in the stack.

down [frames]

Moves down frames stack frames, or one frame if frames is not specified. Has no effect if you’re already on the last frame.

The command d is a synonym for down.

exit

Exit the debugger and the pipeline.

exit

Quits the debugger and aborts execution of the pipeline.

The commands x, quit, and q are synonyms for exit.

help

Get help.

help [command]

Displays a summary of the debugger commands. If command is provided, more detail is given about that command.

The commands h and ? are synonyms for help.

inputs

Show the step inputs.

inputs

Displays a summary of the inputs provided to the step. These can be examined in more detail with the cx:input map.

The command i is a synonym for inputs.

models

Show the pipeline models.

models [model]

In order to execute a pipeline, XML Calabash transforms it into a graph model. These models are then instantiated to be executed. The model command allows you to explore the available models.

(At the moment, the mapping from model identifiers to the identifiers used during pipeline execution is unpredictable. Hopefully, that will be resolved in the future, allowing you to predict a step id for use in a breakpoint directly from the model.)

The commands mod and m are synonyms for model.

namespace

Set or display namespace bindings.

namespace [ prefix = "uri" ]

Assigns the URI uri to the namespace prefix prefix in the local namespace bindings. For the purpose of evaluating expressions, local namespace bindings shadow bindings from the step context.

If entered with no binding, namespace prints the current bindings.

The command ns is a synonym for namespace.

options

Display (atomic) step options and local variables.

options

Displays the names of any options defined on the current (atomic) step or in local variables. These names can be used in expressions. Changing the value of a step option changes the value the step will receive when it runs!

The commands o, variables and var are synonyms for options.

run

Continue running the pipeline.

run

Starts running the pipeline again. Execution will continue until the next breakpoint or until the pipeline finishes.

The command r is a synonym for run.

set

Set the value of a step option of local variable.

set $varname = expression

Changes the value of the stop option or local variable named $varname to the value that results from evaluating the expression. (If an error occurs evaluating the expression, the empty sequence will be assigned.)

You can only set the values of options or variables that already exist. To create a new variable, use define.

show

Show the results of evaluating an expression.

show expression

Evaluates the expression provided and displays the result.

The commands eval and echo are synonyms for show.

stack

Display the stack.

stack

Displays the current stack. The stack frame that is the current context is marked with a *.

step

Run the current step, the return to the debugger.

step [ [to] end ]

Executes the current step, then returns to the debugger. (If a breakpoint occurs before the next step, execution will stop at the breakpoint.)

If the “end” argument is given, the debugger will stop at either the beginning of the next step or the end of the current step, whichever comes first. If it stops at the end, the last stack frame is the step that has just finished. You can inspect its options and inputs, but it has already run, so changing them can have no effect.

The command s is a synonym for step.

subpipeline

Show subpipeline steps.

subpipeline

Displays the models that are children of the current (compound) step.

The command sub is a synonym for subpipeline.

up

Move up in the stack.

up [frames]

Moves up frames stack frames, or one frame if frames is not specified. Has no effect if you’re already on the first frame.

The command u is a synonym for up.

Debugger example

To explore how the debugger works, let’s work through a small example. We’ll use the pipeline in Figure 3.2, “The pipeline model” from Chapter 3, Pipelines vs. Graphs. It may be useful to refer to the graph model, Figure 3.3, “The pipeline graph model”, to see how the graphs fit together.

There are three models, one for each pipeline and one for the compound step. Let’s start running this pipeline under the debugger:

$ xmlcalabash debugger-example.xpl --debugger
Debugger at declare-step
> 

The message tells us that we’re at the start of the “main” pipeline and the debugger is waiting for commands. We can look at the models with the models:

> models
*pipeline   ... !declare-step
 pipeline_2 ... !declare-step_2: ex:ident
> models pipeline_2
identity ... p:identity !identity
> models pipeline
document          ... cx:document !document
xinclude          ... p:xinclude !xinclude
ex1               ... ex:ident ex1
select_IC511      ... cx:select !select_IC511
for-each_pipeline ... S(p:for-each !for-each)
wrap-sequence     ... p:wrap-sequence !wrap-sequence
id2               ... p:identity id2
> 

We can see that these models reflect the structure of the compiled pipelines.

Let’s look at the stack:

> stack
*[0] !declare-step
> step
Debugger at declare-step_head
> step
Debugger at document
> stack
 [0] !declare-step
*[1] !document
> 

The initial stack is just the one step, the main pipeline waiting to be run. Stepping forward once takes us the head of the compound step. Stepping foward again takes us to the p:document step. (Note that the order in which steps are run depends on the connections between them, they are not displayed in any particular order by the model or subpipeline commands.)

We can explore the inputs to the step.

> inputs
> options
Step options:
  $href
  $document-properties
  $parameters
> show $href
../xml/default-input.xml
> 

The step has no inputs and three options. We can use the show command to inspect the values of the options. We could also use the set command to change them.

Let’s set a breakpoint on the ex:ident step:

> up
> stack
*[0] !declare-step
 [1] !document
> subpipeline
document       ... cx:document
xinclude       ... p:xinclude
declare-step_2 ... p:declare-step
select_IC511   ... cx:select
for-each       ... p:for-each
wrap-sequence  ... p:wrap-sequence
id2            ... p:identity
> break on declare-step_2
> 

We need the id of the ex:ident step. To find it, we navigate up to the stack frame for the compound step, then we look at its subpipeline. The step we’re interested is the one named “declare_step_2. Once we know its id, we can set a breakpoint on it.

Note

An obvious improvement here would be to allow the step type as the id of the subpipeline in this case.

Using run, we can advance to the breakpoint.

> run
Debugger at declare-step_2
> stack
 [0] !declare-step
*[1] !declare-step_2
> 

Now let’s do something more interesting. Let’s set a breakpoint on the output of the p:add-attribute step if there’s a role attribute on the output. You can probably predict the id of the step, but it’s also possible to inspect the pipeline to determine it. First, look at the current subpipeline to find the p:for-each step, then look inside it for the p:add-attribute-step.

> subpipeline
document       ... cx:document
xinclude       ... p:xinclude
declare-step_2 ... p:declare-step
select_IC511   ... cx:select
for-each       ... p:for-each
wrap-sequence  ... p:wrap-sequence
id2            ... p:identity
> subpipeline for-each
for-each_head ... p:for-each
for-each_foot ... p:for-each
id1           ... p:identity
add           ... p:add-attribute
> 

The id we want is “add”. Note that you can’t look “further down” into the pipeline, even if there are nested compound steps.

Now we can set the breakpoint and run the pipeline.

> break on add output result when //ex:chap[@role]
> run
Output from add on result satisfies //ex:chap[@role]
Debugger at add
> 

We can look at that document with show:

> show $cx:document
<chap xmlns="https://xmlcalabash.com/ns/examples"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      former="1"
      class="test"
      role="test">
   <title>First Chapter</title>
   <p>…</p>
</chap>
> 

The p:add-attribute step is in a loop. If we run again, we’ll stop at the breakpoint the next time it occurs where we can see the second document:

> run
Output from add on result satisfies //ex:chap[@role]
Debugger at add
> show $cx:document
<chap xmlns="https://xmlcalabash.com/ns/examples" role="test">
   <title>Second Chapter</title>
   <p>…</p>
</chap>
> 

For a last example, let’s be a little cheeky and change the output:

> set $cx:document = parse-xml("<change role='nothing to see here'/>")
> 

We’re not interested in stopping on that breakpoint again, so we can clear it.

> break clear add
> 

Now if we run again, we’ll get to the end of the pipeline.

> run
=== result :: 1 :: file:/…/documentation/src/examples/xml/default-input.xml ===
<set xmlns="https://xmlcalabash.com/ns/examples">
   <chap xmlns:xi="http://www.w3.org/2001/XInclude"
         former="1"
         class="test"
         role="test">
      <title>First Chapter</title>
      <p>…</p>
   </chap>
   <change xmlns="" role="nothing to see here"/>
   <chap xmlns:xi="http://www.w3.org/2001/XInclude" role="test">
      <title>Third Chapter</title>
      <p>…</p>
   </chap>
   <app xmlns:xi="http://www.w3.org/2001/XInclude" role="test">
      <title>Appendix</title>
      <p>…</p>
   </app>
</set>
=================================================================================================================

Note how the second chapter has been replaced by our change!

Hopefully, that gives you a flavor for what’s possible.