Chapter 5. The interactive debugger
If you run XML Calabash with the --debugger
option, the pipeline is loaded
and then the debugger starts.
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.subpipeline
, show the subpipeline steps.up
, move up the stack.
base-uri
Set or display the value of the base 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:
If used with no arguments, the breakpoint
command prints a summary
of enabled breakpoints.
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.
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.
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,
Will clear a breakpoint.
The commands break
and b
are
synonyms for breakpoint
and set
is a synonym for on
.
catch
Catch errors.
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.
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.
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.
Quits the debugger and aborts execution of the pipeline.
The commands x
, quit
,
and q
are
synonyms for exit
.
help
Get help.
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.
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.
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.
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.
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.
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.
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.
Evaluates the expression provided and displays the result.
The commands eval
and echo
are
synonyms for show
.
stack
Display the 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.
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.
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.
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.
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.