From d5a4fb60e3b62f4dc1b67c83cd62c1a264bb4a12 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 1 Jan 2016 20:00:02 +0000 Subject: Generate variable IDs without Eclipse keys --- .../org/zwobble/couscous/ast/ClassNodeBuilder.java | 3 +- .../couscous/ast/LocalVariableDeclarationNode.java | 3 +- .../zwobble/couscous/ast/VariableDeclaration.java | 10 +- .../couscous/ast/VariableReferenceNode.java | 3 +- .../couscous/ast/identifiers/Identifier.java | 44 +++++ .../java/JavaExpressionMethodReferenceReader.java | 10 +- .../frontends/java/JavaExpressionReader.java | 18 +-- .../frontends/java/JavaLambdaExpressionReader.java | 22 ++- .../couscous/frontends/java/JavaReader.java | 178 ++++++++++----------- .../frontends/java/JavaStatementReader.java | 25 +-- .../java/JavaVariableDeclarationReader.java | 8 +- .../org/zwobble/couscous/frontends/java/Scope.java | 91 +++++++++++ .../couscous/frontends/java/UniqueIdentifiers.java | 17 -- .../zwobble/couscous/interpreter/Environment.java | 11 +- .../couscous/interpreter/UnboundVariable.java | 8 +- .../couscous/interpreter/VariableNotInScope.java | 8 +- .../java/org/zwobble/couscous/util/ExtraMaps.java | 18 +++ 17 files changed, 309 insertions(+), 168 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/identifiers/Identifier.java create mode 100644 src/main/java/org/zwobble/couscous/frontends/java/Scope.java delete mode 100644 src/main/java/org/zwobble/couscous/frontends/java/UniqueIdentifiers.java create mode 100644 src/main/java/org/zwobble/couscous/util/ExtraMaps.java (limited to 'src/main/java') diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java index 9be591c..b41c972 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.ast; import com.google.common.collect.ImmutableList; +import org.zwobble.couscous.ast.identifiers.Identifier; import java.util.Optional; import java.util.function.Function; @@ -110,7 +111,7 @@ public class ClassNodeBuilder { return ThisReferenceNode.thisReference(name); } - public MethodBuilder argument(String id, String name, TypeName type) { + public MethodBuilder argument(Identifier id, String name, TypeName type) { arguments.add(formalArg(var(id, name, type))); return this; } diff --git a/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java b/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java index 3499e26..a165070 100644 --- a/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java +++ b/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java @@ -1,5 +1,6 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.identifiers.Identifier; import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import java.util.function.Function; @@ -8,7 +9,7 @@ import static org.zwobble.couscous.ast.VariableDeclaration.var; public class LocalVariableDeclarationNode implements VariableNode, StatementNode { public static LocalVariableDeclarationNode localVariableDeclaration( - String id, + Identifier id, String name, TypeName type, ExpressionNode initialValue) { diff --git a/src/main/java/org/zwobble/couscous/ast/VariableDeclaration.java b/src/main/java/org/zwobble/couscous/ast/VariableDeclaration.java index 713f3a6..5932371 100644 --- a/src/main/java/org/zwobble/couscous/ast/VariableDeclaration.java +++ b/src/main/java/org/zwobble/couscous/ast/VariableDeclaration.java @@ -1,21 +1,23 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.identifiers.Identifier; + public class VariableDeclaration { - public static VariableDeclaration var(String id, String name, TypeName type) { + public static VariableDeclaration var(Identifier id, String name, TypeName type) { return new VariableDeclaration(id, name, type); } - private final String id; + private final Identifier id; private final String name; private final TypeName type; - private VariableDeclaration(String id, String name, TypeName type) { + private VariableDeclaration(Identifier id, String name, TypeName type) { this.id = id; this.name = name; this.type = type; } - public String getId() { + public Identifier getId() { return id; } diff --git a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java index b576389..50462e9 100644 --- a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java @@ -1,5 +1,6 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.identifiers.Identifier; import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor; import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; @@ -34,7 +35,7 @@ public class VariableReferenceNode implements AssignableExpressionNode { return this; } - public String getReferentId() { + public Identifier getReferentId() { return referent.getId(); } diff --git a/src/main/java/org/zwobble/couscous/ast/identifiers/Identifier.java b/src/main/java/org/zwobble/couscous/ast/identifiers/Identifier.java new file mode 100644 index 0000000..29a33eb --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/identifiers/Identifier.java @@ -0,0 +1,44 @@ +package org.zwobble.couscous.ast.identifiers; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +public class Identifier { + public static final Identifier TOP = new Identifier(ImmutableList.of()); + + private final List parts; + + private Identifier(List parts) { + this.parts = parts; + } + + public Identifier extend(String name) { + ImmutableList.Builder parts = ImmutableList.builder(); + parts.addAll(this.parts); + parts.add(name); + return new Identifier(parts.build()); + } + + @Override + public String toString() { + return "Identifier(" + + "parts=" + parts + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Identifier that = (Identifier) o; + + return parts.equals(that.parts); + } + + @Override + public int hashCode() { + return parts.hashCode(); + } +} diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionMethodReferenceReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionMethodReferenceReader.java index d07d1de..8780082 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionMethodReferenceReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionMethodReferenceReader.java @@ -10,18 +10,16 @@ import org.zwobble.couscous.ast.sugar.Lambda; import java.util.List; -import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg; import static org.zwobble.couscous.ast.ReturnNode.returns; import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall; -import static org.zwobble.couscous.ast.VariableDeclaration.var; import static org.zwobble.couscous.ast.sugar.Lambda.lambda; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class JavaExpressionMethodReferenceReader { - public static Lambda javaExpressionMethodReferenceToLambda(ExpressionMethodReference expression) { + public static Lambda expressionMethodReferenceToLambda(Scope scope, ExpressionMethodReference expression) { IMethodBinding functionalInterfaceMethod = expression.resolveTypeBinding().getFunctionalInterfaceMethod(); - List formalArguments = formalArguments(functionalInterfaceMethod); + List formalArguments = formalArguments(scope, functionalInterfaceMethod); return lambda( formalArguments, @@ -34,13 +32,13 @@ public class JavaExpressionMethodReferenceReader { typeOf(expression.resolveMethodBinding().getReturnType())))))); } - private static List formalArguments(IMethodBinding functionalInterfaceMethod) { + private static List formalArguments(Scope scope, IMethodBinding functionalInterfaceMethod) { ImmutableList.Builder arguments = ImmutableList.builder(); ITypeBinding[] parameterTypes = functionalInterfaceMethod.getParameterTypes(); for (int index = 0; index < parameterTypes.length; index++) { ITypeBinding parameterType = parameterTypes[index]; - arguments.add(formalArg(var("arg" + index, "arg" + index, typeOf(parameterType)))); + arguments.add(scope.formalArgument("arg" + index, typeOf(parameterType))); } return arguments.build(); diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java index 2c0ab22..15a910c 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -14,17 +14,17 @@ import static java.util.Arrays.asList; import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall; import static org.zwobble.couscous.ast.LiteralNode.literal; import static org.zwobble.couscous.ast.MethodCallNode.not; -import static org.zwobble.couscous.ast.VariableDeclaration.var; -import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.frontends.java.JavaOperators.readOperator; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; import static org.zwobble.couscous.util.ExtraLists.concat; import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class JavaExpressionReader { + private final Scope scope; private final JavaReader javaReader; - JavaExpressionReader(JavaReader javaReader) { + JavaExpressionReader(Scope scope, JavaReader javaReader) { + this.scope = scope; this.javaReader = javaReader; } @@ -139,7 +139,7 @@ public class JavaExpressionReader { return literal(expression.getLiteralValue()); } - private static ExpressionNode readSimpleName(SimpleName expression) { + private ExpressionNode readSimpleName(SimpleName expression) { IBinding binding = expression.resolveBinding(); if (binding.getKind() == IBinding.VARIABLE) { return readVariableBinding((IVariableBinding)binding); @@ -148,10 +148,10 @@ public class JavaExpressionReader { } } - private static ExpressionNode readVariableBinding(IVariableBinding binding) { + private ExpressionNode readVariableBinding(IVariableBinding binding) { TypeName type = typeOf(binding); if (binding.getDeclaringClass() == null) { - return reference(var(binding.getKey(), binding.getName(), type)); + return scope.reference(binding.getKey()); } else { return FieldAccessNode.fieldAccess(ThisReferenceNode.thisReference(typeOf(binding.getDeclaringClass())), binding.getName(), type); } @@ -201,7 +201,7 @@ public class JavaExpressionReader { if (constructor.getDeclaringClass().isAnonymous()) { GeneratedClosure closure = - javaReader.readAnonymousClass(expression.getAnonymousClassDeclaration()); + javaReader.readAnonymousClass(scope, expression.getAnonymousClassDeclaration()); return constructorCall( closure.getType(), concat(captureArguments(closure), arguments)); @@ -211,14 +211,14 @@ public class JavaExpressionReader { } private ExpressionNode readLambdaExpression(LambdaExpression expression) { - GeneratedClosure closure = javaReader.readLambda(expression); + GeneratedClosure closure = javaReader.readLambda(scope, expression); return constructorCall( closure.getType(), captureArguments(closure)); } private ExpressionNode readExpressionMethodReference(ExpressionMethodReference expression) { - GeneratedClosure closure = javaReader.readExpressionMethodReference(expression); + GeneratedClosure closure = javaReader.readExpressionMethodReference(scope, expression); return constructorCall( closure.getType(), captureArguments(closure)); diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaLambdaExpressionReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaLambdaExpressionReader.java index 44a0f2d..c8b5f4c 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaLambdaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaLambdaExpressionReader.java @@ -10,9 +10,7 @@ import java.util.List; import java.util.Optional; import static java.util.Arrays.asList; -import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg; import static org.zwobble.couscous.ast.ReturnNode.returns; -import static org.zwobble.couscous.ast.VariableDeclaration.var; import static org.zwobble.couscous.ast.sugar.Lambda.lambda; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; import static org.zwobble.couscous.util.ExtraLists.eagerMap; @@ -24,35 +22,35 @@ public class JavaLambdaExpressionReader { this.reader = reader; } - public Lambda javaLambdaToLambda(LambdaExpression expression) { + public Lambda toLambda(Scope scope, LambdaExpression expression) { List formalArguments = eagerMap( (List)expression.parameters(), - this::readLambdaExpressionParameter); + parameter -> readLambdaExpressionParameter(scope, parameter)); - return lambda(formalArguments, readLambdaExpressionBody(expression)); + return lambda(formalArguments, readLambdaExpressionBody(scope, expression)); } - private FormalArgumentNode readLambdaExpressionParameter(Object parameter) { + private FormalArgumentNode readLambdaExpressionParameter(Scope scope, Object parameter) { if (parameter instanceof SingleVariableDeclaration) { - return JavaVariableDeclarationReader.read((SingleVariableDeclaration) parameter); + return JavaVariableDeclarationReader.read(scope, (SingleVariableDeclaration) parameter); } else { VariableDeclarationFragment fragment = (VariableDeclarationFragment)parameter; - return formalArg(var( + return scope.formalArgument( fragment.resolveBinding().getKey(), fragment.getName().getIdentifier(), - typeOf(fragment.resolveBinding().getType()))); + typeOf(fragment.resolveBinding().getType())); } } - private List readLambdaExpressionBody(LambdaExpression expression) { + private List readLambdaExpressionBody(Scope scope, LambdaExpression expression) { TypeName returnType = typeOf(expression.resolveTypeBinding().getFunctionalInterfaceMethod().getReturnType()); if (expression.getBody() instanceof Block) { @SuppressWarnings("unchecked") List statements = ((Block) expression.getBody()).statements(); - return reader.readStatements(statements, Optional.of(returnType)); + return reader.readStatements(scope, statements, Optional.of(returnType)); } else { Expression body = (Expression) expression.getBody(); - return asList(returns(reader.readExpression(returnType, body))); + return asList(returns(reader.readExpression(scope, returnType, body))); } } } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java index 4efd0bf..86ad12d 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -5,6 +5,7 @@ import com.google.common.collect.Maps; import org.eclipse.jdt.core.dom.*; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.VariableDeclaration; +import org.zwobble.couscous.ast.identifiers.Identifier; import org.zwobble.couscous.ast.sugar.Lambda; import org.zwobble.couscous.util.ExtraLists; @@ -18,6 +19,7 @@ import java.util.function.Function; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Maps.immutableEntry; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.zwobble.couscous.ast.AnnotationNode.annotation; @@ -27,13 +29,13 @@ import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess; import static org.zwobble.couscous.ast.FieldDeclarationNode.field; import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg; import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; -import static org.zwobble.couscous.ast.VariableDeclaration.var; import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables; -import static org.zwobble.couscous.frontends.java.JavaExpressionMethodReferenceReader.javaExpressionMethodReferenceToLambda; +import static org.zwobble.couscous.frontends.java.JavaExpressionMethodReferenceReader.expressionMethodReferenceToLambda; import static org.zwobble.couscous.frontends.java.JavaTypes.*; import static org.zwobble.couscous.util.Casts.tryCast; import static org.zwobble.couscous.util.ExtraLists.*; +import static org.zwobble.couscous.util.ExtraMaps.toMap; public class JavaReader { public static List readClassFromFile(Path root, Path sourcePath) throws IOException { @@ -46,7 +48,6 @@ public class JavaReader { return cons(reader.readCompilationUnit(ast), reader.classes.build()); } - private final UniqueIdentifiers identifiers = new UniqueIdentifiers(); private final ImmutableList.Builder classes; private int anonymousClassCount = 0; @@ -56,8 +57,9 @@ public class JavaReader { private ClassNode readCompilationUnit(CompilationUnit ast) { TypeName name = generateClassName(ast); + Scope scope = Scope.create().enterClass(name); TypeDeclaration type = (TypeDeclaration)ast.types().get(0); - TypeDeclarationBody body = readTypeDeclarationBody(type.bodyDeclarations()); + TypeDeclarationBody body = readTypeDeclarationBody(scope, type.bodyDeclarations()); return ClassNode.declareClass( name, superTypes(type), @@ -66,28 +68,27 @@ public class JavaReader { body.getMethods()); } - GeneratedClosure readExpressionMethodReference(ExpressionMethodReference expression) { + GeneratedClosure readExpressionMethodReference(Scope outerScope, ExpressionMethodReference expression) { + TypeName name = generateAnonymousName(expression); + Scope scope = outerScope.enterClass(name); return generateClosure( - expression, + scope, + name, expression.resolveTypeBinding().getFunctionalInterfaceMethod(), - javaExpressionMethodReferenceToLambda(expression)); - } - - private ITypeBinding findDeclaringClass(ASTNode node) { - while (!(node instanceof AbstractTypeDeclaration)) { - node = node.getParent(); - } - return ((AbstractTypeDeclaration)node).resolveBinding(); + expressionMethodReferenceToLambda(scope, expression)); } - GeneratedClosure readLambda(LambdaExpression expression) { + GeneratedClosure readLambda(Scope outerScope, LambdaExpression expression) { + TypeName name = generateAnonymousName(expression); + Scope scope = outerScope.enterClass(name); return generateClosure( - expression, + scope, + name, expression.resolveTypeBinding().getFunctionalInterfaceMethod(), - new JavaLambdaExpressionReader(this).javaLambdaToLambda(expression)); + new JavaLambdaExpressionReader(this).toLambda(scope, expression)); } - GeneratedClosure generateClosure(ASTNode node, IMethodBinding functionalInterfaceMethod, Lambda lambda) { + GeneratedClosure generateClosure(Scope scope, TypeName name, IMethodBinding functionalInterfaceMethod, Lambda lambda) { MethodNode method = MethodNode.method( emptyList(), false, @@ -96,7 +97,8 @@ public class JavaReader { lambda.getBody()); GeneratedClosure closure = classWithCapture( - generateAnonymousClassName(findDeclaringClass(node)), + scope, + name, superTypesAndSelf(functionalInterfaceMethod.getDeclaringClass()), emptyList(), ImmutableList.of(method)); @@ -110,7 +112,7 @@ public class JavaReader { List body, List freeVariables) { - Map freeVariablesById = Maps.transformValues( + Map freeVariablesById = Maps.transformValues( Maps.uniqueIndex(freeVariables, VariableDeclaration::getId), freeVariable -> captureAccess(className, freeVariable)); Function replaceExpression = new CaptureReplacer(freeVariablesById); @@ -118,9 +120,9 @@ public class JavaReader { } private class CaptureReplacer implements Function { - private final Map freeVariablesById; + private final Map freeVariablesById; - public CaptureReplacer(Map freeVariablesById) { + public CaptureReplacer(Map freeVariablesById) { this.freeVariablesById = freeVariablesById; } @@ -132,16 +134,22 @@ public class JavaReader { } } - private ConstructorNode buildConstructor(TypeName type, List freeVariables) { - Map argumentDeclarationsById = Maps.transformValues( - Maps.uniqueIndex(freeVariables, VariableDeclaration::getId), - freeVariable -> var(identifiers.generate(freeVariable.getId() + "__capture"), freeVariable.getName(), freeVariable.getType())); + private ConstructorNode buildConstructor(Scope outerScope, TypeName type, List freeVariables) { + Scope scope = outerScope.enterConstructor(); + Map argumentDeclarationsById = toMap( + freeVariables, + freeVariable -> immutableEntry( + freeVariable.getId(), + scope.generateVariable(freeVariable.getName(), freeVariable.getType()))); + List arguments = eagerMap( freeVariables, freeVariable -> formalArg(argumentDeclarationsById.get(freeVariable.getId()))); + List body = eagerMap(freeVariables, freeVariable -> assignStatement( captureAccess(type, freeVariable), reference(argumentDeclarationsById.get(freeVariable.getId())))); + return constructor(arguments, body); } @@ -149,10 +157,12 @@ public class JavaReader { return fieldAccess(thisReference(type), freeVariable.getName(), freeVariable.getType()); } - GeneratedClosure readAnonymousClass(AnonymousClassDeclaration declaration) { - TypeName className = generateClassName(declaration); - TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(declaration.bodyDeclarations()); + GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) { + TypeName className = generateAnonymousName(declaration); + Scope scope = outerScope.enterClass(className); + TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, declaration.bodyDeclarations()); GeneratedClosure closure = classWithCapture( + scope, className, superTypes(declaration), bodyDeclarations.getFields(), @@ -162,6 +172,7 @@ public class JavaReader { } private GeneratedClosure classWithCapture( + Scope scope, TypeName className, Set superTypes, List declaredFields, @@ -178,24 +189,27 @@ public class JavaReader { className, superTypes, fields, - buildConstructor(className, freeVariables), + buildConstructor(scope, className, freeVariables), eagerMap(methods, method -> method.mapBody(body -> replaceCaptureReferences(className, body, freeVariables)))); return new GeneratedClosure(classNode, freeVariables); } - private TypeName generateClassName(AnonymousClassDeclaration declaration) { - ITypeBinding type = declaration.resolveBinding(); - return generateAnonymousClassName(type); - } - - private TypeName generateAnonymousClassName(ITypeBinding type) { + private TypeName generateAnonymousName(ASTNode node) { + ITypeBinding type = findDeclaringClass(node); while (type.isAnonymous()) { type = type.getDeclaringClass(); } return TypeName.of(type.getQualifiedName() + "_Anonymous_" + (anonymousClassCount++)); } + private ITypeBinding findDeclaringClass(ASTNode node) { + while (!(node instanceof AbstractTypeDeclaration)) { + node = node.getParent(); + } + return ((AbstractTypeDeclaration)node).resolveBinding(); + } + private static class TypeDeclarationBody { private final List fields; private final ConstructorNode constructor; @@ -220,11 +234,11 @@ public class JavaReader { } } - private TypeDeclarationBody readTypeDeclarationBody(List bodyDeclarations) { + private TypeDeclarationBody readTypeDeclarationBody(Scope scope, List bodyDeclarations) { ImmutableList.Builder methods = ImmutableList.builder(); ConstructorNode constructor = ConstructorNode.DEFAULT; - for (CallableNode callable : readMethods(ofType(bodyDeclarations, MethodDeclaration.class))) { + for (CallableNode callable : readMethods(scope, ofType(bodyDeclarations, MethodDeclaration.class))) { if (callable instanceof ConstructorNode) { constructor = (ConstructorNode) callable; } else { @@ -249,81 +263,61 @@ public class JavaReader { field(fragment.getName().getIdentifier(), type)); } - private List readMethods(List methods) { - return eagerMap(methods, this::readMethod); + private List readMethods(Scope scope, List methods) { + return eagerMap(methods, method -> readMethod(scope, method)); } - private CallableNode readMethod(MethodDeclaration method) { - FunctionDeclaration function = functionDeclaration(method); + private CallableNode readMethod(Scope outerScope, MethodDeclaration method) { + Scope scope = outerScope.enterMethod(method.getName().getIdentifier()); + + List formalArguments = readFormalArguments(scope, method); + List body = readBody(scope, method); + List annotations = readAnnotations(method); + if (method.isConstructor()) { return constructor( - function.getFormalArguments(), - function.getBody()); + formalArguments, + body); } else { return MethodNode.method( - function.getAnnotations(), + annotations, Modifier.isStatic(method.getModifiers()), method.getName().getIdentifier(), - function.getFormalArguments(), - function.getBody()); + formalArguments, + body); } } - private FunctionDeclaration functionDeclaration(MethodDeclaration method) { - @SuppressWarnings("unchecked") - List parameters = method.parameters(); - List formalArguments = eagerMap(parameters, JavaVariableDeclarationReader::read); - @SuppressWarnings("unchecked") - List statements = method.getBody().statements(); - Optional returnType = Optional.ofNullable(method.getReturnType2()) - .map(Type::resolveBinding) - .map(JavaTypes::typeOf); - - return new FunctionDeclaration( - eagerMap(asList(method.resolveBinding().getAnnotations()), this::readAnnotation), - formalArguments, - readStatements(statements, returnType)); + private List readAnnotations(MethodDeclaration method) { + return eagerMap(asList(method.resolveBinding().getAnnotations()), this::readAnnotation); } private AnnotationNode readAnnotation(IAnnotationBinding annotationBinding) { return annotation(typeOf(annotationBinding.getAnnotationType())); } - private class FunctionDeclaration { - private final List annotations; - private final List formalArguments; - private final List body; - - private FunctionDeclaration( - List annotations, - List formalArguments, - List body) - { - this.annotations = annotations; - this.formalArguments = formalArguments; - this.body = body; - } - - public List getAnnotations() { - return annotations; - } - - public List getFormalArguments() { - return formalArguments; - } + private List readFormalArguments(Scope scope, MethodDeclaration method) { + @SuppressWarnings("unchecked") + List parameters = method.parameters(); + return eagerMap(parameters, parameter -> JavaVariableDeclarationReader.read(scope, parameter)); + } - public List getBody() { - return body; - } + private List readBody(Scope scope, MethodDeclaration method) { + @SuppressWarnings("unchecked") + List statements = method.getBody().statements(); + Optional returnType = Optional.ofNullable(method.getReturnType2()) + .map(Type::resolveBinding) + .map(JavaTypes::typeOf); + return readStatements(scope, statements, returnType); } - List readStatements(List body, Optional returnType) { - JavaStatementReader statementReader = new JavaStatementReader(expressionReader(), returnType); + List readStatements(Scope scope, List body, Optional returnType) { + JavaStatementReader statementReader = new JavaStatementReader(scope, expressionReader(scope), returnType); return eagerFlatMap(body, statementReader::readStatement); } - ExpressionNode readExpression(TypeName targetType, Expression body) { - return expressionReader().readExpression(targetType, body); + ExpressionNode readExpression(Scope scope, TypeName targetType, Expression body) { + return expressionReader(scope).readExpression(targetType, body); } private TypeName generateClassName(CompilationUnit ast) { @@ -333,7 +327,7 @@ public class JavaReader { return TypeName.of(packageName + "." + className); } - private JavaExpressionReader expressionReader() { - return new JavaExpressionReader(this); + private JavaExpressionReader expressionReader(Scope scope) { + return new JavaExpressionReader(scope, this); } } \ No newline at end of file diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java index 725882f..6a12a72 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java @@ -10,16 +10,21 @@ import java.util.stream.Collectors; import static java.util.Arrays.asList; import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement; -import static org.zwobble.couscous.ast.LocalVariableDeclarationNode.localVariableDeclaration; import static org.zwobble.couscous.ast.WhileNode.whileLoop; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; import static org.zwobble.couscous.util.ExtraLists.*; class JavaStatementReader { + private final Scope scope; private final JavaExpressionReader expressionReader; private final Optional returnType; - JavaStatementReader(JavaExpressionReader expressionReader, Optional returnType) { + JavaStatementReader( + Scope scope, + JavaExpressionReader expressionReader, + Optional returnType + ) { + this.scope = scope; this.expressionReader = expressionReader; this.returnType = returnType; } @@ -108,13 +113,15 @@ class JavaStatementReader { return readDeclarationFragments(fragments, type); } - private List readDeclarationFragments(List fragments, TypeName type) { - return eagerMap(fragments, fragment -> - localVariableDeclaration( - fragment.resolveBinding().getKey(), - fragment.getName().getIdentifier(), - type, - readExpression(type, fragment.getInitializer()))); + private List readDeclarationFragments( + List fragments, + TypeName type + ) { + return eagerMap(fragments, fragment -> scope.localVariable( + fragment.resolveBinding().getKey(), + fragment.getName().getIdentifier(), + type, + readExpression(type, fragment.getInitializer()))); } private ExpressionNode readExpressionWithoutBoxing(Expression expression) { diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaVariableDeclarationReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaVariableDeclarationReader.java index 992475d..812e5d4 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaVariableDeclarationReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaVariableDeclarationReader.java @@ -3,15 +3,13 @@ package org.zwobble.couscous.frontends.java; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.zwobble.couscous.ast.FormalArgumentNode; -import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg; -import static org.zwobble.couscous.ast.VariableDeclaration.var; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; class JavaVariableDeclarationReader { - static FormalArgumentNode read(SingleVariableDeclaration parameter) { - return formalArg(var( + static FormalArgumentNode read(Scope scope, SingleVariableDeclaration parameter) { + return scope.formalArgument( parameter.resolveBinding().getKey(), parameter.getName().getIdentifier(), - typeOf(parameter.resolveBinding()))); + typeOf(parameter.resolveBinding())); } } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/Scope.java b/src/main/java/org/zwobble/couscous/frontends/java/Scope.java new file mode 100644 index 0000000..caf0a96 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/frontends/java/Scope.java @@ -0,0 +1,91 @@ +package org.zwobble.couscous.frontends.java; + +import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.identifiers.Identifier; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class Scope { + public static Scope create() { + return new Scope(new HashMap<>(), new HashSet<>(), Identifier.TOP); + } + + private final Map variablesByKey; + private final Set identifiers; + private final Identifier identifier; + + private Scope(Map variablesByKey, Set identifiers, Identifier identifier) { + this.variablesByKey = variablesByKey; + this.identifiers = identifiers; + this.identifier = identifier; + } + + public Scope enterClass(TypeName className) { + return new Scope(variablesByKey, identifiers, identifier.extend("class#" + className.getQualifiedName())); + } + + public Scope enterConstructor() { + return new Scope(variablesByKey, identifiers, identifier.extend("constructor")); + } + + public Scope enterMethod(String name) { + // TODO: distinguish overloads + return new Scope(variablesByKey, identifiers, identifier.extend("method#" + name)); + } + + public FormalArgumentNode formalArgument(String name, TypeName type) { + VariableDeclaration declaration = generateVariable(name, type); + return FormalArgumentNode.formalArg(declaration); + } + + public FormalArgumentNode formalArgument(String key, String name, TypeName type) { + VariableDeclaration declaration = generateVariable(key, name, type); + return FormalArgumentNode.formalArg(declaration); + } + + public LocalVariableDeclarationNode localVariable( + String key, + String name, + TypeName type, + ExpressionNode initialValue + ) { + VariableDeclaration declaration = generateVariable(key, name, type); + return LocalVariableDeclarationNode.localVariableDeclaration(declaration, initialValue); + } + + public ExpressionNode reference(String key) { + VariableDeclaration variable = variablesByKey.get(key); + if (variable == null) { + throw new IllegalArgumentException("variable not found: " + key); + } + return VariableReferenceNode.reference(variable); + } + + private VariableDeclaration generateVariable(String key, String name, TypeName type) { + if (variablesByKey.containsKey(key)) { + throw new IllegalArgumentException(key + " is already mapped"); + } + VariableDeclaration variable = generateVariable(name, type); + variablesByKey.put(key, variable); + return variable; + } + + public VariableDeclaration generateVariable(String name, TypeName type) { + Identifier identifier = generateIdentifier(name); + return VariableDeclaration.var(identifier, name, type); + } + + private Identifier generateIdentifier(String name) { + Identifier identifier = this.identifier.extend(name); + int index = 0; + while (identifiers.contains(identifier)) { + identifier = this.identifier.extend(name + "_" + index); + index++; + } + identifiers.add(identifier); + return identifier; + } +} diff --git a/src/main/java/org/zwobble/couscous/frontends/java/UniqueIdentifiers.java b/src/main/java/org/zwobble/couscous/frontends/java/UniqueIdentifiers.java deleted file mode 100644 index 1d28de0..0000000 --- a/src/main/java/org/zwobble/couscous/frontends/java/UniqueIdentifiers.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.zwobble.couscous.frontends.java; - -import java.util.HashMap; -import java.util.Map; - -public class UniqueIdentifiers { - private final Map identifiers; - - public UniqueIdentifiers() { - identifiers = new HashMap<>(); - } - - public String generate(String identifier) { - int index = identifiers.computeIfAbsent(identifier, x -> 0); - return identifier + "__" + index; - } -} diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java index b1d59e2..23e2fdf 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java @@ -5,6 +5,7 @@ import java.util.Optional; import org.zwobble.couscous.ast.TypeName; import org.zwobble.couscous.ast.VariableDeclaration; import org.zwobble.couscous.ast.VariableNode; +import org.zwobble.couscous.ast.identifiers.Identifier; import org.zwobble.couscous.interpreter.values.ConcreteType; import org.zwobble.couscous.interpreter.values.InterpreterValue; import static java.util.stream.Collectors.toMap; @@ -12,7 +13,7 @@ import static java.util.stream.Collectors.toMap; public class Environment { private final Project project; private final Optional thisValue; - private final Map stackFrame; + private final Map stackFrame; public Environment(Project project, Optional thisValue, Map> stackFrame) { this.project = project; @@ -27,13 +28,13 @@ public class Environment { return thisValue; } - public InterpreterValue get(String variableId) { + public InterpreterValue get(Identifier variableId) { checkVariableIsInScope(variableId); final org.zwobble.couscous.interpreter.Environment.VariableEntry entry = stackFrame.get(variableId); return entry.getValue().orElseThrow(() -> new UnboundVariable(variableId)); } - public void put(String variableId, InterpreterValue value) { + public void put(Identifier variableId, InterpreterValue value) { checkVariableIsInScope(variableId); checkVariableType(variableId, value); stackFrame.get(variableId).setValue(Optional.of(value)); @@ -51,13 +52,13 @@ public class Environment { return new Environment(project, thisValue, stackFrame); } - private void checkVariableIsInScope(String variableId) { + private void checkVariableIsInScope(Identifier variableId) { if (!stackFrame.containsKey(variableId)) { throw new VariableNotInScope(variableId); } } - private void checkVariableType(String variableId, InterpreterValue value) { + private void checkVariableType(Identifier variableId, InterpreterValue value) { TypeName variableType = stackFrame.get(variableId).getType(); InterpreterTypes.checkIsInstance(variableType, value); } diff --git a/src/main/java/org/zwobble/couscous/interpreter/UnboundVariable.java b/src/main/java/org/zwobble/couscous/interpreter/UnboundVariable.java index e0131f0..1dba54b 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/UnboundVariable.java +++ b/src/main/java/org/zwobble/couscous/interpreter/UnboundVariable.java @@ -1,14 +1,16 @@ package org.zwobble.couscous.interpreter; +import org.zwobble.couscous.ast.identifiers.Identifier; + public final class UnboundVariable extends InterpreterException { private static final long serialVersionUID = 1L; - private final String variableId; + private final Identifier variableId; - public UnboundVariable(final String variableId) { + public UnboundVariable(final Identifier variableId) { this.variableId = variableId; } - public String getVariableId() { + public Identifier getVariableId() { return this.variableId; } diff --git a/src/main/java/org/zwobble/couscous/interpreter/VariableNotInScope.java b/src/main/java/org/zwobble/couscous/interpreter/VariableNotInScope.java index 54b66bd..c5a425e 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/VariableNotInScope.java +++ b/src/main/java/org/zwobble/couscous/interpreter/VariableNotInScope.java @@ -1,14 +1,16 @@ package org.zwobble.couscous.interpreter; +import org.zwobble.couscous.ast.identifiers.Identifier; + public final class VariableNotInScope extends InterpreterException { private static final long serialVersionUID = 1L; - private final String variableId; + private final Identifier variableId; - public VariableNotInScope(final String variableId) { + public VariableNotInScope(final Identifier variableId) { this.variableId = variableId; } - public String getVariableId() { + public Identifier getVariableId() { return this.variableId; } diff --git a/src/main/java/org/zwobble/couscous/util/ExtraMaps.java b/src/main/java/org/zwobble/couscous/util/ExtraMaps.java new file mode 100644 index 0000000..c27e77c --- /dev/null +++ b/src/main/java/org/zwobble/couscous/util/ExtraMaps.java @@ -0,0 +1,18 @@ +package org.zwobble.couscous.util; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; +import java.util.function.Function; + +public class ExtraMaps { + public static Map toMap(Iterable iterable, Function> function) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (T value : iterable) { + builder.put(function.apply(value)); + } + + return builder.build(); + } +} -- cgit v1.2.3