All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.xbase.compiler.ScopeStack Maven / Gradle / Ivy

There is a newer version: 2.4.3
Show newest version
/*******************************************************************************
 * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.xtext.xbase.compiler;

import static com.google.common.collect.Lists.*;
import static com.google.common.collect.Maps.*;
import static com.google.common.collect.Sets.*;
import static java.util.Collections.*;

import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
 * @author Sven Efftinge - Initial contribution and API
 */
public class ScopeStack {
	
	private Stack  scopes = new Stack();
	
	public void openScope(boolean pseudoScope) {
		scopes.push(new Scope(pseudoScope));
	}
	
	public void closeScope() {
		scopes.pop();
	}
	
	@Nullable
	public Object get(String name) {
		if (name == null)
			throw new NullPointerException("name");
		if (scopes.isEmpty())
			return null;
		
		int size = scopes.size();
		int i = size - 1;
		while (i >= 0) {
			Scope currentScope = scopes.get(i--);
			Variable var = currentScope.get(name);
			if (var != null)
				return var.referenced;
		}
		return null;
	}
	
	@Nullable
	public String getName(Object referenced) {
		if (referenced == null)
			throw new NullPointerException("referenced");
		if (scopes.isEmpty())
			return null;
		
		int size = scopes.size();
		int i = size - 1;
		while (i >= 0) {
			Scope currentScope = scopes.get(i--);
			for (Variable v : currentScope.variables()) {
				if (v.referenced.equals(referenced))
					return v.name;
			}
		}
		return null;
	}
	
	/**
	 * provides and registers a fresh variable in the current scope.
	 * It takes parent scopes into account and only reuses names of synthetic variables from parent scopes.
	 * Pseudo scopes are treated as if they were part of their parent scope.
	 */
	@NonNull
	public String declareVariable(@NonNull Object key, @NonNull String proposedName, boolean synthetic) {
		if (scopes.isEmpty())
			throw new IllegalArgumentException("No scope has been opened yet.");
		Scope currentScope = scopes.peek();
		final Set names = newHashSet();
		boolean scopeClosed = false;
		// add only the non-synthetic variables, since they could be referenced from nested scopes.
		for (Scope scope : reverse(newArrayList(scopes))) {
			for (Variable variable : scope.variables()) {
				if (!scopeClosed || !variable.synthetic)
					names.add(variable.name);
			}
			scopeClosed = scopeClosed || !scope.pseudoScope;
			// if we left the current scope (incl. pseudo scopes) and the variable is not synthetic, we can stop collecting names. 
			// Overriding names from outside is ok in that case. 
			if (scopeClosed && !synthetic)
				break;
		}
		String newName = findNewName(names, proposedName);
		currentScope.addVariable(newName, synthetic, key);
		return newName;
	}
	
	@NonNull 
	protected String findNewName(@NonNull Set names, @NonNull String proposedName) {
		if (names.contains(proposedName)) {
			for (int i = 1; i < Integer.MAX_VALUE; i++) {
				String newProposal = proposedName + "_" + i;
				if (!names.contains(newProposal))
					return newProposal;
			}
		}
		return proposedName;
	}

	@NonNullByDefault
	static class Scope {
		public Scope(boolean pseudoScope) {
			this.pseudoScope = pseudoScope;
		}
		/**
		 * whether this scope is just a pseudo scope. 
		 * I.e. not backed up by a real Java scope.
		 */
		public boolean pseudoScope = false;
		
		private Map variables = newHashMap();
		
		public void addVariable(String name, boolean synthetic, Object element) {
			if (variables.containsKey(name))
				throw new IllegalArgumentException("variable '"+name+"' already declared in scope.");
			variables.put(name, new Variable(name, synthetic,element));
		}
		
		public Set variableNames() {
			return variables.keySet();
		}
		
		public Iterable variables() {
			return variables.values();
		}
		
		@Nullable
		public Variable get(String name) {
			return variables.get(name);
		}
		
		@Override
		public String toString() {
			return (pseudoScope?"[PSEUDO]":"")+variables;
		}
	}
	
	@NonNullByDefault
	final static class Variable {
		Variable(String name2, boolean synthetic2, Object referenced) {
			this.name = name2;
			this.synthetic = synthetic2;
			this.referenced = referenced;
		}
		public String name;
		public boolean synthetic;
		public Object referenced;
		
		@Override
		public String toString() {
			return referenced.getClass().getSimpleName();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy