Homework Assignment 7

Syntactic Abstractions


CS 3540
Programming Languages and Paradigms
Spring Semester 2023


Due: Friday, March 24 at 10:00 AM


Introduction

For this assignment, you will write a function to translate a syntactic abstraction into its core form, a few functions to implement a new data type, and one final function to do static analysis of programs in our little language. The assignment should help you begin to feel more comfortable with the idea of syntactic abstraction and get more practice working with the little language.

Template Source File
Download the zipped directory that contains three files. The most important is a template file for your homework solutions. You will submit the completed version of this file as your solution. Please use the given name as the name of the file you submit, and don't modify the provide or require clause.

You will use syntax-procs.rkt whenever you write a function that processes the language. Do not modify syntax-procs.rkt for this assignment.

You can use language-procs.rkt when you test a function that processes the language. It also contains examples of such functions, which you can refer to when you solve Problem 5. Do not modify language-procs.rkt for this assignment.
Organizing Code
As before, you will extend homework07.rkt with the code you write for this assignment. Be sure to update the header block with your personal information. Put any helper functions you write for a problem after the main function of your solution.

You do not have to write Rackunit tests for the functions you write. However, I strongly encourage you to evaluate your code using the examples I give in the assignment and to think of other cases that test your functions.
Little Language
For Problem 5, you will work the little language we saw for the first time in Session 12, processed in Session 13, and extended in Session 16.
               --------------------------- CORE FEATURES
     <exp> ::= <varref>
             | (lambda (<var>) <exp>)
             | (<exp> <exp>)
               --------------------------- ABSTRACTIONS
             | (let (<exp> <exp>) <exp>)
The core language consists of variable references, lambda expressions, and applications. The full language contains the core features plus local variables ("let" expressions), which are a syntactic abstraction.

Note that any function that processes an expression in the core language should consider only three cases: variable references, lambda expressions, and applications.



A Set Data Type

When doing static analysis, we sometimes need to keep track of a set of symbols. For example, we might want to determine the set of all variable references in a program.

We can use a Racket list to implement a set as a list with no duplicates. The order of the items in a set does not matter.

    <set-of-symbols> ::= ()
                       | (<item> . <set-of-symbols>)

Using a Racket list of symbols provides us with a lightweight implementation of sets. All we need to do is to implement set operations as functions that process lists of symbols in a way that preserve the meaning of those operations for sets. You will write six such functions for Problems 2, 3, and 4, to build sets and test membership.



Problems

  1. Write a structurally recursive function named (curry exp) that takes one argument, a Racket lambda expression with n ≥ 1 formal parameters. curry returns a curried function. For example:
         > (curry '(lambda (x) anything))
         '(lambda (x) anything)
         > (curry '(lambda (x y z) anything))
         '(lambda (x)
            (lambda (y)
              (lambda (z)
                anything)))
    
    curry does not process the function body, which can be any Racket expression.

    Hint: Make curry an interface procedure that passes the parameter list and the body (but not the symbol lambda!) to a structurally recursive helper function. The helper recurs on the parameter list, which is a list of symbols.

  2. Define four short non-recursive functions that implement basic set membership for the set datatype described above:
    For example:
         > (empty-set)
         '()
         > (set-member? 'a (empty-set))
         #f
         > (set-member? 'a '(x y z))
         #f
         > (set-member? 'y '(x y z))
         #t
         > (set-add 'a '(x y z))
         '(a x y z)
         > (set-add 'y '(x y z))
         '(x y z)
    
    Hint: You will find the primitive function member helpful for implementing set-member?. It does the same task as set-member?, except that it returns a list when it finds the item. set-member? always returns #t or #f.

    Hint: In set-add, you can use set-member? to determine if S already contains sym before adding it with cons.

  3. Write the recursive constructor function (set-union S1 S2), which takes as arguments two sets of symbols, S1 and S2. set-union returns a set containing all of the items in both S1 and S2, with no duplicates.

    For example:
         > (set-union '(a b) '(x y z))
         '(a b x y z)
         > (set-union '(a x y z b) '(x y z))
         '(a b x y z)
    
    Hint: To implement set-union, recur on set S1. Use set-add from Problem 2 to recursively add each item from S1 to S2.

  4. Write the recursive membership function (set-subset? S1 S2), which takes as arguments two sets of symbols, S1 and S2. set-subset? returns true if every member of S1 is in S2, and false otherwise.

    For example:
         > (set-subset? '(a b) '(x y z))
         #f
         > (set-subset? '(x y z) '(a x y z b))
         #t
    
    Hint: To implement set-subset?, recur on set S1. Use set-member? from Problem 2 to recursively check to see if each item of S1 is in S2.

  5. Write a structurally recursive function named (free-vars exp) that takes as input an expression in the core little language, and returns a set of all of the variables that occur free in expression. For example:
         > (free-vars 'x)
         '(x)
         > (free-vars '(square x))
         '(square x)
         > (free-vars '(lambda (y) (x y)))
         '(x)
         > (free-vars '((lambda (y) (y (square x)))
                        (lambda (y) (f f))))
         '(square x f)
         > (free-vars (preprocess '(let (a b)
                                     (let (c (lambda (d) a))
                                       (c a)))))
         '(b)
    
    Use the functions you wrote for Problems 2 and 3 in your solution, whenever you need to create an empty set, add an item to a set, or find the union two sets.

    You will probably also want to use Racket's primitive remove function. It removes the first occurrence of an item from a list. (Because the lists we pass it here are sets, removing the first occurrence of a symbol removes the only occurrence.)   remove is useful here because we don't want to include a lambda expression's parameter in the set of free variables.


Deliverables

By the due time and date, submit the following files:

If you have a file of utility functions, you may submit that file as well.

Be sure that your submission follows the submission requirements.



Eugene Wallingford ..... wallingf@cs.uni.edu ..... March 10, 2023