Qual è la differenza tra un albero della sintassi astratto e un albero della sintassi concreto?


85

Ho letto un po 'su come funzionano gli interpreti / compilatori e un'area in cui mi sto confondendo è la differenza tra un AST e un CST. La mia comprensione è che il parser fa un CST, lo passa all'analizzatore semantico che lo trasforma in un AST. Tuttavia, la mia comprensione è che l'analizzatore semantico garantisce semplicemente che le regole siano seguite. Non capisco davvero perché avrebbe effettivamente apportato modifiche per renderlo astratto piuttosto che concreto.

C'è qualcosa che mi manca nell'analizzatore semantico o la differenza tra un AST e un CST è in qualche modo artificiale?

Risposte:


65

Un albero di sintassi concreto rappresenta il testo di origine esattamente in forma analizzata. In generale, è conforme alla grammatica libera dal contesto che definisce la lingua di origine.

Tuttavia, la grammatica e l'albero concreti hanno molte cose necessarie per rendere il testo di origine analizzabile in modo univoco, ma non contribuiscono al significato effettivo. Ad esempio, per implementare la precedenza degli operatori, il tuo CFG di solito ha diversi livelli di componenti di espressione (termine, fattore, ecc.), Con gli operatori che li collegano ai diversi livelli (aggiungi termini per ottenere espressioni, i termini sono composti da fattori opzionalmente moltiplicati , eccetera.). Per interpretare o compilare effettivamente il linguaggio, tuttavia, non è necessario questo; hai solo bisogno di nodi Expression che abbiano operatori e operandi. L'albero della sintassi astratto è il risultato della semplificazione dell'albero della sintassi concreto fino alle cose effettivamente necessarie per rappresentare il significato del programma. Questo albero ha una definizione molto più semplice ed è quindi più facile da elaborare nelle fasi successive di esecuzione.

Di solito non è necessario costruire effettivamente un albero della sintassi concreto. Le routine di azione nella grammatica YACC (o Antlr, o Menhir, o qualsiasi altra cosa ...) possono costruire direttamente l'albero della sintassi astratto, quindi l'albero della sintassi concreta esiste solo come entità concettuale che rappresenta la struttura di analisi del testo di origine.


2
Supplemento: l'interprete Python crea prima un CST e poi converte in AST.
cgsdfc

34

Un albero di sintassi concreto corrisponde a ciò che le regole grammaticali dicono è la sintassi. Lo scopo dell'albero della sintassi astratto è avere una rappresentazione "semplice" di ciò che è essenziale "nell'albero della sintassi".

Un valore reale nell'AST IMHO è che è più piccolo del CST e quindi richiede meno tempo per l'elaborazione. (Potresti dire, chi se ne frega? Ma lavoro con uno strumento in cui abbiamo decine di milioni di nodi che vivono contemporaneamente!).

La maggior parte dei generatori di parser che hanno supporto per la costruzione di alberi di sintassi insistono che tu specifichi personalmente esattamente come vengono costruiti partendo dal presupposto che i tuoi nodi ad albero saranno "più semplici" del CST (e in questo, hanno generalmente ragione, dato che i programmatori sono carini pigro). Probabilmente significa che devi codificare meno funzioni del visitatore dell'albero, e questo è anche prezioso, in quanto riduce al minimo l'energia di ingegneria. Quando hai 3500 regole (ad esempio, per COBOL) questo è importante. E questa "semplicità" conduce alla buona proprietà della "piccolezza".

Ma avere tali AST crea un problema che non c'era: non corrisponde alla grammatica, e ora devi tracciarli mentalmente entrambi. E quando ci sono 1500 nodi AST per una grammatica di 3500 regole, questo conta molto. E se la grammatica si evolve (lo fanno sempre!), Ora hai due serie gigantesche di cose da tenere sincronizzate.

Un'altra soluzione è lasciare che il parser crei semplicemente i nodi CST per te e li usi. Questo è un enorme vantaggio quando si costruiscono le grammatiche: non è necessario inventare 1500 nodi AST speciali per modellare 3500 regole grammaticali. Basti pensare che l'albero è isomorfo alla grammatica. Dal punto di vista dell'ingegnere grammaticale questo è completamente senza cervello, il che gli consente di concentrarsi sull'ottenere la grammatica giusta e su come farlo a suo piacimento. Probabilmente devi scrivere più regole per i visitatori del nodo, ma questo può essere gestito. Ne parleremo più avanti.

Quello che facciamo con il DMS Software Reengineering Toolkit è creare automaticamente un CST basato sui risultati di un processo di analisi (GLR). DMS quindi costruisce automaticamente un CST "compresso" per ragioni di efficienza dello spazio, eliminando terminali non portatori di valore (parole chiave, punteggiatura), produzioni unarie semanticamente inutili e formando elenchi per coppie di regole grammaticali che sono elenchi come:

    L = e ;
    L = L e ;
    L2 = e2 ;
    L2 = L2  ','  e2 ;

