BitterSuite: Filtering student code prior to execution
Warning: This has not yet been tested in
BitterSuite 3. This needs to be tested, modified as needed, and verified to a reasonably strong degree that this works correctly before this warning is removed.
NB: The following code assumes that the submission was made in a teaching language, and will not work on submissions in the module language as written.
Create a file called
runTests-preprocess in the root of the testing directory (i.e. at the same level as
in and
provided). Make sure this file is executable (e.g. by using
chmod +x runTests-preprocess
), and put the following code in it:
#!/xhbin/bash
filter_filename=aXqY
filter_code="mzscheme -u filter-code.ss"
mangle=/u/isg/bittersuite3/languages/scheme/plt370mangle
if [ -e "$filter_filename.ss" ]; then
echo "filtering $filter_filename.ss..."
$mangle < $filter_filename.ss | $filter_code > $filter_filename.filt.ss
elif [ -e "$filter_filename.scm" ]; then
echo "filtering $filter_filename.scm..."
$mangle < $filter_filename.scm | $filter_code > $filter_filename.filt.scm
else
echo "$filter_filename.ss/scm not found: no filtering performed"
fi
# don't let errors from this script stop runTests
exit 0
Change
aXqY
to the file to be filtered. If you need to filter more than one file, put this script in
provided/ and make
runTests-preprocess call it for each file which should be filtered. This is left as an exercise for the reader.
In the file
provided/filter-code.ss, put this code:
#lang scheme
(define (do-read)
(define cur-read
(with-handlers ((void (lambda (exn)
(begin (fprintf (current-error-port) "read error: ~a\n" (exn-message exn))
(void)))))
(read)))
(cond
[(eof-object? cur-read) (void)]
[(void? cur-read) (do-read)]
[.1. .2. (do-read)]
[else (write cur-read)
(do-read)]))
(do-read)
or, equivalently but without repeating the explicit calls to
(do-read)
,
#lang scheme
(let do-read ()
(let ([cur-read
(with-handlers ((void (lambda (exn)
(begin (fprintf (current-error-port)
"read error: ~a\n"
(exn-message exn))
(void)))))
(read))])
(unless (eof-object? cur-read)
(unless (void? cur-read)
(cond
[.1. .2.]
[else (write cur-read)]))
(do-read))))
The ".1." in the above script should be replaced by the filtering condition, and the ".2" should be replaced by the filtering you want to perform. There can also be as many filtering conditions as you want in succession in this cond. For example, if we want to strip
out all instances of the statement
(define-struct foo ...)
we can use the code
[(and (list? cur-read)
(>= (length cur-read) 2)
(equal? (first cur-read) 'define-struct)
(equal? (second cur-read) 'foo))
(void)]
with the S-expression starting with
and
replacing ".1." and
(void)
replacing ".2." since we simply want to drop this expression instead of transforming it into something else.
This will remove any
(define-struct foo ...)
S-expressions from the student's code while leaving everything else intact.
Now, instead of using
(loadcode "aXqY.ss")
in
options.ss, you can use
(loadcode "aXqY.filt.ss")
to load the
filtered code.
Note that the language must be one of the teaching languages explicitly. In beginning student, for example, this would require a
(language scheme/beginner)
context or for the loadcode to be modified to
(loadcode beginner "aXqY.filt.ss")
.