Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: introduce the concept of lexical scope (interface LexicalScope) #2813

Merged
merged 7 commits into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/main/java/spoon/reflect/visitor/LexicalScope.java
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 src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java
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;
}
}
104 changes: 104 additions & 0 deletions src/main/java/spoon/reflect/visitor/NameScopeImpl.java
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;
}
}
Loading