Variadic function not working?

I have a function as a PoC which does not work although it works very well from the commandline lua formatNumber.lua.
What’s happenning here, please? Tests (just add to the end of the file and run it with Lua interpreter)…

print(formatNumber('%N', 123456))
print(formatNumber('%N', -123456))
print(formatNumber('%xN', 3))
print(formatNumber('%xN', -3))
print(formatNumber('%x2N', 3))
print(formatNumber('%x2N', -3))
print(formatNumber('%16N', 123456))
print(formatNumber('%16N', -123456))
print(formatNumber('%016N', 123456))
print(formatNumber('%016N', -123456))
print(formatNumber('% 16N', 123456))
print(formatNumber('% 16N', -123456))
print(formatNumber('%.16N', 123456))
print(formatNumber('%+16N', -123456))

I get this on my computer with Lua 5.4.4 interpreter:

123456
-123456
x3
-3
x3
-3
0000000000123456
-000000000123456
0000000000123456
-000000000123456
          123456
         -123456
..........123456
+++++++++-123456

Which is the expected output. From SB I get:

${formatNumber('Test: <%.5N>.', 123)}

This error which leads me to the question in subject…

Lua error: Too many arguments! (Origin: ...)

Thanks for ideas.

I’ll try this later, but you are running an Edge build of SilverBullet right?

$ silverbullet version
0.10.1

Hm, there’s no commit information in the version, so probably no. I just run silverbullet upgrade from time to time…

Ok, unless you followed these instructions Living on the edge (builds) you’re on a regular release, which is pretty old. Please try out edge builds. I fixed a lot there.

Running like this:

$ TZ=Europe/Prague deno run --unstable-kv --unstable-worker-options -A --reload https://edge.silverbullet.md/silverbullet.js -L 192.168.255.1 --port 12345 --reindex --sync-only ~/notes

Is it correct?

Yes. Ok.

But this works, which is very weird:

```space-lua
function variadicTest(...)
  local ret = { ... }
  return ret
end
```
${variadicTest(1, 2, 3, 4, 5)}

It does yield:

  • 1
  • 2
  • 3
  • 4
  • 5

then I really do not understand what’s going on here. :grimacing:

I’m sure it’s a bug in my Lua implementation. I have some tests around this, it don’t use variatic arguments anywhere yet.

Is there any way for me to debug the Lua run somehow (I started with cca a week ago)? By the way, if the small function variadicTest() above works as expected (which it seems it does), the problem doesn’t seem to be connected to the variadicity itself or, at least, it’s just started to look like it isn’t…

I also tried to try:

```space-lua
function formatNumber(...)
  local args = { ... }
  local _, format = next(args)
  -- etc.
end
```

but it seems that there is no next() (“attempted to call nil … etc.”) in SilverBullet (I may be wrong).

@zef What is the nature of the bug? It may be interesting to know. And, should I fill bug request for it so that it get addressed soon? :slight_smile:

@zef Is there any chance you looked into this issue, please? I’ve written some more variadic functions for misc. things (for example I use markdown.parseMarkdown() along with custom table function to traverse the AST and produce abbreviated preview for page listings skipping code blocks etc., and some of the utility table/tree functions happened to be variadic in order to be universal). I suppose it’s been not fixed yet. (?)

No didn’t get to it yet. Could you report this as a GitHub issue with a minimal example? Makes it easier to track.

OK, I will.

Ok, I just looked at this and I’m not sure where this code comes from exactly, but it seems to use Lua features that are not standard as far as I can find. I updated the code as follows:


local function formatNumber(format, ...)
    local args = { ... }
    local argind = 1
    local res = ''
    local i = 1

    while i <= string.len(format) do
        local chr = string.sub(format, i, i)

        if chr == '%' then
            i = i + 1

            local padchr = '0'
            local padcnt = 0

            if string.match(string.sub(format, i, i), '[^%dN]') then
                padchr = string.sub(format, i, i)
                i = i + 1
            end

            local cntstr = ''

            while string.match(string.sub(format, i, i), '%d') do
                cntstr = cntstr .. string.sub(format, i, i)
                i = i + 1
            end

            if cntstr ~= '' then
                padcnt = tonumber(cntstr)
            end

            if string.sub(format, i, i) == 'N' then
                if argind <= #args then
                    local num = tostring(args[argind])
                    local neg = false

                    if string.sub(num, 1, 1) == '-' then
                        neg = true
                        num = string.sub(num, 2)
                    end

                    if padcnt == 0 and padchr ~= '0' then
                        padcnt = 2
                    end

                    local pad = string.rep(padchr,
                        math.max(0, padcnt - #num - (neg and 1 or 0)))

                    if neg and string.match(padchr, '%d') then
                        res = res .. '-' .. pad .. num
                    else
                        res = res .. pad .. (neg and '-' or '') .. num
                    end

                    argind = argind + 1
                    i = i + 1
                else
                    error('Not enough arguments!')
                end
            else
                res = res .. '%' .. string.sub(format, i, i)
                i = i + 1
            end
        else
            res = res .. chr
            i = i + 1
        end
    end

    if argind <= #args then
        error('Too many arguments!')
    end

    return res
end

Although I don’t know exactly what the formats are supposed to output, at least it seems to render strings now.

Ok. I just found I’m not setting string as metatable for string values, which is why that original code didn’t work. Have to implement this.

Ok, implemented this now (just pushed). Your function should work as is (without my modifications). At least if the pattern match library is properly implemented at the moment, which I’m not 100% convinced of.

As you are bringing this up :sweat_smile: :

`[\n]` works:
${string.gsub(table.concat({"Start of string","Start of newline"},"\n"),"[\n]Start","\nSTART").values[1]}

`^` works:
${string.gsub(table.concat({"Start of string","Start of newline"},"\n"),"^Start","\nSTART").values[1]}

`[^]` does not work:
${string.gsub(table.concat({"Start of string","Start of newline"},"\n"),"[^]Start","\nSTART").values[1]}

`[\n^]` does not work:
${string.gsub(table.concat({"Start of string","Start of newline"},"\n"),"[^\n]Start","\nSTART").values[1]}

`[abc]` works: ${string.gsub("abc","[abc]","A").values[1]}

Edit: I am especially confused why [^] matches \n but not ^. :thinking:

Honestly I largely let Claude (AI) generate this pattern matching stuff. I’ll have to give it another shot, feeding it more docs and tests to work with.

2 Likes

At least I created sort of edge case test with the function. :smile: