]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EFieldAccess.java
(refs #7316) Improved error locations for invalid field access
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / EFieldAccess.java
1 package org.simantics.scl.compiler.elaboration.expressions;
2
3 import java.util.List;
4
5 import org.simantics.scl.compiler.common.names.Names;
6 import org.simantics.scl.compiler.constants.Constant;
7 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
8 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
9 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
10 import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous.Alternative;
11 import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor;
12 import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor;
13 import org.simantics.scl.compiler.errors.Locations;
14 import org.simantics.scl.compiler.internal.elaboration.utils.ExpressionDecorator;
15 import org.simantics.scl.compiler.internal.header.ModuleHeader;
16 import org.simantics.scl.compiler.types.Type;
17 import org.simantics.scl.compiler.types.Types;
18 import org.simantics.scl.compiler.types.exceptions.MatchException;
19
20 import gnu.trove.map.hash.TObjectIntHashMap;
21 import gnu.trove.set.hash.THashSet;
22 import gnu.trove.set.hash.TIntHashSet;
23
24 public class EFieldAccess extends SimplifiableExpression {
25
26     private static final Type VARIABLE = Types.con("Simantics/Variables", "Variable");
27     
28     Expression parent;
29     FieldAccessor accessor;
30     boolean lastAccessor = true;
31
32     public EFieldAccess(Expression parent, FieldAccessor accessor) {
33         this.parent = parent;
34         this.accessor = accessor;
35         if(parent instanceof EFieldAccess)
36                 ((EFieldAccess)parent).lastAccessor = false;
37     }
38
39     @Override
40     public void collectRefs(TObjectIntHashMap<Object> allRefs,
41             TIntHashSet refs) {
42         parent.collectRefs(allRefs, refs);
43         accessor.collectRefs(allRefs, refs);
44     }
45
46     @Override
47     public void collectVars(TObjectIntHashMap<Variable> allVars,
48             TIntHashSet vars) {
49         parent.collectVars(allVars, vars);
50         accessor.collectVars(allVars, vars);
51     }
52
53     private boolean returnsValue() {
54         return accessor.accessSeparator == '#' && !accessor.isVariableId();
55     }
56
57     @Override
58     protected void updateType() throws MatchException {
59         // Type is already updated in checkBasicType
60     }
61     
62     private Expression resolveAccessor(TypingContext context, Type requiredType) {
63         if(!(accessor instanceof IdAccessor))
64             return null;
65         IdAccessor idAccessor = (IdAccessor)accessor;
66         if(idAccessor.accessSeparator != '.')
67             return null;
68         List<Constant> accessors = context.getEnvironment().getFieldAccessors(idAccessor.fieldName);
69         if(accessors == null) {
70             context.getErrorLog().log(idAccessor.location, "Couldn't resolve accessor ." + idAccessor.fieldName + ".");
71             return new EError(location);
72         }
73         Expression accessorExpression;
74         if(accessors.size() == 1)
75             accessorExpression = new ELiteral(accessors.get(0));
76         else {
77             Alternative[] alternatives = new Alternative[accessors.size()];
78             for(int i=0;i<alternatives.length;++i) {
79                 final int index = i;
80                 alternatives[i] = new Alternative() {
81                     @Override
82                     public Expression realize() {
83                         return new ELiteral(accessors.get(index));
84                     }
85                     @Override
86                     public Type getType() {
87                         return accessors.get(index).getType();
88                     }
89                     @Override
90                     public String toString() {
91                         return accessors.get(index).toString();
92                     }
93                 };
94             }
95             accessorExpression = new EAmbiguous(alternatives);
96             accessorExpression.location = location;
97         }
98         return new EApply(location, accessorExpression, parent).checkType(context, requiredType);
99     }
100     
101     @Override
102     public Expression checkBasicType(TypingContext context, Type requiredType) {
103         ModuleHeader header = context.getCompilationContext().header;
104         if(header != null && header.fields) {
105             Expression expression = resolveAccessor(context, requiredType);
106             if(expression != null)
107                 return expression;
108         }
109         
110         // Default case
111         if(returnsValue())
112             setType(requiredType);
113         else {
114             setType(VARIABLE);
115             context.subsume(this, requiredType);
116         }
117         parent = parent.checkType(context, VARIABLE);
118         accessor.checkType(context);
119         context.declareEffect(getLocation(), Types.READ_GRAPH);
120         return this;
121     }
122
123     @Override
124     public void collectFreeVariables(THashSet<Variable> vars) {
125         parent.collectFreeVariables(vars);
126         accessor.collectFreeVariables(vars);
127     }
128
129     @Override
130     public Expression simplify(SimplificationContext context) {
131         // Simplify subexpressions
132         parent = parent.simplify(context);
133         accessor.simplify(context);
134         
135         if(accessor.accessSeparator == '.')
136                 return new EApply(
137                                 getLocation(),
138                                 Types.READ_GRAPH,
139                                 context.getConstant(Names.Simantics_Variables_child_),
140                                 parent,
141                                 accessor.asExpression()
142                                 );
143         else if(!lastAccessor)
144                 return new EApply(
145                                 getLocation(),
146                                 Types.READ_GRAPH,
147                                 context.getConstant(Names.Simantics_Variables_property),
148                                 parent,
149                                 accessor.asExpression()
150                                 );
151         else if(accessor.isVariableId())
152                 return parent;
153         else
154                 return new EApply(
155                                 getLocation(),
156                                 Types.READ_GRAPH,
157                                 context.getConstant(Names.Simantics_Variables_untypedPropertyValue, getType()),
158                                 parent,
159                                 accessor.asExpression()
160                                 );
161     }
162
163     @Override
164     public Expression resolve(TranslationContext context) {
165         parent = parent.resolve(context);
166         accessor.resolve(context);
167         return this;
168     }
169
170     @Override
171     public Expression decorate(ExpressionDecorator decorator) {
172         return decorator.decorate(this);
173     }
174
175     @Override
176     public void collectEffects(THashSet<Type> effects) {
177         // FIXME
178         effects.add(Types.READ_GRAPH);
179     }
180     
181     @Override
182     public void setLocationDeep(long loc) {
183         if(location == Locations.NO_LOCATION) {
184             location = loc;
185             parent.setLocationDeep(loc);
186             accessor.setLocationDeep(loc);
187         }
188     }
189     
190     @Override
191     public void accept(ExpressionVisitor visitor) {
192         visitor.visit(this);
193     }
194
195     @Override
196     public void forVariables(VariableProcedure procedure) {
197         parent.forVariables(procedure);
198         accessor.forVariables(procedure);
199     }
200     
201     @Override
202     public Expression accept(ExpressionTransformer transformer) {
203         return transformer.transform(this);
204     }
205
206 }