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:
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;};
|
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();
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;
}
});
}
}
|
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:
- Add [...] list literal notation
- Add {args | code} closure notation (like a lambda function); you must allow multiple arguments, though closures applied to a list only have one argument.
- 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.
- Add @closurevar(args) notation
- Implement the map() method in the Closure class.
- Update SL.g to use ClosureSymbol upon entering a closure scope.
- Update ClosureTrans so that it writes output to x.java from x input file (currently it writes to stdout).
- 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:
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:
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.