e un'ampia varietà di variazioni di tali forme. Pensi in termini di regole grammaticali e CST virtuale; lo strumento opera sulla rappresentazione compressa. Facile per il tuo cervello, più veloce / più piccolo in fase di esecuzione.

Sorprendentemente, il CST compresso costruito in questo modo sembra molto un AST che potresti aver progettato a mano (vedi il collegamento alla fine degli esempi). In particolare, il CST compresso non contiene nodi che siano solo sintassi concreta. Ci sono pezzi minori di disagio: per esempio, mentre i nodi concreti per '(' e ')' classicamente disponibili a subgrammars espressione non sono nella struttura, una "parentesi nodo" non appare nella CST compresso e deve essere gestito. Un vero AST non lo avrebbe. Questo sembra un prezzo piuttosto basso da pagare per la comodità di non dover specificare la costruzione AST, mai. E la documentazione per l'albero è sempre disponibile e corretta: la grammatica è la documentazione.

Come si evitano i "visitatori extra"? Non lo facciamo del tutto, ma DMS fornisce una libreria AST che controlla l'AST e gestisce le differenze tra CST e AST in modo trasparente. DMS offre anche un valutatore di "grammatica degli attributi" (AGE), che è un metodo per passare valori calcolati a nodi su e giù per l'albero; l'AGE gestisce tutti i problemi di rappresentazione ad albero e quindi l'ingegnere dello strumento si preoccupa solo di scrivere i calcoli in modo efficace direttamente sulle regole grammaticali stesse. Infine, DMS fornisce anche modelli di "sintassi superficiale", che consentono di utilizzare frammenti di codice dalla grammatica per trovare tipi specifici di sottostrutture, senza conoscere la maggior parte dei tipi di nodi coinvolti.

Una delle altre risposte osserva che se vuoi costruire strumenti in grado di rigenerare la sorgente, il tuo AST dovrà corrispondere al CST. Non è proprio vero, ma è molto più facile rigenerare la sorgente se si hanno nodi CST. DMS genera automaticamente la maggior parte di prettyprinter perché ha accesso a entrambi: -}

Conclusione: gli AST vanno bene per i piccoli, sia fisici che concettuali. La costruzione AST automatizzata dal CST fornisce entrambi e consente di evitare il problema di tracciare due set diversi.

EDIT Marzo 2015: collegamento ad esempi di CST e "AST" costruiti in questo modo


25

Questo si basa sulla grammatica Expression Evaluator di Terrence Parr.

La grammatica per questo esempio:

grammar Expr002;

options 
{
    output=AST;
    ASTLabelType=CommonTree; // type of $stat.tree ref etc...
}

prog    :   ( stat )+ ;

stat    :   expr NEWLINE        -> expr
        |   ID '=' expr NEWLINE -> ^('=' ID expr)
        |   NEWLINE             ->
        ;

expr    :   multExpr (( '+'^ | '-'^ ) multExpr)*
        ; 

multExpr
        :   atom ('*'^ atom)*
        ; 

atom    :   INT 
        |   ID
        |   '('! expr ')'!
        ;

ID      : ('a'..'z' | 'A'..'Z' )+ ;
INT     : '0'..'9'+ ;
NEWLINE : '\r'? '\n' ;
WS      : ( ' ' | '\t' )+ { skip(); } ;

Ingresso

x=1
y=2
3*(x+y)

Parse Tree

L'albero di analisi è una rappresentazione concreta dell'input. L'albero di analisi conserva tutte le informazioni dell'input. Le caselle vuote rappresentano gli spazi, cioè la fine della riga.

Parse Tree

AST

L'AST è una rappresentazione astratta dell'input. Si noti che le parentesi non sono presenti nell'AST perché le associazioni sono derivabili dalla struttura ad albero.

AST

MODIFICARE

Per una spiegazione più approfondita vedere Compilatori e generatori di compilatori pag. 23


20

Questo post del blog potrebbe essere utile.

Mi sembra che l'AST "butti via" molte informazioni grammaticali / strutturali intermedie che non contribuirebbero alla semantica. Ad esempio, non ti interessa che 3 sia un atomo è un termine è un fattore è un ... Ti interessa solo che sia 3quando stai implementando l'espressione di esponenziazione o qualsiasi altra cosa.


9

L' albero concreto della sintassi segue le regole della grammatica della lingua. Nella grammatica, gli "elenchi di espressioni" sono generalmente definiti con due regole

  • elenco_espressioni può essere: espressione
  • expression_list può essere: expression, expression_list

