#include "TextReader.hpp" #include #include namespace spider { // Text Reader // TextReader::TextReader() : err(false), eof(false), bufferIndex(0) { // Prime the buffer with the first character // so current() is immediately valid fillBufferTo(0); } TextReader::~TextReader() {} bool TextReader::eat(u32 _char) { if(current() == _char) { nextChar(); return true; } return false; } bool TextReader::eat(char _char) { return this->eat(u32(_char)); } bool TextReader::eat(const std::string& chars) { // instead of whatever that was, convert to UTF-32 // and then do an easy compare! std::u32string str; if(!utf8::toUTF32(chars, str)) throw std::runtime_error("Specified invalid UTF-8 string!"); return eat(str); } bool TextReader::eat(const std::u32string& str) { // compare now isize index; for(index = 0; index < str.size(); index++) { if(str[index] != peekChar(index)) return false; } // success! nextChar(index); return true; } char TextReader::readByte() { if (err) return 0; auto& s = getStream(); if(s.bad()) { err = true; errmsg = "Stream raised bad bit."; return 0; } int ch = s.get(); if (ch == std::istream::traits_type::eof()) { eof = true; return 0; } at.byteoff++; return char(ch); } /** * Returns the current character. */ optional TextReader::current() { if (bufferIndex < buffer.size()) { return buffer[bufferIndex]; } return {}; } /** * Reads the next character and advances the position tracker. */ optional TextReader::nextChar(isize n) { // Ensure the character we are moving TO exists if (fillBufferTo(n)) { // advance n characters while(n--) { advance(buffer[bufferIndex]); bufferIndex++; } return current(); } return {}; } /** * Keeps the next n-th character (n = 0 is current). */ optional TextReader::peekChar(isize n) { if (fillBufferTo(n)) return buffer[bufferIndex + n]; return {}; } void TextReader::commit() { if (bufferIndex > 0) { // Erase everything before the current buffer index buffer.erase(buffer.begin(), buffer.begin() + std::ptrdiff_t(bufferIndex)); bufferIndex = 0; } } isize TextReader::push() { return bufferIndex; } void TextReader::pop(isize index) { bufferIndex = std::min(index, bufferIndex); } /** * Updates track position metrics based on the processed character. */ void TextReader::advance(u32 ch) { if (ch == '\n') { at.line++; at.col = 1; } else { at.col++; } } bool TextReader::readChar() { // Read one byte char bytes[4]; isize bindex = 0; bytes[bindex] = readByte(); if (err) return false; if (eof) return false; isize chsize = utf8::seqlen(u8(bytes[bindex])); if(chsize == 0) { err = true; errmsg = "Invalid start of UTF-8 sequence."; return false; } bindex++; while (bindex < chsize) { bytes[bindex] = readByte(); if (err) return false; if (eof) return false; } u32 decodedChar; if(!utf8::decodeArr(bytes, chsize, decodedChar)) { err = true; errmsg = "Invalid UTF-8 sequence."; return false; } buffer.push_back(decodedChar); return true; } /** * Fills the buffer sequentially until it contains at least up * to (bufferIndex + targetOffset). */ bool TextReader::fillBufferTo(isize targetOffset) { isize targetSize = bufferIndex + targetOffset + 1; while (buffer.size() < targetSize) { if(readChar()) continue; return false; } return true; } pos TextReader::getPosition() const { return at; } bool TextReader::isEOF() const{ return eof && bufferIndex >= buffer.size(); } bool TextReader::hasError() const{ return err; } TextReader::operator bool() const { return !isEOF() && !hasError(); } std::string TextReader::getError() const { return errmsg; } // File Reader // FileTextReader::FileTextReader(const std::string& filename) : fileStream(filename, std::ios::binary) { if (!fileStream.is_open()) { throw std::runtime_error("Failed to open file: " + filename); } } std::istream& FileTextReader::getStream() { return fileStream; } // String Reader // StringTextReader::StringTextReader(std::string initialText) : buffer(std::move(initialText)), stringStream(std::make_unique(buffer)) { } std::istream& StringTextReader::getStream() { return *stringStream; } void StringTextReader::set(const std::string& newText) { buffer = newText; stringStream = std::make_unique(buffer); } void StringTextReader::append(const std::string& extraText) { std::streampos pos = stringStream->tellg(); buffer += extraText; stringStream = std::make_unique(buffer); stringStream->seekg(pos); } }