-
-
Notifications
You must be signed in to change notification settings - Fork 351
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: introduce the concept of lexical scope (interface LexicalSco…
…pe) (#2813)
- Loading branch information
1 parent
8b5f0c5
commit a16f436
Showing
6 changed files
with
632 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* Copyright (C) 2006-2018 INRIA and contributors | ||
* Spoon - http://spoon.gforge.inria.fr/ | ||
* | ||
* This software is governed by the CeCILL-C License under French law and | ||
* abiding by the rules of distribution of free software. You can use, modify | ||
* and/or redistribute the software under the terms of the CeCILL-C license as | ||
* circulated by CEA, CNRS and INRIA at http://www.cecill.info. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. | ||
* | ||
* The fact that you are presently reading this means that you have had | ||
* knowledge of the CeCILL-C license and that you accept its terms. | ||
*/ | ||
package spoon.reflect.visitor; | ||
|
||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.declaration.CtNamedElement; | ||
import spoon.support.Experimental; | ||
|
||
import java.util.function.Function; | ||
|
||
/** | ||
* Represents that a lexical scope in the language | ||
* | ||
* Note that scopes are changing after variable declaration. For example: | ||
* | ||
* void draw() { | ||
* //scope1 | ||
* int a; | ||
* //scope2 | ||
* int b; | ||
* //scope3 | ||
* } | ||
* | ||
* See https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping | ||
*/ | ||
@Experimental | ||
public interface LexicalScope { | ||
/** adds an element to the scope */ | ||
LexicalScope addNamedElement(CtNamedElement element); | ||
|
||
/** | ||
* @return the {@link CtElement} which represents the current scope | ||
*/ | ||
CtElement getScopeElement(); | ||
|
||
/** | ||
* @param name to be searched simple name | ||
* @param fnc is called for each named element with same simple name, which is defined in this or parent {@link LexicalScope}. | ||
* Function `fnc` is called as long as there are some matching elements and `fnc` returns null. | ||
* If `fnc` returns not null value then searching is stopped and that value is a returned | ||
* @return the value returned by `fnc` or null | ||
*/ | ||
<T> T forEachElementByName(String name, Function<? super CtNamedElement, T> fnc); | ||
|
||
} |
107 changes: 107 additions & 0 deletions
107
src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package spoon.reflect.visitor; | ||
|
||
import spoon.reflect.code.CtBlock; | ||
import spoon.reflect.code.CtCatch; | ||
import spoon.reflect.code.CtLambda; | ||
import spoon.reflect.code.CtLocalVariable; | ||
import spoon.reflect.declaration.CtAnnotationType; | ||
import spoon.reflect.declaration.CtClass; | ||
import spoon.reflect.declaration.CtCompilationUnit; | ||
import spoon.reflect.declaration.CtConstructor; | ||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.declaration.CtEnum; | ||
import spoon.reflect.declaration.CtInterface; | ||
import spoon.reflect.declaration.CtMethod; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.util.ArrayDeque; | ||
import java.util.Deque; | ||
|
||
/** | ||
* Responsible for building lexical scopes. | ||
* | ||
*/ | ||
public class LexicalScopeScanner extends EarlyTerminatingScanner<Object> { | ||
private final Deque<LexicalScope> scopes = new ArrayDeque<>(); | ||
protected void enter(spoon.reflect.declaration.CtElement e) { | ||
LexicalScope newFinder = onElement(scopes.peek(), e); | ||
if (newFinder != null) { | ||
scopes.push(newFinder); | ||
} | ||
} | ||
protected void exit(spoon.reflect.declaration.CtElement e) { | ||
LexicalScope topFinder = scopes.peek(); | ||
if (topFinder != null && topFinder.getScopeElement() == e) { | ||
//we are living scope of this ConflictFinder. Pop it | ||
scopes.pop(); | ||
} | ||
} | ||
private static NameScopeImpl EMPTY = new NameScopeImpl(null, null); | ||
/** | ||
* @return {@link LexicalScope} of actually scanned element. The {@link LexicalScope#forEachElementByName(String, java.util.function.Function)} can be used | ||
* to get all {@link CtElement}s which are mapped to that simple name | ||
*/ | ||
public LexicalScope getCurrentNameScope() { | ||
LexicalScope ns = scopes.peek(); | ||
return ns == null ? EMPTY : ns; | ||
} | ||
|
||
/** | ||
* Call it for each visited CtElement | ||
* @param parent the parent ConflictFinder | ||
* @param target an element | ||
* @return new {@link NameScopeImpl} if `target` element declares new naming scope or null if there is no new scope | ||
*/ | ||
private NameScopeImpl onElement(LexicalScope parent, CtElement target) { | ||
class Visitor extends CtAbstractVisitor { | ||
NameScopeImpl finder = null; | ||
@Override | ||
public void visitCtCompilationUnit(CtCompilationUnit compilationUnit) { | ||
//compilation unit items are added in TypeNameScope, because they depend on the inhertance hierarchy of the type itself | ||
} | ||
@Override | ||
public <T> void visitCtClass(CtClass<T> ctClass) { | ||
finder = new TypeNameScope(parent, ctClass); | ||
} | ||
@Override | ||
public <T> void visitCtInterface(CtInterface<T> intrface) { | ||
finder = new TypeNameScope(parent, intrface); | ||
} | ||
@Override | ||
public <T extends Enum<?>> void visitCtEnum(CtEnum<T> ctEnum) { | ||
finder = new TypeNameScope(parent, ctEnum); | ||
} | ||
@Override | ||
public <A extends Annotation> void visitCtAnnotationType(CtAnnotationType<A> annotationType) { | ||
finder = new TypeNameScope(parent, annotationType); | ||
} | ||
@Override | ||
public <T> void visitCtMethod(CtMethod<T> m) { | ||
finder = new NameScopeImpl(parent, m, m.getParameters()); | ||
} | ||
@Override | ||
public <T> void visitCtConstructor(CtConstructor<T> c) { | ||
finder = new NameScopeImpl(parent, c, c.getParameters()); | ||
} | ||
@Override | ||
public <T> void visitCtLambda(CtLambda<T> lambda) { | ||
finder = new NameScopeImpl(parent, lambda, lambda.getParameters()); | ||
} | ||
@Override | ||
public void visitCtCatch(CtCatch catchBlock) { | ||
finder = new NameScopeImpl(parent, catchBlock).addNamedElement(catchBlock.getParameter()); | ||
} | ||
@Override | ||
public <R> void visitCtBlock(CtBlock<R> block) { | ||
finder = new NameScopeImpl(parent, block); | ||
} | ||
@Override | ||
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) { | ||
parent.addNamedElement(localVariable); | ||
} | ||
}; | ||
Visitor scanner = new Visitor(); | ||
target.accept(scanner); | ||
return scanner.finder; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/** | ||
* Copyright (C) 2006-2018 INRIA and contributors | ||
* Spoon - http://spoon.gforge.inria.fr/ | ||
* | ||
* This software is governed by the CeCILL-C License under French law and | ||
* abiding by the rules of distribution of free software. You can use, modify | ||
* and/or redistribute the software under the terms of the CeCILL-C license as | ||
* circulated by CEA, CNRS and INRIA at http://www.cecill.info. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. | ||
* | ||
* The fact that you are presently reading this means that you have had | ||
* knowledge of the CeCILL-C license and that you accept its terms. | ||
*/ | ||
package spoon.reflect.visitor; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.declaration.CtNamedElement; | ||
import spoon.reflect.declaration.CtParameter; | ||
|
||
/** | ||
* Maps names to CtElements, which are visible at current scanning place | ||
*/ | ||
class NameScopeImpl implements LexicalScope { | ||
|
||
private final LexicalScope parent; | ||
private final CtElement scopeElement; | ||
|
||
public Map<String, CtNamedElement> getElementsByName() { | ||
return elementsByName; | ||
} | ||
|
||
private final Map<String, CtNamedElement> elementsByName = new HashMap<>(); | ||
|
||
NameScopeImpl(LexicalScope parent, CtElement scopeElement, List<CtParameter<?>> parameters) { | ||
this(parent, scopeElement); | ||
for (CtParameter<?> parameter : parameters) { | ||
addNamedElement(parameter); | ||
} | ||
} | ||
|
||
protected NameScopeImpl(LexicalScope parent, CtElement scopeElement) { | ||
this.parent = parent; | ||
this.scopeElement = scopeElement; | ||
} | ||
|
||
@Override | ||
public NameScopeImpl addNamedElement(CtNamedElement element) { | ||
elementsByName.put(element.getSimpleName(), element); | ||
return this; | ||
} | ||
|
||
/** | ||
* @return the {@link CtElement} which represents the current scope | ||
*/ | ||
@Override | ||
public final CtElement getScopeElement() { | ||
return scopeElement; | ||
} | ||
|
||
public final Optional<LexicalScope> getParent() { | ||
return Optional.ofNullable(parent); | ||
} | ||
|
||
/** | ||
* @param name to be searched simple name | ||
* @param consumer is called for each named element with same name which are accessible from this {@link NameScopeImpl} | ||
* as long as there are some elements and consumer returns null. If `consumer` return not null value then it is returned | ||
* @return the value returned by `consumer` or null | ||
*/ | ||
@Override | ||
public <T> T forEachElementByName(String name, Function<? super CtNamedElement, T> consumer) { | ||
T r = forEachByName(elementsByName, name, consumer); | ||
if (scopeElement instanceof CtNamedElement) { | ||
CtNamedElement named = (CtNamedElement) scopeElement; | ||
if (name.equals(named.getSimpleName())) { | ||
r = consumer.apply(named); | ||
if (r != null) { | ||
return r; | ||
} | ||
} | ||
} | ||
if (parent != null) { | ||
return parent.forEachElementByName(name, consumer); | ||
} | ||
return null; | ||
} | ||
|
||
protected static <T> T forEachByName(Map<String, CtNamedElement> map, String name, Function<? super CtNamedElement, T> consumer) { | ||
CtNamedElement named = map.get(name); | ||
if (named != null) { | ||
return consumer.apply(named); | ||
} | ||
return null; | ||
} | ||
} |
Oops, something went wrong.