• Announcement: Lua.org now officially recommends this forum as a meeting place for the Lua community

Want to do something similar to GNUmake's patsubst function (1 Viewer)

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
I got as far as this:

Code:
function values(prefix,text)
    return text:gsub('@"%"','$$(' .. prefix .. '%)')
end
Which didn't work as intended, am I using the wrong function or have I simply written the pattern wrong?
 

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
As an alternative I tried re-creating the function in lua directly, not quite finished but for some reason lua is complaining about the #arg near the bottom:

Code:
function patsubst( match, subst, ... )
    local args = {...}
    local list = {}
    -- optimises memory allocation
    list[#args] = ''
    for a,arg in ipairs(args) do
        if type(arg) == "table" then
            list[a] = {}
            list[a][#arg] = ''
            for i,v in ipairs(arg) do
                list[a][i] = patsubst( match, subst, list[a][i] )
            end
        else
            local str = ''
            local m, c, i, j, k
            for i = 1, #subst, 1 do
                m = subst:sub(i,1)
                if m == '%' then
                    if subst:sub(i,2) == '%%' then
                        i = i + 1
                    else
                        break
                    end
                end
                str = str .. m
            end
            j = 1
            for i = 1, #match, 1 do
                m = match:sub(i,1)
                c = arg:sub(j,1)
                if m == '%' then
                    if match:sub(i,2) == '%%' then
                        i = i + 1
                    else
                        break
                    end
                end
                if c ~= m then
                    str = arg
                    i = nil
                    break
                end
                j = j + 1
            end
            if i and m == '%' then
                i = i + 1
                m = match:sub(i,1)
                for j, #arg, 1 do
                    c = arg:sub(j,1)
                    if c == m then break end
                    str = str .. c
                end
                if i <= #match and c == m then
                    for i, #match, 1 do
                        m = match:sub(i,1)
                        c = arg:sub(j,1)
                        if c ~= m then
                            str = arg
                            break
                        end
                        str = str .. c
                        j = j + 1
                    end
                else
                    str = arg
                end
            end
            list[a] = str
        end
    end
end

I can see no reason for it to complain when it is supposed to be the one filling the value in the first place
 

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
Didn't realise the developers of lua decided to be stupid about index declarations, requiring an equals operation where none is appropriate leaving us with daft declarations like
Code:
for i = i,...
, if there was a more commonplace scripting language that did things more sensibly than lua then I would've used it instead, it's unfortunate that it was the only option for what I wanted to do, anyways the alternative function is looking like this at the moment, I know I'm missing something but just getting a brain block at the moment, if someone has a simpler solution that achieves the same thing I'm all eyes.

Code:
function _patsubst( match, subst, arg )
    local str = ''
    local m, c, i, j, k
    local text = ''
    local expr = ''
    local special = { '%', '+', '*', '?', '-' }
    for i = 1, #match, 1 do
        text = match:sub(i,1)
        if text == '.' then
            text = '..'
        else
            for j,x in ipairs(special) do
                if text == x then
                    text = '%' .. x
                    break
                end
            end
        end
        expr = expr .. text
    end
    local args = arg:match(expr)
    for a,arg in ipairs(args) do
        for i = 1, #subst, 1 do
            m = subst:sub(i,1)
            if m == '%' then
                if subst:sub(i,2) == '%%' then
                    i = i + 1
                else
                    break
                end
            end
            str = str .. m
        end
        j = 1
        for i = 1, #match, 1 do
            m = match:sub(i,1)
            c = arg:sub(j,1)
            if m == '%' then
                if match:sub(i,2) == '%%' then
                    i = i + 1
                else
                    break
                end
            end
            if c ~= m then return arg end
            j = j + 1
        end
        if i and m == '%' then
            i = i + 1
            m = match:sub(i,1)
            for j = j, #arg, 1 do
                c = arg:sub(j,1)
                if c == m then break end
                str = str .. c
            end
            if i <= #match and c == m then
                for i = i, #match, 1 do
                    m = match:sub(i,1)
                    c = arg:sub(j,1)
                    if c ~= m then return arg end
                    j = j + 1
                end
            else
                str = arg
            end
        end
    end
    return str
end

function patsubst( match, subst, ... )
    local args = {...}
    local list = {}
    -- optimises memory allocation
    list[#args] = ''
    for a,arg in ipairs(args) do
        if type(arg) == "table" then
            list[a] = {}
            list[a][#arg] = ''
            for i,v in ipairs(arg) do
                list[a][i] = patsubst( match, subst, v )
            end
        else
            list[a] = _patsubst( match, subst )
        end
    end
    return list
end
 

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
Well I got a step closer to my goal:
Code:
function __patsubst( subst, arg )
    local i
    for i = 1, #subst, 1 do
        m = subst:sub(i,i)
        if m == '%' then
            if subst:sub(i,i+1) == '%%' then
                i = i + 1
            else
                return subst:sub(1,i) .. arg .. subst:sub(i+1)
            end
        end
    end
    return subst
end

function __patmatch( match, arg )
    local i, j, c, m = 1, 1, '', ''
    for i = 1, #arg, 1 do
        m = match:sub(i,i)
        c = arg:sub(j,j)
        if m == '%' then
            if match:sub(i,i+1) == '%%' then
                i = i + 1
            else
                break
            end
        end
        if m ~= c then return nil end
        j = j + 1
    end
    i = i + 1
    if i > #match then return arg:sub( 1, j ) end
    m = match:sub(i,i)
    for j = j, #arg, 1 do
        c = arg:sub(j,j)
        if c == m then break end
    end
    j = j + 1
    for i = i + 1, #match, 1 do
        m = match:sub(i,i)
        c = arg:sub(j,j)
        if c ~= m then return arg:sub( 1, j ) end
        j = j + 1
    end
    return arg:sub(1,j)
end

function _patsubst( match, subst, arg )
    local str, tmp, i = '', '', 1
    for i = 1, #arg, 1 do
        tmp = __patmatch( match, arg:sub(i) )
        if tmp then
            str = str .. __patsubst( subst, tmp )
            i = i + #tmp
        end
    end
    str = str .. arg:sub(i)
    Info( 'str(final) = "' .. str .. '"' )
    return str
end

function patsubst( match, subst, ... )
    local args = {...}
    local list = {}
    -- optimises memory allocation
    list[#args] = ''
    for a,arg in ipairs(args) do
        if type(arg) == "table" then
            list[a] = {}
            list[a][#arg] = ''
            for i,v in ipairs(arg) do
                list[a][i] = patsubst( match, subst, v )
            end
        else
            list[a] = _patsubst( match, subst, arg )
        end
    end
    return list
end

function values(prefix,text)
    return patsubst('@"%"','$(' .. prefix .. '.%)', text)[1]
end

However not quite there
Code:
'@"dst_dir"/bin/@"name"@"suffix"'
gives me
Code:
$(cfg.tryextra.QUICK.2.%@"ds)$(cfg.tryextra.QUICK.2.%@"pr)$(cfg.tryextra.QUICK.2.%@"sr)$(cfg.tryextra.QUICK.2.%@"pr)@"dst_dir"/src/@"project"/%.c: @"src_dir"/@"project"/%.c

Edit: Noticed I did the logic for _patsubst() slightly wrong, edited to what it is now, likewise I replaced the output to reflect the current output
 
Last edited:

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
simplified the code a bit, managed to lose the effect I had almost achieved though, can't see why either:

Code:
function patsubst_str2vec( pattern )
    local i, j, a, c, list = 1, 1, '', '', {}
    for j = 1, #pattern, 1 do
        c = pattern:sub(j,j)
        if c == '%' then
            if pattern:sub(j,j+1) == '%%' then
                a = a .. pattern:sub(i,j)
                j = j + 1
                i = j + 1
            else
                a = a .. pattern:sub(i,j-1)
                list[1] = a
                list[2] = pattern:sub(j+1)
                return list
            end
        end
    end
    list[1] = a .. pattern:sub( i )
    return list
end

function patsubst_replace( subst, arg )
    if subst[2] then return subst[1] .. arg .. subst[2] end
    return subst[1]
end

function patsubst_compare( match, arg )
    local i, j, c, m = 1, 1, '', ''
    local a = arg:sub(1,#(match[1]))
    if a ~= match[1] then return nil end
    local b = match[2]
    if not b then return a end
    for i = #a, #arg, 1 do
        c = arg:sub(j,j + #b)
        if c == b then break end
    end
    if c ~= b then return nil end
    return arg:sub(1,i)
end

function patsubst_arg2str( match, subst, arg )
    local str, tmp, i = '', '', 1
    for i = 1, #arg, 1 do
        tmp = patsubst_compare( match, arg:sub(i) )
        if tmp then
            i = i + #tmp
            str = str .. patsubst_replace( subst, tmp )
        end
    end
    return str .. arg:sub(i)
end

function patsubst( match, subst, ... )
    local args = {...}
    match = patsubst_str2vec( match )
    subst = patsubst_str2vec( subst )
    for a,arg in ipairs(args) do
        if type(arg) == "table" then
            for i,v in ipairs(arg) do
                args[a][i] = patsubst( match, subst, v )
            end
        else
            args[a] = patsubst_arg2str( match, subst, arg )
        end
    end
    return args
end

Edit: Just noticed a left over usage of j index instead of i index from code I changed, fixing that got me closer to the goal
 
Last edited:

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
Few more fixes to indexing issues, now I'm way closer to the intended result:
Code:
function patsubst_str2vec( pattern )
    local i, j, a, c, list = 1, 1, '', '', {}
    for j = 1, #pattern, 1 do
        c = pattern:sub(j,j)
        if c == '%' then
            if pattern:sub(j,j+1) == '%%' then
                a = a .. pattern:sub(i,j)
                j = j + 1
                i = j + 1
            else
                a = a .. pattern:sub(i,j-1)
                list[1] = a
                list[2] = pattern:sub(j+1)
                return list
            end
        end
    end
    list[1] = a .. pattern:sub( i )
    return list
end

function patsubst_replace( subst, tmp )
    if subst[2] then return subst[1] .. tmp .. subst[2] end
    return subst[1]
end

function patsubst_compare( match, arg )
    local a = match[1]
    local i, j, k, c = 1, #a, ''
    local b = arg:sub(1,#a)
    local c = ''
    if b ~= a then return nil end
    local c = match[2]
    if not c then return { a, '', '', a } end
    i = #a+1
    for j = #a+1, #arg, 1 do
        k = j + (#c - 1)
        b = arg:sub(j,k)
        if b == c then
            b = arg:sub(i,j)
            d = arg:sub(1,k)
            return { a, b, c, d }
        end
    end
    return nil
end

function patsubst_arg2str( match, subst, arg )
    local str, tmp, i, j = '', '', 1, 1
    for j = 1, #arg, 1 do
        tmp = patsubst_compare( match, arg:sub(j) )
        if tmp then
            if j - i > 0 then
                str = str .. arg:sub(i,j)
            end
            str = str .. patsubst_replace( subst, tmp[2] )
            tmp = tmp[4]
            j = j + #tmp
            i = j
        end
    end
    if j > i then
        str = str .. arg:sub(i,j)
    end
    return str
end
which gives:
Code:
$(info $(cfg.tryextra.QUICK.dst_dir")/src/#$(cfg.tryextra.QUICK.project")/%.c: #$(cfg.tryextra.QUICK.src_dir")/#$(cfg.tryextra.QUICK.project"))
 

awsdert

Newcomer
Joined
Apr 7, 2022
Messages
11
Reaction score
0
Welp I got it working despite the lack of help here, for the benefit of others who wanted the same thing, this is what I got in a dedicated file:
Code:
local function patsubst_str2vec( pattern )
    local i, j, a, c, list = 1, 1, '', '', {}
    for j = 1, #pattern, 1 do
        c = pattern:sub(j,j)
        if c == '%' then
            if pattern:sub(j,j+1) == '%%' then
                a = a .. pattern:sub(i,j)
                j = j + 1
                i = j + 1
            else
                a = a .. pattern:sub(i,j-1)
                list[1] = a
                list[2] = pattern:sub(j+1)
                return list
            end
        end
    end
    list[1] = a .. pattern:sub( i )
    return list
end

local function patsubst_replace( subst, tmp )
    if subst[2] then return subst[1] .. tmp .. subst[2] end
    return subst[1]
end

local function patsubst_compare( match, arg )
    local a = match[1]
    local i, j, k, c = 1, #a, ''
    local b = arg:sub(1,#a)
    local c = ''
    if b ~= a then return nil end
    local c = match[2]
    if not c then return { a, '', '', a } end
    i = #a+1
    for j = #a+1, #arg, 1 do
        k = j + (#c - 1)
        b = arg:sub(j,k)
        if b == c then
            b = arg:sub(i,j-1)
            d = arg:sub(1,k)
            return { a, b, c, d }
        end
    end
    return nil
end

local function patsubst_arg2str( match, subst, arg )
    local str, tmp, i, j = '', '', 1, 1
    for j = 1, #arg, 1 do
        tmp = patsubst_compare( match, arg:sub(j) )
        if tmp then
            if j > i then
                str = str .. arg:sub(i,j-1)
            end
            str = str .. patsubst_replace( subst, tmp[2] )
            tmp = tmp[4]
            j = j + #tmp
            i = j
        end
    end
    if j > i then
        str = str .. arg:sub(i,j)
    end
    return str
end

function patsubst( match, subst, ... )
    local args = {...}
    match = patsubst_str2vec( match )
    subst = patsubst_str2vec( subst )
    for a,arg in ipairs(args) do
        if type(arg) == "table" then
            for i,v in ipairs(arg) do
                args[a][i] = patsubst( match, subst, v )
            end
        else
            args[a] = patsubst_arg2str( match, subst, arg )
        end
    end
    return args
end
 
Top