lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Thanks for the responses on my request for string I/0. In particular,
I took a good look at pl.stringio. As always with Steve's modules,
reading the code was a good refresher course in module design.

In my application I don't need more than the equivalent of 'lines',
except that I do need the ability to peek, or alternatively, to put
back a line to be reread after possibly modifying it.

Reader 1.0 is not on a repository, it is attached here. Martin should
be pleased to see there is (briefly) a license and usage example in
the inlined comments, which are formatted to be visible to LDoc
(thus creating external docs) and IHelp. This email serves as the
readme. Self-tests via standalone test system, training course videos
and product site will have to wait for Reader 2.0.

The module returns a reader factory, which constructs a reader
for reading a file, string or list line by line, with rereading ability.

Usage:
    reader = require "reader"
    rdr = reader(source,position) -- construct the reader
    for line, pos in rdr do   -- read line if any
      ...
      rdr:reread(line)   -- put back (possibly modifed) line for rereading
      ...
    end
-- reader.lua  © Dirk Laurie 2017    MIT license as in Lua 5.3.4 source.

--- `reader` object: read and reread lines from file, list or string
-- Usage:
--    rdr = reader(source,position) -- construct the reader
--    for line, pos in rdr do   -- read line if any
--      ...
--      rdr:reread(line)   -- put back (possibly modifed) line for rereading
--      ...
--    end
return function(source,linepos)
-- Undocumented feature, provided for debugging: if `linepos` is a function,
-- it overrides the line and position routine constructed by default 
  if type(linepos) ~= 'function' then
    assert(not linepos or type(linepos) =='number',
     "bad argument #2 to reader, expected number, got "..type(linepos))
    if io.type(source)=='file' then
      local lines, seek = source:lines(), source:seek("set",linepos or 0)
      linepos = function()
        local pos = source:seek()
        return lines(), pos
      end
    elseif type(source)=='string' then
      local match = source:sub(linepos or 1):gmatch"()([^\n]+)" 
      linepos = function()
        local pos,line = match()
        return line,pos
      end
    elseif type(source)=='table' then 
      local pos = (linepos or 1)-1
      linepos = function()
        pos = pos+1
        return source[pos],pos
      end
    else 
      assert(false,"no default `linepos` defined for type "..type(source))
    end
  end
----
  return setmetatable ( 
  { line = nil,
    pos = nil,
    reread = function(rdr,line)
      rdr.line = line
    end },
  { __call = function(rdr)
      local line = rdr.line
      if not line then
        line, rdr.pos = linepos()
      end      
      rdr.line = nil
      return line, rdr.pos
    end } )
end