Dashboard > USF Computer Science 652 - Programming Languages > CS652 Home > Adding Closures to Java
  USF Computer Science 652 - Programming Languages Log In | Sign Up   View a printable version of the current page.  
  Adding Closures to Java
Added by Terence Parr, last edited by Leon, Jen-Yuan Su on Jun 27, 2008  (view change)
Labels: 
(None)

Introduction

In this project you're going to add syntax and semantics to a subset of Java so that it understands anonymous code blocks called closures. The only hard part will be that closures can see local variables and parameters from surrounding scopes. These variables, which are normally stored on the Java stack, will have to be held in objects on the heap. Then, any references to those variables will have to become object field accesses.

You will build a source to source translator that accepts a Java+closures file and translates it to a straight Java version in the same directory:

$ java ClosureTrans t1

The ClosureTrans program reads in t1 and writes pure Java into t1.java.

Besides Java classes, your translator must also accept a simple series of statements in lieu of the usual Java main() method within a class.

List names = ["bob", "mary"];
print names;

To get started, download the support code and grammar skeletons.

Translation rules

As we discussed in class, you must first come up with a series of manual transformations so that you can identify precisely what subphrase maps to what output subphrase. Here is a set of transformations to help guide you in your project. Notice that when a closure is detected within a function, all of the locals and arguments must go into the heap via some temporary classes.

SL Java
int x;
x=1;
List names = ["bob", "mary"];
print names;
import java.util.*;
public class t1 {
    public static void main(String[] args) {
        int x;
        x = 1;
        List names = new ArrayList()
                    {{add("bob");add("mary");}};
        System.out.println(names);
    }
}
int x = 1;
int y = x + 2;
if ( x==y ) {
	print x;
}
else {
	print y;
}
import java.util.*;
public class t2 {
    public static void main(String[] args) {
        int x = 1;
        int y = x + 2;
        if ( x==y ) System.out.println(x);
        else System.out.println(y);
    }
}
int one() { return 1; }
print one();
        
int f(int x) { return 2*x; }
print f(3);
import java.util.*;
public class t3 {
    int one() { return 1; }
    int f(int x) { return 2*x; }
    public static void main(String[] args) {
        System.out.println(one());
        System.out.println(f(3));
    }
}
List a = ["a","b"];
a:{String s | print s;}; // foreach
import java.util.*;
public class t9 {
    public static void main(String[] args) {
        List a = new ArrayList() {{add("a");add("b");}};
        Closure.map(a, new Closure() {
        class closure1_locals_ctx {
            String s;
        }
        public Object run(Object... args) {
            final closure1_locals_ctx closure1_locals = 
                    new closure1_locals_ctx();
            closure1_locals.s=(String)args[0];
            System.out.println(closure1_locals.s);
        return null;
        }
        });
    }
}
Closure c= { int x | return x*2; };
List nums = [1,2,3,4]:c;
print nums;
import java.util.*;
public class t8 {
    public static void main(String[] args) {
        Closure c = new Closure() {
        class closure1_locals_ctx {
            int x;
        }
        public Object run(Object... args) {
            final closure1_locals_ctx closure1_locals =
                    new closure1_locals_ctx();
// !!!!! NOTE: arg type int yields cast to Integer not int !!!!!
            closure1_locals.x=(Integer)args[0];
            return closure1_locals.x*2;
        }
        };
        List nums = Closure.map(new ArrayList()
                    {{add(1);add(2);add(3);add(4);}}, c);
        System.out.println(nums);
    }
}
Closure c = {int x | return x*2;};
int y = (Integer)@c((Integer)34);
print y;
import java.util.*;
public class t7 {
    public static void main(String[] args) {
        Closure c = new Closure() {
        class closure1_locals_ctx {
            int x;
        }
        public Object run(Object... args) {
            final closure1_locals_ctx closure1_locals =
                    new closure1_locals_ctx();
            closure1_locals.x=(Integer)args[0];
            return closure1_locals.x*2;
        }
        };
        int y = ((Integer)(c).run(((Integer)34)));
        System.out.println(y);
    }
}
class t5 {
  int x = 0;
  String y = "hi";
  t5(int a, String b) { int z=a; y=b; }
}
import java.util.*;
class t5 {
  int x = 0;
  String y = "hi";
  t5(int a, String b) { int z=a; y=b; }
}
class t6 {
  int x = 0;
  void count(List values) {
	int n = 0;
	values:{ Object o | n=n+1; x=x+1; };
  }
}
import java.util.*;
class t6 {
  int x = 0;
  static class count_locals_ctx {
    List values;
    int n;
  }
  public void count(List values) {
    final count_locals_ctx count_locals =
                    new count_locals_ctx();
    count_locals.values=values;
    count_locals.n = 0;
    Closure.map(count_locals.values, new Closure() {
    class closure1_locals_ctx {
        Object o;
    }
    public Object run(Object... args) {
        final closure1_locals_ctx closure1_locals = 
                    new closure1_locals_ctx();
        closure1_locals.o=(Object)args[0];
        count_locals.n = count_locals.n+1;
        x = x+1;
        return null;
    }
    });
}
}
// Nested closure with local closure var
List a = ["Apple","Pear"];
a:{String s |
	 int n=0;
	 s:{char c | print c; n=n+1;};
	 print n;
};
import java.util.*;
public class t9 {
    public static void main(String[] args) {
        List a = new ArrayList() {{add("Apple");add("Pear");}};
        Closure.map(a, new Closure() {
        class closure1_locals_ctx {
            String s;
            int n;
        }
        public Object run(Object... args) {
            final closure1_locals_ctx closure1_locals =
                    new closure1_locals_ctx();
            closure1_locals.s=(String)args[0];
            closure1_locals.n = 0;
            Closure.map(closure1_locals.s, new Closure() {
            class closure2_locals_ctx {
                char c;
            }
            public Object run(Object... args) {
                final closure2_locals_ctx closure2_locals =
                    new closure2_locals_ctx();
                closure2_locals.c=(Character)args[0];
                System.out.println(closure2_locals.c);
                closure1_locals.n = closure1_locals.n+1;
            return null;
            }
            });
            System.out.println(closure1_locals.n);
        return null;
        }
        });
    }
}

