/*******************************************************************************
* Copyright (c) 2009-2011 CWI
* 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
*
* Contributors:
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.eclipse.editor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.IValue;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.ProductionAdapter;
import org.rascalmpl.values.uptr.TreeAdapter;
import org.rascalmpl.values.uptr.visitors.TreeVisitor;
public class TokenIterator implements Iterator<Object>{
private final List<Token> tokenList;
private final Iterator<Token> tokenIterator;
private boolean showAmb;
public TokenIterator(boolean showAmb, IConstructor parseTree){
this.tokenList = new ArrayList<Token>(1000);
this.showAmb = false;
if(parseTree != null){
parseTree.accept(new LexicalCollector());
}
tokenIterator = tokenList.iterator();
}
public boolean hasNext(){
return tokenIterator.hasNext();
}
public Token next(){
return tokenIterator.next();
}
public void remove(){
throw new UnsupportedOperationException();
}
private class LexicalCollector extends TreeVisitor<RuntimeException>{
private int location;
public LexicalCollector(){
super();
location = 0;
}
public ITree visitTreeAmb(ITree arg) {
if (showAmb) {
int offset = location;
ISourceLocation ambLoc = TreeAdapter.getLocation(arg);
int length = ambLoc != null ? ambLoc.getLength() : TreeAdapter.yield(arg).length();
location += length;
tokenList.add(new Token(TreeAdapter.META_AMBIGUITY, offset, length));
}
else {
TreeAdapter.getAlternatives(arg).iterator().next().accept(this);
}
return arg;
}
public ITree visitTreeAppl(ITree arg){
IValue catAnno = arg.asAnnotatable().getAnnotation("category");
String category = null;
if (catAnno != null) {
category = ((IString) catAnno).getValue();
}
IConstructor prod = TreeAdapter.getProduction(arg);
if (category == null && ProductionAdapter.isDefault(prod)) {
category = ProductionAdapter.getCategory(prod);
}
// It's not so nice to link the sort name to the token color constant ...
if(TreeAdapter.NONTERMINAL_LABEL.equals(ProductionAdapter.getSortName(prod))){
category = TreeAdapter.NONTERMINAL_LABEL;
}
// short cut, if we have source locations and a category we found a long token
ISourceLocation loc = TreeAdapter.getLocation(arg);
// Always sync location with locs because of concrete syntax stuff in Rascal.
if (loc != null) {
location = loc.getOffset();
}
if (category != null && loc != null) {
tokenList.add(new Token(category, location, loc.getLength()));
location += loc.getLength();
return arg;
}
// now we go down in the tree to find more tokens
int offset = location;
for (IValue child : TreeAdapter.getArgs(arg)){
child.accept(this);
}
if (ProductionAdapter.isSkipped(prod)) {
category = TreeAdapter.META_SKIPPED;
}
if (ProductionAdapter.isDefault(prod) && (TreeAdapter.isLiteral(arg) || TreeAdapter.isCILiteral(arg))) {
if (category == null){
category = TreeAdapter.META_KEYWORD;
for (IValue child : TreeAdapter.getArgs(arg)) {
int c = TreeAdapter.getCharacter((ITree) child);
if (c != '-' && !Character.isJavaIdentifierPart(c)){
category = null;
}
}
if (category == null) {
category = TreeAdapter.NORMAL;
}
}
}
if (category != null) {
tokenList.add(new Token(category, offset, loc != null ? loc.getLength() : location - offset));
}
return arg;
}
public ITree visitTreeChar(ITree arg){
++location;
return arg;
}
public ITree visitTreeCycle(ITree arg){
return arg;
}
}
}