/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ #include "Token.h" #include "Exceptions.h" #include "assert.h" #include "TokenSource.h" #include "support/Arrays.h" #include "misc/Interval.h" #include "RuleContext.h" #include "WritableToken.h" #include "UnbufferedTokenStream.h" using namespace antlr4; UnbufferedTokenStream::UnbufferedTokenStream(TokenSource *tokenSource) : UnbufferedTokenStream(tokenSource, 256) { } UnbufferedTokenStream::UnbufferedTokenStream(TokenSource *tokenSource, int /*bufferSize*/) : _tokenSource(tokenSource), _lastToken(nullptr), _lastTokenBufferStart(nullptr) { InitializeInstanceFields(); fill(1); // prime the pump } UnbufferedTokenStream::~UnbufferedTokenStream() { } Token* UnbufferedTokenStream::get(size_t i) const { // get absolute index size_t bufferStartIndex = getBufferStartIndex(); if (i < bufferStartIndex || i >= bufferStartIndex + _tokens.size()) { throw IndexOutOfBoundsException(std::string("get(") + std::to_string(i) + std::string(") outside buffer: ") + std::to_string(bufferStartIndex) + std::string("..") + std::to_string(bufferStartIndex + _tokens.size())); } return _tokens[i - bufferStartIndex].get(); } Token* UnbufferedTokenStream::LT(ssize_t i) { if (i == -1) { return _lastToken; } sync(i); ssize_t index = static_cast(_p) + i - 1; if (index < 0) { throw IndexOutOfBoundsException(std::string("LT(") + std::to_string(i) + std::string(") gives negative index")); } if (index >= static_cast(_tokens.size())) { assert(_tokens.size() > 0 && _tokens.back()->getType() == EOF); return _tokens.back().get(); } return _tokens[static_cast(index)].get(); } size_t UnbufferedTokenStream::LA(ssize_t i) { return LT(i)->getType(); } TokenSource* UnbufferedTokenStream::getTokenSource() const { return _tokenSource; } std::string UnbufferedTokenStream::getText() { return ""; } std::string UnbufferedTokenStream::getText(RuleContext* ctx) { return getText(ctx->getSourceInterval()); } std::string UnbufferedTokenStream::getText(Token *start, Token *stop) { return getText(misc::Interval(start->getTokenIndex(), stop->getTokenIndex())); } void UnbufferedTokenStream::consume() { if (LA(1) == EOF) { throw IllegalStateException("cannot consume EOF"); } // buf always has at least tokens[p==0] in this method due to ctor _lastToken = _tokens[_p].get(); // track last token for LT(-1) // if we're at last token and no markers, opportunity to flush buffer if (_p == _tokens.size() - 1 && _numMarkers == 0) { _tokens.clear(); _p = 0; _lastTokenBufferStart = _lastToken; } else { ++_p; } ++_currentTokenIndex; sync(1); } /// /// Make sure we have 'need' elements from current position . Last valid /// {@code p} index is {@code tokens.length-1}. {@code p+need-1} is the tokens index 'need' elements /// ahead. If we need 1 element, {@code (p+1-1)==p} must be less than {@code tokens.length}. /// void UnbufferedTokenStream::sync(ssize_t want) { ssize_t need = (static_cast(_p) + want - 1) - static_cast(_tokens.size()) + 1; // how many more elements we need? if (need > 0) { fill(static_cast(need)); } } /// /// Add {@code n} elements to the buffer. Returns the number of tokens /// actually added to the buffer. If the return value is less than {@code n}, /// then EOF was reached before {@code n} tokens could be added. /// size_t UnbufferedTokenStream::fill(size_t n) { for (size_t i = 0; i < n; i++) { if (_tokens.size() > 0 && _tokens.back()->getType() == EOF) { return i; } add(_tokenSource->nextToken()); } return n; } void UnbufferedTokenStream::add(std::unique_ptr t) { WritableToken *writable = dynamic_cast(t.get()); if (writable != nullptr) { writable->setTokenIndex(int(getBufferStartIndex() + _tokens.size())); } _tokens.push_back(std::move(t)); } /// /// Return a marker that we can release later. ///

/// The specific marker value used for this class allows for some level of /// protection against misuse where {@code seek()} is called on a mark or /// {@code release()} is called in the wrong order. ///

ssize_t UnbufferedTokenStream::mark() { if (_numMarkers == 0) { _lastTokenBufferStart = _lastToken; } int mark = -_numMarkers - 1; _numMarkers++; return mark; } void UnbufferedTokenStream::release(ssize_t marker) { ssize_t expectedMark = -_numMarkers; if (marker != expectedMark) { throw IllegalStateException("release() called with an invalid marker."); } _numMarkers--; if (_numMarkers == 0) { // can we release buffer? if (_p > 0) { // Copy tokens[p]..tokens[n-1] to tokens[0]..tokens[(n-1)-p], reset ptrs // p is last valid token; move nothing if p==n as we have no valid char _tokens.erase(_tokens.begin(), _tokens.begin() + static_cast(_p)); _p = 0; } _lastTokenBufferStart = _lastToken; } } size_t UnbufferedTokenStream::index() { return _currentTokenIndex; } void UnbufferedTokenStream::seek(size_t index) { // seek to absolute index if (index == _currentTokenIndex) { return; } if (index > _currentTokenIndex) { sync(ssize_t(index - _currentTokenIndex)); index = std::min(index, getBufferStartIndex() + _tokens.size() - 1); } size_t bufferStartIndex = getBufferStartIndex(); if (bufferStartIndex > index) { throw IllegalArgumentException(std::string("cannot seek to negative index ") + std::to_string(index)); } size_t i = index - bufferStartIndex; if (i >= _tokens.size()) { throw UnsupportedOperationException(std::string("seek to index outside buffer: ") + std::to_string(index) + " not in " + std::to_string(bufferStartIndex) + ".." + std::to_string(bufferStartIndex + _tokens.size())); } _p = i; _currentTokenIndex = index; if (_p == 0) { _lastToken = _lastTokenBufferStart; } else { _lastToken = _tokens[_p - 1].get(); } } size_t UnbufferedTokenStream::size() { throw UnsupportedOperationException("Unbuffered stream cannot know its size"); } std::string UnbufferedTokenStream::getSourceName() const { return _tokenSource->getSourceName(); } std::string UnbufferedTokenStream::getText(const misc::Interval &interval) { size_t bufferStartIndex = getBufferStartIndex(); size_t bufferStopIndex = bufferStartIndex + _tokens.size() - 1; size_t start = interval.a; size_t stop = interval.b; if (start < bufferStartIndex || stop > bufferStopIndex) { throw UnsupportedOperationException(std::string("interval ") + interval.toString() + " not in token buffer window: " + std::to_string(bufferStartIndex) + ".." + std::to_string(bufferStopIndex)); } size_t a = start - bufferStartIndex; size_t b = stop - bufferStartIndex; std::stringstream ss; for (size_t i = a; i <= b; i++) { Token *t = _tokens[i].get(); if (i > 0) ss << ", "; ss << t->getText(); } return ss.str(); } size_t UnbufferedTokenStream::getBufferStartIndex() const { return _currentTokenIndex - _p; } void UnbufferedTokenStream::InitializeInstanceFields() { _p = 0; _numMarkers = 0; _currentTokenIndex = 0; }