From 9226c537ad63b67429ce645c4907294ae59265ff Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Wed, 28 Oct 2015 14:56:38 +0100 Subject: [PATCH 01/11] fixing a bug in generator function creation in ASTBuilder --- .../java/de/uni/bremen/monty/moco/ast/ASTBuilder.java | 8 +++++++- .../java/de/uni/bremen/monty/moco/ast/BasicASTNode.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java index b24be75..51d20f0 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java @@ -398,8 +398,14 @@ private ASTNode createGenerator(Position pos, ResolvableIdentifier className, Re allParams.addAll(params); allParams.addAll(defaultParams); + List allParamsCopy = new ArrayList<>(allParams.size()); + for (VariableDeclaration param : allParams) { + allParamsCopy.add(new VariableDeclaration(param.getPosition(), param.getIdentifier(), + param.getTypeIdentifier(), param.getDeclarationType())); + } + ClassDeclaration iterator = - GeneratorClassFactory.generateGeneratorIteratorClass(pos, allParams, body, returnType); + GeneratorClassFactory.generateGeneratorIteratorClass(pos, allParamsCopy, body, returnType); currentBlocks.peek().addDeclaration(iterator); ClassDeclaration generator = diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java b/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java index 5963a86..b9d38c7 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java @@ -82,7 +82,7 @@ public ASTNode getParentNode() { * * @return the parent node */ public ASTNode getParentNodeByType(Class type) { - ASTNode n = this; + ASTNode n = getParentNode(); while ((!type.isInstance(n)) && (n != null)) { n = n.getParentNode(); } From 93d61964af52fa5de96ef11477e9a42a7a99a5f3 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Thu, 29 Oct 2015 18:05:49 +0100 Subject: [PATCH 02/11] changing the way how native nodes are described - introducing a new @native annotation for native functions and classes in order to be able to control the behavior of native modules more precisely - this also adds the ability to declare non-native code in the core library - this is also important for the implementation of closures, since native functions can not be closures --- .../de/uni/bremen/monty/moco/antlr/Monty.g4 | 9 ++++-- .../uni/bremen/monty/moco/ast/ASTBuilder.java | 15 +++++++--- .../bremen/monty/moco/ast/BasicASTNode.java | 10 +++++++ .../ast/declaration/ModuleDeclaration.java | 4 --- .../moco/ast/expression/VariableAccess.java | 9 ++++++ .../moco/visitor/CodeGenerationVisitor.java | 27 ++---------------- src/main/resources/corelib/Array.monty | 10 +++---- src/main/resources/corelib/Bool.monty | 10 +++---- src/main/resources/corelib/Char.monty | 4 +-- src/main/resources/corelib/Float.monty | 26 ++++++++--------- src/main/resources/corelib/Int.monty | 28 +++++++++---------- src/main/resources/corelib/Object.monty | 4 +-- src/main/resources/corelib/String.monty | 8 +++--- src/main/resources/corelib/System.monty | 24 ++++++++-------- 14 files changed, 96 insertions(+), 92 deletions(-) diff --git a/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 b/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 index 53b07cf..d15bfc4 100644 --- a/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 +++ b/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 @@ -2,6 +2,11 @@ grammar Monty; import lex; + +nativeAnnotation + : '@native' + ; + compilationUnit : EndOfLine* moduleDeclaration EOF ; @@ -28,7 +33,7 @@ independentDeclaration ; classDeclaration - : AbstractKeyword? 'class' type ('inherits' typeList)? + : nativeAnnotation? AbstractKeyword? 'class' type ('inherits' typeList)? ':' EndOfLine Indent (memberDeclaration+ | 'pass' EndOfLine) @@ -83,7 +88,7 @@ typeList ; functionDeclaration - : (type)? + : nativeAnnotation? (type)? Identifier Lparenthesis parameterList? Rparenthesis ':' EndOfLine statementBlock diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java index 51d20f0..4251ffb 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java @@ -233,7 +233,8 @@ public ASTNode visitFunctionCall(FunctionCallContext ctx) { private void buildDefaultFunctions(boolean isFunction, List defaultParameter, List allVariableDeclarations, List params, List defaultExpression, List defaultVariableDeclaration, - Identifier identifier, Token token, TypeContext typeContext, DeclarationType declarationTypeCopy) { + Identifier identifier, Token token, TypeContext typeContext, DeclarationType declarationTypeCopy, + boolean isNative) { for (int defaultParameterIdx = 0; defaultParameterIdx < defaultParameter.size(); defaultParameterIdx++) { Block block = new Block(position(token)); @@ -275,13 +276,15 @@ private void buildDefaultFunctions(boolean isFunction, List params = parameterListToVarDeclList(parameterListContext); @@ -308,7 +311,8 @@ private FunctionDeclaration buildFunctions(boolean isFunction, ParameterListCont identifier, token, typeContext, - declarationTypeCopy); + declarationTypeCopy, + isNative); FunctionDeclaration funDecl; @@ -320,6 +324,7 @@ private FunctionDeclaration buildFunctions(boolean isFunction, ParameterListCont funDecl = new FunctionDeclaration(position(token), identifier, (Block) visit(statementBlockContext), allVariableDeclarations, declarationTypeCopy, returnTypeIdent); + funDecl.setNative(isNative); if (funDecl.isUnbound()) { FunctionWrapperFactory.generateWrapperClass(funDecl, tupleDeclarationFactory); currentBlocks.peek().addDeclaration(funDecl.getWrapperClass()); @@ -350,6 +355,7 @@ private FunctionDeclaration buildAbstractMethod(boolean functionDeclaration, @Override public ASTNode visitFunctionDeclaration(FunctionDeclarationContext ctx) { currentGeneratorReturnType.push(null); + boolean isNativeFunction = ctx.nativeAnnotation() != null; FunctionDeclaration proc = buildFunctions( ctx.type() != null, @@ -357,7 +363,8 @@ public ASTNode visitFunctionDeclaration(FunctionDeclarationContext ctx) { ctx.getStart(), ctx.type(), ctx.statementBlock(), - new Identifier(getText(ctx.Identifier()))); + new Identifier(getText(ctx.Identifier())), + isNativeFunction); // if the function does not have any return type, we have to add a return statement if (ctx.type() == null) { List list = proc.getBody().getStatements(); diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java b/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java index b9d38c7..3cb43dc 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/BasicASTNode.java @@ -56,6 +56,8 @@ public abstract class BasicASTNode implements ASTNode { private BitSet visitedFlags = new BitSet(NUMBER_OF_VISITORS); + private boolean isNativeNode = false; + /** Constructor. * * @param position @@ -128,4 +130,12 @@ public Scope getScope() { public BitSet getVisitedFlags() { return visitedFlags; } + + public boolean isNative() { + return isNativeNode; + } + + public void setNative(boolean isNative) { + isNativeNode = isNative; + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/ModuleDeclaration.java b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/ModuleDeclaration.java index c44722c..688bf0a 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/ModuleDeclaration.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/ModuleDeclaration.java @@ -98,8 +98,4 @@ public void visitChildren(BaseVisitor visitor) { } visitor.visitDoubleDispatched(block); } - - public boolean isNative() { - return !getIdentifier().getSymbol().startsWith("_"); - } } diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/expression/VariableAccess.java b/src/main/java/de/uni/bremen/monty/moco/ast/expression/VariableAccess.java index 2fc84d1..3e9d781 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/expression/VariableAccess.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/expression/VariableAccess.java @@ -49,6 +49,7 @@ public class VariableAccess extends Expression { /** Identifier of the variable to access. */ private final ResolvableIdentifier identifier; private Declaration declaration; + private boolean isClosureVar = false; /** Is this a L-value? */ private boolean lValue = false; @@ -98,4 +99,12 @@ public Declaration getDeclaration() { public void setDeclaration(Declaration declaration) { this.declaration = declaration; } + + public void setClosureVariable(boolean closure) { + isClosureVar = closure; + } + + public boolean isClosureVariable() { + return isClosureVar; + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index 9d5d96b..8e4f30b 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -196,29 +196,6 @@ private void addNativeFunction(FunctionDeclaration node, TypeDeclaration returnT codeGenerator.addNativeFunction(contextUtils.active(), returnType, llvmParameter, name); } - private boolean isNative(ASTNode node) { - if ((node instanceof FunctionDeclaration) && (((FunctionDeclaration) node).isAbstract())) { - return false; // abstract methods are never native - } - while (node.getParentNode() != null) { - node = node.getParentNode(); - if (node instanceof ModuleDeclaration) { - if (!((ModuleDeclaration) node).isNative()) { - return false; - } - } - if (node instanceof Package) { - if (((Package) node).isNativePackage()) { - return true; - } - } else if ((node instanceof ClassDeclaration) && (((ClassDeclaration) node).isFunctionWrapper())) { - return false; // function wrappers are never native, - // because they are always automatically generated monty code - } - } - return false; - } - @Override public void visit(Package node) { if (node.getParentNode() == null) { @@ -669,7 +646,7 @@ public void visit(FunctionDeclaration node) { } else { if (node.isFunction()) { openNewFunctionScope(); - if (isNative(node)) { + if (node.isNative()) { addNativeFunction(node, returnType); } else { addFunction(node, returnType); @@ -678,7 +655,7 @@ public void visit(FunctionDeclaration node) { closeFunctionContext(); } else { openNewFunctionScope(); - if (isNative(node) && !node.isInitializer()) { + if (node.isNative() && !node.isInitializer()) { addNativeFunction(node, returnType); } else { addFunction(node, returnType); diff --git a/src/main/resources/corelib/Array.monty b/src/main/resources/corelib/Array.monty index d7867cc..f763d6b 100644 --- a/src/main/resources/corelib/Array.monty +++ b/src/main/resources/corelib/Array.monty @@ -1,12 +1,12 @@ -class Array: - + initializer(Array a): +@native class Array: + + @native initializer(Array a): pass - + Int length(): + + @native Int length(): pass - + Object get(Int index): + + @native Object get(Int index): pass - + set(Int index, Object element): + + @native set(Int index, Object element): pass diff --git a/src/main/resources/corelib/Bool.monty b/src/main/resources/corelib/Bool.monty index 01a92a0..236aa92 100644 --- a/src/main/resources/corelib/Bool.monty +++ b/src/main/resources/corelib/Bool.monty @@ -1,12 +1,12 @@ -class Bool: - + initializer(Bool b): +@native class Bool: + + @native initializer(Bool b): pass - + Bool _not_(): + + @native Bool _not_(): pass - + Bool _eq_(Bool other): + + @native Bool _eq_(Bool other): pass - + Bool _neq_(Bool other): + + @native Bool _neq_(Bool other): pass diff --git a/src/main/resources/corelib/Char.monty b/src/main/resources/corelib/Char.monty index 50aa2a8..3aef9bc 100644 --- a/src/main/resources/corelib/Char.monty +++ b/src/main/resources/corelib/Char.monty @@ -1,3 +1,3 @@ -class Char: - + initializer(Char c): +@native class Char: + + @native initializer(Char c): pass diff --git a/src/main/resources/corelib/Float.monty b/src/main/resources/corelib/Float.monty index d65ab37..a4708e3 100644 --- a/src/main/resources/corelib/Float.monty +++ b/src/main/resources/corelib/Float.monty @@ -1,36 +1,36 @@ -class Float: - + initializer(Float f): +@native class Float: + + @native initializer(Float f): pass - + Float _neg_(): + + @native Float _neg_(): pass - + Float _add_(Float other): + + @native Float _add_(Float other): pass - + Float _sub_(Float other): + + @native Float _sub_(Float other): pass - + Float _mul_(Float other): + + @native Float _mul_(Float other): pass - + Float _div_(Float other): + + @native Float _div_(Float other): pass - + Bool _lt_(Float other): + + @native Bool _lt_(Float other): pass - + Bool _gt_(Float other): + + @native Bool _gt_(Float other): pass - + Bool _leq_(Float other): + + @native Bool _leq_(Float other): pass - + Bool _geq_(Float other): + + @native Bool _geq_(Float other): pass - + Bool _eq_(Float other): + + @native Bool _eq_(Float other): pass - + Bool _neq_(Float other): + + @native Bool _neq_(Float other): pass diff --git a/src/main/resources/corelib/Int.monty b/src/main/resources/corelib/Int.monty index 015b996..8a98d23 100644 --- a/src/main/resources/corelib/Int.monty +++ b/src/main/resources/corelib/Int.monty @@ -1,39 +1,39 @@ -class Int: - + initializer(Int i): +@native class Int: + + @native initializer(Int i): pass - + Int _neg_(): + + @native Int _neg_(): pass - + Int _add_(Int other): + + @native Int _add_(Int other): pass - + Int _sub_(Int other): + + @native Int _sub_(Int other): pass - + Int _mul_(Int other): + + @native Int _mul_(Int other): pass - + Int _div_(Int other): + + @native Int _div_(Int other): pass - + Int _mod_(Int other): + + @native Int _mod_(Int other): pass - + Bool _lt_(Int other): + + @native Bool _lt_(Int other): pass - + Bool _gt_(Int other): + + @native Bool _gt_(Int other): pass - + Bool _leq_(Int other): + + @native Bool _leq_(Int other): pass - + Bool _geq_(Int other): + + @native Bool _geq_(Int other): pass - + Bool _eq_(Int other): + + @native Bool _eq_(Int other): pass - + Bool _neq_(Int other): + + @native Bool _neq_(Int other): pass diff --git a/src/main/resources/corelib/Object.monty b/src/main/resources/corelib/Object.monty index 0ace051..78b5ad1 100644 --- a/src/main/resources/corelib/Object.monty +++ b/src/main/resources/corelib/Object.monty @@ -1,3 +1,3 @@ -class Object: - + Bool _eq_(Object other): +@native class Object: + + @native Bool _eq_(Object other): pass \ No newline at end of file diff --git a/src/main/resources/corelib/String.monty b/src/main/resources/corelib/String.monty index 4dcbf1f..abc2108 100644 --- a/src/main/resources/corelib/String.monty +++ b/src/main/resources/corelib/String.monty @@ -1,6 +1,6 @@ -class String: - + initializer(String s): +@native class String: + + @native initializer(String s): pass - + Bool _eq_(String other): - pass \ No newline at end of file + + @native Bool _eq_(String other): + pass diff --git a/src/main/resources/corelib/System.monty b/src/main/resources/corelib/System.monty index ce559b7..a38d4e7 100644 --- a/src/main/resources/corelib/System.monty +++ b/src/main/resources/corelib/System.monty @@ -1,35 +1,35 @@ -print(String arg1): +@native print(String arg1): pass -print(Int arg1): +@native print(Int arg1): pass -print(Float arg1): +@native print(Float arg1): pass -print(Bool arg1): +@native print(Bool arg1): pass -print(Char arg1): +@native print(Char arg1): pass -println(String arg1): +@native println(String arg1): pass -println(Int arg1): +@native println(Int arg1): pass -println(Float arg1): +@native println(Float arg1): pass -println(Bool arg1): +@native println(Bool arg1): pass -println(Char arg1): +@native println(Char arg1): pass -String read(Int num): +@native String read(Int num): pass -String readln(): +@native String readln(): pass From fe3c578e579edbbbbd7168964ba09fcc53c55e06 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 14:01:41 +0100 Subject: [PATCH 03/11] implementing detection whether variable accesses are local or closure variables --- .../uni/bremen/monty/moco/ast/ASTBuilder.java | 8 +++-- .../ast/declaration/FunctionDeclaration.java | 27 ++++++++++++++-- .../moco/codegeneration/CodeGenerator.java | 25 ++++++++++++++- .../codegeneration/types/TypeConverter.java | 32 +++++++++++++++++++ .../moco/util/GeneratorClassFactory.java | 8 +++-- .../moco/visitor/CodeGenerationVisitor.java | 12 ++++++- .../monty/moco/visitor/ResolveVisitor.java | 25 +++++++++++++++ 7 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java index 4251ffb..98ab0d5 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/ASTBuilder.java @@ -253,8 +253,12 @@ private void buildDefaultFunctions(boolean isFunction, List subParams = - allVariableDeclarations.subList(0, params.size() + defaultParameterIdx); + List subParams = new ArrayList<>(params.size() + defaultParameterIdx); + for (int i = 0; i < params.size() + defaultParameterIdx; i++) { + VariableDeclaration var = allVariableDeclarations.get(i); + subParams.add(new VariableDeclaration(var.getPosition(), var.getIdentifier(), var.getTypeIdentifier(), + var.getDeclarationType())); + } Expression expression = new FunctionCall(position(token), new ResolvableIdentifier(identifier.getSymbol()), l); diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java index 1568247..1573c9b 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java @@ -46,8 +46,7 @@ import de.uni.bremen.monty.moco.ast.statement.ReturnStatement; import de.uni.bremen.monty.moco.visitor.BaseVisitor; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** A FunctionDeclaration represents the declaration of a function in the AST. *

@@ -80,6 +79,8 @@ public enum DeclarationType { private boolean returnTypeMustBeInferred = false; + private Map closureVars = new HashMap<>(); + /** Constructor. * * @param position @@ -414,4 +415,26 @@ public String toString() { } return String.format("%s(%s)", getIdentifier().toString(), params); } + + public VariableDeclaration addClosureVariable(VariableDeclaration var) { + if (!closureVars.containsKey(var)) { + VariableDeclaration attr = + new VariableDeclaration(var.getPosition(), var.getIdentifier(), var.getTypeIdentifier(), + VariableDeclaration.DeclarationType.ATTRIBUTE); + attr.setParentNode(var.getParentNode()); + attr.setScope(var.getScope()); + attr.setType(var.getType()); + closureVars.put(var, attr); + return attr; + } + return closureVars.get(var); + } + + public boolean isClosure() { + return closureVars.size() > 0; + } + + public Collection getClosureVariables() { + return closureVars.values(); + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java index a5280a2..d62f0a0 100644 --- a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java +++ b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java @@ -398,6 +398,29 @@ public LLVMIdentifier accessMember(CodeContext c, LLVMIdentifier accessClosureContextMember(CodeContext c, ClassDeclaration closureClass, + VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType) { + + LLVMType contextType = LLVMTypeFactory.struct(nameMangler.mangleClass(closureClass) + "_closure_context"); + + LLVMIdentifier self = resolveLocalVarName("self", closureClass, false); + LLVMIdentifier contextLlvmIdentifier = + accessMember( + c, + (LLVMIdentifier>) self, + closureClass.getLastAttributeIndex(), + contextType, + false); + LLVMIdentifier result = + accessMember( + c, + llvmIdentifierFactory.pointerTo(contextLlvmIdentifier), + varDecl.getAttributeIndex(), + variableType, + !varAccess.getLValue()); + return result; + } + public LLVMIdentifier accessContextMember(CodeContext c, ClassDeclaration generatorClass, VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType) { @@ -410,7 +433,7 @@ public LLVMIdentifier accessContextMember(CodeContext c, ClassDeclarat (LLVMIdentifier>) self, generatorClass.getLastAttributeIndex(), contextType, - false); // TODO: richtig? + false); LLVMIdentifier result = accessMember( c, diff --git a/src/main/java/de/uni/bremen/monty/moco/codegeneration/types/TypeConverter.java b/src/main/java/de/uni/bremen/monty/moco/codegeneration/types/TypeConverter.java index d37007a..3210728 100644 --- a/src/main/java/de/uni/bremen/monty/moco/codegeneration/types/TypeConverter.java +++ b/src/main/java/de/uni/bremen/monty/moco/codegeneration/types/TypeConverter.java @@ -159,6 +159,7 @@ private void addClass(ClassDeclaration classDecl) { } } + // generator functions if (classDecl.isGenerator()) { // create the structure LLVMType contextStruct = addGeneratorContext(classDecl, mangledNodeName); @@ -166,6 +167,16 @@ private void addClass(ClassDeclaration classDecl) { llvmClassTypeDeclarations.add(contextStruct); } + // closures + FunctionDeclaration wrappedFunction = classDecl.getWrappedFunction(); + if ((wrappedFunction != null) && (wrappedFunction.isClosure())) { + // create the structure + LLVMType closureContextStruct = + addClosureContext(classDecl, wrappedFunction.getClosureVariables(), mangledNodeName); + // add context struct as the last index to the class declaration + llvmClassTypeDeclarations.add(closureContextStruct); + } + for (FunctionDeclaration function : classDecl.getVirtualMethodTable()) { if (!function.isInitializer()) { LLVMType signature = mapToLLVMType(function); @@ -189,6 +200,27 @@ private void addClass(ClassDeclaration classDecl) { llvmIdentifierFactory.constant(llvmVMTType, llvmVMTDataInitializer)); } + protected LLVMType addClosureContext(ClassDeclaration classDecl, Collection variables, + String mangledNodeName) { + // declaration types inside the struct + List llvmStructTypeDeclarations = new ArrayList<>(); + + // add state i8 pointer to context structure + llvmStructTypeDeclarations.add(LLVMTypeFactory.pointer(LLVMTypeFactory.int8())); + // add the variable declarations inside the generator to the struct + int attributeIndex = 1; + for (VariableDeclaration decl : variables) { + llvmStructTypeDeclarations.add(mapToLLVMType(decl.getType())); + decl.setAttributeIndex(attributeIndex); + attributeIndex += 1; + } + + LLVMStructType llvmStructType = struct(mangledNodeName + "_closure_context"); + constantContext.type(llvmStructType, llvmStructTypeDeclarations); + + return llvmStructType; + } + protected LLVMType addGeneratorContext(ClassDeclaration classDecl, String mangledNodeName) { GeneratorFunctionDeclaration inFunction = null; for (FunctionDeclaration fun : classDecl.getMethods()) { diff --git a/src/main/java/de/uni/bremen/monty/moco/util/GeneratorClassFactory.java b/src/main/java/de/uni/bremen/monty/moco/util/GeneratorClassFactory.java index 2530203..7d94877 100644 --- a/src/main/java/de/uni/bremen/monty/moco/util/GeneratorClassFactory.java +++ b/src/main/java/de/uni/bremen/monty/moco/util/GeneratorClassFactory.java @@ -180,13 +180,17 @@ private static void addInitializers(Position pos, Block classBody, List pars = new ArrayList<>(); - pars.addAll(params); + for (VariableDeclaration par : params) { + pars.add(new VariableDeclaration(par.getPosition(), par.getIdentifier(), par.getTypeIdentifier(), + par.getDeclarationType())); + } List arguments = new ArrayList<>(i + paramAccesses.size()); arguments.addAll(paramAccesses); for (int j = 0; j < i; j++) { VariableDeclaration var = defaultParams.get(j); - pars.add(var); + pars.add(new VariableDeclaration(var.getPosition(), var.getIdentifier(), var.getTypeIdentifier(), + var.getDeclarationType())); arguments.add(new VariableAccess(var.getPosition(), ResolvableIdentifier.convert(var.getIdentifier()))); } for (int j = i; j < defaultParams.size(); j++) { diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index 8e4f30b..ae26bdb 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -304,7 +304,17 @@ public void visit(VariableAccess node) { varDeclaration, node, type); - + } else if (node.isClosureVariable()) { + System.err.println(node.getIdentifier() + " is closure var"); + ClassDeclaration containingClass = + ((FunctionDeclaration) node.getParentNodeByType(FunctionDeclaration.class)).getWrapperClass(); + llvmIdentifier = + codeGenerator.accessClosureContextMember( + contextUtils.active(), + containingClass, + varDeclaration, + node, + type); } else if (varDeclaration.isAttribute()) { LLVMIdentifier leftIdentifier = stack.pop(); llvmIdentifier = diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java index 5e0020d..c105178 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java @@ -165,6 +165,9 @@ public void visit(VariableAccess node) { if (declaration instanceof VariableDeclaration) { VariableDeclaration variable = (VariableDeclaration) declaration; node.setDeclaration(variable); + + resolveClosureVariables(node, variable); + visitDoubleDispatched(variable); node.setType(variable.getType()); if (!(scope instanceof ClassScope) && findEnclosingClass(node) == CoreClasses.voidType()) { @@ -176,6 +179,28 @@ public void visit(VariableAccess node) { } } + protected void resolveClosureVariables(VariableAccess access, VariableDeclaration declaration) { + // everything that is not an attribute + if (declaration.getDeclarationType() != VariableDeclaration.DeclarationType.ATTRIBUTE) { + Declaration declParent = (Declaration) declaration.getParentNodeByType(Declaration.class); + Declaration accessParent = (Declaration) access.getParentNodeByType(Declaration.class); + + if (declParent != accessParent) { + if (!(declParent instanceof ModuleDeclaration)) { + FunctionDeclaration fn = ((FunctionDeclaration) accessParent); + // VariableDeclaration attr = fn.registerClosureVariable(declaration); + // access.setDeclaration(attr); + VariableDeclaration closureVarDecl = fn.addClosureVariable(declaration); + access.setClosureVariable(true); + access.setDeclaration(closureVarDecl); + + System.err.print("### " + declaration.getIdentifier() + " "); + System.err.print(declaration.getDeclarationType() + ":\n"); + } + } + } + } + /** If a variable access is casted into a function, we can do overload resolution just like for function calls e.g. * "Int -> Int myFunction := square as (Int -> Int)" will look for the implementation of square with the correct * signature. From abe7d99f33b33ffa60bdda93147e0c43e49058d0 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 15:33:06 +0100 Subject: [PATCH 04/11] passing context reference to function closures (without assigning values properly, yet) --- .../monty/moco/codegeneration/CodeGenerator.java | 2 +- .../moco/visitor/CodeGenerationVisitor.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java index d62f0a0..c6aafc5 100644 --- a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java +++ b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java @@ -403,7 +403,7 @@ public LLVMIdentifier accessClosureContextMember(CodeContext c, ClassD LLVMType contextType = LLVMTypeFactory.struct(nameMangler.mangleClass(closureClass) + "_closure_context"); - LLVMIdentifier self = resolveLocalVarName("self", closureClass, false); + LLVMIdentifier self = resolveLocalVarName("..ctx..", closureClass, false); LLVMIdentifier contextLlvmIdentifier = accessMember( c, diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index ae26bdb..1a68360 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -124,6 +124,13 @@ private List> buildLLVMParameter(FunctionDecl llvmParameter.add(selfReference); } + if (node.isClosure()) { + ClassDeclaration typeDeclaration = node.getWrapperClass(); + LLVMType contextType = codeGenerator.mapToLLVMType(typeDeclaration); + LLVMIdentifier ctxReference = llvmIdentifierFactory.newLocal("..ctx..", contextType, false); + llvmParameter.add(ctxReference); + } + for (VariableDeclaration param : node.getParameters()) { LLVMType llvmType = codeGenerator.mapToLLVMType(param.getType()); llvmType = llvmType instanceof LLVMStructType ? pointer(llvmType) : llvmType; @@ -600,6 +607,15 @@ Arrays.> asList(selfReference), } } + if (declaration.isClosure()) { + // the 'self' of a function wrapper implementation is passed as a context to the actual function + TypeDeclaration ctxType = + useClassVariationIfApplicable((ClassDeclaration) node.getParentNodeByType(ClassDeclaration.class)); + LLVMIdentifier> ctx = codeGenerator.resolveLocalVarName("self", ctxType, false); + expectedParameters.add(0, ctxType); + arguments.add(0, ctx); + } + if (declaration.isMethod() && !declaration.isInitializer()) { if (declaration.isFunction()) { stack.push((LLVMIdentifier) codeGenerator.callMethod( From 89681d8424f931872fd9d6b06216f971d32ab93b Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 17:05:42 +0100 Subject: [PATCH 05/11] accessing closure contexts correctly now - there is still a need to assign the values of the closure variables when a closure is created --- .../moco/visitor/CodeGenerationVisitor.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index 1a68360..659889b 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -609,9 +609,20 @@ Arrays.> asList(selfReference), if (declaration.isClosure()) { // the 'self' of a function wrapper implementation is passed as a context to the actual function - TypeDeclaration ctxType = - useClassVariationIfApplicable((ClassDeclaration) node.getParentNodeByType(ClassDeclaration.class)); - LLVMIdentifier> ctx = codeGenerator.resolveLocalVarName("self", ctxType, false); + TypeDeclaration ctxType = useClassVariationIfApplicable(declaration.getWrapperClass()); + + LLVMIdentifier> ctx; + // if we're inside the wrapper class' _apply_ method, use "self" as the function's context + if (declaration.getWrapperClass() == node.getParentNodeByType(ClassDeclaration.class)) { + ctx = codeGenerator.resolveLocalVarName("self", ctxType, false); + } else { + // if not, use the respective wrapper object + ctx = + codeGenerator.resolveLocalVarName( + nameMangler.mangleVariable(declaration.getWrapperFunctionObjectDeclaration()), + ctxType, + true); + } expectedParameters.add(0, ctxType); arguments.add(0, ctx); } From 775d26384cc320ea44a744e2b62f2a0192fb7bac Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 18:09:38 +0100 Subject: [PATCH 06/11] implementing a working closures, adding a test case --- .../ast/declaration/FunctionDeclaration.java | 8 +++++ .../monty/moco/ast/statement/Assignment.java | 14 ++++++++ .../moco/codegeneration/CodeGenerator.java | 19 ++++++----- .../moco/util/FunctionWrapperFactory.java | 1 + .../moco/visitor/CodeGenerationVisitor.java | 34 ++++++++++++++++++- .../monty/moco/visitor/ResolveVisitor.java | 3 -- .../functionObjects/closure1.monty | 29 ++++++++++++++++ .../functionObjects/closure1.output | 9 +++++ 8 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/testPrograms/functionObjects/closure1.monty create mode 100644 src/test/resources/testPrograms/functionObjects/closure1.output diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java index 1573c9b..03ea1fa 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java @@ -434,7 +434,15 @@ public boolean isClosure() { return closureVars.size() > 0; } + public VariableDeclaration getClosureVariable(VariableDeclaration var) { + return closureVars.get(var); + } + public Collection getClosureVariables() { return closureVars.values(); } + + public Collection getClosureVariableOriginalDeclarations() { + return closureVars.keySet(); + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/statement/Assignment.java b/src/main/java/de/uni/bremen/monty/moco/ast/statement/Assignment.java index bd6b4bb..cc43977 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/statement/Assignment.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/statement/Assignment.java @@ -39,6 +39,7 @@ package de.uni.bremen.monty.moco.ast.statement; import de.uni.bremen.monty.moco.ast.*; +import de.uni.bremen.monty.moco.ast.declaration.FunctionDeclaration; import de.uni.bremen.monty.moco.ast.expression.Expression; import de.uni.bremen.monty.moco.visitor.BaseVisitor; @@ -50,6 +51,8 @@ public class Assignment extends BasicASTNode implements Statement { /** The right side. */ private final Expression right; + private FunctionDeclaration correspondingFunWrapper = null; + /** Constructor. * * @param position @@ -91,4 +94,15 @@ public void visitChildren(BaseVisitor visitor) { visitor.visitDoubleDispatched(right); } + public boolean belongsToFunctionWrapper() { + return correspondingFunWrapper != null; + } + + public void setCorrespondingFunctionWrapper(FunctionDeclaration fun) { + correspondingFunWrapper = fun; + } + + public FunctionDeclaration getCorrespondingFunctionWrapper() { + return correspondingFunWrapper; + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java index c6aafc5..a381f65 100644 --- a/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java +++ b/src/main/java/de/uni/bremen/monty/moco/codegeneration/CodeGenerator.java @@ -399,18 +399,12 @@ public LLVMIdentifier accessMember(CodeContext c, LLVMIdentifier accessClosureContextMember(CodeContext c, ClassDeclaration closureClass, - VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType) { + VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType, + LLVMIdentifier> context) { LLVMType contextType = LLVMTypeFactory.struct(nameMangler.mangleClass(closureClass) + "_closure_context"); - - LLVMIdentifier self = resolveLocalVarName("..ctx..", closureClass, false); LLVMIdentifier contextLlvmIdentifier = - accessMember( - c, - (LLVMIdentifier>) self, - closureClass.getLastAttributeIndex(), - contextType, - false); + accessMember(c, context, closureClass.getLastAttributeIndex(), contextType, false); LLVMIdentifier result = accessMember( c, @@ -421,6 +415,13 @@ public LLVMIdentifier accessClosureContextMember(CodeContext c, ClassD return result; } + public LLVMIdentifier accessClosureContextMember(CodeContext c, ClassDeclaration closureClass, + VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType) { + + LLVMIdentifier> self = resolveLocalVarName("..ctx..", closureClass, false); + return accessClosureContextMember(c, closureClass, varDecl, varAccess, variableType, self); + } + public LLVMIdentifier accessContextMember(CodeContext c, ClassDeclaration generatorClass, VariableDeclaration varDecl, VariableAccess varAccess, TypeDeclaration variableType) { diff --git a/src/main/java/de/uni/bremen/monty/moco/util/FunctionWrapperFactory.java b/src/main/java/de/uni/bremen/monty/moco/util/FunctionWrapperFactory.java index 4770b78..c9827a8 100644 --- a/src/main/java/de/uni/bremen/monty/moco/util/FunctionWrapperFactory.java +++ b/src/main/java/de/uni/bremen/monty/moco/util/FunctionWrapperFactory.java @@ -87,6 +87,7 @@ public static void generateWrapperClass(FunctionDeclaration function, TupleDecla function.setWrapperClass(wrapperClass); function.setWrapperFunctionObjectDeclaration(functionObjectDeclaration); function.setWrapperFunctionAssignment(functionObjectAssignment); + functionObjectAssignment.setCorrespondingFunctionWrapper(function); } protected static FunctionDeclaration createApplyMethod(FunctionDeclaration function, diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index 659889b..0b933ea 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -235,6 +235,39 @@ public void visit(Assignment node) { LLVMIdentifier source = stack.pop(); LLVMIdentifier target = stack.pop(); codeGenerator.assign(contextUtils.active(), target, source); + + if (node.belongsToFunctionWrapper()) { + initializeClosureWrapper(node, source); + } + } + + protected void initializeClosureWrapper(Assignment node, LLVMIdentifier source) { + Position pos = node.getPosition(); + FunctionDeclaration function = node.getCorrespondingFunctionWrapper(); + if (function.isClosure()) { + ClassDeclaration functionWrapper = function.getWrapperClass(); + for (VariableDeclaration var : function.getClosureVariableOriginalDeclarations()) { + VariableDeclaration localVar = function.getClosureVariable(var); + VariableAccess lvalue = new VariableAccess(pos, ResolvableIdentifier.convert(var.getIdentifier())); + lvalue.setLValue(); + LLVMIdentifier closureTarget = + codeGenerator.accessClosureContextMember( + contextUtils.active(), + functionWrapper, + localVar, + lvalue, + var.getType(), + (LLVMIdentifier>) source); + + VariableAccess varAccess = new VariableAccess(pos, ResolvableIdentifier.convert(var.getIdentifier())); + varAccess.setType(var.getType()); + varAccess.setDeclaration(var); + varAccess.setParentNode(node.getParentNode()); + visit(varAccess); + LLVMIdentifier closureSource = stack.pop(); + codeGenerator.assign(contextUtils.active(), closureTarget, closureSource); + } + } } @Override @@ -312,7 +345,6 @@ public void visit(VariableAccess node) { node, type); } else if (node.isClosureVariable()) { - System.err.println(node.getIdentifier() + " is closure var"); ClassDeclaration containingClass = ((FunctionDeclaration) node.getParentNodeByType(FunctionDeclaration.class)).getWrapperClass(); llvmIdentifier = diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java index c105178..792801d 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java @@ -193,9 +193,6 @@ protected void resolveClosureVariables(VariableAccess access, VariableDeclaratio VariableDeclaration closureVarDecl = fn.addClosureVariable(declaration); access.setClosureVariable(true); access.setDeclaration(closureVarDecl); - - System.err.print("### " + declaration.getIdentifier() + " "); - System.err.print(declaration.getDeclarationType() + ":\n"); } } } diff --git a/src/test/resources/testPrograms/functionObjects/closure1.monty b/src/test/resources/testPrograms/functionObjects/closure1.monty new file mode 100644 index 0000000..facb2fb --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closure1.monty @@ -0,0 +1,29 @@ +// Testing: Closures, functions that use variables from enclosing scopes +// +// Expected Output: --- 20 25 30 --- 16 20 24 --- + +println("-------------------") + +// a function that returns a closure +(Int -> Int) foo(Int x): + Int innerfoo(Int y): + return x*y + return innerfoo + +// ------------------------------- + +// use the closure +(Int -> Int) bar := foo(5) +println(bar(4)) // 20 +println(bar(5)) // 25 +println(bar(6)) // 30 + +println("-------------------") + +// use the closure +(Int -> Int) baz := foo(4) +println(baz(4)) // 16 +println(baz(5)) // 20 +println(baz(6)) // 24 + +println("-------------------") \ No newline at end of file diff --git a/src/test/resources/testPrograms/functionObjects/closure1.output b/src/test/resources/testPrograms/functionObjects/closure1.output new file mode 100644 index 0000000..1e14908 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closure1.output @@ -0,0 +1,9 @@ +------------------- +20 +25 +30 +------------------- +16 +20 +24 +------------------- From 99f21e94e8338d17d39e7b03cd5dbace8afe3b16 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 18:13:14 +0100 Subject: [PATCH 07/11] adding a further test case that also tests anonymous closures --- .../testPrograms/functionObjects/closure2.monty | 13 +++++++++++++ .../testPrograms/functionObjects/closure2.output | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 src/test/resources/testPrograms/functionObjects/closure2.monty create mode 100644 src/test/resources/testPrograms/functionObjects/closure2.output diff --git a/src/test/resources/testPrograms/functionObjects/closure2.monty b/src/test/resources/testPrograms/functionObjects/closure2.monty new file mode 100644 index 0000000..0e09226 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closure2.monty @@ -0,0 +1,13 @@ +// Testing: Do closures work with anonymous functions? +// +// In this case an Int list is defined and a map function, which takes a list and a function +// +// Expected Output: 6 12 18 + +(Int -> Int) foo(Int x): + return (Int y) -> x*y + +Int -> Int bar := foo(3) +println(bar(2)) // 6 +println(bar(4)) // 12 +println(bar(6)) // 18 \ No newline at end of file diff --git a/src/test/resources/testPrograms/functionObjects/closure2.output b/src/test/resources/testPrograms/functionObjects/closure2.output new file mode 100644 index 0000000..3f56dbb --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closure2.output @@ -0,0 +1,3 @@ +6 +12 +18 From 81cfe216b28927c0389ba064a9cd45eaba59e415 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Sat, 31 Oct 2015 20:35:42 +0100 Subject: [PATCH 08/11] making the 'self' keyword available inside closures --- .../ast/declaration/FunctionDeclaration.java | 14 ++++++ .../moco/ast/expression/SelfExpression.java | 9 ++++ .../moco/visitor/CodeGenerationVisitor.java | 35 +++++++++++--- .../monty/moco/visitor/ResolveVisitor.java | 15 ++++-- .../functionObjects/closureSelf.monty | 46 +++++++++++++++++++ .../functionObjects/closureSelf.output | 6 +++ 6 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/testPrograms/functionObjects/closureSelf.monty create mode 100644 src/test/resources/testPrograms/functionObjects/closureSelf.output diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java index 03ea1fa..265a78a 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java @@ -402,6 +402,7 @@ protected void splitReturnStatement(boolean tupleInsteadOfVoid) { } else if (oldCall instanceof WrappedFunctionCall) { getBody().addStatement((WrappedFunctionCall) oldCall); } else { + System.out.println(oldCall); throw new RuntimeException("invalid AST!"); } getBody().addStatement(newRet); @@ -416,7 +417,19 @@ public String toString() { return String.format("%s(%s)", getIdentifier().toString(), params); } + private VariableDeclaration checkSelfVariable(VariableDeclaration var) { + if (var.getIdentifier().getSymbol().equals("self")) { + for (VariableDeclaration key : closureVars.keySet()) { + if (key.getIdentifier().getSymbol().equals("self")) { + return key; + } + } + } + return var; + } + public VariableDeclaration addClosureVariable(VariableDeclaration var) { + var = checkSelfVariable(var); if (!closureVars.containsKey(var)) { VariableDeclaration attr = new VariableDeclaration(var.getPosition(), var.getIdentifier(), var.getTypeIdentifier(), @@ -435,6 +448,7 @@ public boolean isClosure() { } public VariableDeclaration getClosureVariable(VariableDeclaration var) { + var = checkSelfVariable(var); return closureVars.get(var); } diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/expression/SelfExpression.java b/src/main/java/de/uni/bremen/monty/moco/ast/expression/SelfExpression.java index 95a1885..1253b4b 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/expression/SelfExpression.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/expression/SelfExpression.java @@ -42,6 +42,7 @@ import de.uni.bremen.monty.moco.visitor.BaseVisitor; public class SelfExpression extends Expression { + private boolean inClosure = false; public SelfExpression(Position position) { super(position); @@ -55,4 +56,12 @@ public void visit(BaseVisitor visitor) { @Override public void visitChildren(BaseVisitor visitor) { } + + public void setInClosure() { + inClosure = true; + } + + public boolean isInClosure() { + return inClosure; + } } diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java index 0b933ea..76a6d52 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/CodeGenerationVisitor.java @@ -259,11 +259,19 @@ protected void initializeClosureWrapper(Assignment node, LLVMIdentifier sourc var.getType(), (LLVMIdentifier>) source); - VariableAccess varAccess = new VariableAccess(pos, ResolvableIdentifier.convert(var.getIdentifier())); - varAccess.setType(var.getType()); - varAccess.setDeclaration(var); - varAccess.setParentNode(node.getParentNode()); - visit(varAccess); + if (var.getIdentifier().getSymbol().equals("self")) { + SelfExpression self = new SelfExpression(pos); + self.setType(var.getType()); + self.setParentNode(node.getParentNode()); + visit(self); + } else { + VariableAccess varAccess = + new VariableAccess(pos, ResolvableIdentifier.convert(var.getIdentifier())); + varAccess.setType(var.getType()); + varAccess.setDeclaration(var); + varAccess.setParentNode(node.getParentNode()); + visit(varAccess); + } LLVMIdentifier closureSource = stack.pop(); codeGenerator.assign(contextUtils.active(), closureTarget, closureSource); } @@ -390,7 +398,22 @@ private TypeDeclaration useClassVariationIfApplicable(TypeDeclaration type) { @Override public void visit(SelfExpression node) { TypeDeclaration type = useClassVariationIfApplicable(node.getType()); - stack.push(codeGenerator.resolveLocalVarName("self", type, false)); + // if 'self' is used inside a closure, we can not use the usual 'self' + if (node.isInClosure()) { + FunctionDeclaration funDecl = (FunctionDeclaration) node.getParentNodeByType(FunctionDeclaration.class); + ClassDeclaration containingClass = funDecl.getWrapperClass(); + VariableAccess lvalue = new VariableAccess(new Position(), new ResolvableIdentifier("self")); + + stack.push(codeGenerator.accessClosureContextMember( + contextUtils.active(), + containingClass, + funDecl.getClosureVariable(new VariableDeclaration(new Position(), new Identifier("self"), type, + VariableDeclaration.DeclarationType.VARIABLE)), + lvalue, + type)); + } else { + stack.push(codeGenerator.resolveLocalVarName("self", type, false)); + } } @SuppressWarnings("unchecked") diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java index 792801d..ce892b7 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java @@ -180,7 +180,7 @@ public void visit(VariableAccess node) { } protected void resolveClosureVariables(VariableAccess access, VariableDeclaration declaration) { - // everything that is not an attribute + // everything that is not an attribute (since attributes are always defined outside the current scope) if (declaration.getDeclarationType() != VariableDeclaration.DeclarationType.ATTRIBUTE) { Declaration declParent = (Declaration) declaration.getParentNodeByType(Declaration.class); Declaration accessParent = (Declaration) access.getParentNodeByType(Declaration.class); @@ -188,8 +188,6 @@ protected void resolveClosureVariables(VariableAccess access, VariableDeclaratio if (declParent != accessParent) { if (!(declParent instanceof ModuleDeclaration)) { FunctionDeclaration fn = ((FunctionDeclaration) accessParent); - // VariableDeclaration attr = fn.registerClosureVariable(declaration); - // access.setDeclaration(attr); VariableDeclaration closureVarDecl = fn.addClosureVariable(declaration); access.setClosureVariable(true); access.setDeclaration(closureVarDecl); @@ -198,6 +196,16 @@ protected void resolveClosureVariables(VariableAccess access, VariableDeclaratio } } + protected void resolveClosureVariablesForSelf(SelfExpression self) { + FunctionDeclaration selfParent = (FunctionDeclaration) self.getParentNodeByType(FunctionDeclaration.class); + // self expressions in unbound functions must be closure variables + if (selfParent != null && selfParent.isUnbound()) { + self.setInClosure(); + selfParent.addClosureVariable(new VariableDeclaration(self.getPosition(), new Identifier("self"), + self.getType(), VariableDeclaration.DeclarationType.VARIABLE)); + } + } + /** If a variable access is casted into a function, we can do overload resolution just like for function calls e.g. * "Int -> Int myFunction := square as (Int -> Int)" will look for the implementation of square with the correct * signature. @@ -238,6 +246,7 @@ protected Declaration overloadResolutionForVariableAccess(VariableAccess node) { public void visit(SelfExpression node) { super.visit(node); node.setType(findEnclosingClass(node)); + resolveClosureVariablesForSelf(node); } /** {@inheritDoc} */ diff --git a/src/test/resources/testPrograms/functionObjects/closureSelf.monty b/src/test/resources/testPrograms/functionObjects/closureSelf.monty new file mode 100644 index 0000000..154f7fa --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closureSelf.monty @@ -0,0 +1,46 @@ +// Testing: whether 'self' is available inside closures +// +// Expected Output: +// 7 +// World +// Hello +// Hello +// World +// World + + +class Foo: + Int x := 7 + + (() -> Int) bar(): + return () -> self.x + +Foo f := Foo() +(() -> Int) fn := f.bar() +println(fn()) + + +class Container: + String message + + initializer(String message): + self.message := message + + (() -> String) getMessageGetter(): + return () -> self.getMessage() + + String getMessage(): + return self.message + + +Container h := Container("Hello") +Container w := Container("World") + +() -> String fh := h.getMessageGetter() +() -> String fw := w.getMessageGetter() + +println(fw()) +println(fh()) +println(fh()) +println(fw()) +println(fw()) \ No newline at end of file diff --git a/src/test/resources/testPrograms/functionObjects/closureSelf.output b/src/test/resources/testPrograms/functionObjects/closureSelf.output new file mode 100644 index 0000000..9107348 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/closureSelf.output @@ -0,0 +1,6 @@ +7 +World +Hello +Hello +World +World From e34b39cde427912478a0f4fe0fa379a3492540a3 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Mon, 2 Nov 2015 15:20:25 +0100 Subject: [PATCH 09/11] fixing grammar ambiguity regarding anonymous function expressions + adding a test case --- .../antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 | 4 ++-- .../functionObjects/anonFunSyntaxAmbiguity.monty | 13 +++++++++++++ .../functionObjects/anonFunSyntaxAmbiguity.output | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.monty create mode 100644 src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.output diff --git a/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 b/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 index d15bfc4..dac3e3a 100644 --- a/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 +++ b/src/main/antlr4/de/uni/bremen/monty/moco/antlr/Monty.g4 @@ -198,9 +198,9 @@ expressionList ; expression - : functionCall - | primary + : primary | functionExpression + | functionCall | ifExprThen=expression 'if' ifExpCondition=expression 'else' ifExprElse=expression | left=expression accessOperator right=expression | (plusMinusOperator | notOperator) singleExpression=expression diff --git a/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.monty b/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.monty new file mode 100644 index 0000000..10cf168 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.monty @@ -0,0 +1,13 @@ +// Testing: Anonymous functions syntax ambiguity (see description below...) +// +// Expected Output: 5 + +class Foo: + Int x := 5 + +// the following is meant to be an anonymous function +() -> Int getFive := () -> Foo().x +// instead of the instantiation of ( ()-> Foo ) and accessing its x +// this case will never happen, the grammar should also avoid this + +println(getFive()) \ No newline at end of file diff --git a/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.output b/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.output new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/anonFunSyntaxAmbiguity.output @@ -0,0 +1 @@ +5 From f71f8b8d0ac30fa63efe66b76276b37ef66b7621 Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Mon, 2 Nov 2015 16:50:48 +0100 Subject: [PATCH 10/11] fixing an anonymous function bug - there was a problem with method calls as anonymous function bodies - further, attributes of function types could not be called as functions (fixed now) - adding a test case --- .../uni/bremen/monty/moco/ast/ClassScope.java | 8 ++- .../ast/declaration/FunctionDeclaration.java | 9 ++-- .../functionObjects/anonFunClass.monty | 51 +++++++++++++++++++ .../functionObjects/anonFunClass.output | 2 + 4 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/testPrograms/functionObjects/anonFunClass.monty create mode 100644 src/test/resources/testPrograms/functionObjects/anonFunClass.output diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/ClassScope.java b/src/main/java/de/uni/bremen/monty/moco/ast/ClassScope.java index 4986915..330107b 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/ClassScope.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/ClassScope.java @@ -109,12 +109,16 @@ protected Declaration resolveMember(ResolvableIdentifier identifier) { * @param identifier * the identifier to resolve * @return the list of function declarations */ - protected List resolveFunctionMember(ResolvableIdentifier identifier) { - List result = new ArrayList(); + protected List resolveFunctionMember(ResolvableIdentifier identifier) { + List result = new ArrayList<>(); if (functions.containsKey(identifier)) { result.addAll(functions.get(identifier)); } + if (members.containsKey(identifier)) { + result.add(members.get(identifier)); + } + for (ClassScope scope : parentClassesScopes) { result.addAll(scope.resolveFunctionMember(identifier)); } diff --git a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java index 265a78a..6474a64 100644 --- a/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java +++ b/src/main/java/de/uni/bremen/monty/moco/ast/declaration/FunctionDeclaration.java @@ -44,6 +44,7 @@ import de.uni.bremen.monty.moco.ast.expression.WrappedFunctionCall; import de.uni.bremen.monty.moco.ast.statement.Assignment; import de.uni.bremen.monty.moco.ast.statement.ReturnStatement; +import de.uni.bremen.monty.moco.ast.statement.Statement; import de.uni.bremen.monty.moco.visitor.BaseVisitor; import java.util.*; @@ -397,12 +398,10 @@ protected void splitReturnStatement(boolean tupleInsteadOfVoid) { // add the new ones Expression oldCall = oldRet.getParameter(); oldCall.setParentNode(getBody()); - if (oldCall instanceof FunctionCall) { - getBody().addStatement((FunctionCall) oldCall); - } else if (oldCall instanceof WrappedFunctionCall) { - getBody().addStatement((WrappedFunctionCall) oldCall); + + if (oldCall instanceof Statement) { + getBody().addStatement((Statement) oldCall); } else { - System.out.println(oldCall); throw new RuntimeException("invalid AST!"); } getBody().addStatement(newRet); diff --git a/src/test/resources/testPrograms/functionObjects/anonFunClass.monty b/src/test/resources/testPrograms/functionObjects/anonFunClass.monty new file mode 100644 index 0000000..64639cc --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/anonFunClass.monty @@ -0,0 +1,51 @@ +// Testing: Anonymous functions in classes, in combination with methods +// +// Expected Output: + // 'Ok' clicked, performing the next action + // 'Cancel' clicked, closing the Dialog + + + +// ---------------------------------------------------------------------------- + +class Button: + String label + String -> () onclick + + initializer(String label, (String -> ()) onclick): + self.onclick := onclick + self.label := label + + click(): + self.onclick(self.label) + +// ---------------------------------------------------------------------------- + +class Dialog: + Button ok + Button cancel + + initializer(): + // pass + String -> () okAction := (String s) -> self.nextAction(s) + + self.ok := Button("Ok", okAction) + self.cancel := Button("Cancel", (String s) -> self.closeDialog(s)) + + closeDialog(String s): + print("'") + print(s) + print("' clicked, closing the Dialog\n") + // return -1 + + nextAction(String s): + print("'") + print(s) + print("' clicked, performing the next action\n") + +// ---------------------------------------------------------------------------- + +Dialog dlg := Dialog() +dlg.ok.click() +dlg.cancel.click() + diff --git a/src/test/resources/testPrograms/functionObjects/anonFunClass.output b/src/test/resources/testPrograms/functionObjects/anonFunClass.output new file mode 100644 index 0000000..6bf54f2 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/anonFunClass.output @@ -0,0 +1,2 @@ +'Ok' clicked, performing the next action +'Cancel' clicked, closing the Dialog From 7c52f0c0a0f4a5bb5188597f2af9e2e6a8ca4d6c Mon Sep 17 00:00:00 2001 From: Carsten Pfeffer Date: Fri, 20 Nov 2015 14:01:42 +0100 Subject: [PATCH 11/11] Implementing support for function objects that only take one tuple paramter --- .../bremen/monty/moco/visitor/ResolveVisitor.java | 11 +++++++++++ .../functionObjects/tuple1function.monty | 14 ++++++++++++++ .../functionObjects/tuple1function.output | 2 ++ 3 files changed, 27 insertions(+) create mode 100644 src/test/resources/testPrograms/functionObjects/tuple1function.monty create mode 100644 src/test/resources/testPrograms/functionObjects/tuple1function.output diff --git a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java index ce892b7..882c847 100644 --- a/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java +++ b/src/main/java/de/uni/bremen/monty/moco/visitor/ResolveVisitor.java @@ -431,6 +431,17 @@ public void visit(FunctionCall node) { } Declaration callableDeclaration = overloadResolution(argTypes, scope.resolveFunction(node.getIdentifier())); + // if the parameter is just one tuple, we might have to unpack it + if ((callableDeclaration == null) && (argTypes.size() == 1) + && (argTypes.get(0).getIdentifier().getSymbol().startsWith("Tuple"))) { + List argCls = ((ClassDeclarationVariation) argTypes.get(0)).getConcreteGenericTypes(); + argTypes = new ArrayList<>(argCls.size()); + for (ClassDeclaration cls : argCls) { + argTypes.add(cls); + } + callableDeclaration = overloadResolution(argTypes, scope.resolveFunction(node.getIdentifier())); + } + if (callableDeclaration instanceof FunctionDeclaration) { FunctionDeclaration function = (FunctionDeclaration) callableDeclaration; node.setDeclaration(function); diff --git a/src/test/resources/testPrograms/functionObjects/tuple1function.monty b/src/test/resources/testPrograms/functionObjects/tuple1function.monty new file mode 100644 index 0000000..50088f4 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/tuple1function.monty @@ -0,0 +1,14 @@ +// Testing: 1-Tuple function objects without 1-Tuples +// +// Expected Output: 1\n8\n + +// a function that takes exactly one tuple +Int takeFirst( (Int, Int) t): + return t._1 +// and a function variable that points to this function +(Int, Int) -> Int fn := takeFirst + +// call the function the standard way +println(takeFirst((1,2))) +// call the function variable with a tuple +println( fn((8,2)) ) \ No newline at end of file diff --git a/src/test/resources/testPrograms/functionObjects/tuple1function.output b/src/test/resources/testPrograms/functionObjects/tuple1function.output new file mode 100644 index 0000000..19be058 --- /dev/null +++ b/src/test/resources/testPrograms/functionObjects/tuple1function.output @@ -0,0 +1,2 @@ +1 +8