1 A Metaprogramming Language
The Medic debugger treats debugging as a metaprogramming activity. Programmers write a debugging
program to augment the source program with desirable debugging behaviors without changing the source
program. The separation of a debugging program from a source program enables reusing and programming
of the debugging interaction as well as keeping the source program intact. The debugging program can
serve as a form of documentation, which preserves
the efforts invested in debugging, and act as something akin to testing suites that run against
a modified program later during the process of software development.
Here is the grammar for the Medic metaprogramming language:
top-level-form | = | layer-def... | ||
layer-def | = | (layer layer-id layer-form ...) | ||
| | (layer layer-id #:enable flag layer-form ...) | |||
layer-form | = | (export id ...) | ||
| | (import layer-id ...) | |||
| | debug-def | |||
| | (in #:module module-name match-form ...) | |||
debug-def | = | (define-source debug-src-id source-expr) | ||
| | (define-source (debug-src-id arg-id ...) source-expr ...) | |||
| | (define-match debug-id match-form) | |||
| | (define-match (debug-id arg-id ...) match-form ...) | |||
match-form | = | (with-behavior f template) | ||
| | (with-behavior f template #:renamed ret id) | |||
| | match-ref-form | |||
| | insert-form | |||
| | [each-function insert-form ...] | |||
| | [(f ...) insert-form ...] | |||
insert-form | = | border-form | ||
| | at-form | |||
border-form | = | [on-entry source-expr ...] | ||
| | [on-exit source-expr ...] | |||
at-form | = | [at location-form border-form ...] | ||
| | [at location-form #:before location-form border-form ...] | |||
| | [at location-form #:after location-form border-form ...] | |||
| | [at location-form #:before location-form #:after location-form border-form ...] | |||
location-form | = | target-language-expression | ||
| | expression-pattern | |||
source-expr | = | src-ref-form | ||
| | target-language-expression | |||
match-ref-form | = | debug-id | ||
| | (debug-id match-arg ...) | |||
src-ref-form | = | debug-src-id | ||
| | (debug-src-id src-arg ...) | |||
flag | = | boolean | ||
f | = | variable-not-otherwise-mentioned | ||
id | = | variable-not-otherwise-mentioned | ||
layer-id | = | variable-not-otherwise-mentioned | ||
(debug-src-id arg-id) | = | variable-not-otherwise-mentioned | ||
debug-id | = | variable-not-otherwise-mentioned |
Modularizes debugging code and facilitates organizing debugging traces into different units.
The #:enable keyword permits enabling and disabling adding to the source code
the debugging behaviors described within layer-form, while the debugging definitions
within the layer are still available to other layers.
syntax
(export id ...)
Declares exports of a layer where the id is the identifier of an internal
layer definition.
syntax
(import layer-id ...)
Declares imports of a layer where the layer-id is some layer identifier.
syntax
(define-source debug-src-id source-expr)
(define-source (debug-src-id arg-id ...) source-expr ...)
Binds debug-src-id to a sequence of source expressions.
syntax
(define-match debug-id match-form)
(define-match (debug-id arg-id ...) match-form ...)
Binds debug-id to a sequence of match forms.
syntax
(in #:module module-name match-expr ...)
Specifies the module to apply debugging code. The module-name
can be three kinds of paths: a relative path, an absolute path, or a library path.
For example, the following are possible specifications for module-name.
; a relative path (in #:module "src.rkt" ....) ; an absolute path (in #:module (file "/home/xiangqi/test/src.rkt" ....)) ; a library path (in #:module test/src ....)
syntax
(with-behavior f template)
(with-behavior f template #:renamed ret id)
Defines the logging behavior of the f function call. The form of template
is @{text-body}. In text-body,
@expr enables the evaluation of an expression expr with access to functions’
arguments and return value. A function’s arguments can be accessed by par where par is the
parameter’s name. By default, ret reveals the return value of a function. To avoid
confusions of referring to some argument with ret name rather than the default return value,
use #:renamed ret id explicitly to rename the symbol of the return value. For example,
(with-behavior f @{f takes @x and @y and returns @ret}) (with-behavior f @{f takes @x and @ret and returns @ret1} #:renamed ret ret1)
See Tracing Log for more information.
value
function-name : (or/c #f string?)
Returns the function name for the current scope of evaluation of @funtion-name. The
debugging primitive @funtion-name exposes the run-time function scope, which is only
available to debuggers, to programmers.
syntax
[each-function insert-form ...]
Adds a sequence of insert-forms to every function defined in the module.
syntax
[(f ...) insert-form ...]
Supports function-level matching where (f ...) constrains the scope of
a sequence of insert-form to be within one or more functions. The fourth
clause of the match-form non-terminal is module-level matching.
syntax
[at location-form border-form ...]
[at location-form #:before location-form border-form ...] [at location-form #:after location-form border-form ...] [at location-form #:before location-form #:after location-form border-form ...]
Supports expression-level matching of the target expression location-form. For Racket, location-form
can be any expression in the source program except internal definitions, such as the (define ....) form inside a
function or the (let ....) local binding form. To avoid the confusions of multiple matches of
location-form in the target program, specification of #:before
and #:after can be employed to confine the lexical context of location-form.
Expressions within location-form, or after #:before and #:after can be a legal
source expression or an expression pattern with _ wildcard character matching any legal expression. For
example,
[at (+ _ 1) #:before (define x (inc 4)) [on-entry (log x)]]
syntax
[on-entry source-expr ...]
Inserts a sequence of debugging code source-exprs before the target expression located by at-form,
or at the beginning of a function or module depending on the scope of matching.
syntax
[on-exit source-expr ...]
Inserts a sequence of debugging code source-expr after the target expression located by at-form,
or at the end of a function or module depending on the scope of matching.