Associativity Of Concatenation

lua-users home
wiki

The only right-associative operators in Lua are .. (concatenation) and ^ (exponentiation). Having ^ be right associative is not unusual for languages, but it is somewhat unusual for .. to be right associative. For most cases, it doesn't matter whether .. is evaluated with right or left associativity because string concatenation is associative, but if the objects being concatenated have metamethods or if a large number of objects are being concatenated (e.g. 199 or more -- the precise limit is determined by LUAI_MAXCCALLS in luaconf.h), then the effect can be noticeable. The reason for .. being right-associative rather than the usual left-associative for Lua operators seems mainly to do with efficiency of implementation (see LuaList:2002-08/msg00218.html and LuaList:2002-08/msg00104.html).

The problem of concatenating more than approximately 199 elements at a time is shown below. This Lua program creates a Lua program that does not compile:

print([[return 0]] .. ([[ .. 0]]):rep(198))

lua test.lua | luac -p -l -
luac: stdin:1: chunk has too many syntax levels

If we reduce the 198 to 10, we can see why:

main <stdin:0,0> (14 instructions, 56 bytes at 0x671200)
0+ params, 11 slots, 0 upvalues, 0 locals, 1 constant, 0 functions
        1       [1]     LOADK           0 -1    ; ""
        2       [1]     LOADK           1 -1    ; ""
        3       [1]     LOADK           2 -1    ; ""
        4       [1]     LOADK           3 -1    ; ""
        5       [1]     LOADK           4 -1    ; ""
        6       [1]     LOADK           5 -1    ; ""
        7       [1]     LOADK           6 -1    ; ""
        8       [1]     LOADK           7 -1    ; ""
        9       [1]     LOADK           8 -1    ; ""
        10      [1]     LOADK           9 -1    ; ""
        11      [1]     LOADK           10 -1   ; ""
        12      [1]     CONCAT          0 0 10
        13      [1]     RETURN          0 2
        14      [1]     RETURN          0 1

The compiler causes all the constants to be placed on the stack prior to the concatenation. The same error occurs when the operator is ^, though the need for exponentiating approximately 200 objects is probably quite rare, unless maybe if ^ is overridden with different semantics (which might not be a good idea anyway).

Now if a left-associative operator is used (e.g. addition), then this type of code compiles fine:

print([[return a]] .. ([[ + a]]):rep(198))

main <stdin:0,0> (399 instructions, 1596 bytes at 0x671200)
0+ params, 2 slots, 0 upvalues, 0 locals, 1 constant, 0 functions
        1       [1]     GETGLOBAL       0 -1    ; a
        2       [1]     GETGLOBAL       1 -1    ; a
        3       [1]     ADD             0 0 1
        4       [1]     GETGLOBAL       1 -1    ; a
        5       [1]     ADD             0 0 1
        6       [1]     GETGLOBAL       1 -1    ; a
        7       [1]     ADD             0 0 1
...
        390     [1]     GETGLOBAL       1 -1    ; a
        391     [1]     ADD             0 0 1
        392     [1]     GETGLOBAL       1 -1    ; a
        393     [1]     ADD             0 0 1
        394     [1]     GETGLOBAL       1 -1    ; a
        395     [1]     ADD             0 0 1
        396     [1]     GETGLOBAL       1 -1    ; a
        397     [1]     ADD             0 0 1
        398     [1]     RETURN          0 2
        399     [1]     RETURN          0 1

The objects are being pushed on the stack and operated on "[just in time]".

Concatenation as an operation is associative so its precedence is almost irrelevant; moreover, Lua optimizes chained association into a single (virtual) operation. The precedence associativity of concatenation only becomes visible when you implement the __concat metamethod for some object. If your implementation of .. is truly a concatenation operation, then it will also be associative, but the chained concatenation is broken down into pairwise calls to the __concat metamethod, so your implementation will be called from right-to-left. One case where concatenation is not associative and is naturally right-binding is Posix regular expression concatenation; the first-longest match rule assumes that match components are concatenated right to left. I don't know if that's an answer: the real reason is probably that it's more efficient to implement in a stack machine. --RiciLake

--DavidManura

See Also


RecentChanges · preferences
edit · history
Last edited February 2, 2008 7:21 pm GMT (diff)