Skip to content

Commit 3672375

Browse files
committed
Start Java 8 support with new parser syntax, AST node for lambda expressions, and some unit tests.
1 parent 6179df7 commit 3672375

File tree

4 files changed

+191
-10
lines changed

4 files changed

+191
-10
lines changed

dynamicjava/src/koala/dynamicjava/parser/grammar.jj

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
options {
55
JAVA_UNICODE_ESCAPE = true;
66
STATIC = false;
7-
JDK_VERSION = "1.5";
7+
DEBUG_PARSER = true;
88
}
99

1010
PARSER_BEGIN(Parser)
@@ -666,7 +666,7 @@ public class Parser {
666666
}
667667

668668
/**
669-
* An expresion followed by a dot, "new", optional type arguments, a class name, optional class type arguments,
669+
* An expression followed by a dot, "new", optional type arguments, a class name, optional class type arguments,
670670
* and constructor arguments. May be an anonymous allocation if followed by a class body.
671671
*/
672672
class InnerAllocationPrimary extends CompletedPrimary {
@@ -692,8 +692,36 @@ public class Parser {
692692
return new ExpressionPrimary(new AnonymousInnerAllocation(_outer, _targs, _name, _ctargs, _args, members, si));
693693
}
694694
}
695-
696-
695+
696+
class LambdaParams extends Primary {
697+
private final List<FormalParameter> formal;
698+
private final List<String> inferred;
699+
700+
public LambdaParams(List<FormalParameter> fparams, List<String> iparams, SourceInfo si) {
701+
super(si);
702+
703+
formal = fparams;
704+
inferred = iparams;
705+
}
706+
707+
public LambdaParams(String param, SourceInfo si) {
708+
super(si);
709+
710+
List<String> params = new LinkedList<String>();
711+
params.add(param);
712+
inferred = params;
713+
formal = null;
714+
}
715+
716+
public LambdaParams(SourceInfo si) {
717+
this(null, null, si);
718+
}
719+
720+
public boolean isFormal() { return formal != null; }
721+
public boolean isInferred() { return inferred != null; }
722+
public List<FormalParameter> getFormalParams() { return formal; }
723+
public List<String> getInferredParams() { return inferred; }
724+
}
697725
}
698726

699727
PARSER_END(Parser)
@@ -934,6 +962,7 @@ PARSER_END(Parser)
934962
| < SEMICOLON: ";" >
935963
| < COMMA: "," >
936964
| < DOT: "." >
965+
| < ARROW: "->" >
937966
}
938967
939968
/* OPERATORS */
@@ -1794,7 +1823,6 @@ PARSER_END(Parser)
17941823
}
17951824
}
17961825

1797-
17981826
// Production for Initializer ===================================================
17991827

18001828
/**
@@ -2654,8 +2682,10 @@ PARSER_END(Parser)
26542682
Expression exp;
26552683
}
26562684
{
2657-
exp=conditionalExpression() [ exp=expressionSuffix(exp) ]
2685+
( LOOKAHEAD(lambdaExpression()) exp=lambdaExpression()
2686+
| exp=conditionalExpression() [ exp=expressionSuffix(exp) ])
26582687
{
2688+
System.out.println("Parsing expression...");
26592689
return exp;
26602690
}
26612691
}
@@ -3103,6 +3133,7 @@ PARSER_END(Parser)
31033133
t="("
31043134
( LOOKAHEAD(2) typ=primitiveType() ")" exp=unaryExpression()
31053135
| typ=type() ")" exp=unaryExpressionNotPlusMinus()
3136+
| LOOKAHEAD(<ARROW>) typ=type() ")" exp=lambdaExpression()
31063137
)
31073138
{ return new CastExpression(typ, exp, _range(t, exp)); }
31083139
}
@@ -3203,6 +3234,61 @@ PARSER_END(Parser)
32033234
)*
32043235
{ return p.asExpression(); }
32053236
}
3237+
3238+
List<String> inferredParameters() :
3239+
{
3240+
Token param;
3241+
List<String> params = new LinkedList<String>();
3242+
}
3243+
{
3244+
(param=<IDENTIFIER> { params.add(param.image); } ("," param=<IDENTIFIER> { params.add(param.image); })*)?
3245+
{ return params; }
3246+
}
3247+
3248+
LambdaParams lambdaParams() :
3249+
{
3250+
Token id, t1, t2;
3251+
List<String> iparams;
3252+
List<FormalParameter> fparams;
3253+
}
3254+
{
3255+
id=<IDENTIFIER>
3256+
{ return new LambdaParams(id.image, _range(id, id)); }
3257+
| LOOKAHEAD(3) t1=<LPAREN> t2=<RPAREN>
3258+
{ return new LambdaParams(_range(t1, t2)); }
3259+
| LOOKAHEAD(3) t1=<LPAREN> iparams=inferredParameters() t2=<RPAREN>
3260+
{ return new LambdaParams(null, iparams, _range(t1, t2)); }
3261+
| t1=<LPAREN> fparams=formalParameters() t2=<RPAREN>
3262+
{ return new LambdaParams(fparams, null, _range(t1, t2)); }
3263+
}
3264+
3265+
Expression lambdaExpression () :
3266+
{
3267+
LambdaParams params;
3268+
Expression exprBody = null;
3269+
BlockStatement blockBody = null;
3270+
Expression exp = null;
3271+
}
3272+
{
3273+
params=lambdaParams() <ARROW> [ LOOKAHEAD(2) blockBody=block()
3274+
| LOOKAHEAD(2) exprBody=expression()
3275+
]
3276+
{
3277+
SourceInfo si = SourceInfo.NONE;
3278+
if (blockBody != null) {
3279+
si = _range(params.getSourceInfo(), token);
3280+
} else if (exprBody != null) {
3281+
si = _range(params.getSourceInfo(), token);
3282+
}
3283+
3284+
if (params.isFormal()) {
3285+
exp = new LambdaExpression(params.getFormalParams(), null, blockBody, exprBody, si);
3286+
} else if (params.isInferred()) {
3287+
exp = new LambdaExpression(null, params.getInferredParams(), blockBody, exprBody, si);
3288+
}
3289+
return exp;
3290+
}
3291+
}
32063292

32073293
/**
32083294
* Used internally to parse an expression

dynamicjava/src/koala/dynamicjava/parser/wrapper/ParserTest.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555
/**
5656
* Tests the interactions parser.
57-
* @version $Id$
57+
* @version $Id: ParserTest.java 5175 2010-01-20 08:46:32Z mgricken $
5858
*/
5959
public class ParserTest extends TestCase {
6060

@@ -625,9 +625,22 @@ public void testImplicitMethodInvocation() throws ParseException {
625625
args.add(new IntegerLiteral("5"));
626626
Expression expected = new SimpleMethodCall("meth", args, SourceInfo.NONE);
627627
verifyExprOutput("meth(5)", expected);
628-
}
628+
}
629+
630+
public void testLambdaNoArguments() throws ParseException {
631+
Expression body = new AmbiguousName("a");
632+
Expression expected = new LambdaExpression(null, null, null, body);
633+
verifyExprOutput("() -> a", expected);
634+
}
635+
636+
public void testLambdaOneArgument() throws ParseException {
637+
List<String> args = new LinkedList<String>();
638+
args.add("a");
639+
Expression body = new AmbiguousName("a");
640+
Expression expected = new LambdaExpression(null, args, null, body);
641+
verifyExprOutput("a -> a", expected);
642+
}
629643

