<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.luatex.org/index.php?action=history&amp;feed=atom&amp;title=T_wo_T_r%3Areader.lua</id>
	<title>T wo T r:reader.lua - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.luatex.org/index.php?action=history&amp;feed=atom&amp;title=T_wo_T_r%3Areader.lua"/>
	<link rel="alternate" type="text/html" href="https://wiki.luatex.org/index.php?title=T_wo_T_r:reader.lua&amp;action=history"/>
	<updated>2026-05-31T15:12:29Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.31.1</generator>
	<entry>
		<id>https://wiki.luatex.org/index.php?title=T_wo_T_r:reader.lua&amp;diff=3559&amp;oldid=prev</id>
		<title>Rkrug: Created page with &quot; &lt;nowiki&gt;   --- Copyright (c) 2021 by Toadstone Enterprises. --- ISC-type license, see License.txt for details.   -------------------------------------------------------------...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.luatex.org/index.php?title=T_wo_T_r:reader.lua&amp;diff=3559&amp;oldid=prev"/>
		<updated>2021-02-22T18:31:09Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot; &amp;lt;nowiki&amp;gt;   --- Copyright (c) 2021 by Toadstone Enterprises. --- ISC-type license, see License.txt for details.   -------------------------------------------------------------...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt; &amp;lt;nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Copyright (c) 2021 by Toadstone Enterprises.&lt;br /&gt;
--- ISC-type license, see License.txt for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- We maintain a stack of &amp;#039;readers&amp;#039;.&lt;br /&gt;
--- push_reader will push a new &amp;#039;reader&amp;#039; onto the stack, and&lt;br /&gt;
--- pop_reader will pop it. push_reader should be given a string or a&lt;br /&gt;
--- filename (of the form &amp;quot;file:filename&amp;quot;) If a string is passed in,&lt;br /&gt;
--- its contents will be used by the &amp;#039;reader&amp;#039;.  If a filename is&lt;br /&gt;
--- passed in, the file will be read, and its contents will form the&lt;br /&gt;
--- contents of the &amp;#039;reader&amp;#039;.&lt;br /&gt;
--- The top &amp;#039;reader&amp;#039; is the current one, and read_value will return a&lt;br /&gt;
--- char (really a Unicode integer value) from that &amp;#039;reader&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- The index of the current &amp;#039;reader&amp;#039;. No &amp;#039;readers&amp;#039; yet.&lt;br /&gt;
&lt;br /&gt;
local n = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local explode = function(s)&lt;br /&gt;
  -- This is the lower level function used by push_reader to create&lt;br /&gt;
  -- a &amp;#039;reader&amp;#039;.&lt;br /&gt;
  --&lt;br /&gt;
  -- Take the string s, and break it into unicode values (integers)&lt;br /&gt;
  -- and place these into the table, text, indexed by position.&lt;br /&gt;
  -- text.max is the maximum index.&lt;br /&gt;
  -- text.pos is the current position to be read from. Initially the&lt;br /&gt;
  -- start of the text.&lt;br /&gt;
  &lt;br /&gt;
  assert((type(s) == &amp;quot;string&amp;quot;),&lt;br /&gt;
         &amp;quot;explode was given a &amp;quot; .. type(s) .. &amp;quot; instead of a string.&amp;quot;)&lt;br /&gt;
  local text = {}&lt;br /&gt;
  local i = 0&lt;br /&gt;
  &lt;br /&gt;
  for v in string.utfvalues(s) do&lt;br /&gt;
    i = i + 1&lt;br /&gt;
    text[i] = v&lt;br /&gt;
  end&lt;br /&gt;
  &lt;br /&gt;
  text.max = i&lt;br /&gt;
  text.pos = 1&lt;br /&gt;
  return text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local push_reader = function(data)&lt;br /&gt;
  -- Create a &amp;#039;reader&amp;#039;. That is, use explode to create a table&lt;br /&gt;
  -- containing the text from data (either a file pointer or the&lt;br /&gt;
  -- text itself). Then place this table into the next index of&lt;br /&gt;
  -- the &amp;#039;reader&amp;#039; table, Reader, in the next available index.&lt;br /&gt;
  &lt;br /&gt;
  local text&lt;br /&gt;
&lt;br /&gt;
  assert((type(data) == &amp;quot;string&amp;quot;), &amp;quot;Bad arg to push_reader.&amp;quot;)&lt;br /&gt;
  &lt;br /&gt;
  if (unicode.utf8.sub(data, 1, 5) == &amp;quot;file:&amp;quot;) then&lt;br /&gt;
    local f = io.open(unicode.utf8.sub(data, 6, -1))&lt;br /&gt;
    assert(f, &amp;quot;File &amp;quot; .. unicode.utf8.sub(data, 6, -1) .. &amp;quot;failed to open.&amp;quot;)&lt;br /&gt;
    text = f:read(&amp;quot;a&amp;quot;)&lt;br /&gt;
  else&lt;br /&gt;
    text = data&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  local reader = explode(text)&lt;br /&gt;