Details

You have a number of goals in this project:

  1. Add [...] list literal notation
  2. Add {args | code} closure notation (like a lambda function); you must allow multiple arguments, though closures applied to a list only have one argument.
  3. Add ':' map operator that applies a closure (right operand) to a collection (left operand). As a special case, allow Strings as left operands, which turn into a stream of char. If the left operand is not Iterable or a Map or a String, simply apply the closure to the object as a single value.
  4. Add @closurevar(args) notation
  5. Implement the map() method in the Closure class.
  6. Update SL.g to use ClosureSymbol upon entering a closure scope.
  7. Update ClosureTrans so that it writes output to x.java from x input file (currently it writes to stdout).
  8. You'll have to add symbol table management to SL.g and tweak Rewrite.g so it uses the information.

Closure looks like:

abstract class Closure {
        public abstract Object call(Object... args);
        public static List map(Collection data, Closure c) {
                ...
        }
}

Testing

Leon Su has created an awesome grammar unit testing tool called gUnit. It can test both parsers and parser+tree walker combos. For example:

gunit Rewrite walks SL;

prog walks prog:
t1 -> t1.java // file t1 should yield t1.java
"print 32;" -> <<
import java.util.*;
public class t1 {
	public static void main(String[] args) {
		System.out.println(32);
	}
}
>>

For example, you could use gUnit to compare the above input / output pairs.

Submission

You will create a jar file called closures.jar containing grammars, source, and *.class files and place in your lib directory:

https://www/svn/userid/cs652/proj6/trunk/lib

You can use the svn account for development of the software to if you would like, but I will only be looking at your sym.jar file in the lib directory.

Please bring a printout of SL.g, Rewrite.g, your gUnit tests, and Closure.java (which has the map() method) as well as the output code generated from all examples shown in the table of transformations above.

Grading

Make sure your jar has the ClosureTrans class in the default package with a main method so that I can test your code. I will run a number of examples that you have not seen through your project.

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.1 Build:#806 May 06, 2007) - Bug/feature request - Contact Administrators