Assignment 3: Cobra:   Multiple types of values
1 The Cobra Language
1.1 Concrete Syntax
1.2 Abstract Syntax
1.3 Semantics
2 Examples
3 Implementation strategies
3.1 Rendering errors
3.2 Memory Layout and Calling Rust Functions
3.3 New Assembly Constructs
3.4 Debugging
4 Recommended TODO List
5 Running main
6 List of Deliverables
7 Grading Standards
8 Submission
8.7

Assignment 3: Cobra: Multiple types of values

Due: Fri 10/14 at 5:00pm

git clone

In this compiler, you’ll deal with COded Binary RepresntAtions of values. Click here for scary snake picture!

1 The Cobra Language

1.1 Concrete Syntax

The concrete syntax of Cobra is very similar to Boa, with a few new additions.

‹expr›: ... | true | false | ‹expr› < ‹expr› | ‹expr› > ‹expr› | ‹expr› <= ‹expr› | ‹expr› >= ‹expr› | ‹expr› == ‹expr› | ‹expr› && ‹expr› | ‹expr› || ‹expr› | ! ‹expr› | isbool ( ‹expr› ) | isnum ( ‹expr› ) | print ( ‹expr› )

1.2 Abstract Syntax

The abstract syntax is very similar to Boa, also:

pub enum Prim1 {
    ...
    Not,
    Print,
    IsBool,
    IsNum,
}
pub enum Prim2 {
    ...
    And,
    Or,
    Lt,
    Gt,
    Le,
    Ge,
    Eq,
}

pub enum Exp<Ann> {
    ...
    Bool(bool, Ann),
}

1.3 Semantics

The semantics of booleans are straightforward. The semantics of Exp::If changes slightly: its condition must evaluate to a boolean, and it branches on the truth or falsehood of that value, rather than whether it’s nonzero.

With the addition of two types to the language, there are two main changes that ripple through the implementation:

There is one other major addition, which is the print primitive, discussed more below.

The representation of values requires a definition. We’ll use the following representations for the Cobra runtime:

You should raise errors at runtime in the following cases:

These error messages should be printed on standard error. The other operators never fail, and so never produce errors.

The == should work polymorphically, regardless of the types of its inputs. If we ask if a boolean is equal to a number, the result should be false, not a runtime error. A value is only == to itself.

We add two new primitive operations, isbool and isnum. These two operations have an effective type of Any -> Bool, and will return true if the argument they receive is indeed a boolean or number, respectively.

The last required primitive operation is print, which prints its single argument to the command line (with a newline), and then returns it. The print_snake_val function in stub.rs explicitly returns its argument; you will need to retrieve and use that value in your compiled output.

2 Examples

  1. The expression

    let x = 1 in
    let y = print(x + 1) in
    print(y + 2)

    will output

    2
    4
    4

    The first 2 comes from the first print expression. The first 4 comes from the second print expression. The final line prints the answer of the program as usual, so there’s an “extra” 4.

  2. The expression

    if 54: true else: false

    prints (on standard error) something like:

    Error: if expected a boolean, got 54

3 Implementation strategies

3.1 Rendering errors

To display error messages on standard error, you’ll need to use a call something like:

eprintln!("Error: if expected a boolean");

I have included a function snake_error() in stub.rs. You will need to add inputs of your design to this function to get your desired error messages. You can follow what we did in class or design it however you like.

3.2 Memory Layout and Calling Rust Functions

In order to set up the stack properly to call Rust functions, like print_snake_val and your error functions, it’s necessary to make a few changes to what we had in Boa.

If you write any functions in stub.rs that you need to be available in your assembly, you need to declare them in the assembly via:

;; In your generated assembly
extern "sysv64" <exported name of function>

3.3 New Assembly Constructs

3.4 Debugging

If you are having trouble producing correct code, I highly recommend

4 Recommended TODO List

Here’s an order in which you could consider tackling the implementation:

  1. Fix the Num case and implement the Bool case of the compiler to correctly encode the values.

  2. Extend check (renamed from check_scope) to use the Overflow error to indicate integer constants that are too big to fit into 63-bit signed integers.

  3. Implement the Prim2 operations without checking runtime type tags. Test your arithmetic and logical operations with programs where the tag checks would succeed.

  4. Similarly, implement the Prim1 operations besides print without checking runtime type tags.

  5. Figure out how to test for errors and call the snake_error function when an error is detected. Test as you go. Be aware that if the function call segfaults, it may be because you need to refine your space_needed function.

  6. Complete the if case and test as you go.

  7. Implement print and in the process space_needed. Test as you go; be aware that you should test interesting sequences of print expressions and let-bindings to make sure your stack integrity is good before and after calls.

Be sure to get your alignment correct! Stack misalignment does not cause errors on Mac, but does on Linux and in particular on the autograder servers so if you are getting errors you can’t replicate on Mac, make sure to think about alignment.

5 Running main

Running your own programs is the same as with Boa, except you’ll give them the .cobra file extension.

6 List of Deliverables

Please ensure the your code builds properly. The autograder will give you a 0 on that portion of the graade if it cannot compile your code.

7 Grading Standards

For this assignment, you will be graded on

8 Submission

Wait! Please read the assignment again and verify that you have not forgotten anything!

Please submit your homework to gradescope by the above deadline.