Linked List

Table of Contents

For this first part of the interpreter project, you'll create a linked list that you'll then use throughout the rest of the project.

This is an interpreter project team assignment, which means that if you are working on a team with someone else, you and your partner should do your best to even the workload between you. Strict pair programming is not required, but you should make sure that both team members are contributing to the project. I do recommend that you try to do pair programming if it is convenient to do so.

1 Get started

1.1 Use GitHub classroom to create a repository for your assignment

The first thing you'll need to do is to create a repository for your project using GitHub classroom. If you are working on a team, this repository will be shared, so only one of you should do this. To help reduce a round of communication, I'm going to designate that if you are working in a pair, the member of your team whose Carleton official email address comes second alphabetically should do this next step of setting up the GitHub repository. This is opposite from the previous pair assignment; I'm trying to give everyone an equal chance to participate. If you are working alone, then you should do this step.

If you are the designated person above, visit this GitHub classroom link (which I've placed in Moodle). Log into GitHub if your browser prompts you to after you get to GitHub. You should be taken to a screen that invites you to either "Join an existing team" or to "Create a new team". We're starting over with new teams for this assignment, so you'll have to do this from scratch (I think) and create a new team. Please name the team with both of your GitHub usernames. For example, if users FSchiller and StevieP are working together, please name the team FSchiller-StevieP. The order doesn't matter. This will make it easier for the graders to know who the repositories belong to. GitHub should then hopefully respond with a screen that says "You are ready to go!" and supply you with a link for the repository that you are creating. Click on that link, and you should be taken to the repository. If you've gotten this far, you have successfully created the repository, and you the user who is logged in have access to it. Contact your partner if you have one and tell them that this step worked. (If you are working alone on the project, you can skip the next step.)

The other one of you, after the above is complete, should visit the same GitHub classroom link in the first sentence in the previous paragraph. You'll see the screen asking you to "Join an existing team" or to "Create a new team." You should be able to see the team that your partner created above; click the "Join" button. This should then take you to the "You are ready to go!" screen, and you can hopefully find a link to the repository. Click on it. You now both have access to the same GitHub repository.

1.2 Select the correct repl in Repl.it (only if you're in a pair)

If you are working individually, you can continue to work in the my-work repl. If you are working in a pair and if this is the same pair that you did for the Scheme assignments, use the paired-work repl that you created for the BST assignment. Sharing with your partner should already be set up. If you are working in a different pair than previously, the same person who created the repo above should create a new Bash repl called interpreter-work, making sure that it is private. Once you have created the repl, click the "invite" button at the top, and invite your partner. You can do this by typing in their Carleton email address, or by emailing them directly the sharing link. Sometimes the email is a little slow to send, but it works. Perhaps try both strategies.

Again for the person designated above as the starting person, you should now clone the repository from GitHub into Repl.it. If you're in a pair, you can in principle both be watching in multiplayer mode while you do this.

In Repl.it, make sure that you are in your home directory in the terminal window. You can always navigate there by typing

cd ~

in the terminal window. To further confirm, type pwd (which is short for "print working directory.") You should see /home/runner. If you see something different, you're not in the home directory, so try again or ask for help.

In a different browser tab (leave the one with Repl.it open), visit our our class GitHub organization page online. Once you've landed there, you should see the GitHub repository you created in the GitHub Classroom step above. It should be named linkedlist-username (where username is either own username, or a combination of the two of you if you're working in a pair). Contact us for help if the repository isn't there. Click on that repository title. This should take you to the home page for that repository. There is a green box on the page that says "Clone or download." Click that box, and make sure it says "Clone with HTTPS." Highlight the https link shown, and copy it to the clipboard. The link that you're copying should look something like

https://github.com/carleton251-term/linkedlist-username.git

… though your username and term will be different.

Then navigate back to the Repl.it tab with the repl that you created above, and click on the terminal window within your new repl. Your next step is to clone the repository from GitHub. To do that, type git clone, then a space, then paste in the URL from the github page that you copied from above. (To paste, you might need to right-click in the terminal window and choose paste rather than using a keyboard shortcut.)

For example, I would type the following, though your username and term will be different:

git clone https://github.com/carleton251-term/linkedlist-username.git

You'll be prompted to enter your GitHub username and password.

If all goes well, you should receive a directory, titled linkedlist-username. If you type ls at the prompt, you should be able to see it. It will also appear in your file browser window with Repl.it on the left. Then navigate into that directory by typing

cd linkedlist-username

… and you should be all set to work.

2 Values and lists

One of the first questions to arise will be "What is this a linked list of? Integers? Strings? More lists?" The catch is that you'll be storing lists created in Scheme, which can contain values of a whole variety of types. You might think: "Ah! I'll have a Value class, with subclasses for IntValue, StringValue, etc." And that would be totally awesome, if you were implementing this list using an OOP (object-oriented programming) language. Oops.

In C, one way to handle this type of typing issue is to use union types. (See our online C reference that I assigned or other online tutorials.) These types allow you to have a single datatype that sometimes includes an int, sometimes a char *, etc.) Every such value should also include a tag telling you what kind of data you're storing.