Seguite letteralmente, queste due regole danno una forma a pettine a qualsiasi elenco di espressioni che appare nel programma.

L' albero della sintassi astratto è nella forma che è conveniente per ulteriori manipolazioni. Rappresenta le cose in un modo che ha senso per qualcuno che capisce il significato dei programmi e non solo il modo in cui sono scritti. L'elenco delle espressioni sopra, che può essere l'elenco degli argomenti di una funzione, può essere convenientemente rappresentato come un vettore di espressioni, poiché è meglio per l'analisi statica avere il numero totale di espressioni esplicitamente disponibili ed essere in grado di accedere a ciascuna espressione dal suo indice.


2

Semplicemente, AST contiene solo la semantica del codice, Parse tree / CST include anche informazioni su come esattamente il codice è stato scritto.


1

L'albero della sintassi concreta contiene tutte le informazioni come parentesi superflue e spazi bianchi e commenti, l'albero della sintassi astratto si astrae da queste informazioni.

 

NB: abbastanza divertente, quando implementi un motore di refactoring il tuo AST conterrà di nuovo tutte le informazioni concrete, ma continuerai a chiamarlo AST perché è diventato il termine standard nel campo (quindi si potrebbe dire che è lungo fa ha perso il suo significato originale).


Bene, potrebbe non avere tutte le informazioni concrete. Tutto ciò che serve è che sia in grado di rigenerare quelle informazioni. Vedi la mia risposta.
Ira Baxter

Commentato ieri? Quindi bug o c'è un badge da negromante di commento da guadagnare di cui non sono a conoscenza? :) (PS: ma è un piacere sentirti, ti sei appena imbattuto nel tuo discorso di Google Tech su DMS ...)
akuhn

1

È una differenza che non fa differenza.

Un AST viene solitamente spiegato come un modo per approssimare la semantica di un'espressione del linguaggio di programmazione gettando via il contenuto lessicale. Ad esempio, in una grammatica libera dal contesto potresti scrivere la seguente regola EBNF

term: atom (('*' | '/') term )*

mentre nel caso AST si usano solo mul_rule e div_rule che esprimono le operazioni aritmetiche appropriate.

Queste regole non possono essere introdotte nella grammatica in primo luogo? Ovviamente. È possibile riscrivere la regola compatta e astratta sopra suddividendola in regole più concrete utilizzate per imitare i nodi AST menzionati:

term: mul_rule | div_rule
mul_rule: atom ('*' term)*
div_rule: atom ('/' term)*

Ora, quando si pensa in termini di analisi top-down, il secondo termine introduce un PRIMO / PRIMO conflitto tra mul_rule e div_rule qualcosa che un parser LL (1) non può affrontare. La prima forma di regola era la versione fattorizzata a sinistra della seconda che eliminava efficacemente la struttura. Devi pagare un premio per l'utilizzo di LL (1) qui.

Quindi gli AST sono un supplemento ad hoc utilizzato per correggere le carenze di grammatiche e parser. La trasformazione CST -> AST è una mossa di refactoring. Nessuno si è mai preoccupato quando una virgola o due punti aggiuntivi vengono memorizzati nell'albero della sintassi. Al contrario, alcuni autori li adattano in AST perché preferiscono utilizzare gli AST per fare refactoring invece di mantenere vari alberi contemporaneamente o scrivere un motore di inferenza aggiuntivo. I programmatori sono pigri per buone ragioni. In realtà memorizzano anche le informazioni di riga e colonna raccolte dall'analisi lessicale negli AST per la segnalazione degli errori. Davvero molto astratto.


0

CST (Concrete Syntax Tree) è una rappresentazione ad albero della grammatica (regole di come il programma dovrebbe essere scritto). A seconda dell'architettura del compilatore, può essere utilizzato dal parser per produrre un AST.

AST (Abstract Syntax Tree) è una rappresentazione ad albero del sorgente Parsed, prodotta dalla parte Parser del compilatore. Memorizza le informazioni sui token + la grammatica.

A seconda dell'architettura del compilatore, il CST può essere utilizzato per produrre un AST. È giusto dire che CST evolve in AST. Oppure AST è un CST più ricco.

Ulteriori spiegazioni possono essere trovate su questo link: http://eli.thegreenplace.net/2009/02/16/abstract-vs-concrete-syntax-trees#id6


1
Penso che questi necessitino di chiarimenti in particolare su "semplificato". Tendo a vederlo come "complicato" almeno concettualmente, il che è l'opposto, e ancora non descrive nulla di utile.
Joshua Hedges

1
Ho cambiato il mio -1 in +1. Credo che le precisazioni che hai fatto siano sufficienti.
Joshua Hedges
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.