(refs #7250) Support for record syntax for CHR relations 94/594/1 feature/modularCHR
authorHannu Niemistö <hannu.niemisto@semantum.fi>
Sun, 4 Jun 2017 11:49:30 +0000 (14:49 +0300)
committerHannu Niemistö <hannu.niemisto@semantum.fi>
Sun, 4 Jun 2017 12:13:19 +0000 (15:13 +0300)
Change-Id: I8dd80eb5216a1b6023ab9097af58d4f1aaa077b2

23 files changed:
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRLiteral.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRelation.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/relations/CHRConstraint.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/relations/ExternalCHRRelation.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/translation/CHRTranslation.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EVar.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/block/ConstraintStatement.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/relations/SCLRelation.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/ConstructorAst.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCL.grammar
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParser.dat
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParser.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.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/CHR10.scl [new file with mode: 0644]
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR5.scl
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR6.scl
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR7.scl
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR8.scl
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR9.scl [new file with mode: 0644]

index 359448e1d65adf505e970e80e328ddb8d62cc079..fccf2681507466ef7f4c9b6f44dc6aab04f76b04 100644 (file)
@@ -376,7 +376,7 @@ public class Elaboration {
             dataTypes.add(dataType);
             for(int j=0;j<constructors.length;++j) {
                 ConstructorAst constructor = dataTypeAst.constructors[j];
-                String name = constructor.name;
+                String name = constructor.name.text;
                 Type[] parameterTypes = new Type[constructor.parameters.length];
                 for(int i=constructor.parameters.length-1;i>=0;--i)
                     parameterTypes[i] = context.toType(constructor.parameters[i]);
index b34328a53ebb494d0586d331b712b9c361bcb6ab..7b4a5bc26b1c7afb59addb97fa91d93985d7cc39 100644 (file)
@@ -7,11 +7,14 @@ import org.simantics.scl.compiler.elaboration.chr.relations.SpecialCHRRelation;
 import org.simantics.scl.compiler.elaboration.chr.relations.UnresolvedCHRRelation;
 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
+import org.simantics.scl.compiler.elaboration.contexts.TranslationContext.ExistentialFrame;
 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
+import org.simantics.scl.compiler.elaboration.expressions.ERecord;
 import org.simantics.scl.compiler.elaboration.expressions.Expression;
 import org.simantics.scl.compiler.elaboration.expressions.Variable;
 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
 import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;
+import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
 import org.simantics.scl.compiler.errors.Locations;
 import org.simantics.scl.compiler.internal.parsing.Symbol;
@@ -29,6 +32,7 @@ public class CHRLiteral extends Symbol {
     public CHRRelation relation;
     public Type[] typeParameters;
     public Expression[] parameters;
+    public FieldAssignment[] fields; // optional
     public Expression[] typeConstraintEvidenceParameters;
     public boolean killAfterMatch;
     public boolean negated;
@@ -55,6 +59,10 @@ public class CHRLiteral extends Symbol {
                 if(sclRelation != null)
                     relation = new ExternalCHRRelation(sclRelation);
                 else {
+                    if(parameters == null) {
+                        context.getErrorLog().log(location, "Relation must be declared if record syntax is used.");
+                        return;
+                    }
                     Type[] parameterTypes = new Type[parameters.length];
                     for(int i=0;i<parameterTypes.length;++i)
                         parameterTypes[i] = Types.metaVar(Kinds.STAR);
@@ -67,8 +75,33 @@ public class CHRLiteral extends Symbol {
                 }
             }
         }
-        for(int i=0;i<parameters.length;++i)
-            parameters[i] = parameters[i].resolve(context);
+        if(parameters == null && fields != null) {
+            String[] fieldNames = relation.getFieldNames();
+            if(fieldNames == null) {
+                context.getErrorLog().log(location, "Relation " + relation + " does not define field names.");
+                return;
+            }
+            parameters = ERecord.translateFieldsToFunctionParameters(context, fields, fieldNames);
+            if(parameters == null)
+                return;
+            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 " + fieldNames[i] + " not defined.");
+                    else
+                        parameters[i] = frame.createBlank(location);
+                }
+                else
+                    parameters[i] = parameters[i].resolve(context);
+            }
+            fields = null;
+        }
+        else {
+            for(int i=0;i<parameters.length;++i)
+                parameters[i] = parameters[i].resolve(context);
+        }
     }
 
     public void collectRefs(TObjectIntHashMap<Object> allRefs, TIntHashSet refs) {
index 7369d496c95530b0726f2a3333a17bafb9c8ba54..3daae67a94a348681d489f19915bf7ba36020e23 100644 (file)
@@ -12,4 +12,7 @@ public interface CHRRelation {
     TVar[] getTypeVariables();
     Type[] getParameterTypes();
     TPred[] getTypeConstraints();
+    default String[] getFieldNames() {
+        return null;
+    }
 }
index c0f43928877458cbb9a9241ad3e764481acd3ce5..ee73477912e3d8ff385f83a29e33562eae8ce0a2 100644 (file)
@@ -34,6 +34,7 @@ import gnu.trove.map.hash.TIntObjectHashMap;
 public class CHRConstraint extends Symbol implements CHRRelation {
     public final String name;
     public final Type[] parameterTypes;
+    public String[] fieldNames;
     
     public boolean implicitlyDeclared;
 
@@ -229,4 +230,9 @@ public class CHRConstraint extends Symbol implements CHRRelation {
         else
             return w.apply(location, accessor, fact);
     }
+    
+    @Override
+    public String[] getFieldNames() {
+        return fieldNames;
+    }
 }
index 5e4f5868c15bccec310e3dca43295a02a6299abb..6f44bc2ab6852bd0211f6f508f5b629ff8fda4a2 100644 (file)
@@ -32,4 +32,9 @@ public class ExternalCHRRelation implements CHRRelation {
     public String toString() {
         return relation.toString();
     }
+    
+    @Override
+    public String[] getFieldNames() {
+        return relation.getFieldNames();
+    }
 }
index 6972eb8647a1e6646fd88956d6ddee9c9dc52121..367960dfafc0a35f5f2ddb0d9870bd9152c6924c 100644 (file)
@@ -3,6 +3,7 @@ package org.simantics.scl.compiler.elaboration.chr.translation;
 import java.util.ArrayList;
 import java.util.Arrays;
 
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
 import org.simantics.scl.compiler.elaboration.chr.CHRLiteral;
 import org.simantics.scl.compiler.elaboration.chr.CHRQuery;
 import org.simantics.scl.compiler.elaboration.chr.CHRRule;
@@ -12,6 +13,7 @@ import org.simantics.scl.compiler.elaboration.chr.relations.UnresolvedCHRRelatio
 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
 import org.simantics.scl.compiler.elaboration.expressions.EApply;
 import org.simantics.scl.compiler.elaboration.expressions.EBinary;
+import org.simantics.scl.compiler.elaboration.expressions.ERecord;
 import org.simantics.scl.compiler.elaboration.expressions.EVar;
 import org.simantics.scl.compiler.elaboration.expressions.Expression;
 import org.simantics.scl.compiler.elaboration.expressions.block.CHRStatement;
@@ -20,9 +22,11 @@ import org.simantics.scl.compiler.elaboration.expressions.list.ListAssignment;
 import org.simantics.scl.compiler.elaboration.expressions.list.ListGenerator;
 import org.simantics.scl.compiler.elaboration.expressions.list.ListGuard;
 import org.simantics.scl.compiler.elaboration.expressions.list.ListQualifier;
+import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
 import org.simantics.scl.compiler.environment.AmbiguousNameException;
 import org.simantics.scl.compiler.environment.Environments;
 import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.compiler.internal.parsing.declarations.DAnnotationAst;
 import org.simantics.scl.compiler.internal.parsing.types.TypeAst;
 
 public class CHRTranslation {
@@ -35,18 +39,27 @@ public class CHRTranslation {
     }
 
     private static CHRLiteral convertConstraint(boolean remove, boolean negated, Expression expression) {
-        ArrayList<Expression> parameters = new ArrayList<Expression>(4);
-        while(expression instanceof EApply) {
+        long location = expression.location;
+        Expression[] parameters;
+        FieldAssignment[] fields = null;
+        if(expression instanceof EApply) {
             EApply apply = (EApply)expression;
-            for(int i=apply.parameters.length-1;i>=0;--i)
-                parameters.add(apply.parameters[i]);
+            parameters = apply.parameters;
             expression = apply.function;
         }
-        EVar var = (EVar)expression;
-        Expression[] parametersArray = new Expression[parameters.size()];
-        for(int i=0,j=parametersArray.length-1;i<parametersArray.length;++i,--j)
-            parametersArray[i] = parameters.get(j);
-        return new CHRLiteral(expression.location, new UnresolvedCHRRelation(var.location, var.name), parametersArray, remove, negated);
+        else if(expression instanceof ERecord) {
+            ERecord record = (ERecord)expression;
+            parameters = null;
+            fields = record.fields;
+            expression = record.constructor;
+        }
+        else // if(expression instanceof EVar)
+            parameters = Expression.EMPTY_ARRAY;
+        EVar var = (EVar)expression; // this should succeed because of isConstraint test
+        CHRLiteral literal = new CHRLiteral(location, new UnresolvedCHRRelation(var.location, var.name),
+                parameters, remove, negated);
+        literal.fields = fields;
+        return literal;
     }
     
     private static CHRLiteral convertListQualifier(TranslationContext context, boolean isHead, ListQualifier qualifier) {
@@ -102,8 +115,10 @@ public class CHRTranslation {
     }
     
     private static boolean isConstraint(TranslationContext context, Expression expression) {
-        while(expression instanceof EApply)
+        if(expression instanceof EApply)
             expression = ((EApply)expression).function;
+        else if(expression instanceof ERecord)
+            expression = ((ERecord)expression).constructor;
         if(!(expression instanceof EVar))
             return false;
         String name = ((EVar)expression).name;
@@ -136,7 +151,15 @@ public class CHRTranslation {
     }
 
     public static CHRConstraint convertConstraintStatement(TranslationContext context, ConstraintStatement statement) {
-        return new CHRConstraint(statement.location, statement.name.text, TypeAst.toTypes(context, statement.parameterTypes));
+        CHRConstraint constraint = new CHRConstraint(statement.location, statement.name.text, TypeAst.toTypes(context, statement.parameterTypes));
+        for(DAnnotationAst annotation : statement.annotations)
+            applyConstraintAnnotation(context, constraint, annotation);
+        constraint.fieldNames = statement.fieldNames;
+        return constraint;
+    }
+
+    private static void applyConstraintAnnotation(TranslationContext context, CHRConstraint constraint, DAnnotationAst annotation) {
+        context.getErrorLog().log(annotation.location, "Invalid constraint annotation");
     }
     
 }
index 75090ae3833f9f07c2643874bf95fdc9c6bd41c2..0331f9a50b86a7715405afc9bb37da2441d8b5b2 100644 (file)
@@ -46,7 +46,15 @@ public class TranslationContext extends TypeTranslationContext implements Enviro
     public static class ExistentialFrame {
         THashSet<String> variables = new THashSet<String>(4);
         ArrayList<Variable> blanks = new ArrayList<Variable>(2); 
-        boolean disallowNewExistentials;
+        public boolean disallowNewExistentials;
+        
+        public EVariable createBlank(long location) {
+            Variable variable = new Variable("_");
+            blanks.add(variable);
+            EVariable result = new EVariable(variable);
+            result.location = location;
+            return result;
+        }
     }
     
     THashMap<String, Variable> variables = new THashMap<String, Variable>();
@@ -128,14 +136,12 @@ public class TranslationContext extends TypeTranslationContext implements Enviro
         }
         case '_': {
             if(name.length()==1) {
-                variable = new Variable("_");
                 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
                 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
                     errorLog.log(location, "Blank variables can be used only in queries.");
                     return new EError(location);
                 }
-                existentialFrame.blanks.add(variable);
-                return new EVariable(variable);
+                return existentialFrame.createBlank(location);
             }
             break;
         }
@@ -143,7 +149,7 @@ public class TranslationContext extends TypeTranslationContext implements Enviro
         return null;
     }
     
-    private ExistentialFrame getCurrentExistentialFrame() {
+    public ExistentialFrame getCurrentExistentialFrame() {
         int size = existentialFrames.size(); 
         if(size == 0)
             return null;
index a41c0f042d46034e782ec06b78888bfe146cf7c9..e1f444d8220a12801d3ad430c55c11065a4edef8 100644 (file)
@@ -2,20 +2,20 @@ package org.simantics.scl.compiler.elaboration.expressions;
 
 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 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,30 +33,67 @@ public class ERecord extends ASTExpression {
     public Expression resolve(TranslationContext context, boolean asPattern) {
         SCLValue constructorValue; 
         try {
-            constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.text);
+            constructorValue = context.getEnvironment().getLocalNamespace().getValue(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.");
+            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.");
+            context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
             return new EError(constructor.location);
         }
+        Expression[] parameters = translateFieldsToFunctionParameters(context, fields, fieldNames);
+        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 " + fieldNames[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) {
         THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
+        boolean error = false;
         for(FieldAssignment field : fields) {
             if(field.value == null) {
-               String actualName = field.name;
-               if(actualName.charAt(0) == '?')
-                       actualName = actualName.substring(1);
+                String actualName = field.name;
+                if(actualName.charAt(0) == '?')
+                    actualName = actualName.substring(1);
                 String bestMatch = null;
                 int bestMatchLength = 0;
                 for(int i=0;i<fieldNames.length;++i) {
@@ -68,46 +105,30 @@ public class ERecord extends ASTExpression {
                 }
                 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;
-                }
-            }
-            else
-                parameters[i] = asPattern
-                        ? assignment.value.resolveAsPattern(context) 
-                        : assignment.value.resolve(context);
+            if(assignment != null)
+                parameters[i] = assignment.value;
         }
         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;
         }
+        return parameters;
     }
 
     @Override
index fda9d37ace3971278859e75ab390de257dccee14..d78fa8565abf2fa3c8147baf797bd0a10f7920b1 100644 (file)
@@ -2,17 +2,22 @@ package org.simantics.scl.compiler.elaboration.expressions;
 
 import java.util.ArrayList;
 
-import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
 import org.simantics.scl.compiler.elaboration.errors.NotPatternException;
 import org.simantics.scl.compiler.elaboration.expressions.lhstype.FunctionDefinitionLhs;
 import org.simantics.scl.compiler.elaboration.expressions.lhstype.LhsType;
 import org.simantics.scl.compiler.elaboration.expressions.lhstype.PatternMatchingLhs;
 import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.compiler.internal.parsing.Token;
 
 public class EVar extends ASTExpression {
     public final String name;
 
+    public EVar(Token token) {
+        this.location = token.location;
+        this.name = token.text;
+    }
+    
     public EVar(long location, String name) {
         this.location = location;
         this.name = name;
index fc92190a9c5657034cb7e7e9bae0cc203a5cb27e..4cccf2f46e591df3091a421b424563c3bd6fa17d 100644 (file)
@@ -5,16 +5,21 @@ import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
 import org.simantics.scl.compiler.elaboration.expressions.Expression;
 import org.simantics.scl.compiler.errors.Locations;
 import org.simantics.scl.compiler.internal.parsing.Token;
+import org.simantics.scl.compiler.internal.parsing.declarations.DAnnotationAst;
 import org.simantics.scl.compiler.internal.parsing.types.TypeAst;
 
 public class ConstraintStatement extends Statement {
 
     public Token name;
     public TypeAst[] parameterTypes;
+    public String[] fieldNames;
+    public DAnnotationAst[] annotations;
     
-    public ConstraintStatement(Token name, TypeAst[] parameterTypes) {
+    public ConstraintStatement(Token name, TypeAst[] parameterTypes, String[] fieldNames, DAnnotationAst[] annotations) {
         this.name = name;
         this.parameterTypes = parameterTypes;
+        this.fieldNames = fieldNames;
+        this.annotations = annotations;
     }
     
     @Override
index 4ac37bbc1f733b4084f22a17d6bde77c141bf50d..e93ff6e8fe16a7b5eb1aeb5a45164a86302c0be2 100644 (file)
@@ -35,7 +35,7 @@ public interface SCLRelation {
         return TPred.EMPTY_ARRAY;
     }
     int getPhase();
-    
+
     double getSelectivity(int boundVariables);
     int getRequiredVariablesMask();
     void generate(long location,
@@ -45,7 +45,10 @@ public interface SCLRelation {
     Expression generateEnforce(long location, EnforcingContext context,
             Type[] typeParameters,
             Variable[] parameters);
-
+    default String[] getFieldNames() {
+        return null;
+    }
+    
     void generateIterate(
             PlanContext context,
             CodeWriter w,
index 3edb9ce816dc83abcb988cd48d8251adf924853f..bbb669506f7c3d6b3511354c48e178eb28686cf8 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.scl.compiler.internal.parsing.declarations;
 
 import org.simantics.scl.compiler.internal.parsing.Symbol;
+import org.simantics.scl.compiler.internal.parsing.Token;
 import org.simantics.scl.compiler.internal.parsing.types.TypeAst;
 
 
@@ -8,11 +9,11 @@ public class ConstructorAst extends Symbol {
     public static final ConstructorAst[] EMPTY_ARRAY = new ConstructorAst[0];
     
     public final DAnnotationAst[] annotations;
-    public final String name;
+    public final Token name;
     public final TypeAst[] parameters;
     public final String[] fieldNames; // null, if no field names
     
-    public ConstructorAst(DAnnotationAst[] annotations, String name,
+    public ConstructorAst(DAnnotationAst[] annotations, Token name,
             TypeAst[] parameters, String[] fieldNames) {
         this.annotations = annotations;
         this.name = name;
index b0911febe69bf3540f928cee928e9b84c5086edf..f7ea1b263617cee6eae8dbc4e5dda34139e10e45 100644 (file)
@@ -200,7 +200,7 @@ statement
     | exp FOLLOWS queryBlock                                 # RuleStatement
     | chrQuery IMPLIES chrQuery                              # CHRStatement
     | WHEN verboseChrQuery THEN_AFTER_WHEN verboseChrQuery   # VerboseCHRStatement
-    | CONSTRAINT ID atype*                                   # ConstraintStatement
+    | CONSTRAINT constructor                                 # ConstraintStatement
     | INCLUDE ID aexp                                        # LocalInclude
     ;
 
index 3b8a4c400293523d105ea223a57485b7a3b2e43a..378013be5fbabbeb1085e6d43b3854b269873bf0 100644 (file)
Binary files a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParser.dat and b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParser.dat differ
index 527c66f5dc0b86e78988c8cf83b5e3e23970078a..fc2242dce632d482d95170a31f9bafe86d8a1eb3 100644 (file)
@@ -13,18 +13,18 @@ public abstract class SCLParser {
     public static final boolean TRACE = false;
 
     private static final int INITIAL_CAPACITY = 16;
-    private static final int STATE_COUNT = 353;
+    private static final int STATE_COUNT = 358;
     private static final int TERMINAL_COUNT = 84;
-    private static final int NONTERMINAL_COUNT = 51;
-    private static final int PRODUCT_COUNT = 134;
+    private static final int NONTERMINAL_COUNT = 52;
+    private static final int PRODUCT_COUNT = 135;
     
     private static final int[] ACTION_ROW_ID = new int[STATE_COUNT];
     private static final int[] ACTION_COLUMN_ID = new int[TERMINAL_COUNT];
-    private static final short[] ACTION_TABLE = new short[6588];
-    private static final int[] ERROR_TABLE = new int[927];
+    private static final short[] ACTION_TABLE = new short[6944];
+    private static final int[] ERROR_TABLE = new int[940];
     private static final int[] GOTO_ROW_ID = new int[STATE_COUNT];
     private static final int[] GOTO_COLUMN_ID = new int[NONTERMINAL_COUNT];
-    private static final short[] GOTO_TABLE = new short[1620];
+    private static final short[] GOTO_TABLE = new short[1708];
     private static final int[] PRODUCT_LHS = new int[PRODUCT_COUNT];
 
     private static final short STATE_MASK = (short)0x0fff;
@@ -161,6 +161,7 @@ public abstract class SCLParser {
         "listQualifier",
         "chrQuery",
         "verboseChrQuery",
+        "constraintSpec",
         "caseRhs",
         "guardedExpArrow",
         "equation",
@@ -394,19 +395,19 @@ public abstract class SCLParser {
         return parse(0);
     }
     public Object parseCommands() {
-        return parse(337);
+        return parse(342);
     }
     public Object parseImport() {
-        return parse(345);
+        return parse(350);
     }
     public Object parseType() {
-        return parse(347);
+        return parse(352);
     }
     public Object parseExp() {
-        return parse(349);
+        return parse(354);
     }
     public Object parseEquationBlock() {
-        return parse(351);
+        return parse(356);
     }
 
 
@@ -650,24 +651,26 @@ public abstract class SCLParser {
         case 117:
             return reduceVerboseCHRQuery();
         case 118:
-            return reduceSimpleCaseRhs();
+            return reduceConstraintSpec();
         case 119:
-            return reduceGuardedCaseRhs();
+            return reduceSimpleCaseRhs();
         case 120:
-            return reduceGuardedExpArrow();
+            return reduceGuardedCaseRhs();
         case 121:
-            return reduceGuardEquation();
+            return reduceGuardedExpArrow();
         case 122:
-            return reduceBasicEquation();
+            return reduceGuardEquation();
         case 123:
-            return reduceEffect();
+            return reduceBasicEquation();
         case 124:
-            return reduceJustEtype();
+            return reduceEffect();
         case 125:
-            return reduceForAll();
+            return reduceJustEtype();
         case 126:
-            return reduceApplyType();
+            return reduceForAll();
         case 127:
+            return reduceApplyType();
+        case 128:
             return reduceDummy();
 
         default:
@@ -821,7 +824,7 @@ public abstract class SCLParser {
      */
     protected abstract Object reduceVerboseCHRStatement();
     /**
-     * statement ::= CONSTRAINT ID atype&#42;
+     * statement ::= CONSTRAINT constructor (WHERE constraintSpec)?
      */
     protected abstract Object reduceConstraintStatement();
     /**
@@ -1136,6 +1139,10 @@ public abstract class SCLParser {
      * verboseChrQuery ::= LBRACE listQualifier (SEMICOLON listQualifier)&#42; RBRACE
      */
     protected abstract Object reduceVerboseCHRQuery();
+    /**
+     * constraintSpec ::= LBRACE exp (SEMICOLON exp)&#42; RBRACE
+     */
+    protected abstract Object reduceConstraintSpec();
     /**
      * caseRhs ::= ARROW exp (WHERE statements)?
      */
index 09fc79e0f531030b8ff2d1dfa720a00c22428dfb..34f8d6a9f0da95e4e6a01a95021d82f893176ca8 100644 (file)
@@ -308,7 +308,7 @@ public class SCLParserImpl extends SCLParser {
         else
             context = (ArrayList<TypeAst>)get(i++);
         Token nameToken = (Token)get(i++);
-        EVar name = new EVar(nameToken.location, nameToken.text);
+        EVar name = new EVar(nameToken);
         ArrayList<TypeAst> parameters = new ArrayList<TypeAst>();
         while(i < length()) {
             Object symbol = get(i++);
@@ -334,7 +334,7 @@ public class SCLParserImpl extends SCLParser {
         else
             context = (ArrayList<TypeAst>)get(i++);
         Token nameToken = (Token)get(i++);
-        EVar name = new EVar(nameToken.location, nameToken.text);
+        EVar name = new EVar(nameToken);
         ArrayList<TypeAst> parameters = new ArrayList<TypeAst>();
         while(i < length()) {
             Object symbol = get(i++);
@@ -424,12 +424,12 @@ public class SCLParserImpl extends SCLParser {
 
     @Override
     protected Object reduceVarId() {
-        return new EVar(((Token)get(0)).text);
+        return new EVar((Token)get(0));
     }
 
     @Override
     protected Object reduceEscapedSymbol() {
-        return new EVar(((Token)get(0)).text);
+        return new EVar((Token)get(0));
     }
 
     @Override
@@ -460,7 +460,7 @@ public class SCLParserImpl extends SCLParser {
         EVar negation = null;
         if(get(i) instanceof Token) {
             Token token = (Token)get(i++);
-            negation = new EVar(token.location, token.text);
+            negation = new EVar(token);
         }
         EBinary binary = new EBinary((Expression)get(i++), negation);
         while(i < length()) {
@@ -527,7 +527,7 @@ public class SCLParserImpl extends SCLParser {
         TypeAst[] parameters = new TypeAst[length()-idPos-1];
         for(int i=0;i<parameters.length;++i)
             parameters[i] = (TypeAst)get(i+idPos+1);
-        return new ConstructorAst(annotations, ((Token)get(idPos)).text, parameters, null);
+        return new ConstructorAst(annotations, (Token)get(idPos), parameters, null);
     }
 
     @Override
@@ -581,7 +581,7 @@ public class SCLParserImpl extends SCLParser {
 
     @Override
     protected Object reduceBlank() {
-        return new EVar("_");
+        return new EVar(((Token)get(0)).location, "_");
     }
 
     @Override
@@ -1178,7 +1178,7 @@ public class SCLParserImpl extends SCLParser {
         FieldAssignment[] fields = new FieldAssignment[length()/2-1];
         for(int i=0;i<fields.length;++i)
             fields[i] = (FieldAssignment)get(2+i*2);
-        return new ERecord((Token)get(0), fields);
+        return new ERecord(new EVar((Token)get(0)), fields);
     }
 
     @Override
@@ -1208,7 +1208,7 @@ public class SCLParserImpl extends SCLParser {
             parameters[i] = fieldDesc.type;
             fieldNames[i] = fieldDesc.name;
         }
-        return new ConstructorAst(annotations, ((Token)get(idPos)).text, parameters, fieldNames);
+        return new ConstructorAst(annotations, (Token)get(idPos), parameters, fieldNames);
     }
 
     @Override
@@ -1253,10 +1253,8 @@ public class SCLParserImpl extends SCLParser {
 
     @Override
     protected Object reduceConstraintStatement() {
-        TypeAst[] parameterTypes = new TypeAst[length()-2];
-        for(int i=0;i<parameterTypes.length;++i)
-            parameterTypes[i] = (TypeAst)get(2+i);
-        return new ConstraintStatement((Token)get(1), parameterTypes);
+        ConstructorAst constructor = (ConstructorAst)get(1);
+        return new ConstraintStatement(constructor.name, constructor.parameters, constructor.fieldNames, constructor.annotations);
     }
 
     @Override
@@ -1307,4 +1305,12 @@ public class SCLParserImpl extends SCLParser {
         return new IncludeStatement(name, value);
     }
 
+    @Override
+    protected Object reduceConstraintSpec() {
+        Expression[] expressions = new Expression[length()/2-1];
+        for(int i=0;i<expressions.length;++i)
+            expressions[i] = (Expression)get(2*i+1);
+        return expressions;
+    }
+
 }
index f3aa463d5e579344f5099cc243476d41efce994c..f065b765e7ce871bb91882e9edcdecf8d28ee1c6 100644 (file)
@@ -30,6 +30,8 @@ public class ModuleRegressionTests extends TestBase {
     @Test public void CHR6() { test(); }
     @Test public void CHR7() { test(); }
     @Test public void CHR8() { test(); }
+    @Test public void CHR9() { test(); }
+    @Test public void CHR10() { test(); }
     @Test public void ClosureRecursion() { test(); }
     @Test public void Collaz() { test(); }
     @Test public void Compose() { test(); }
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR10.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR10.scl
new file mode 100644 (file)
index 0000000..758074d
--- /dev/null
@@ -0,0 +1,30 @@
+module { export = [main], chr }
+import "StandardLibrary"
+
+data V = V { x :: Double, y :: Double }
+
+main = ()
+  where
+    constraint X V 
+    X V { ?x } => print ?x
+    X V { ?y } => print ?y
+    True => X V { x = 1.0, y = 2.0 }
+    True => X V { x = 3.0, y = 4.0 }
+--
+1.0
+2.0
+3.0
+4.0
+()
+--
+module { export = [main], chr }
+import "StandardLibrary"
+
+data V = V { x :: Double, y :: Double }
+
+main = ()
+  where
+    constraint X V 
+    True => X V { x = 1.0 }
+--
+9:15-9:28: Field y not defined.
\ No newline at end of file
index edefae634f7925436fcf16b272e6ba9d41815a3e..c59c02dbb2b3a65cefd61c6266e5a3d94b71043d 100644 (file)
@@ -1,8 +1,4 @@
-module {
-    export = [main],
-    chr
-}
-
+module { export = [main], chr }
 import "StandardLibrary"
 
 ruleset IntegerSet where
index 325af52708e8a831d7e3a56bb9ea42b2ea829c4a..f1e28d08a157bfb9ca324cd7ef6ce58c1aee1d93 100644 (file)
@@ -1,8 +1,4 @@
-module {
-    export = [main],
-    chr
-}
-
+module { export = [main], chr }
 import "StandardLibrary"
 
 ruleset RS where
index 6576cf47e662cf479ed7ae2d05969f010a3299df..b10257ca89a4144dcfbb75fbf001c6e129d918b4 100644 (file)
@@ -1,8 +1,4 @@
-module {
-    export = [main],
-    chr
-}
-
+module { export = [main], chr }
 import "StandardLibrary"
 
 ruleset RS where
index ce3064aa427359a46dd24c47126a636853fa9bcb..c3a88f58a7199f59ccde3a512046d978ad3fe7bd 100644 (file)
@@ -1,12 +1,8 @@
-module {
-    export = [main],
-    chr
-}
-
+module { export = [main], chr }
 import "StandardLibrary"
 
 main = ()
   where
     X ?x => Y ?y
 --
-10:15-10:17: New existential variables can be defined only in queries.
\ No newline at end of file
+6:15-6:17: New existential variables can be defined only in queries.
\ No newline at end of file
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR9.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/scl/CHR9.scl
new file mode 100644 (file)
index 0000000..0751482
--- /dev/null
@@ -0,0 +1,42 @@
+module { export = [main], chr }
+import "StandardLibrary"
+
+main = ()
+  where
+    constraint V { x :: Double, y :: Double } 
+    V { ?x } => print ?x
+    True => V { x = 1.0, y = 2.0 }
+--
+1.0
+()
+--
+module { export = [main], chr }
+import "StandardLibrary"
+
+main = ()
+  where
+    constraint V { x :: Double, y :: Double }
+    True => V { x = 1.0 }
+--
+7:13-7:26: Field y not defined.
+--
+module { export = [main], chr }
+
+import "StandardLibrary"
+
+main = ()
+  where
+    constraint V Double Double
+    True => V { x = 1.0, y = 2.0 }
+--
+8:13-8:35: Relation V does not define field names.
+--
+module { export = [main], chr }
+
+import "StandardLibrary"
+
+main = ()
+  where
+    True => V { x = 1.0, y = 2.0 }
+--
+7:13-7:35: Relation must be declared if record syntax is used.
\ No newline at end of file