&lt;br /&gt;
  n = n + 1&lt;br /&gt;
  Reader[n] = reader&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local pop_reader = function()&lt;br /&gt;
  -- Remove the currently active &amp;#039;reader&amp;#039; from Reader.&lt;br /&gt;
  &lt;br /&gt;
  assert((n &amp;gt; 0), &amp;quot;No reader to pop!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  Reader[n] = nil&lt;br /&gt;
  n = n - 1&lt;br /&gt;
  &lt;br /&gt;
end&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
local read_value = function()&lt;br /&gt;
  -- Get the char (a Unicode integer) from the currenly active&lt;br /&gt;
  -- &amp;#039;reader&amp;#039;.&lt;br /&gt;
  &lt;br /&gt;
  local value&lt;br /&gt;
  &lt;br /&gt;
  assert((n &amp;gt; 0), &amp;quot;No reader to read from!&amp;quot;)&lt;br /&gt;
  &lt;br /&gt;
  local reader = Reader[n]&lt;br /&gt;
  local max = reader.max&lt;br /&gt;
  local pos = reader.pos&lt;br /&gt;
  &lt;br /&gt;
  if (pos &amp;lt;= max) then&lt;br /&gt;
    value = reader[pos]&lt;br /&gt;
  else&lt;br /&gt;
    -- We are already at the end.&lt;br /&gt;
    return nil&lt;br /&gt;
  end&lt;br /&gt;
  &lt;br /&gt;
  reader.pos = pos + 1&lt;br /&gt;
  return value&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local unread_value = function()&lt;br /&gt;
  -- Back the indexed position of the currently active &amp;#039;reader&amp;#039;&lt;br /&gt;
  -- by one.&lt;br /&gt;
  &lt;br /&gt;
  assert((n &amp;gt; 0), &amp;quot;No reader to unread to!&amp;quot;)&lt;br /&gt;
  &lt;br /&gt;
  local reader = Reader[n]&lt;br /&gt;
  local pos = reader.pos&lt;br /&gt;
  &lt;br /&gt;
  assert((pos &amp;gt; 1), &amp;quot;Already at start of text!&amp;quot;)&lt;br /&gt;
  &lt;br /&gt;
  reader.pos = pos - 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local replace_text = function(text, start, stop)&lt;br /&gt;
  -- Insert some text (a string) into the current &amp;#039;reader&amp;#039; replacing&lt;br /&gt;
  -- the text from the indices start to stop, and reset the &amp;#039;reader&amp;#039;&lt;br /&gt;
  -- to the beginning of inserted text&lt;br /&gt;
  -- There are three special cases,&lt;br /&gt;
  -- 1. prepend some text, before any reading&lt;br /&gt;
  -- 2. insert without replacing any text&lt;br /&gt;
  -- 3. deleting text, without inserting&lt;br /&gt;
  -- These can be handled by using&lt;br /&gt;
  -- 1. start = 0, stop = -1&lt;br /&gt;
  -- 2. stop = start -1&lt;br /&gt;
  -- 3. text = &amp;quot;&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  assert((n &amp;gt; 0), &amp;quot;No reader to replace text in!&amp;quot;)&lt;br /&gt;
  local reader = Reader[n]&lt;br /&gt;
  local max = reader.max&lt;br /&gt;
&lt;br /&gt;
  assert((type(text) == &amp;quot;string&amp;quot;), &amp;quot;Text is not a string!&amp;quot;)&lt;br /&gt;
  assert((start &amp;gt;= 0), &amp;quot;Start must be a non-negative integer&amp;quot;)&lt;br /&gt;
  assert((stop &amp;lt;= max), &amp;quot;Stop must be less than max&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  local insert = explode(text)&lt;br /&gt;
  local difference = insert.max - (stop - start + 1)&lt;br /&gt;
&lt;br /&gt;
  if (difference &amp;gt; 0) then&lt;br /&gt;
    -- move up&lt;br /&gt;
    for i = max, stop + 1, -1 do&lt;br /&gt;
      reader[i + difference] = reader[i]&lt;br /&gt;
    end&lt;br /&gt;
  elseif (difference &amp;lt; 0) then&lt;br /&gt;
    -- move down&lt;br /&gt;
    for i = stop + 1, max do&lt;br /&gt;
      reader[i + difference] = reader[i]&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  &lt;br /&gt;
  -- insert&lt;br /&gt;
  -- If we are prepending some text (special case 1. above)&lt;br /&gt;
  -- we need to reset start from 0 to 1.&lt;br /&gt;
  if (start == 0) then start = 1 end&lt;br /&gt;
  for i = 1, insert.max do&lt;br /&gt;
    reader[start + i - 1] = insert[i]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  reader.max = reader.max + difference&lt;br /&gt;
  reader.pos = start&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local pos = function()&lt;br /&gt;
  -- Return the position of the current &amp;#039;reader&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
  assert((n &amp;gt; 0), &amp;quot;No reader to replace text in!&amp;quot;)&lt;br /&gt;
  local reader = Reader[n]&lt;br /&gt;
&lt;br /&gt;
  return reader.pos&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reader = {push_reader   = push_reader,&lt;br /&gt;
          pop_reader    = pop_reader,&lt;br /&gt;
          read_value    = read_value,&lt;br /&gt;
          unread_value  = unread_value,&lt;br /&gt;
          replace_text  = replace_text,&lt;br /&gt;
          pos           = pos,}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return Reader&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Rkrug</name></author>
		
	</entry>
</feed>