Testing GRL code from the keyboard

The compiler contains a number of tools for testing signal procedures and transducers from the keyboard by allowing you to manually enter signal values and print their results.   The support for this is found in the scheme48-run-time package.  This package contains the function definitions required to allow compiled GRL code to run within the Scheme48 environment.  In addition, it contains a number of useful macros for reading, writing, and testing, signal expressions.

Using the package

To use package, simply open it from whatever package you are working in:

,open scheme48-run-time

Not that this package doesn't export the basic GRL definitions, so you still need to open a package like standard-girl to get ahold of them.

Reading and writing signal values

The following transducers can be used to read and write signal values to the terminal.   They each read or print their associated values once per cycle of the program's main control loop.  Note: running prompt with the clock overrun warning enabled (see below) is very irritating.  You probably want to disable it when using prompt.

(prompt string)
(prompt-float string)
(prompt-boolean string)
Generates an integer (or float or boolean) signal that prints string to the display and reads a new value for the signal each clock tick.
(prompt-vector string  length)
(prompt-float-vector string  length)
(prompt-boolean-vector string  length)
Generates an integer (or float or boolean) vector-valued signal of length length that prints string to the display and reads new values for its elements each clock tick.
(show-value signal)             macro
Returns a signal of type void (meaning it doesn't really have a value) that prints the value of signal once per clock-tick.  The effect of this is that if you include it in the list of signals you compile, it will automatically print the value of the signal every clock tick.
(show-values signal ...)        macro
Same, but allows multiple signals.  Equivalent to:
    (list (show-value signal1) (show-value signal2) ...)

In addition, the following transducers are provided to facillitate parsers and command interpreters:

(maybe-show-value signal)      macro
Like show-value but only prints the value of signal when it is non-false.
(word-stream)
Returns a signal of type symbol, which consists of whatever stream of words is typed in the Scheme48 listener window.  One word is returned on each clock tick. Words are represented as scheme symbols.  When there are no new words to return, the value of the signal is #f.

Running code within Scheme48

The following convenience macros are included to aid debugging.  The differ from the compile-and-run and compile procedures only in that they allow full signal expressions as arguments.

(try-signals signal-expression ...)
Compiles and runs the signal expressions.
(try-compilation signal-expression ...)
Compiles and the signal expressions and pretty-prints the resulting Scheme code.

Clock-period under Scheme48

The GRL run-time clock can operate within Scheme48 in either fixed mode or free running mode.  In fixed mode, you set the clock period manually.  The run-time system then sleeps at the end of each clock cycle until the designated period has elapsed.  If the clock cycle takes longer than designated time, then a clock overrun warning is optionally issued.  In free running mode, the GRL code is allow to update as fast as it can (no sleeping) and the run time system estimates the mean clock period.  In either mode, the scheme variable measured-clock-period always contains the measured length of the previous clock period.

(set-clock-period! milliseconds)
Sets the target clock period to the specified number of milliseconds.  The default clock period is 50ms (20Hz).
(set-clock-period! #f)
Sets the clock to free running mode.
(enable-clock-overrun-warning! boolean)
If boolean is true, the system will issue a warning when a cycle of the GRL code takes more than the target number of milliseconds.  The warning is enabled by default.

Debugging example

Here's a simple example of loading up GRL and trying out a signal.  The things you type is listed in boldface.  The rest is typed by the system.

The first thing we have to do is load the compiler, so we load the packages file and open the standard-girl and scheme48-run-time packages.

Welcome to Scheme 48 0.52.1 (suspended image).
Copyright (c) 1993, 1994 by Richard Kelsey and Jonathan Rees.
Copyright (c) 1996 by NEC Research Institute, Inc.
Copyright (c) 1998, 1999 by Northwestern University.
Please report all bugs to bug-scheme48@cs.nwu.edu

The first thing we have to do is load the compiler, so we load the packages file and open the standard-girl and scheme48-run-time packages.

> ,config ,load C:\WINDOWS\Desktop\GiRL\packages.scm 
C:\WINDOWS\Desktop\GiRL\packages.scm 
> ,open standard-girl scheme48-run-time
Load structure standard-girl (y/n)? y
[nice-features C:\WINDOWS\Desktop\GiRL\features.scm]
[list-library C:\WINDOWS\Desktop\GiRL\list-library.scm
Analyzing... 
Calls will be compiled in line: (del-assoc! del-assoc del-assv! del-assv del-assq! del-assq delete-duplicates! delv-duplicates! delq-duplicates! delete-duplicates delv-duplicates delq-duplicates delete! delete delv! delv delq! delq reverse-append xcons)
]
[match-patterns C:\WINDOWS\Desktop\GiRL\match-patterns.scm]
[girl-language C:\WINDOWS\Desktop\GiRL\primitives.scm ...]
[girl-library C:\WINDOWS\Desktop\GiRL\library/utilities.scm]
[rulesets C:\WINDOWS\Desktop\GiRL\library/rulesets.scm]
[behavior-utilities C:\WINDOWS\Desktop\GiRL\library/behavior-utilities.scm]
[standard-girl]
Load structure scheme48-run-time (y/n)? y
[scheme48-run-time C:\WINDOWS\Desktop\GiRL\scheme48-run-time.scm]

OK.  Now the compiler is loaded.  We're going to try out the low-pass-filter  transducer.  It needs to know the clock period in order to compute the appropriate filter gains, so we'll start by declaring a 100ms (10Hz) clock period.  Of course, we won't be able to type numbers at 10Hz, so we'll also turn off the clock overrun warning.

> (set-clock-period! 100)
> (enable-clock-overrun-warning! #f)

Now we can try out a low pass filter.  Let's start by just trying to compile it.

> (try-compilation (low-pass-filter (prompt-float "Enter number: ")
                                    100))
An error occurred while compiling.
Signal name: "signal"
UID: 21
Source code: 
  (low-pass-filter (prompt-float "Enter number: ") 100) 
Compiler subgoal stack:
  Inferring type of #{Transducer-signal 21 "signal"} 
Error: Invalid argument type(s) passed to low-pass-filter.
Wanted: (float float)
Got:
        (float integer)

Unfortunately, the type checker got persnickety because we passed an integer into a float parameter, so we quit out of the debugger using ,reset (or control-D when running under GNU Emacs on Unix or control-R in the windows-based version) and add ".0" to the time constant.  This time it works and we get to see the code it compiled.

1> ,reset 
Top level
> (try-compilation (low-pass-filter (prompt-float "Enter number: ")
                                    100.0))
(begin (define (run)
         (let* ((signal 0.))
           (let* ((signal-2 0.)
                  (decay (exp (* -1. (/ clock-period 100.))))
                  (input-gain (- 1. decay)))
             (while #t
                    (update-girl-time!)
                    (before-signal-update)
                    (comment "Signal signal, transducer prompt-float")
                    (newline)
                    (display "Enter number: ")
                    (set! signal (read))
                    (comment "Signal signal, transducer low-pass-filter")
                    (set! signal-2 (+ (* decay signal-2) (* input-gain signal)))
                    (after-signal-update))))))

Since it worked, we'll try running it now.

> (try-signals (low-pass-filter (prompt-float "Enter number: ")
                                100.0)) 
Enter number: 100 
Enter number: 100 
Enter number: <user interrupts with control-break (windows) or control-C control-C (unix/emacs)>
Interrupt: keyboard

Of course, it's rather boring if you don't tell it to print anything, so we reset and add a call to show-value:

1> ,reset 
Top level
> (try-signals (show-value (low-pass-filter (prompt-float "Enter number: ") 100.0))) 
Enter number: 100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = 63.2121 
Enter number: 100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = 86.4665 
Enter number: 100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = 95.0213 
Enter number: 100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = 98.1684 
Enter number: -100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -27.0979 
Enter number: -100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -73.1808 
Enter number: -100 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -90.1338 
Enter number: 0 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -33.1584 
Enter number: 0 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -12.1983 
Enter number: 0 
(low-pass-filter (prompt-float "Enter number: ") 100.) = -4.4875 
Enter number: <interrupt>
Interrupt: keyboard
1> ,reset 
Top level
>