Come viene utilizzato un albero di sintassi astratto per eseguire il codice sorgente?

4

Dopo aver studiato come un parser genera un AST, credo di poter provare a crearne uno. Prima di iniziare questo progetto, ho iniziato a riflettere su cosa avrei dovuto fare dopo aver creato un AST che rappresentasse la mia grammatica linguistica. Nonostante le mie ricerche su questo argomento, non sono emerse risorse di qualità che spiegano cosa dovrebbe essere fatto con l'AST per eseguire il codice sorgente.

Prendi questo esempio per esempio:

var = 10 + 2 .

Un parser potrebbe creare un AST simile a questo:

     =
    / \
  var  +
      / \
     10  2

Cosa si farebbe successivamente con l'esempio AST sopra. Il parser registrerebbe la variabile e il suo valore? Oppure il parser genera semplicemente l'AST, e il suo fino ad un altro programma per valutare l'AST.

Mi sembra che creare un AST stia facendo più lavoro per il resto del programma. Qualunque cosa legga l'AST deve tenere traccia di tutti i tipi di dichiarazioni e scopi. Non avrebbe più senso raggruppare i token in istruzioni e eseguire ogni singola istruzione senza un AST?

Nota : la mia domanda non è un duplicato di È sufficiente un AST per creare qualsiasi traduttore? . L'OP di quella domanda sta chiedendo se un AST è sufficiente per implementare qualsiasi funzione linguistica? . Mi sto chiedendo come si dovrebbe eseguire il codice sorgente di una lingua da un AST? . alcune parti del post possono essere simili, ma le domande complessive di ognuna sono molto diverse.

    
posta Christian Dean 16.09.2016 - 19:33
fonte

2 risposte

3

Il parser semplicemente analizza il codice in parti logicamente significative. È quindi in una fase successiva dell'elaborazione fare qualcosa con il risultato dell'analisi.

Il seguente passaggio (nel tuo caso un interprete di codice) può concentrarsi sulla semantica. Il parser ha fatto tutto il lavoro sporco, il suo risultato è pulito e semplice. Quindi rende le cose più facili. Si noti che si inizia con una porzione di testo (una stringa o una raccolta di linee) e il parser fornisce un modello a oggetti. Sembra che tu non abbia un'immagine chiara di un AST, di cosa significhi in termini di implementazione nel codice. Quindi entriamo in questo.

Un motore di esecuzione dovrebbe leggere l'albero e trovare un oggetto: un assegnatario. La classe di assegnazione contiene due proprietà: sinistra e destra. Sinistra è un oggetto variabile, a destra è un oggetto espressione.

Anche l'oggetto espressione è un albero che è fondamentalmente un insieme di oggetti che contengono altri oggetti. Questi vengono tipicamente elaborati in modo ricorsivo, poiché il processore colpisce il primo oggetto nell'albero che non conosce o si preoccupa della profondità dell'albero, elabora solo quell'oggetto e delega l'elaborazione degli oggetti contenuti allo stesso codice. Quando quel codice colpisce una foglia (un oggetto che non è complesso, che non ha oggetti contenuti) esegue un'azione e / o restituisce un risultato. Quindi tutto bolle indietro nello stack delle chiamate fino a quando l'oggetto in alto ottiene il suo risultato e può fare la sua cosa, nel tuo esempio aggiungi 10 e 2.

Quindi ciò che rende semplice è la possibilità di scovare una raccolta nidificata di oggetti e risolverla ricorsivamente. Analizzare il testo e agire su di esso in una volta sola può diventare presto disordinato e potrebbe produrre risultati scadenti perché potrebbero esserci errori di sintassi più in basso e hai già iniziato l'esecuzione.

    
risposta data 17.09.2016 - 08:16
fonte
3

Would the parser record the variable and its value?

No. Il parser prende solo i token e li sistema in un AST (possibilmente restituendo errori).

Would it not make more sense to just group your tokens into statements, and execute each statement individually without a AST?

Non proprio. L'AST consente di suddividere il programma nelle sue parti costitutive e quindi di implementare tali parti minuscole e semplici in modo chiaro e privo di errori. Meglio ancora, lo fa in un modo molto agnostico. Se vuoi prendere quell'AST e fare qualche analisi statica su di esso, puoi farlo. Se si vuole prendere questo AST ed eseguirlo attraverso un interprete, l'interprete non deve preoccuparsi dell'associatività, o se le istruzioni vengono analizzate correttamente, o a cosa si riferisce effettivamente. Se vuoi prendere questo AST e compilarlo contro qualche nuovo processore, puoi farlo poiché i piccoli nodi di un AST probabilmente si allineano con il nuovo assemblaggio di quel processore.

Tu puoi evitare di costruire un AST quando fai un interprete o un compilatore, ma non lo consiglierei. E come lo trasformi in qualcosa che è eseguibile dipende da te, ma lo troverai molto più facile da implementare se l'output del tuo parser si trova in una semplice struttura ad albero.

    
risposta data 16.09.2016 - 19:42
fonte

Leggi altre domande sui tag