Search code examples
filerecursionreplaceimportlua

Importing file, recursive function and infinite loop in Lua


I have a file structured like this: we can call it input.tex

\begin{document}
\import{./}{Capitolo1.tex}
\import{./}{Capitolo2.tex}
\end{document}

Using this function I can perform a serger operation, replacing the \import command with the content of the desired file (Chapter 1, Chapter 2)

function file_exists(name)
    local f = io.open(name, "r")
    if f ~= nil then
        io.close(f)
        return true
    else
        return false
    end
end

function rows_from(file)
    if not file_exists(file) then
        return {}
    end
    local rows = {}
    for line in io.lines(file) do
        rows[#rows + 1] = line
    end
    return rows
end

function search_for_import(tex)
    local tbl = {}
    for j = 1, #rows_from(tex) do
        if string.sub(rows[j],1,7) == "\\import" then
            table.insert(tbl,{
                [1] = j, --line
                [2] = string.sub(rows[j],13,-2), --file name
            })
            print(string.sub(rows[j],13,-2))
            search_for_import(string.sub(rows[j],13,-2))
        end
    end
    if #tbl > 0 then
        temp = tex..".mirror" --create mirror file
        io.output(temp)
        local line
        row = rows_from(tex)
        for i = 1, #row do
            line = row[i]
            for key, value in pairs(tbl) do
                
                if i == value[1] then
                    print(value[2])
                    line = row[i+1]
                    for j = 1, #rows_from(value[2]) do
                        io.write(rows_from(value[2])[j], "\n")
                    end
                end
            end
            io.write(line, "\n")
        end
        io.close()
    end
    os.execute("move "..temp.." "..tex) --final result
end

This function works fine. But what if the imported file itself had an \import command? My attempt is to use the recursive function

for j = 1, #rows_from(tex) do
    if string.sub(rows[j],1,7) == "\\import" then
        table.insert(tbl,{
            [1] = j,
            [2] = string.sub(rows[j],13,-2),
        })
        print(string.sub(rows[j],13,-2))
        search_for_import(string.sub(rows[j],13,-2)) --this
    end
end

But this end with an infinite loop.

Ant ideas?


Solution

  • You could set a recursion limit so that it will recursively load the imports until it reaches a certain depth.

    function search_for_import(tex, depth) -- First time this is called set depth to 0
        if depth > 5 then
            return
        end
        local tbl = {}
        for j = 1, #rows_from(tex) do
            if string.sub(rows[j],1,7) == "\\import" then
                table.insert(tbl,{
                    [1] = j, --line
                    [2] = string.sub(rows[j],13,-2), --file name
                })
                print(string.sub(rows[j],13,-2))
                search_for_import(string.sub(rows[j],13,-2), depth + 1)
            end
        end
        ... Rest of the code
    

    This way it only ever recurses to a depth of 5.

    If you want it to recurse for any depth you could also implement some logic to detect if you have already visited this node. (This may require some tweaking to make it work properly)

    function tableContains(haystack, needle)
        for _, v in ipairs(haystack)
            if v == needle then
                return true
            end
        end
    end
    function search_for_import(tex, visited) -- First time this is called set visited to an empty array
        if tableContains(visited, tex) then
            return -- Dont visit an import weve already seen.
        end
        table.insert(visited, tex)
        local tbl = {}
        for j = 1, #rows_from(tex) do
            if string.sub(rows[j],1,7) == "\\import" then
                ...
                search_for_import(string.sub(rows[j],13,-2), visited)
            end
        end
        ... Rest of the code
        os.execute("move "..temp.." "..tex) --final result
        table.remove(visited, #visited)
    end