Checking Helper Functions and Recursion
This page assumes knowledge of how to do rst test setups for cs135 (
TestingGuide), how options.rkt files work (
OptionsRkt), and how instructor-scripts work (
OptionsRkt).
Usage
Inside
/u/cs135/marking/provided-library/analyze-helpers
are the scripts which you can use to do a number of checks related to helper functions and recursive functions. To use this you want to copy this
analyze-helpers
directory inside the provided directory inside the testing directory (test.pt, test.0, etc) - so
test.pt/provided/analyze-helpers
. It is used as an instructor script. For each question part you want to do these checks, you want to make a copy of
analyze-helpers.sh inside the same directory
(test.pt/provided/analyze-helpers
) . For example, say for question 2c) students are required to define a function named
my-function
, but they are not allowed to use self defined global helper functions or recursion. They are allowed to use the functions they defined in 2a) and 2b),
my-2a-function and
my-2b-function which are recursive. So you would make a copy of
analyze-helpers.sh
, called
analyze-helpers-2b.sh
and set the variables inside the file as follows:
- The
required_functions
variable is set to the function name(s) that is being checked.
- If
checkprovided
is set to true then it will only pass a student's submission if they call the function named in the provided_helper
variable. Note that it doesn't work if that function is not called explicitly (for example passed into another function, like in map, filter, etc).
- If
globalhelpers
is set to true, then it will ban any usage of global helper functions except the functions mention in allowed_helpers
. Note that this ban does not include built-ins.
- If
allhelpers
is set to true, then it will ban any usage of local or global helpers except for the global functions mention in allowed_helpers
. Note that this ban does not include built-ins.
- If
allrecursion
is set to true, then it will ban the required function and any of its helpers from being recursive.
- If
mainrecursion
is set to true, then it will only ban the required function from being recursive but its helpers can be recursive.
After you have set the variables to suit your needs, then if it does not already exist, create an
options.rkt
in
test.pt/in/2b/
and add the following line to it:
(instructor-script "provided/analyze-helpers/analyze-helpers-2b.sh")
Note that you can only have one
instructor-script
per
options.rkt
file. Additionally, if the outer
options.rkt
(inside
in/
) file also has the
instructor-script
constant defined, then this one will overwrite it. Similarly if an
options.rkt
inside a specific test, for example
in/2b/001/options.rkt
has the
instructor-script constant defined, then the one in
in/2b/001/options.rkt
will overwrite the constant in
in/2b/options.rkt
for only test 001, not the other tests. For each test, the innermost constant definition for a particular constant is used.
Also note, that a local helper named
fn
would actually be named
fn#x
where
x
is some combination of numbers behind the scenes. A global function named
fn
would still be
fn
behind the scenes.This is to differentiate functions from functions with the same name in other scopes. Keep this in mind as it may affect your understanding of how these scripts work.
Development
We can expand on the functionality of this script. All the needed scripts are in the
analyze-helpers
directory that you copy over. If you do add new functionality make sure to update the master scripts inside
marking/provided-library/analyze-helpers
after testing your changes. The
analyze-helpers.sh
scripts call
analyze-helpers.rkt
which has functions defined to do the specific checks that are asked for. In order to scan the student file and get all this information about helpers and recursive functions, the
analyze-helpers.rkt
script uses library functions defined in
static-analysis.rkt
- a library written by Dan Holtby. This library has a lot of useful functions which can be called to get all sorts of information from a student's file.
If
static-analysis.rkt
already has the library functions that you need for whatever you want to do, then you only need to add a way to use those functions properly inside
analyze-helpers.rkt and add some commands to call it inside
analyze-functions.sh
. You can look inside both of these scripts to see how the previously implemented things are done. Some of these may not have had the best coding practices used because they were written hours before they needed using, so feel free to refactor as well, as long as you test it properly. Always make backups before you make changes.
If
statis-analysis.rkt
does not have the library functions you need, then that is more complicated. You need to know some good full racket to add to it, if Dan is available, then you can also reach out to him for help. A quick reference of what
static-analysis.rkt
has at the time of writing this is below:
(static-analysis filename)
will produce a global name map (hash table). This is used as the first parameter to a bunch of functions old and new.
All "
name"s below are symbols representing function names.
gnm
is the global name map mentioned in the line above.
(extract-helpers gnm name)
produces a list of all global helpers called by
name
(including itself)
(extract-all-helpers gnm name)
produces a list of all helpers (local and global) called by
name
, or called by helpers called by
name
, or called…you get the idea. You can also add a third optional parameter, a list of symbols representing function names of built-ins that if called by
name or its helpers, also appear in the helper list produced. If not called, then they won't appear. This is useful for checking if a student is using a function we provided in a different file, since a function included from another file behaves as a built-in.
(extract-local-helpers gnm name)
as extract-all-helpers but restricted to local helper functions…this does include local helpers called by global helpers
(extract-global-helpers gnm name)
as extract-all-helpers but restricted to global helper functions (again, this is a filter applied after the fact, so it will include global helpers called by local helpers).
(recursive? gnm name)
produces #t if the function calls itself (including taking part in mutual recursion), #f if it does not, and ‘not-defined if
gnm
doesn’t contain name.
(uses-recursion? Gnm name)
produces #t if either
name
itself, or any helpers it uses, are
recursive? Again, ‘not-defined indicates name doesn’t exist.
(uses-global-helpers? Gnm name)
produces #t if
name
calls at least one globally defined function (other than itself), #f if it doesn’t, ‘not-defined if it isn’t defined
(uses-local-helpers? Gnm name)
produces #t if
name
calls at least one locally defined helper…this includes if it calls global helpers and those have their own locals. Maybe not useful info.
(helper-usage-statistics gnm func-names)
produces a hash table where the keys are global helper function, and the values are how many of the functions in
func-names
have references to them. If
func-names
is left off, or is #f, then the counts are how many different global functions call each helper in total.