]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java
Merge "Minor refactorings related to SCL constructors"
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / ERecord.java
index 32999dcb48877a689c980d7e7c1bc73973b46345..47ef205df17cbce0d9ff1d74dd8f1154adc1bcfa 100644 (file)
@@ -1,21 +1,25 @@
 package org.simantics.scl.compiler.elaboration.expressions;
 
+import java.util.Arrays;
+
 import org.simantics.scl.compiler.constants.SCLConstructor;
 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
+import org.simantics.scl.compiler.elaboration.contexts.TranslationContext.ExistentialFrame;
 import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
 import org.simantics.scl.compiler.environment.AmbiguousNameException;
 import org.simantics.scl.compiler.errors.Locations;
-import org.simantics.scl.compiler.internal.parsing.Token;
 
 import gnu.trove.map.hash.THashMap;
 
 public class ERecord extends ASTExpression {
 
-    Token constructor;
-    FieldAssignment[] fields;
+    public static final boolean DEBUG = false;
+    
+    public final EVar constructor;
+    public final FieldAssignment[] fields;
     
-    public ERecord(Token constructor, FieldAssignment[] fields) {
+    public ERecord(EVar constructor, FieldAssignment[] fields) {
         this.constructor = constructor;
         this.fields = fields;
     }
@@ -33,78 +37,130 @@ public class ERecord extends ASTExpression {
     public Expression resolve(TranslationContext context, boolean asPattern) {
         SCLValue constructorValue; 
         try {
-            constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.text);
+            constructorValue = context.resolveRecordConstructor(location, constructor.name);
         } catch (AmbiguousNameException e) {
             context.getErrorLog().log(constructor.location, e.getMessage());
             return new EError(constructor.location);
         }
         if(constructorValue == null) {
-            context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.text + ".");
+            context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.name + ".");
             return new EError(constructor.location);
         }
-        if(!(constructorValue.getValue() instanceof SCLConstructor)) {
-            context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
+        String[] parameterNames = constructorValue.parameterNames;
+        if(parameterNames == null) {
+            context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
             return new EError(constructor.location);
         }
-        String[] fieldNames = ((SCLConstructor)constructorValue.getValue()).recordFieldNames;
-        if(fieldNames == null) {
-            context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
-            return new EError(constructor.location);
+        Expression[] parameters = translateFieldsToFunctionParameters(context, fields, parameterNames, false);
+        if(parameters == null)
+            return new EError(location);
+        if(asPattern)
+            for(int i=0;i<parameters.length;++i) {
+                Expression parameter = parameters[i];
+                if(parameter == null)
+                    parameters[i] = Expressions.blank(null);
+                else
+                    parameters[i] = parameter.resolveAsPattern(context);
+            }
+        else {
+            boolean error = false;
+            for(int i=0;i<parameters.length;++i) {
+                Expression parameter = parameters[i];
+                if(parameter == null) {
+                    ExistentialFrame frame = context.getCurrentExistentialFrame();
+                    if(frame == null || frame.disallowNewExistentials) {
+                        context.getErrorLog().log(location, "Field " + parameterNames[i] + " not defined.");
+                        error = true;
+                    }
+                    else
+                        parameters[i] = frame.createBlank(location); 
+                }
+                else
+                    parameters[i] = parameter.resolve(context);
+            }
+            if(error)
+                return new EError(location);
+        }
+        EApply result = new EApply(new EConstant(constructorValue), parameters);
+        result.setLocationDeep(location);
+        return result;
+    }
+    
+    public static Expression[] translateFieldsToFunctionParameters(TranslationContext context, FieldAssignment[] fields, String[] fieldNames, boolean chrLiteral) {
+        if(DEBUG) {
+            System.out.println("translateFieldsToFunctionParameters");
+            System.out.println("    fieldNames = " + Arrays.toString(fieldNames));
+            System.out.print("    fields = {");
+            for(int i=0;i<fields.length;++i) {
+                FieldAssignment field = fields[i];
+                if(i > 0)
+                    System.out.print(", ");
+                System.out.print(field.name);
+                if(field.value != null) {
+                    System.out.print(" = ");
+                    System.out.print(field.value);
+                }
+            }
+            System.out.println("}");
         }
+        
         THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
+        boolean error = false;
+        FieldAssignment wildcardField = null;
         for(FieldAssignment field : fields) {
             if(field.value == null) {
+                String actualName = field.name;
+                if(actualName.equals(FieldAssignment.WILDCARD)) {
+                    if(wildcardField != null)
+                        context.getErrorLog().log(field.location, "The record has more than one wildcard.");
+                    wildcardField = field;
+                    continue;
+                }
+                if(actualName.charAt(0) == '?')
+                    actualName = actualName.substring(1);
                 String bestMatch = null;
                 int bestMatchLength = 0;
                 for(int i=0;i<fieldNames.length;++i) {
                     String fieldName = fieldNames[i];
-                    if(field.name.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
+                    if(actualName.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
                         bestMatch = fieldName;
                         bestMatchLength = fieldName.length();
                     }
                 }
                 if(bestMatch == null) {
                     context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
-                    return new EError(location);
+                    error = true;
                 }
                 field.value = new EVar(field.location, field.name);
                 field.name = bestMatch;
             }
             if(recordMap.put(field.name, field) != null) {
                 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
-                return new EError(location);
+                error = true;
             }
         }
+        if(error)
+            return null;
         Expression[] parameters = new Expression[fieldNames.length];
-        boolean error = false;
         for(int i=0;i<fieldNames.length;++i) {
             FieldAssignment assignment = recordMap.remove(fieldNames[i]);
-            if(assignment == null) {
-                if(asPattern) {
-                    parameters[i] = Expressions.blank(null);
-                }
-                else {
-                    context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
-                    error = true;
-                }
+            if(assignment != null)
+                parameters[i] = assignment.value;
+            else if(wildcardField != null) {
+                String variableName = fieldNames[i];
+                if(chrLiteral)
+                    variableName = "?" + variableName;
+                parameters[i] = new EVar(wildcardField.location, variableName);
             }
-            else
-                parameters[i] = asPattern
-                        ? assignment.value.resolveAsPattern(context) 
-                        : assignment.value.resolve(context);
         }
         if(!recordMap.isEmpty()) {
             for(FieldAssignment field : recordMap.values())
                 context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
-            error = true;
-        }
-        if(error)
-            return new EError(location);
-        else {
-            EApply result = new EApply(new EConstant(constructorValue), parameters);
-            result.setLocationDeep(location);
-            return result;
+            return null;
         }
+        if(DEBUG)
+            System.out.println(" => parameters = " + Arrays.toString(parameters));
+        return parameters;
     }
 
     @Override
@@ -121,5 +177,9 @@ public class ERecord extends ASTExpression {
     public Expression accept(ExpressionTransformer transformer) {
         return transformer.transform(this);
     }
-
+    
+    @Override
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit(this);
+    }
 }