(refs #6923) Explicit export annotation for SCL modules 60/560/1
authorHannu Niemistö <hannu.niemisto@semantum.fi>
Fri, 26 May 2017 07:53:51 +0000 (10:53 +0300)
committerHannu Niemistö <hannu.niemisto@semantum.fi>
Fri, 26 May 2017 07:53:51 +0000 (10:53 +0300)
Added module export list to module header. It looks like this:

module {
    export = [foo, bar]
}

When using export list, @private annotation is not supported anymore.
All symbols not exported are effectively private.

Change-Id: I7827f6cf6062388ddd06be0ab401409bd08464fe

bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/annotations/AnnotationUtils.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/header/ModuleHeader.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/ActiveTests.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/ModuleRegressionTests.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/ModuleExport.scl [new file with mode: 0644]

index 9dbd94691a82cd27bab16a4a7be498e39fee2852..1cba44207ee7c124de787ceff3b7631421084c1b 100644 (file)
@@ -1098,7 +1098,8 @@ public class Elaboration {
                                 constructor.getParameterTypes());
                 if(dataType.constructors.length == 1 && (
                         dataType.getTypeDesc() == null ||
-                        dataType.constructors[0].javaName.equals(MethodBuilderBase.getClassName(dataType.getTypeDesc()))))
+                        (dataType.constructors[0].javaName != null &&
+                        dataType.constructors[0].javaName.equals(MethodBuilderBase.getClassName(dataType.getTypeDesc())))))
                     sclConstructor.setOnlyConstructor(true);
                 value.setValue(sclConstructor);
                 value.setType(constructor.getType());
@@ -1170,6 +1171,14 @@ public class Elaboration {
                     typeMap.put(name.name, valueTypeAst);
             }
         