630-
631644
// /** Multiple input tests. */
632645
//
633646
// public void testMultipleInputs0() throws ParseException {
@@ -776,7 +789,7 @@ public void testImplicitMethodInvocation() throws ParseException {
776789
//
777790
///**
778791
// * Tests the parser.
779-
// * @version $Id$
792+
// * @version $Id: ParserTest.java 5175 2010-01-20 08:46:32Z mgricken $
780793
// */
781794
//public class ParserTest extends ASTTestCase {
782795
// public ParserTest(String name) {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package koala.dynamicjava.tree;
2+
3+
import edu.rice.cs.plt.tuple.Option;
4+
import koala.dynamicjava.tree.visitor.Visitor;
5+
6+
import java.util.*;
7+
8+
public class LambdaExpression extends PrimaryExpression {
9+
private Option<List<FormalParameter>> typedParams;
10+
private Option<List<String>> inferredParams;
11+
12+
// A lambda expression can have either a block or an expression as its body.
13+
private Option<BlockStatement> blockBody;
14+
private Option<Expression> exprBody;
15+
16+
public LambdaExpression(List<FormalParameter> typedParams,
17+
List<String> inferredParams,
18+
BlockStatement blockBody,
19+
Expression exprBody) {
20+
this(typedParams, inferredParams, blockBody, exprBody, SourceInfo.NONE);
21+
}
22+
23+
/**
24+
* Creates a new LambdaExpression.
25+
*
26+
* @param typedParams A list of parameters annotated with types.
27+
* @param inferredParams A list of parameters without type annotations.
28+
* @param blockBody A block, used as the body of the lambda.
29+
* @param exprBody An expression, used as the body of the lambda
30+
* @param si Source information.
31+
* @throws java.lang.IllegalArgumentException Thrown if parameters or body arguments are both null or are both
32+
* provided.
33+
*/
34+
public LambdaExpression(List<FormalParameter> typedParams,
35+
List<String> inferredParams,
36+
BlockStatement blockBody,
37+
Expression exprBody,
38+
SourceInfo si) {
39+
super(si);
40+
41+
if (blockBody == null && exprBody == null) {
42+
throw new IllegalArgumentException("No body provided.");
43+
}
44+
if (typedParams != null && inferredParams != null) {
45+
throw new IllegalArgumentException("Provided both kinds of parameter list.");
46+
}
47+
if (blockBody != null && exprBody != null) {
48+
throw new IllegalArgumentException("Provided both kinds of body.");
49+
}
50+
51+
if (blockBody == null) {
52+
this.blockBody = Option.none();
53+
} else {
54+
this.blockBody = Option.wrap(blockBody);
55+
}
56+
57+
if (exprBody == null) {
58+
this.exprBody = Option.none();
59+
} else {
60+
this.exprBody = Option.wrap(exprBody);
61+
}
62+
63+
if (typedParams == null) {
64+
this.typedParams = Option.none();
65+
} else {
66+
this.typedParams = Option.wrap(typedParams);
67+
}
68+
69+
if (inferredParams == null) {
70+
this.inferredParams = Option.none();
71+
} else {
72+
this.inferredParams = Option.wrap(inferredParams);
73+
}
74+
}
75+
76+
@Override
77+
public <T> T acceptVisitor(Visitor<T> visitor) {
78+
return visitor.visit(this);
79+
}
80+
81+
}

dynamicjava/src/koala/dynamicjava/tree/visitor/Visitor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public interface Visitor<T> {
138138
T visit(BitAndAssignExpression node);
139139
T visit(ExclusiveOrAssignExpression node);
140140
T visit(BitOrAssignExpression node);
141+
T visit(LambdaExpression node);
141142
T visit(BlockStatement node);
142143
T visit(ClassDeclaration node);
143144
T visit(InterfaceDeclaration node);

0 commit comments

Comments
 (0)