export class Stream { private pos: number; constructor(private data: string) { this.data = data; this.pos = 0; } get eof() { return this.pos === this.data.length; } peek() { return this.data.slice(this.pos, this.pos + 1); } read() { const value = this.data[this.pos]; this.pos++; return value; } panic(message: string) { throw new Error(`Stream Error: ${message}, at position: ${this.pos}`); } readSpaces() { return this.readWhile(" ", "\t"); } readWhile(...chars: string[]) { let value = ""; while (!this.eof && chars.includes(this.peek())) { value += this.read(); } return value; } readUntil(...chars: string[]) { const nextIndex = chars.reduce((index, chr) => { const i = this.data.indexOf(chr, this.pos); return i < index && i != -1 ? i : index; }, this.data.length - 1); const value = this.data.substring(this.pos, nextIndex); this.pos = nextIndex; return value; } }