]> gerrit.simantics Code Review - simantics/platform.git/blob
a41c0f042d46034e782ec06b78888bfe146cf7c9
[simantics/platform.git] /
1 package org.simantics.scl.compiler.elaboration.expressions;
2
3 import org.simantics.scl.compiler.constants.SCLConstructor;
4 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
5 import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
6 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
7 import org.simantics.scl.compiler.environment.AmbiguousNameException;
8 import org.simantics.scl.compiler.errors.Locations;
9 import org.simantics.scl.compiler.internal.parsing.Token;
10
11 import gnu.trove.map.hash.THashMap;
12
13 public class ERecord extends ASTExpression {
14
15     Token constructor;
16     FieldAssignment[] fields;
17     
18     public ERecord(Token constructor, FieldAssignment[] fields) {
19         this.constructor = constructor;
20         this.fields = fields;
21     }
22
23     @Override
24     public Expression resolve(TranslationContext context) {
25         return resolve(context, false);
26     }
27     
28     @Override
29     public Expression resolveAsPattern(TranslationContext context) {
30         return resolve(context, true);
31     }
32     
33     public Expression resolve(TranslationContext context, boolean asPattern) {
34         SCLValue constructorValue; 
35         try {
36             constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.text);
37         } catch (AmbiguousNameException e) {
38             context.getErrorLog().log(constructor.location, e.getMessage());
39             return new EError(constructor.location);
40         }
41         if(constructorValue == null) {
42             context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.text + ".");
43             return new EError(constructor.location);
44         }
45         if(!(constructorValue.getValue() instanceof SCLConstructor)) {
46             context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
47             return new EError(constructor.location);
48         }
49         String[] fieldNames = ((SCLConstructor)constructorValue.getValue()).recordFieldNames;
50         if(fieldNames == null) {
51             context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
52             return new EError(constructor.location);
53         }
54         THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
55         for(FieldAssignment field : fields) {
56             if(field.value == null) {
57                 String actualName = field.name;
58                 if(actualName.charAt(0) == '?')
59                         actualName = actualName.substring(1);
60                 String bestMatch = null;
61                 int bestMatchLength = 0;
62                 for(int i=0;i<fieldNames.length;++i) {
63                     String fieldName = fieldNames[i];
64                     if(actualName.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
65                         bestMatch = fieldName;
66                         bestMatchLength = fieldName.length();
67                     }
68                 }
69                 if(bestMatch == null) {
70                     context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
71                     return new EError(location);
72                 }
73                 field.value = new EVar(field.location, field.name);
74                 field.name = bestMatch;
75             }
76             if(recordMap.put(field.name, field) != null) {
77                 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
78                 return new EError(location);
79             }
80         }
81         Expression[] parameters = new Expression[fieldNames.length];
82         boolean error = false;
83         for(int i=0;i<fieldNames.length;++i) {
84             FieldAssignment assignment = recordMap.remove(fieldNames[i]);
85             if(assignment == null) {
86                 if(asPattern) {
87                     parameters[i] = Expressions.blank(null);
88                 }
89                 else {
90                     context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
91                     error = true;
92                 }
93             }
94             else
95                 parameters[i] = asPattern
96                         ? assignment.value.resolveAsPattern(context) 
97                         : assignment.value.resolve(context);
98         }
99         if(!recordMap.isEmpty()) {
100             for(FieldAssignment field : recordMap.values())
101                 context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
102             error = true;
103         }
104         if(error)
105             return new EError(location);
106         else {
107             EApply result = new EApply(new EConstant(constructorValue), parameters);
108             result.setLocationDeep(location);
109             return result;
110         }
111     }
112
113     @Override
114     public void setLocationDeep(long loc) {
115         if(location == Locations.NO_LOCATION) {
116             location = loc;
117             for(FieldAssignment field : fields)
118                 if(field.value != null)
119                     field.value.setLocationDeep(loc);
120         }
121     }
122     
123     @Override
124     public Expression accept(ExpressionTransformer transformer) {
125         return transformer.transform(this);
126     }
127     
128     @Override
129     public void accept(ExpressionVisitor visitor) {
130         visitor.visit(this);
131     }
132 }