+        THashMap<String, EVar> exportMap = null;
+        if(moduleHeader != null && moduleHeader.export != null) {
+            exportMap = new THashMap<String, EVar>();
+            for(EVar export : moduleHeader.export)
+                if(exportMap.put(export.name, export) != null)
+                    errorLog.log(export.location, "The symbol " + export.name + " is exported multiple times.");
+        }
+        
         for(String name : valueDefinitionsAst.getValueNames()) {
             ArrayList<DValueAst> defs = valueDefinitionsAst.getDefinition(name);
             try {
@@ -1187,11 +1196,16 @@ public class Elaboration {
                     for(DAnnotationAst annotation : annotations) {
                         handleAnnotation(value, defs, annotation);
                     }
+                if(exportMap != null && exportMap.remove(name) == null)
+                    value.addProperty(PrivateProperty.INSTANCE);
             } catch(RuntimeException e) {
                 errorLog.setExceptionPosition(defs.get(0).location);
                 throw e;
             }
         }
+        if(exportMap != null)
+            for(EVar export : exportMap.values())
+                errorLog.log(export.location, "The symbol " + export.name + " is not defined in the module.");
         for(String name : relationDefinitionsAst.getRelationNames()) {
             ArrayList<DRelationAst> definitions = relationDefinitionsAst.getDefinition(name);
             if(definitions.size() > 1) {
@@ -1229,6 +1243,8 @@ public class Elaboration {
             }
         }
         else if(annotation.id.text.equals("@private")) {
+            if(moduleHeader != null && moduleHeader.export != null)
+                errorLog.log(annotation.location, "Annotation @private is not used when module header contains export property.");
             value.addProperty(PrivateProperty.INSTANCE);
         }
         else if(annotation.id.text.equals("@deprecated")) {
index 8cc088b955b8ded475ba86d150551f5a5f2ec44c..08ea47297b7363bd8974c24154aa767b8e4d076b 100644 (file)
@@ -1,6 +1,11 @@
 package org.simantics.scl.compiler.elaboration.expressions.annotations;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import org.simantics.scl.compiler.constants.StringConstant;
+import org.simantics.scl.compiler.elaboration.expressions.EListLiteral;
 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
 import org.simantics.scl.compiler.elaboration.expressions.EStringLiteral;
 import org.simantics.scl.compiler.elaboration.expressions.EVar;
@@ -46,4 +51,21 @@ public class AnnotationUtils {
         }
         return null;
     }
+    
+    public static List<EVar> extractIdentifierList(Expression expression) {
+        if(expression instanceof EVar || expression instanceof EStringLiteral || expression instanceof ELiteral)
+            return Collections.singletonList(new EVar(expression.location, extractString(expression)));
+        else if(expression instanceof EListLiteral) {
+            Expression[] components = ((EListLiteral)expression).getComponents();
+            EVar[] items = new EVar[components.length];
+            for(int i=0;i<components.length;++i) {
+                String value = extractString(components[i]);
+                if(value == null)
+                    return null;
+                items[i] = new EVar(components[i].location, value);
+            }
+            return Arrays.asList(items);
+        }
+        return null;
+    }
 }
index c408b4bb6e12f01a3fe33610a072055692b958c6..33a88ffba83dee70893e841c6cb516407ebca6fb 100644 (file)
@@ -1,5 +1,8 @@
 package org.simantics.scl.compiler.internal.header;
 
+import java.util.List;
+
+import org.simantics.scl.compiler.elaboration.expressions.EVar;
 import org.simantics.scl.compiler.elaboration.expressions.annotations.AnnotationUtils;
 import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
 import org.simantics.scl.compiler.errors.ErrorLog;
@@ -10,6 +13,7 @@ public class ModuleHeader {
     public long classLoaderLocation;
     public String defaultLocalName;
     public boolean fields;
+    public List<EVar> export;
     
     private void read(ErrorLog errorLog, DModuleHeader header) {
         for(FieldAssignment assignment : header.fields)
@@ -25,6 +29,15 @@ public class ModuleHeader {
                         classLoaderLocation = assignment.location;
                 }
                 break;
+            case "export":
+                if(assignment.value == null)
+                    errorLog.log(assignment.location, "Property export needs to be given a string list value.");
+                else {
+                    export = AnnotationUtils.extractIdentifierList(assignment.value);
+                    if(export == null)
+                        errorLog.log(assignment.value.location, "Expected a list of exported items.");
+                }
+                break;
             case "defaultLocalName":
                if(assignment.value == null)
                     errorLog.log(assignment.location, "Property defaultLocalName needs to be given a string value.");
@@ -35,6 +48,8 @@ public class ModuleHeader {
                }
                break;
             case "fields":
+                if(assignment.value != null)
+                    errorLog.log(assignment.location, "No value expected for property fields.");
                 this.fields = true;
                 break;
             default:
index 82193b4813df7c3f060cdf460f85ed31c67384b7..f3069f13b0bfa36b508243c679ef8e5cd65b3f94 100644 (file)
@@ -1,5 +1,7 @@
 package org.simantics.scl.compiler.tests;
 
+import org.junit.Test;
+
 public class ActiveTests extends TestBase {
     
     public ActiveTests() { super("scl"); }
@@ -18,4 +20,5 @@ public class ActiveTests extends TestBase {
     
     //@Test public void Bug6989() { test(); }
     
+    
 }
index dc2c775241574595b88c5f36ccdf75e8610782c6..f50056e8d230e37a8b309b908736607ae13667ee 100644 (file)
@@ -157,6 +157,7 @@ public class ModuleRegressionTests extends TestBase {
     @Test public void Maybe4() { test(); }
     @Test public void MissingEffect() { test(); }
     @Test public void MissingMethod() { test(); }
+    @Test public void ModuleExport() { test(); }
     @Test public void ModuleInitialization() { test(); }
     @Test public void MonadBug1() { test(); }
     @Test public void Monads1() { test(); }
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/ModuleExport.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/ModuleExport.scl
new file mode 100644 (file)
index 0000000..afc15c7
--- /dev/null
@@ -0,0 +1,16 @@
+// module FooBar
+module {
+    export = [foo]
+}
+foo = "foo"
+bar = "bar"
+--
+import "FooBar"
+main = foo
+--
+foo
+--
+import "FooBar"
+main = bar
+--
+2:8-2:11: Couldn't resolve bar.
\ No newline at end of file