local type InputStream = record str: string offset: number refill: function(): string new: function(InputStream, function(): string): InputStream __setRefill: function(InputStream, function(): string) readCurrentCharacter: function(): string advanceOffset: function(): string end local istream_mt = {__index = InputStream} function InputStream:_manageBuffer() local length = 0 if self.str then length = #self.str else length = -1 end if not self.str then self.str = self.refill() self.offset = 1 end if self.offset > #self.str then self.str = self.refill() self.offset = 1 end end local function initial_stream(): string return "" end -- constructors function InputStream:new(refill: function(): string): InputStream refill = refill or initial_stream local istream = setmetatable( { string = "", offset = 1, } as InputStream, istream_mt) istream:__setRefill(refill) return istream end -- setters/getters function InputStream:__setRefill(func: function(): string) self.refill = func self:_manageBuffer() end -- function InputStream:curr(): string | nil if self.offset > #self.str then self:_manageBuffer() end if #self.str > 0 and self.offset < #self.str + 1 then return self.str:sub(self.offset, self.offset) end return nil end function InputStream:next(): string | nil if self.offset > #self.str then self:_manageBuffer() end local current_char: string | nil = nil if #self.str > 0 then current_char = self.str:sub(self.offset, self.offset) self.offset = self.offset + 1 end return current_char end return InputStream