2013-10-12 8 views
6

Ecco una Lua 5.2.2 trascrizione, che mostra la dichiarazione e l'indicizzazione di una tabella:Perché i letterali di tabella sono trattati in modo diverso dai riferimenti di tabella in Lua?

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

Perché non è la seguente dichiarazione legale?

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

non posso pensare di qualsiasi altra lingua in cui un letterale non può essere sostituito per un riferimento (ad eccezione, ovviamente, quando è necessario un lvalue).

FWIW, mettere tra parentesi la tabella letterale rende la dichiarazione legale:

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

risposta

10

'anche in relazione al fatto che in Lua questa sintassi è valida:

myfunc { 1, 2, 3 } 

ed è equivalente a:

myfunc({ 1, 2, 3 }) 

Pertanto un'espressione quale:

myfunc { 1, 2, 3 } [2] 

viene analizzato come:

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

quindi prima chiamata di funzione viene valutata, quindi l'indicizzazione avviene.

Se {1,2,3}[2] può essere analizzato come operazione di indicizzazione valida, potrebbe portare a delle ambiguità nell'espressione precedente che richiederebbero più lookahead. Il team Lua ha scelto di rendere veloce il compilatore bytecode Lua rendendolo un compilatore a passaggio singolo, quindi esegue la scansione del codice sorgente solo una volta con un minimo di lookahead. This message to lua mailing list from Roberto Ierusalimschy (lead Lua developer) punti in quella direzione.

Lo stesso problema esiste per stringhe letterali e chiamate di metodo. Questo non è valido:

"my literal":sub(1) 

ma questo è valido:

("my literal"):sub(1) 

Anche in questo caso, Lua permette questo:

func "my literal" 

equivalenti al presente:

func("my literal") 
+0

+1; questo in realtà spiega la ragione. Non sono amico della barra di '' foo 'bar "=> foo (" bar ") però zucchero sintattico. – dualed

+1

@dualed Beh, se il suo unico uso fosse per evitare una coppia di parents, sarei d'accordo, ma il vero potere di quello "zucchero" è evidente quando si progettano linguaggi specifici di dominio. Ad esempio, qualcosa come "Widget {w = 100, h = 10}" Warning "" overflow dello stack nel frobnicator! "' È una normale sintassi di Lua e può essere "analizzata" dalla funzione 'Widget' usando un po 'di trucco di chiusura molto facilmente , senza la necessità di scrivere un parser! Questo può essere molto utile quando si utilizza Lua come linguaggio di configurazione o per altre applicazioni in cui gli utenti finali non sono programmatori esperti o per creare linguaggi di descrizione dei dati personalizzati. –

+0

Non sono assolutamente d'accordo, questo genere di cose confonde solo un utente, specialmente se commette degli errori. Il messaggio di errore non avrebbe mai avuto senso per loro. Mai. È più sicuro usare la sintassi Lua semplice, alcuni plist esistenti, xml, ini, lo si chiami o addirittura si crei il tuo e scrivi un vero parser per esso. – dualed

5

Andando della grammatica definita here, la ragione per cui la versione non tra parentesi è valido ancora la parentesi è, è perché l'albero di sintassi prende un percorso diverso e si aspetta una parentesi di chiusura ) perché ci dovrebbe non essere un altro simbolo in quel contesto.

Nel primo caso:

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 ')' 

D'altra parte con parentesi:

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) 
+0

Grazie per il spiegazione e indicarmi la grammatica.Qualche idea sul perché non hanno creato la grammatica nel modo più usuale, liberandosi di prefixexp e spostando il suo RHS in exp? La mia ipotesi è che stessero cercando di individuare errori come "3 [2]" in fase di analisi anziché in fase di esecuzione. –

+0

Trovo adatto che mi piacesse un linguaggio progettato per principianti piuttosto che programmatori esperti, dal momento che ho lavorato sulla creazione di linguaggi (come App Inventor per Android) che erano intenzionalmente progettati per sorprendere il programmatore esperto piuttosto che il principiante ogni volta qualcuno doveva essere sorpreso. Ad esempio, come Lua, App Inventor utilizza elenchi basati su 1. –

+2

@espertus Non sono sicuro del motivo per cui hanno deciso di farlo in questo modo, potresti avere ragione a proposito di errori precedenti. In realtà ho già incontrato questo problema ** esatto ** e ho pensato (dopo essere venuto da un sacco di pitone) * cosa? perché non pensa al lavoro *. – HennyH