stop using tasks in start_reading/start_writing
We just need queues/streams for file I/O. No need to complect concurrency concerns with them.
This commit is contained in:
parent
ab89be1ed3
commit
b9c187d259
|
@ -3600,35 +3600,26 @@ idiomatic Teliva uses some slightly different primitives.
|
||||||
<hr><h3><a name="pdf-start_reading"><code>start_reading (fs, filename)</code></a></h3>
|
<hr><h3><a name="pdf-start_reading"><code>start_reading (fs, filename)</code></a></h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This function opens a file exclusively for reading, and returns a
|
This function opens a file exclusively for reading, and returns an object (NOT
|
||||||
<code>channel</code> (NOT a file as in Lua) or <b>nil</b> on error.
|
a file handle as in Lua) or <b>nil</b> on error. If the returned object is
|
||||||
<a href='#pdf-channel:recv'><code>recv</code></a> from the channel to read a
|
stored in variable <code>f</code>, <code>f.read(format)</code> will read from
|
||||||
line at a time from the file.
|
the file. Legal values for <code>format</code> are identical to
|
||||||
|
<a href='#pdf-file:read'><code>file:read(format)</code></a>.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
(The <code>fs</code> parameter is currently unused. It will be used to pass in
|
(The <code>fs</code> parameter is currently unused. It will be used to pass in
|
||||||
fake file systems for tests.)
|
fake file systems for tests.)
|
||||||
|
|
||||||
<p>
|
|
||||||
<hr><h3><a name="pdf-character_by_character"><code>character_by_character(chanin, chanout)</code></a></h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This function converts a channel that can <a href='#pdf-channel:recv'><code>recv</code></a>
|
|
||||||
a line at a time from a file into a channel that can <a href='#pdf-channel:recv'><code>recv</code></a>
|
|
||||||
a character at a time. Don't try to mix by-line reads with by-character reads.
|
|
||||||
Once a channel is passed into <code>character_by_character</code>, calling
|
|
||||||
code should stop trying to use it.
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<hr><h3><a name="pdf-start_writing"><code>start_writing(fs, filename)</code></a></h3>
|
<hr><h3><a name="pdf-start_writing"><code>start_writing(fs, filename)</code></a></h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This function opens a file exclusively for writing, and returns a
|
This function opens a file exclusively for writing, and returns an object (NOT
|
||||||
<code>channel</code> (NOT a file as in Lua) or <b>nil</b> on error.
|
a file handle as in Lua) or <b>nil</b> on error. If the result is stored in
|
||||||
<a href='#pdf-channel:send'><code>send</code></a> to the channel to write to
|
variable <code>f</code>, <code>f.write(x)</code> will write <code>x</code> to
|
||||||
the file. <code>close</code> on the channel will persist the changes and make
|
the file. <code>f.close()</code> will persist the changes and make them
|
||||||
them externally visible. All writes are hidden until <code>close</code>.
|
externally visible. All changes will be hidden until <code>f.close()</code>.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
(The <code>fs</code> parameter is currently unused. It will be used to pass in
|
(The <code>fs</code> parameter is currently unused. It will be used to pass in
|
||||||
|
@ -4408,17 +4399,19 @@ Returns a value representing the JSON string <code>str</code>.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<hr><h3><a name="pdf-jsonf.decode"><code>jsonf.decode (chan)</code></a></h3>
|
<hr><h3><a name="pdf-jsonf.decode"><code>jsonf.decode (f)</code></a></h3>
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Returns a value representing the JSON string read from channel
|
Reads a value encoded in JSON from a file and returns it.
|
||||||
<code>chan</code>.
|
<code>f</code> is the type of file object returned by
|
||||||
|
<a href='#pdf-start_reading'><code>start_reading</code></a> (i.e. supporting
|
||||||
|
<code>f.read(format)</code>).
|
||||||
|
|
||||||
|
For example, suppose file <code>foo</code> contains '[1,2,3,{"x":10}]'. Then:
|
||||||
<pre>
|
<pre>
|
||||||
local channel = task.Channel:new()
|
local infile = start_reading(nil, 'foo')
|
||||||
channel:send('[1,2,3,{"x":10}]')
|
jsonf.decode(infile) -- Returns { 1, 2, 3, { x = 10 } }
|
||||||
jsonf.decode(channel) -- Returns { 1, 2, 3, { x = 10 } }
|
|
||||||
</pre><p>
|
</pre><p>
|
||||||
|
|
||||||
|
|
||||||
|
|
2
life.tlv
2
life.tlv
|
@ -522,7 +522,7 @@
|
||||||
> if infile == nil then return end
|
> if infile == nil then return end
|
||||||
> local line_index = lines -- quarter of the way down in pixels
|
> local line_index = lines -- quarter of the way down in pixels
|
||||||
> while true do
|
> while true do
|
||||||
> local line = infile:recv()
|
> local line = infile.read()
|
||||||
> if line == nil then break end
|
> if line == nil then break end
|
||||||
> if line:sub(1,1) ~= '!' then -- comment; plaintext files can't have whitespace before comments
|
> if line:sub(1,1) ~= '!' then -- comment; plaintext files can't have whitespace before comments
|
||||||
> local col_index = cols
|
> local col_index = cols
|
||||||
|
|
79
src/file.lua
79
src/file.lua
|
@ -1,42 +1,43 @@
|
||||||
-- primitive for reading files from a file system (or, later, network)
|
-- primitive for reading files from a file system (or, later, network)
|
||||||
-- returns a channel or nil on error
|
-- returns an object or nil on error
|
||||||
-- read lines from the channel using :recv()
|
-- read lines from the object using .read() with args similar to file:read()
|
||||||
-- recv() on the channel will indicate end of file.
|
-- read() will indicate end of file by returning nil.
|
||||||
function start_reading(fs, filename)
|
function start_reading(fs, filename)
|
||||||
local result = task.Channel:new()
|
|
||||||
local infile = io.open(filename)
|
local infile = io.open(filename)
|
||||||
if infile == nil then return nil end
|
if infile == nil then return nil end
|
||||||
task.spawn(reading_task, infile, result)
|
return {
|
||||||
return result
|
read = coroutine.wrap(function(format)
|
||||||
end
|
while true do
|
||||||
|
if format == nil then format = '*l' end
|
||||||
function reading_task(infile, chanout)
|
format = coroutine.yield(infile:read(format))
|
||||||
for line in infile:lines() do
|
end
|
||||||
chanout:send(line)
|
end),
|
||||||
end
|
}
|
||||||
chanout:send(nil) -- eof
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- primitive for writing files to a file system (or, later, network)
|
-- primitive for writing files to a file system (or, later, network)
|
||||||
-- returns a channel or nil on error
|
-- returns an object or nil on error
|
||||||
-- write to the channel using :send()
|
-- write to the object using .write()
|
||||||
-- indicate you're done writing by calling :close()
|
-- indicate you're done writing by calling .close()
|
||||||
-- file will not be externally visible until :close()
|
-- file will not be externally visible until .close()
|
||||||
function start_writing(fs, filename)
|
function start_writing(fs, filename)
|
||||||
if filename == nil then
|
if filename == nil then
|
||||||
error('start_writing requires two arguments: a file-system (nil for real disk) and a filename')
|
error('start_writing requires two arguments: a file-system (nil for real disk) and a filename')
|
||||||
end
|
end
|
||||||
local result = task.Channel:new()
|
|
||||||
local initial_filename = temporary_filename_in_same_volume(filename)
|
local initial_filename = temporary_filename_in_same_volume(filename)
|
||||||
local outfile = io.open(initial_filename, 'w')
|
local outfile = io.open(initial_filename, 'w')
|
||||||
if outfile == nil then return nil end
|
if outfile == nil then return nil end
|
||||||
result.close = function()
|
return {
|
||||||
result:send(nil) -- end of file
|
write = coroutine.wrap(function(x)
|
||||||
outfile:close()
|
while true do
|
||||||
os.rename(initial_filename, filename)
|
x = coroutine.yield(outfile:write(x))
|
||||||
end
|
end
|
||||||
task.spawn(writing_task, outfile, result)
|
end),
|
||||||
return result
|
close = function()
|
||||||
|
outfile:close()
|
||||||
|
os.rename(initial_filename, filename)
|
||||||
|
end,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function temporary_filename_in_same_volume(filename)
|
function temporary_filename_in_same_volume(filename)
|
||||||
|
@ -57,31 +58,3 @@ function temporary_filename_in_same_volume(filename)
|
||||||
i = i+1
|
i = i+1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function writing_task(outfile, chanin)
|
|
||||||
while true do
|
|
||||||
local line = chanin:recv()
|
|
||||||
if line == nil then break end -- end of file
|
|
||||||
outfile:write(line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- start_reading reads line by line by default
|
|
||||||
-- this helper permits character-by-character reading
|
|
||||||
function character_by_character(chanin, buffer_size)
|
|
||||||
local chanout = task.Channel:new(buffer_size or 50)
|
|
||||||
task.spawn(character_splitting_task, chanin, chanout)
|
|
||||||
return chanout
|
|
||||||
end
|
|
||||||
|
|
||||||
function character_splitting_task(chanin, chanout)
|
|
||||||
while true do
|
|
||||||
local line = chanin:recv()
|
|
||||||
if line == nil then break end
|
|
||||||
local linesz = line:len()
|
|
||||||
for i=1,linesz do
|
|
||||||
chanout:send(line:sub(i, i))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
chanout:send(nil) -- end of file
|
|
||||||
end
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ local literal_map = {
|
||||||
|
|
||||||
local function skip_spaces(infile)
|
local function skip_spaces(infile)
|
||||||
while true do
|
while true do
|
||||||
local c = infile:recv()
|
local c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
if space_chars[c] == nil then return c end
|
if space_chars[c] == nil then return c end
|
||||||
end
|
end
|
||||||
|
@ -79,7 +79,7 @@ local function next_chars(infile, set, firstc)
|
||||||
local res = {firstc}
|
local res = {firstc}
|
||||||
local nextc
|
local nextc
|
||||||
while true do
|
while true do
|
||||||
nextc = infile:recv()
|
nextc = infile.read(1)
|
||||||
if nextc == nil then break end
|
if nextc == nil then break end
|
||||||
if set[nextc] then break end
|
if set[nextc] then break end
|
||||||
table.insert(res, nextc)
|
table.insert(res, nextc)
|
||||||
|
@ -121,27 +121,27 @@ local function parse_string(infile, firstc)
|
||||||
local res = {}
|
local res = {}
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local chr = infile:recv()
|
local chr = infile.read(1)
|
||||||
if chr == nil then break end
|
if chr == nil then break end
|
||||||
local x = chr:byte()
|
local x = chr:byte()
|
||||||
|
|
||||||
if x < 32 then
|
if x < 32 then
|
||||||
error("control character in string")
|
error("control character in string")
|
||||||
elseif chr == '\\' then
|
elseif chr == '\\' then
|
||||||
local c = infile:recv()
|
local c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
if c == "u" then
|
if c == "u" then
|
||||||
local hex = ''
|
local hex = ''
|
||||||
c = infile:recv()
|
c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
hex = hex..c
|
hex = hex..c
|
||||||
c = infile:recv()
|
c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
hex = hex..c
|
hex = hex..c
|
||||||
c = infile:recv()
|
c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
hex = hex..c
|
hex = hex..c
|
||||||
c = infile:recv()
|
c = infile.read(1)
|
||||||
if c == nil then break end
|
if c == nil then break end
|
||||||
hex = hex..c
|
hex = hex..c
|
||||||
if not hex:match('^%x%x%x%x') then
|
if not hex:match('^%x%x%x%x') then
|
||||||
|
@ -155,7 +155,7 @@ local function parse_string(infile, firstc)
|
||||||
table.insert(res, escape_char_map_inv[c])
|
table.insert(res, escape_char_map_inv[c])
|
||||||
end
|
end
|
||||||
elseif chr == '"' then
|
elseif chr == '"' then
|
||||||
return table.concat(res), infile:recv()
|
return table.concat(res), infile.read(1)
|
||||||
else
|
else
|
||||||
table.insert(res, chr)
|
table.insert(res, chr)
|
||||||
end
|
end
|
||||||
|
@ -170,7 +170,7 @@ local function parse_number(infile, firstc)
|
||||||
local res = {firstc}
|
local res = {firstc}
|
||||||
local nextc
|
local nextc
|
||||||
while true do
|
while true do
|
||||||
nextc = infile:recv()
|
nextc = infile.read(1)
|
||||||
if nextc == nil then break end
|
if nextc == nil then break end
|
||||||
if delim_chars[nextc] then break end
|
if delim_chars[nextc] then break end
|
||||||
table.insert(res, nextc)
|
table.insert(res, nextc)
|
||||||
|
@ -295,14 +295,6 @@ end
|
||||||
|
|
||||||
|
|
||||||
function jsonf.decode(infile)
|
function jsonf.decode(infile)
|
||||||
return decode2(character_by_character(infile))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function decode2(infile)
|
|
||||||
if not ischannel(infile) then
|
|
||||||
error("expected channel, got " .. type(f))
|
|
||||||
end
|
|
||||||
local firstc = skip_spaces(infile)
|
local firstc = skip_spaces(infile)
|
||||||
local res, nextc = parse(infile, firstc)
|
local res, nextc = parse(infile, firstc)
|
||||||
if nextc then
|
if nextc then
|
||||||
|
|
4
zet.tlv
4
zet.tlv
|
@ -3732,8 +3732,8 @@
|
||||||
>switch to new file API for writing
|
>switch to new file API for writing
|
||||||
write_zettels:
|
write_zettels:
|
||||||
>function write_zettels(outfile)
|
>function write_zettels(outfile)
|
||||||
> outfile:send(json.encode(zettels))
|
> outfile.write(json.encode(zettels))
|
||||||
> outfile:close()
|
> outfile.close()
|
||||||
>end
|
>end
|
||||||
- __teliva_timestamp:
|
- __teliva_timestamp:
|
||||||
>Thu Mar 10 04:21:28 2022
|
>Thu Mar 10 04:21:28 2022
|
||||||
|
|
Loading…
Reference in New Issue