source: java/Framclipse/com.framsticks.framclipse/src/com/framsticks/framclipse/scoping/FramScriptScopeProvider.java @ 440

Last change on this file since 440 was 440, checked in by Mateusz Poszwa, 9 years ago
  • Updated Xtext-based Framclipse
  • Deleted previous version of Framclipse
  • Property svn:eol-style set to native
File size: 16.4 KB
Line 
1/*
2 * generated by Xtext
3 */
4package com.framsticks.framclipse.scoping;
5
6import static com.framsticks.framclipse.framScript.FramScriptPackage.Literals.ARRAY_ELEMENT_EXPRESSION__ARRAY;
7import static com.framsticks.framclipse.framScript.FramScriptPackage.Literals.QUALIFIED_EXPRESSION__CHILD;
8import static com.framsticks.framclipse.framScript.FramScriptPackage.Literals.QUALIFIED_EXPRESSION__PARENT;
9
10import java.util.ArrayList;
11import java.util.List;
12import java.util.Map;
13import java.util.Map.Entry;
14
15import org.eclipse.emf.common.util.EList;
16import org.eclipse.emf.common.util.TreeIterator;
17import org.eclipse.emf.common.util.URI;
18import org.eclipse.emf.ecore.EObject;
19import org.eclipse.emf.ecore.EReference;
20import org.eclipse.emf.ecore.EStructuralFeature;
21import org.eclipse.emf.ecore.resource.Resource;
22import org.eclipse.emf.ecore.resource.ResourceSet;
23import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
24import org.eclipse.xtext.resource.EObjectDescription;
25import org.eclipse.xtext.resource.IEObjectDescription;
26import org.eclipse.xtext.resource.IResourceDescription;
27import org.eclipse.xtext.scoping.IGlobalScopeProvider;
28import org.eclipse.xtext.scoping.IScope;
29import org.eclipse.xtext.scoping.Scopes;
30import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
31import org.eclipse.xtext.scoping.impl.SimpleScope;
32
33import com.framsticks.framclipse.framScript.Block;
34import com.framsticks.framclipse.framScript.Code;
35import com.framsticks.framclipse.framScript.Expdef;
36import com.framsticks.framclipse.framScript.ForEachStatement;
37import com.framsticks.framclipse.framScript.ForStatement;
38import com.framsticks.framclipse.framScript.FramScriptFactory;
39import com.framsticks.framclipse.framScript.Function;
40import com.framsticks.framclipse.framScript.GotoStatment;
41import com.framsticks.framclipse.framScript.Header;
42import com.framsticks.framclipse.framScript.IncludeDeclaration;
43import com.framsticks.framclipse.framScript.Invocation;
44import com.framsticks.framclipse.framScript.LabeledStatement;
45import com.framsticks.framclipse.framScript.Model;
46import com.framsticks.framclipse.framScript.PropertyIncludeDeclaration;
47import com.framsticks.framclipse.framScript.ProposableFunctionImpl;
48import com.framsticks.framclipse.framScript.ProposableVariableDeclarationImpl;
49import com.framsticks.framclipse.framScript.QualifiedExpression;
50import com.framsticks.framclipse.framScript.Show;
51import com.framsticks.framclipse.framScript.Statement;
52import com.framsticks.framclipse.framScript.VariableDeclaration;
53import com.framsticks.framclipse.framScript.VariableDeclarations;
54import com.framsticks.framclipse.framScript.VariableRef;
55import com.framsticks.framclipse.resource.FramScriptResourceDescriptionManager;
56import com.framsticks.framclipse.script.ConstantProvider;
57import com.framsticks.framclipse.script.ExpressionTraverser;
58import com.framsticks.framclipse.script.model.Element;
59import com.framsticks.framclipse.script.model.Type;
60import com.google.common.collect.Lists;
61import com.google.common.collect.Maps;
62import com.google.inject.Inject;
63
64/**
65 * This class contains custom scoping description.
66 *
67 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
68 * on how and when to use it.
69 */
70public class FramScriptScopeProvider extends AbstractDeclarativeScopeProvider {
71
72        private final ConstantProvider provider;
73
74        private final ExpressionTraverser traverser;
75
76        @Inject
77        private IGlobalScopeProvider globalScopeProvider;
78
79        @Inject
80        private FramScriptResourceDescriptionManager descriptionManager;
81
82        private Resource resource;
83
84        private Map<String, IScope> typesScopes = Maps.newHashMap();
85        private Map<Type, IScope> functionScopes = Maps.newHashMap();
86        private Map<Type, IScope> fieldScopes = Maps.newHashMap();
87
88        private FramScriptImportUriResolver resolver = new FramScriptImportUriResolver();
89
90        @Inject
91        public FramScriptScopeProvider(ConstantProvider provider, ExpressionTraverser traverser) {
92                this.provider = provider;
93                this.traverser = traverser;
94                this.resource = new ResourceImpl(URI.createURI("/"));
95                initTypes();
96        }
97
98        @Override
99        public IScope getScope(EObject context, EReference reference) {
100                if (reference.getEContainingClass().getName().equals("VariableRef") &&
101                                reference.getName().equals("var")) {
102                        if (context instanceof VariableRef) {
103                                QualifiedExpression parent = getParent((VariableRef) context);
104                                if (parent != null) {
105                                        return getScope(parent, reference);
106                                }
107                                EObject object = context;
108                                while (object != null && !(object.eContainer() instanceof Block ||
109                                                object.eContainer() instanceof ForStatement ||
110                                                object.eContainer() instanceof ForEachStatement)) {
111                                        object = object.eContainer();
112                                }
113                                Statement statement = (Statement) object;
114                                EObject blockOrFor = object.eContainer();
115
116                                if (blockOrFor instanceof Block) {
117                                        Block block = (Block) blockOrFor;
118                                        return Scopes.scopeFor(precedingDeclarations(block, statement),
119                                                        nextValidScope(block, reference));
120                                } else
121                                        return getScope(blockOrFor, reference);
122                        } else if (context.eContainer() instanceof Block) {
123                                Block block = (Block) context.eContainer();
124                                List<VariableDeclaration> vars = precedingDeclarations(block, (Statement)context);
125                                IScope outerScope = Scopes.scopeFor(vars, nextValidScope(block, reference));
126                                if (context instanceof Block) {
127                                        return outerScope;
128                                }
129                                return new SimpleScope(outerScope,
130                                                super.getScope(context, reference).getAllElements());
131                        }
132                }
133                return super.getScope(context, reference);
134        }
135
136        private IScope nextValidScope(EObject context, EReference ref) {
137                if (ref.getEContainingClass().getName().equals("VariableRef") &&
138                                ref.getName().equals("var")) {
139                        if (context instanceof Block) {
140                                EObject parent = context.eContainer();
141                                if (parent == null) {   // may happen in .inc files.
142                                        return scope_VariableRef_var((Model)context, ref);
143                                }
144                                if (!(parent instanceof Block)) {
145                                        return getScope(parent, ref);
146                                }
147                        }
148                }
149                return getScope(context, ref);
150        }
151
152        private List<VariableDeclaration> precedingDeclarations(Block block, Statement statement){
153                List<VariableDeclaration> vars = Lists.newArrayList();
154                for (Statement stmt : block.getStatements()) {
155                        // Everything after the statement is out of scope.
156                        if (stmt == statement) {
157                                break;
158                        } else if (stmt instanceof VariableDeclarations) {
159                                vars.addAll(((VariableDeclarations) stmt).getVars());
160                        }
161                }
162                return vars;
163        }
164
165        protected void initTypes() {
166                List<Function> allFunctions = Lists.newArrayList();
167                List<VariableDeclaration> allFields = Lists.newArrayList();
168               
169                for (Entry<String, Map<String, Type>> entry : provider.getTypes().entrySet()) {
170                        List<VariableDeclaration> types = Lists.newArrayList();
171                        for (Type type : entry.getValue().values()) {
172                                types.add(createType(type));
173                               
174                                ArrayList<Function> functions = Lists.newArrayList();
175                                ArrayList<VariableDeclaration> fields = Lists.newArrayList();
176                                for (Element element : type.getElements()) {
177                                        if (element.isFunction()) {
178                                                functions.add(createFunction(element));
179                                        } else {
180                                                fields.add(createField(element));
181                                        }
182                                }
183                                functionScopes.put(type, Scopes.scopeFor(functions));
184                                fieldScopes.put(type, Scopes.scopeFor(fields));
185                                allFunctions.addAll(functions);
186                                allFields.addAll(fields);
187                        }
188                        typesScopes.put(entry.getKey(), Scopes.scopeFor(types));
189                }
190               
191                functionScopes.put(null, Scopes.scopeFor(allFunctions));
192                fieldScopes.put(null, Scopes.scopeFor(allFields));
193        }
194       
195        protected IScope scope_Invocation_function(Invocation inv, EReference ref) {
196                QualifiedExpression parent = getParent(inv);
197                IScope scope;
198                if (parent != null) {
199                        scope = scope_Invocation_function(parent, ref);
200                } else {
201                        scope = scope_Invocation_function(getFunction(inv), ref);
202                }
203                Show show = getShow(inv);
204                IScope showScope = show == null ? IScope.NULLSCOPE :
205                        new SimpleScope(filterByArgumentsCount(getScope(show, ref), inv.getArgs().size()));
206                return new SimpleScope(showScope, filterByArgumentsCount(scope, inv.getArgs().size()));
207        }
208
209        protected IScope scope_Invocation_function(QualifiedExpression inv, EReference ref) {
210                Type type = traverser.getLastType(inv);
211                IScope scope = functionScopes.get(type);
212                return scope != null ? scope : IScope.NULLSCOPE;
213        }
214
215        protected IScope scope_Invocation_function(Function f, EReference ref) {
216                IScope scope = delegateGetScope(f, ref);
217                scope = addAliases(scope);
218                return scope;
219        }
220
221        protected IScope scope_Invocation_function(Show show, EReference ref) {
222                return addAliases(new SimpleScope(showExpdefIncludes(show, ref)));
223        }
224
225        /*
226         * Make sure scope contains only function descriptions.
227         */
228        private List<IEObjectDescription> filterByArgumentsCount(IScope scope, int size) {
229                List<IEObjectDescription> filtered = Lists.newArrayList();
230                for (IEObjectDescription desc : scope.getAllElements()) {
231                        Function function = (Function) desc.getEObjectOrProxy();
232                        if (function.getParams().size() == size) {
233                                filtered.add(desc);
234                        }
235                }
236                return filtered;
237        }
238
239        private IScope addAliases(IScope scope) {
240                Iterable<IEObjectDescription> contents = scope.getAllElements();
241                List<IEObjectDescription> withAliases = Lists.newArrayList(contents);
242                for (IEObjectDescription desc : contents) {
243                        Function function = (Function) desc.getEObjectOrProxy();
244                        for (String string : function.getAliases()) {
245                                withAliases.add(EObjectDescription.create(string, function));
246                        }
247                }
248                return new SimpleScope(withAliases);
249        }
250
251        protected IScope scope_VariableRef_var(QualifiedExpression var, EReference ref) {
252                Type type = traverser.getLastType(var);
253                IScope scope = fieldScopes.get(type);
254                return new SimpleScope(nextValidScope(var.eContainer(), ref),
255                                scope != null ? scope.getAllElements() : new ArrayList<IEObjectDescription>());
256        }
257
258        protected IScope scope_VariableRef_var(Block block, EReference ref) {
259                // Scope for Blocks is constructed earlier in getScope, so we stop here.
260                return IScope.NULLSCOPE;
261        }
262
263        protected IScope scope_VariableRef_var(ForStatement fs, EReference ref) {
264                List<VariableDeclaration> vars = Lists.newArrayList();
265                Statement init = fs.getInit();
266                if (init instanceof VariableDeclarations) {
267                        vars.addAll(((VariableDeclarations)init).getVars());
268                }
269                return Scopes.scopeFor(vars, getScope(fs.eContainer(), ref));
270        }
271
272        protected IScope scope_VariableRef_var(ForEachStatement fes, EReference ref) {
273                List<VariableDeclaration> vars = Lists.newArrayList();
274                if (fes.getDecl() == null)
275                        return getScope(fes.eContainer(), ref);
276                else {
277                        vars.add(fes.getDecl());
278                        return Scopes.scopeFor(vars, getScope(fes.eContainer(), ref));
279                }
280        }
281
282        /*
283         * Blocks are handled specially in getScope,
284         * and scope_VariableRef_var(Block, EReference) returns IScope.NULLSCOPE,
285         * which breaks the chain of scopes,
286         * so we have to ensure each unspecified scope_VariableRef_var call
287         * is redirected to getScope in this class, instead of super.getScope.
288         */
289        protected IScope scope_VariableRef_var(EObject ctx, EReference ref) {
290                EObject parent = ctx.eContainer();
291                if (parent == null)
292                        return IScope.NULLSCOPE;
293                else
294                        return getScope(parent, ref);
295        }
296
297        protected IScope scope_VariableRef_var(Function f, EReference ref) {
298                List<VariableDeclaration> vars = Lists.newArrayList();
299                vars.addAll(f.getParams());
300                return Scopes.scopeFor(vars, getScope(f.eContainer(), ref));
301        }
302
303        protected IScope scope_VariableRef_var(Code code, EReference ref) {
304                List<VariableDeclaration> vars = Lists.newArrayList();
305                for (VariableDeclarations declarations : code.getGlobals()) {
306                        vars.addAll(declarations.getVars());
307                }
308                return Scopes.scopeFor(vars, getScope(code.eContainer(), ref));
309        }
310
311        private List<IEObjectDescription> showExpdefIncludes(Show show, EReference ref) {
312                ArrayList<IEObjectDescription> result = Lists.newArrayList();
313                // Get expdef header
314                Header expdefHeader= null;
315                for (Header h : show.getHeaders()) {
316                        if ("expdef:".equals(h.getName())) {
317                                        expdefHeader = h;
318                                        break;
319                        }
320                }
321                if (expdefHeader == null)
322                        return result;
323                // Find resource by name
324                ResourceSet resourceSet = show.eResource().getResourceSet();
325                String expdefFile = resolver.resolve(expdefHeader);
326                Resource expdefResource = findResourceByName(resourceSet, expdefFile);
327                if (expdefResource == null)
328                        return result;
329                // Mine include directives
330                ArrayList<String> included = Lists.newArrayList();
331                Expdef expdef = (Expdef) expdefResource.getContents().get(0);
332                for (PropertyIncludeDeclaration pid : expdef.getPropertyImports())
333                        included.add(pid.getImportURI());
334                for (IncludeDeclaration id : expdef.getCode().getIncludes())
335                        included.add(id.getImportURI());
336                // Get globals
337                for (String filename : included) {
338                        Resource includedResource = findResourceByName(resourceSet, filename);
339                        if (includedResource == null) {
340                                String showURI = show.eResource().getURI().toString();
341                                URI includeURI = URI.createURI(showURI.subSequence(
342                                                0, showURI.lastIndexOf("/")+1) + filename);
343                                includedResource = resourceSet.getResource(
344                                                includeURI, true);
345                                if (includedResource == null)
346                                        continue;
347                        }
348                        IResourceDescription resourceDescription =
349                                        descriptionManager.getResourceDescription(includedResource);
350                        result.addAll(Lists.newArrayList(resourceDescription.getExportedObjectsByType(ref.getEReferenceType())));
351                }
352                return result;
353        }
354
355        private Resource findResourceByName(ResourceSet resourceSet,
356                        String filename) {
357                for (Resource res : resourceSet.getResources()) {
358                        if (res.getURI().toString().endsWith("/" + filename)) {
359                                return res;
360                        }
361                }
362                return null;
363        }
364
365        protected IScope scope_VariableRef_var(Model model, EReference ref) {
366                IScope outerScope = globalScopeProvider.getScope(model.eResource(), ref, null);
367                if (model instanceof Show) {
368                        outerScope = new SimpleScope(outerScope, showExpdefIncludes((Show)model, ref));
369                }
370                String ext = getExtension(model);
371                if ("inc".equals(ext))  // There is no description for .inc in XML,
372                        ext = "script"; // so we pretend it is a .script.
373                IScope types = typesScopes.get(ext);
374                return new SimpleScope(outerScope, types.getAllElements());
375        }
376
377        protected IScope scope_GotoStatment_dest(GotoStatment stmt, EReference ref) {
378                Block code = getFunction(stmt).getCode();
379                List<LabeledStatement> labels = Lists.newArrayList();
380                for (TreeIterator<EObject> i = code.eAllContents(); i.hasNext();) {
381                        EObject object = i.next();
382                        if (object instanceof LabeledStatement) {
383                                labels.add((LabeledStatement) object);
384                        }
385                }
386                return Scopes.scopeFor(labels);
387        }
388
389        protected IScope scope_Property(Show show, EReference ref) {
390                return new SimpleScope(delegateGetScope(show, ref), showExpdefIncludes(show, ref));
391        }
392
393        protected IScope scope_State(Show show, EReference ref) {
394                return new SimpleScope(delegateGetScope(show, ref), showExpdefIncludes(show, ref));
395        }
396
397        private Function createFunction(Element element) {
398                Function function = new ProposableFunctionImpl(element);
399                EList<VariableDeclaration> params = function.getParams();
400                for (int i = 0; i < element.getArguments().size(); i++) {
401                        params.add(createVariableDeclaration(element.getArguments().get(i).getName(i)));
402                }
403                resource.getContents().add(function);
404                return function;
405        }
406
407        private VariableDeclaration createField(Element element) {
408                VariableDeclaration field = new ProposableVariableDeclarationImpl(element);
409                resource.getContents().add(field);
410                return field;
411        }
412
413        private VariableDeclaration createType(Type type) {
414                VariableDeclaration global = new ProposableVariableDeclarationImpl(type);
415                resource.getContents().add(global);
416                return global;
417        }
418
419        private VariableDeclaration createVariableDeclaration(String name) {
420                VariableDeclaration var = FramScriptFactory.eINSTANCE.createVariableDeclaration();
421                var.setName(name);
422                resource.getContents().add(var);
423                return var;
424        }
425
426        private QualifiedExpression getParent(QualifiedExpression expr) {
427                EStructuralFeature feature = expr.eContainingFeature();
428                if (feature.equals(QUALIFIED_EXPRESSION__CHILD)) {
429                        return (QualifiedExpression) expr.eContainer();
430                } else if (feature.equals(QUALIFIED_EXPRESSION__PARENT)
431                                || feature.equals(ARRAY_ELEMENT_EXPRESSION__ARRAY)) {
432                        return getParent((QualifiedExpression) expr.eContainer());
433                } else {
434                        return null;
435                }
436        }
437
438        private Function getFunction(EObject object) {
439                while (object != null && !(object instanceof Function)) {
440                        object = object.eContainer();
441                }
442                return (Function) object;
443        }
444
445        private Show getShow(EObject object) {
446                while (object != null && !(object instanceof Show)) {
447                        object = object.eContainer();
448                }
449                return (Show) object;
450        }
451
452        private String getExtension(EObject object) {
453                return object.eResource().getURI().fileExtension();
454        }
455
456}
Note: See TracBrowser for help on using the repository browser.