Files
spider-compiler/src/spider/compiler/text/TextReader.cpp
T

236 lines
5.7 KiB
C++

#include "TextReader.hpp"
#include <spider/compiler/text/utf8.hpp>
#include <stdexcept>
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<u32> TextReader::current() {
if (bufferIndex < buffer.size()) {
return buffer[bufferIndex];
}
return {};
}
/**
* Reads the next character and advances the position tracker.
*/
optional<u32> 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<u32> 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<std::istringstream>(buffer)) {
}
std::istream& StringTextReader::getStream() {
return *stringStream;
}
void StringTextReader::set(const std::string& newText) {
buffer = newText;
stringStream = std::make_unique<std::istringstream>(buffer);
}
void StringTextReader::append(const std::string& extraText) {
std::streampos pos = stringStream->tellg();
buffer += extraText;
stringStream = std::make_unique<std::istringstream>(buffer);
stringStream->seekg(pos);
}
}