CSCI 1730 · Fall 2026

Implementation🔗

Time

These can take several hours, as is common with programming assignments. Come get help if you’ve spent more than five hours. The calendar duration of an assignment is proportional to the expected implementation time.

Grading Standard

Your implementation needs to be correct, robust (well-tested), and clear (well-structured, clean code). Yes, we do care about “software engineering” issues, not only whether the code “works”.The correctness expectations for languages are exceptionally high, especially when compared to most other software artifacts. How often do you run into errors in your language implementation? Essentially, the onus is on you to convince us that your program is correct, not for us to convince you it’s not. The “software engineering” bits (clean code, strong tests, etc.) are the best way to make that case.

Your grade will be in two parts:

  1. Correctness scores for the implementation and tests from an autograder. How we grade tests is described in the Testing Guidelines.

  2. Style and other concerns from manual review. We will grade your work using the following codes:
    • CORR: The work was done correctly.

    • LACK: The work was done correctly but the answer should have been more detailed.

    • OFF: The work was fairly good but had some mistakes that we enumerate.

    • PROB: The work had notable problems, more than we can quickly enumerate. Please talk to a course staff member for help. Future work needs to be a lot better.

    • ZERO: The work was either completely off or missing.

    We expect to see you make steady progress improving on both over the course of the semester.

Software

You will program in Shplait, a language that runs inside Racket:
  • Install Shplait by going into DrRacket, File | Package Manager… | Do What I Mean, and typing shplait into the box. DrRacket will find it in the catalog and install it. Your programs start with #lang shplait.

  • The documentation is online and, once the package is installed, in your local installation: go to Help | Racket Documentation and search for shplait. Start with the tutorial.

Each assignment comes with support files that define the language you are working on (its grammar, abstract syntax, and parser) and a stencil for your code and your tests. You will be provided with those files on an ongoing basis. Never modify the support files: we grade your work against our copies of them.

Tasks

Testing Guidelines

We care that you test programs well. Programming language implementations are expected to be rock-solid (when’s the last time you ran into an implementation bug?). You need to uphold this standard.

This isn’t a course in something like AI, where we don’t even know what the right answer might be!

So, in addition to the quality and correctness of your code, you will be evaluated on the quality and correctness of your tests.

How We Test Tests

What’s the job of a test suite? It’s to find errors in a program. (Examples help you understand the problem before you start writing code; tests help you catch errors in the program as and after you write it.) In short, test suites are like sorting hats, putting programs in a “good” or “bad” bin.

If you are a mathy person, you might call a test suite a classifier.

So, here’s how we will test your test suites. We construct a collection of implementations for the problem. Some are known to be correct (because we built them that way); we call each of these a wheat. The others are known to be incorrect (because we intentionally introduce errors); we call each of these a chaff. Your test suite’s job is to separate the wheat from the chaff. That is, we will run each of the wheats and chaffs against your test suite and see what happens:

  

On a wheat…

  

On a chaff…

…all tests passed

  

GREAT!

  

Not great…

…some tests failed

  

Ooops!

  

GREAT!

All tests passing a wheat, and at least one test failing on a chaff, is exactly what we are hoping for. If all tests pass on a chaff, that’s not ideal, but you may miss some chaffs, so it may be okay. But when any test fails on a wheat, that’s definitely a problem because it should never happen. It quite likely means you’ve misunderstood the problem statement, or perhaps the problem statement is ambiguous, or something like that. This should get cleared up right away.

The quality of your test suite is then a measure of whether you passed the wheats and how many chaffs you caught. Of course, we can make the latter arbitrarily hard. For instance, we could define a chaff that always works correctly except when the given list has, say, exactly 1729 elements. We won’t do things like that, both because it’s cruel and because real implementations are very rarely buggy in this way. Instead, we will make “reasonable” mistakes (but not all of them will be easy!). For more on why we grade this way, read this blog post.

Structure

Your tests go in a separate file from your implementation. The test file imports the support module for the assignment’s language and your implementation module, by their fixed names, and consists of check forms:

#lang shplait

 

import:

  open: "paret_core.rhm"

  open: "interpreter.rhm"

 

check:

  interp(parse_str("(+ 1 2)"))

  ~is v_num(3)

When we grade your tests, we replace your implementation module with each wheat and chaff in turn, and run your test file. Therefore:

  • Your test file must refer to your implementation only through the functions the assignment names (here, interp), and must not define helpers that depend on how your implementation represents things. Anything implementation-specific belongs in your implementation file, where you are welcome, and encouraged, to test your helper functions too.

  • Use only check with ~is and ~raises. Do not import any other testing library; we would not be able to run your file.

Testing for Errors

Every assignment says which symbol its errors are raised with, for example, error(#interp, "unbound variable"). Note that the error is raised with a symbol but tested with a string. This is because Shplait converts the symbol to text when it builds the error message: the message of the error above is the string interp: unbound variable, that is, the symbol’s name (without the #), a colon, a space, and your text. ~raises takes a string and checks that the message contains it. So a test that an expression raises the right kind of error should match the symbol’s name and nothing more:

check:

  interp(parse_str("x"))

  ~raises "interp"

Never match the rest of the message: our wheats may word their messages differently from yours, and a test that depends on the wording may then fail on a wheat.

A related point. When your test runs one of our chaffs, a call may raise an error that the chaff was not supposed to raise. Inside a check that is fine: the check fails and the file continues. Outside one it is not:

// don't do this

def result = interp(parse_str("(+ 2 2)"))

check:

  result

  ~is v_num(4)

If interp raises here, your whole test file stops, every check after it is lost, and you get no credit for them. Keep every call to the implementation inside a check.

Testing Values You Cannot Write Down

Some results, like closures, have a representation that you chose and we may have chosen differently. Test for what kind of value came back, not for the value:

check:

  interp(parse_str("(lam x 5)")) is_a v_fun

  ~is #true

Do not write ~is v_fun(#x, ..., ...): it depends on your representation, and may fail on ours.

Check Your Understanding

Implementation assignments that ask for test cases will have a “Tests” upload on Gradescope for you to submit your test file. Prior to the deadline, you are welcome (and encouraged) to upload it early. Gradescope will run all of the wheats and a subset of the chaffs against your test suite and tell you whether your tests passed all the wheats, and which of the starter chaffs they caught.

The starter chaffs are designed to catch misunderstandings of the problem statement. When we evaluate your final test suite we run it against many more chaffs, which mainly reflect errors that occur during implementation. So keep developing your tests while you implement, even after you have caught all the starter chaffs.