/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package dtool.parser.common; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import static melnorme.utilbox.misc.NumberUtil.isInRange; import java.util.Collections; import java.util.List; import java.util.ListIterator; import melnorme.utilbox.misc.IteratorUtil; import dtool.parser.DeeLexer; import dtool.parser.DeeTokens; public class LexerResult { public final String source; public final List<LexElement> tokenList; public LexerResult(String source, List<LexElement> tokenList) { this.source = source; this.tokenList = Collections.unmodifiableList(tokenList); assertTrue(tokenList.size() > 0); assertTrue(tokenList.get(tokenList.size()-1).isEOF()); } public ListIterator<LexElement> getFirstLexElementAtOffset(final int offset) { assertTrue(offset <= source.length()); ListIterator<LexElement> iterator = tokenList.listIterator(); while(true) { assertTrue(iterator.hasNext()); LexElement lexElement = iterator.next(); assertTrue(lexElement.getFullRangeStartPos() <= offset); if(lexElement.isEOF()) break; if(offset <= lexElement.getEndPos()) break; } return iterator; } public IToken findFirstTokenAtOffset(final int offset) { ListIterator<LexElement> iterator = getFirstLexElementAtOffset(offset); LexElement lexElement = IteratorUtil.getCurrentElement(iterator); return findTokenInLexElement(lexElement, offset); } public IToken findTokenInLexElement(LexElement lexElement, int offset) { assertTrue(isInRange(lexElement.getFullRangeStartPos(), offset, lexElement.getEndPos())); if(offset >= lexElement.getStartPos()) { return lexElement; } else { // Search in comments, token is in the subchannel range [FullRangeStartPos .. StartPos] int searchStartPos = lexElement.getFullRangeStartPos(); // We don't store subchannel tokens, so we have to reparse to find them: return findFirstTokenAtOffset(source, searchStartPos, offset); } } public TokenAtOffsetResult findTokenAtOffset(int offset) { ListIterator<LexElement> lexElementCursor = getFirstLexElementAtOffset(offset); LexElement lexElement = IteratorUtil.getCurrentElement(lexElementCursor); IToken tokenAtLeft; IToken tokenAtRight; if(lexElement.getFullRangeStartPos() < offset) { assertTrue(offset <= lexElement.getEndPos()); tokenAtLeft = findTokenInLexElement(lexElement, offset); if(offset < tokenAtLeft.getEndPos()) { tokenAtRight = tokenAtLeft; } else { assertTrue(tokenAtLeft.getEndPos() == offset); if(offset < lexElement.getEndPos()) { tokenAtRight = findTokenInLexElement(lexElement, tokenAtLeft.getEndPos()); } else { assertTrue(tokenAtLeft.getEndPos() == offset); if(lexElementCursor.hasNext()) { LexElement nextLexElement = lexElementCursor.next(); tokenAtRight = findTokenInLexElement(nextLexElement, offset); } else { tokenAtRight = null; } } } } else { tokenAtLeft = null; assertTrue(offset == lexElement.getFullRangeStartPos()); tokenAtRight = findTokenInLexElement(lexElement, offset); } return new TokenAtOffsetResult(tokenAtLeft, tokenAtRight); } public static class TokenAtOffsetResult { public final IToken atLeft; public final IToken atRight; public TokenAtOffsetResult(IToken tokenAtOffsetLeft, IToken tokenAtOffsetRight) { this.atLeft = tokenAtOffsetLeft; this.atRight = tokenAtOffsetRight; } public boolean isSingleToken() { return atLeft == atRight; } } protected static Token findFirstTokenAtOffset(String source, int startPos, final int offset) { assertTrue(startPos <= offset); DeeLexer lexer = new DeeLexer(source); lexer.reset(startPos); while(true) { Token token = lexer.next(); if(offset <= token.getEndPos()) return token; assertTrue(token.type != DeeTokens.EOF); } } }