3

There is load function that compiles chunk if chunk is string. But i don't understand, what does it do with function.

They said:

If chunk is a function, load calls it repeatedly to get the chunk pieces. Each call to function must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk.

but i can't find examples about it.

DarkKill
  • 31
  • 1

2 Answers2

2

Often you have a "stream" of strings - think chunks of a file, or from a socket - and want to pass them directly to Lua to lex, parse and emit bytecode.

Usually, the bottleneck here will be the I/O, not the CPU. You want to pass those chunks directly to Lua so that you can effectively parallelize waiting for the next chunk while Lua gets started loading.

It also saves memory usage: You don't have to build a temporary table of strings (or even worse, doing repeated string concatenation) to concatenate your chunks to a single string to pass to loadstring; you can directly pass the chunks you get to Lua.

So here's a simple implementation of dofile in terms of load (ignoring filename = nil for simplicity):

function dofile(filename)
    local file <close> = io.open(filename)
    assert(load(function() return file:read(1024) end, filename))()
end

This reads file in chunks of 1024 bytes, passing them to Lua to load. assert is used to ensure that the loading was successful (no syntax errors). Then the chunk is executed.

As another example, here's a weird way to write "Hello World!" using load:

local t = {"print", '("', "Hello World!", '")'}
local i = 0
local function next_chunk()
    i = i + 1
    return t[i] -- `nil` if `i` is out of bounds
end
assert(load(next_chunk))()
Luatic
  • 8,513
  • 2
  • 13
  • 34
1

As the documentation says, passing load a function will cause load to call this function again and again, building a string from its results.

A simple example would be a function that when called prompts the user for a line of input. If the return value of this function is a nonzero length string, then it is concatenated to the string being built by load. Returning '', nil, or nothing causes load to stop calling the function.

load then attempts to create a chunk from the string it has built.

--[[ Example of loading a chunk from a string ]]
local chunk = load('print("Hello world")')
-- can be called as `chunk()`, if there are no syntax errors

--[[ Example of loading a chunk from a generating function ]]
local chunk, msg = load(function ()
    io.write('> ')
    return io.read('*l')
end)

-- catch syntax errors, e.g., `print(a + )`
if chunk then
    -- catch runtime errors, e.g., `print(nil + nil)`
    msg = select(2, pcall(chunk))
end

-- handle any errors; here we simply propagate it
if msg then
    error(msg)
end

In use:

$ lua chunks.lua 
> print('hello world')
> print(51)
> 
hello world
51

We can see the third line was empty, which ended the calls to the function.


We can very roughly define the functionality like this:

local function chunk_from_func(fn)
    local chunkstring = ''

    while true do
        local s = fn()

        if not s or s == '' then
            break
        end

        chunkstring = chunkstring .. s
    end

    return chunk_from_string(chunkstring)
end
Oka
  • 23,367
  • 6
  • 42
  • 53
  • This is just a rough reference implementation, but *perhaps* it is worth pointing out that string concatenation in a loop is generally a bad idea due to string copying leading to O(n²) runtime (and lots of garbage in the meantime). – Luatic Jun 10 '23 at 19:56
  • Of course, but calling functions that don't exist (`chunk_from_string`) is also a bad idea. That example should be treated as pseudocode. If you want efficient pseudocode, then use `table.concat`, but I feel that detracts from the literal nature of the example. – Oka Jun 10 '23 at 20:11