disambiguazione della grammatica della lingua Javascript / Ecmascript

3

Sto lavorando alla progettazione di un compilatore per una lingua in cui devo utilizzare parentesi graffa per due scopi diversi. Attualmente sono bloccato nello scrivere una grammatica non ambigua, ma ho capito che alcune altre lingue hanno lo stesso comportamento. Questo è il motivo per cui sto cercando di capire come viene costruita una parte grammatica JS / Ecmascript.

Più precisamente, vorrei capire come il parser del parser identifichi correttamente i blocchi di istruzioni come il corpo della funzione dalle definizioni degli oggetti poiché sono entrambi circondati da parentesi graffe e il loro contenuto può essere sintatticamente lo stesso.

Ho cercato la grammatica JS sul web ma la maggior parte della documentazione è molto caotica. Ho trovato questo, che è molto chiaro: link ma l'unica regola che utilizza le parentesi graffe è di seguito:

CompoundStatement:
      { Statements }

Qualcuno potrebbe aiutarmi con questo

    
posta ibi0tux 05.10.2016 - 13:31
fonte

1 risposta

6

La grammatica che hai collegato sembra incompleta e buggata (non menziona i letterali degli oggetti). Parliamo invece di questo . Qui, le parentesi si presentano in quattro regole:

ObjectLiteral ⇒
   { }
|  { FieldList }

...

Block ⇒ { BlockStatements }

...

SwitchStatement ⇒
   switch ParenthesizedExpression { }
|  switch ParenthesizedExpression { CaseGroups LastCaseGroup }

...

NamedFunction ⇒ function Identifier FormalParametersAndBody
FormalParametersAndBody ⇒ ( FormalParameters ) { TopStatements }

La funzione e i casi di switch non entreranno mai in conflitto con il blocco e il letterale dell'oggetto, perché sono preceduti dalle parole chiave function e switch . Non appena il parser riconosce queste parole chiave, tutte le altre produzioni alternative possono essere scartate.

Tuttavia, sembra esserci un'ambiguità tra i blocchi e i letterali degli oggetti. Come deve essere analizzata la istruzione {} ? Questo potrebbe essere un letterale dell'oggetto vuoto o un blocco vuoto.

La grammatica lo impedisce rilevando se un'espressione è l'espressione iniziale dell'istruzione e fornisce produzioni diverse per ogni caso:

PrimaryExpression(normal) ⇒
   SimpleExpression
|  FunctionExpression
|  ObjectLiteral
PrimaryExpression(initial) ⇒ SimpleExpression

Ciò non consente i valori letterali degli oggetti nella prima espressione e anche le definizioni di funzione - se un function si verifica all'inizio di un'istruzione, è sempre un'istruzione di definizione di funzione, mai un'espressione di funzione.

Ciò rende la grammatica JavaScript non ambigua. Questo è importante per le grammatiche LR, poiché evita di ridurre / ridurre i conflitti.

Per i parser top-down (ad esempio, la discesa ricorsiva scritta a mano), potremmo lasciare la grammatica ambigua ma assegnare priorità a ciascuna alternativa. Per esempio. in un Statement and TopStatement, le alternative Block e FunctionDefinition dovrebbero sempre essere tentate prima dell'opzione ExpressionStatement.

Per parser generalizzati in grado di analizzare tutte le grammatiche context-free (non solo i sottogruppi LL o LR), le ambiguità non ostacolano il parser. A seconda del parser, o restituisce un albero di analisi arbitrario di tutti gli alberi di analisi validi (in pratica indesiderabili) o itera su tutti i possibili alberi di analisi. Una grammatica effettivamente ambigua non è buona per i linguaggi di programmazione, poiché di solito vogliamo scrivere programmi che non possono essere fraintesi dal compilatore.

Tuttavia, esiste un'interessante classe di grammatiche non ambigue che non possono essere analizzate dai parser LR e richiede un parser generalizzato. Qui abbiamo un conflitto di riduzione / riduzione tra le regole A e B , ma è solo un'ambiguità locale e viene risolto più avanti nel programma:

Top = X | Y
X = A Balanced x
Y = B Balanced y
A = a b
B = a b
Balanced = x | y | () | ( Balanced ) | ( Balanced Balanced )

Poiché la regola Balanced è una grammatica context-free in sé, nessuna quantità di lookahead risolverà tutto ciò. L'input ab((y()))x ha esattamente un albero di analisi (non è ambiguo), ma non è analizzabile dai parser comuni. Quando si progetta una lingua, dovrebbero essere evitate anche le ambiguità locali.

    
risposta data 05.10.2016 - 15:13
fonte

Leggi altre domande sui tag