typedef enum {INT_TYPE, DOUBLE_TYPE, STR_TYPE,...} valueType;

struct Value {
    valueType type;
    union {
        int i;
        double d;
        char *s;
        ...
    };
};

typedef struct Value Value;

The names INT_TYPE, DOUBLE_TYPE, etc., represent types of Values. The Value struct includes a field (type) containing one of these tags, as well as a union of various possible fields. For example, you could create a value containing the integer 5 as follows:

Value myInt;
myInt.type = INT_TYPE;
myInt.i = 5;

If you have a Value and want to determine its type, you might choose to use a switch statement:

switch (val.type) {
case INT_TYPE:
    // do something with val.i
    break;
case DOUBLE_TYPE:
    // do something with val.d
    break;
...
}

You will thus want to make some sort of linked list implementation where the nodes contain Values. There are many different ways that one can construct a linked list. The most common approach you have likely seen is one that consists of nodes, where each node is a struct containing two items: a pointer to a value, and a pointer to another node.

Don't do it this way for the assignment. There's a better way that will save you much pain later.

Because you will eventually be using your linked list to represent Scheme S-expressions, you will have a much easier time if your linked list actually resembles a Scheme linked list. Specifically, each node should be a "cons cell" with two pointers within, and it should not be strictly typed.

Here is an abbreviation of the technique that you will use:

struct Value {
    valueType type; // type will also have a CONS_TYPE as an option
    union {
        int i;
        double d;
        /* .... */
        struct ConsCell {
            struct Value *car;
            struct Value *cdr;
        } c;       
    };
};


typedef struct Value Value;

The "head" pointer for your linked list, whatever you choose to call it, should be of type Value*. It should be NULL_TYPE if the list is empty, or point to a Value. That Value should be one that holds a cons cell. The car of that cell should point to the first item in your linked list; the cdr should point to another Value. And so on. At the end of the linked list, the cdr of that Value should point to a NULL_TYPE Value.

And finally: if you insert tokens at the beginning of the list, which is the simplest way, your tokens will be represented in backwards order from what you typically want. One could handle this efficiently by writing code to add at the tail of the linked list instead of the head. Instead, we'll make things simpler by writing an additional function to reverse a linked list. Is this less efficient? Yeah. This project is large enough; we won't focus very much on efficiency, though you might think about tracking places to make it more efficient if you want to improve it at the end.

You can feel free to leverage any linked list code that we may or may not have written in class, though bear in mind that it might not fit this framework.

3 Some specifics

After you clone your repository, you should be able to see that you get the following starting files:

  • value.h: this defines the Value structure, described above
  • linkedlist.h: this defines all of the functions that you will need to write
  • main.c: this is a tester function that makes some nodes, puts them into a linked list, displays them, and cleans up memory afterwards
  • Makefile: contains instructions for the command make, which will compile and test your code

The missing file here is linkedlist.c, which you'll need to create yourself in order to implement everything in linkedlist.h. To compile your code, issue the command make at the command prompt. This will follow the instructions in the Makefile for building your project in order to produce an executable called linkedlist. At first, it won't build at all because your linkedlist.c file isn't there. Create the file and for now, create every function that you need with no code inside it so that you can get everything to build. Once you have done that, you can begin implementing your functions, and testing appropriately.

The tester code creates an executable that you can run by typing ./linkedlist. However, if you find it more convenient, you can type make test which will compile your code and run the tests all in one shot.

Your code should have no memory errors when running on any input (correct or incorrect) using valgrind. If you use assertions to catch incorrect input, that might reasonably result in memory leaks when your assertion terminates the program. (minor update on May 5, 7:13 pm, to allow for memory leaks when assertions fire)

To run your code through valgrind, I have added to the Makefile a section to do this. You can compile your code and run the tests through valgrind by typing make valgrind at the terminal prompt.

4 Capstone work

Work in this section is 100% optional, and not worth any points. Nonetheless, if you're looking for an extra challenge, these are fun additional exercises to try.

You've already implemented some Scheme functions: car, cdr, and cons. In the same spirit, add new functions titled append, and list that are analogs to the functions of the same name in Scheme. You've got more flexibility in how you go about this; we won't stress test these in quite the same way as the rest, so make sure to indicate in your comment and in your tests what you've done.

5 What to submit

Add all of your files into your git repository and push to GitHub. (You can leave out your compiled executables as well as the .o object files if you wish.) All of your work should be in linkedlist.c, so all you should really need to add is linkedlist.c.

The graders and I need to know which commit is the one that we should use for grading. This is handled well in Git with tags. You did this back in the original clab. If you've forgotten how to tag in Git, look at the end of part 1 of the C lab. Name your tag linkedlist-done.

Good luck, and have fun!


This assignment was originally created by David Liben-Nowell and has since been updated by Dave Musicant and Laura Effinger-Dean.