Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are table literals treated differently from table references in Lua?

Tags:

lua

lua-table

Here is a Lua 5.2.2 transcript, showing the declaration and indexing of a table:

> mylist = {'foo', 'bar'}
> print(mylist[1])
foo

Why isn't the following statement legal?

> print({'foo', 'bar'}[1])
stdin:1: ')' expected near '['

I can't think of any other language where a literal can't be substituted for a reference (except, of course, when an lvalue is needed).

FWIW, parenthesizing the table literal makes the statement legal:

> print(({'foo', 'bar'})[1])
foo
like image 785
Ellen Spertus Avatar asked Oct 12 '13 06:10

Ellen Spertus


2 Answers

It is also related to the fact that in Lua this syntax is valid:

myfunc { 1, 2, 3 }

and it is equivalent to:

myfunc( { 1, 2, 3 } )

Therefore an expression such as:

myfunc { 1, 2, 3 } [2]

is parsed as:

myfunc( { 1, 2, 3 } )[2]

so first the function call is evaluated, then the indexing takes place.

If {1,2,3}[2] could be parsed as a valid indexing operation it could lead to ambiguities in the previous expression that would require more lookahead. The Lua team chose to make the Lua bytecode compiler fast by making it a single pass compiler, so it scans source code only once with a minimum of lookahead. This message to lua mailing list from Roberto Ierusalimschy (lead Lua developer) points in that direction.

The same problem exists for string literals and method calls. This is invalid:

"my literal":sub(1)

but this is valid:

("my literal"):sub(1)

Again, Lua allows this:

func "my literal"

as equivalent to this:

func( "my literal" )
like image 58
Lorenzo Donati -- Codidact.com Avatar answered Sep 29 '22 06:09

Lorenzo Donati -- Codidact.com


Going of the grammar defined here, the reason that the non parenthesized version is invalid yet the parenthesized is, is because the syntax tree takes a different path and expects a closing bracket ) because there should be no other symbol in that context.

In the first case:

functioncall ::=  prefixexp args | prefixexp `:´ Name args 

    prefixexp =>
        prefixexp ::= var | functioncall | `(´ exp `)´

            var -> print
    THEREFORE prefixexp -> print

    args =>
        args ::=  `(´ [explist] `)´ | tableconstructor | String 
            match '('

            explist =>
                explist ::= {exp `,´} exp
                    exp =>
                        exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp
                            tableconstructor => 
                    explist-> {'foo','bar'}

            THEREFORE explist = {'foo','bar'}

            match ')' ERROR!!! found '[' expected ')'

On the other hand with parenthesis:

functioncall ::=  prefixexp args | prefixexp `:´ Name args 

    prefixexp =>
        prefixexp ::= var | functioncall | `(´ exp `)´

            var -> print
    THEREFORE prefixexp -> print

    args =>
        args ::=  `(´ [explist] `)´ | tableconstructor | String 
            match '('

            explist =>
                explist ::= {exp `,´} exp
                    exp =>
                        exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp 
                            prefixexp =>
                                prefixexp ::= var | functioncall | `(´ exp `)´
                                    var => 
                                        var ::=  Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name 
                                            prefixexp =>
                                                prefixexp ::= var | functioncall | `(´ exp `)´
                                                match '('
                                                exp => 
                                                    exp ::=  nil | false | true | Number | String | `...´ | function | prefixexp | tableconstructor | exp binop exp | unop exp 
                                                    tableconstructor => 
                                                THEREFORE exp = {'foo','bar'}
                                                match ')'
                                            THEREFORE prefixexp = ({'foo','bar'})
                                            match '['
                                            exp => Number = 1
                                            match ']'
                                    THEREFORE VAR = ({'foo','bar'})[1]
                            THEREFORE prefixexp = VAR
                    THEREFOER exp = VAR
            THEREFORE explist = VAR
            match ')'
    THEREFORE args = (VAR)
=> print(VAR)
like image 23
HennyH Avatar answered Sep 29 '22 07:09

HennyH