2012-02-24 19 views
5

Sto cercando un modo per implementare un lettore di espressioni S (da utilizzare in seguito con un interprete Scheme e un compilatore), ma mi sono chiesto come (se non del tutto) dovrei scrivere un AST per questo.Modo corretto di analizzare le espressioni S in OOP

Ho letto SICP e questo è abbastanza semplice da Scheme, ma sto cercando di implementare l'interprete e il compilatore in C++, in modo OO.

Per favore, tieni presente che lo sto facendo solo a scopo di apprendimento, quindi non sto davvero cercando il modo più semplice o più veloce per farlo, ma piuttosto il modo corretto e riusabile di farlo.

che ho visto in alcune implementazioni Schema che le persone analizzano s-espressioni e le celle cons prontamente uscita, qualcosa di simile:

struct Sexpr 
    { 
    }; 

    struct Cons : public Sexpr 
    { 
    Sexpr* left; 
    Sexpr* right; 
    }; 

    struct IntAtom : Sexpr 
    { 
    int value; 
    }; 

E una sottoclasse di sEspr per ogni tipo di regime Atom, o qualcosa del genere quelle linee.

Non sono sicuro, ma questo mi sembra un trucco ... Non dovrebbe questo lavoro essere eseguito da un interprete piuttosto che dal lettore?

Quello che voglio sapere è se questo è considerato il modo migliore (o corretto) di leggere le S-espressioni, o è più un lavoro dell'interprete che del parser? Il parser dovrebbe avere il proprio AST invece di fare affidamento sulle celle contro?

+0

Se sto leggendo bene, la domanda non ha molto a che fare con l'analisi. Piuttosto, penso che tu stia chiedendo: qual è la rappresentazione più appropriata per il tipo di dati di espressione s. Sei d'accordo? – dyoo

+0

@dyoo Sì e no. Sì, hai ragione, sto cercando la rappresentazione più appropriata per le espressioni s. E no, ti sbagli, questa domanda ovviamente ha a che fare con l'analisi. Se fossi solo alla ricerca della rappresentazione più appropriata per sexpr, non ci sarebbero dubbi sul fatto che sarebbero state le cellule. Tuttavia, sto cercando la rappresentazione più appropriata di sexpr in particolare ** per l'analisi **. – ivanmp

+0

cool. Buon chiarimento Quindi: una cosa che potrebbe distinguere il compito di analisi è la necessità di informazioni sulla posizione della fonte. Le cellule normali non ricordano da dove provenissero nella fonte originale. Durante l'analisi, è possibile che si desideri supportare i messaggi di errore che possono puntare all'origine. Quali altre cose potremmo aver bisogno di analizzare? – dyoo

risposta

3

Per seguire dal lato Scheme/Racket della recinzione:

Racket (e alcune altre implementazioni Scheme) utilizzare una rappresentazione più ricco per oggetti di sintassi, in modo che possano avere proprietà ad essi indicano (in Racket almeno) in quale contesto sono associati, da quale fonte di provenienza provengono, quale passata del compilatore è stato inserito e qualsiasi altra informazione che potresti voler memorizzare (vedere "proprietà della sintassi" in Racket).

Questa informazione aggiuntiva abilita le cose come i messaggi di errore con i puntatori all'origine e i macro igienici.

Si noti che intendo "più ricco" qui semplicemente nel senso "contiene più valori", non in alcun modo non neutro rispetto al valore.

Dovrei anche aggiungere --- prima di cadere nel pozzo del Turing Tar --- che puoi anche rappresentare questa stessa identica informazione usando un tavolo sul lato; supponendo di avere confronti tra puntatori, non c'è alcuna differenza di espressività tra l'inserimento di un valore all'interno di una struttura e l'utilizzo di una tabella per associare la struttura al valore.

3

Se si vuole essere un po 'completo nella tua sintassi, sarà necessario sostenere

sexpr ::= atom | sexpr sexpr 
atom ::= nil | intatom | etc. 

ma che è più generale rispetto alla maggior parte sEspr si incontrano. La forma più semplice e più comune di S-expr che è in LISP/Scheme (a b c d) dove ognuno di a, b, c, d è un atomo o una lista. In coppia questo è [a [b [c [d nil]]], il che significa che tutti i lati positivi delle tue consegne sono elenchi.

Quindi, se si sta andando per la pulizia, si potrebbe anche fare

class sexpr {}; 
class atom : sexpr {}; 
class s_list : forward_list<smart_ptr<sexpr>> {}; 
+0

per favore puoi formattare la tua risposta? – ivanmp

+0

cercando, spiacente :-) –

+0

Nessun problema! Questo è qualcosa sulla falsariga di ciò che avevo in mente, ma ho due domande: 1) vuol dire che non dovrei preoccuparmi di "contro le cellule" a questo punto (parser) e lasciarlo come un problema all'interprete, e usa questo tipo di AST come invece hai mostrato? 2) Potrei aver sbagliato qualcosa, ma S_list non dovrebbe ereditare da Sexpr? – ivanmp

3

Mentre si può probabilmente affermare avanti e indietro su ciò che l'approccio “corretto” è, a mio parere, l'approccio suggerisci-using le stesse strutture di dati per la lettura, la compilazione, la valutazione, e l'elaborazione - è quella che ti insegnerà di più su cosa siano i mantra di Lisp e del "codice è dati", e in particolare cosa significhi realmente l'operatore quote (che è una cosa abbastanza profonda).

E 'anche, per inciso, il modo in cui la maggior parte dei Lisps (interessante, non incluso Schema) funziona tradizionalmente.

Quindi sì, è necessario che il lettore generi dati Lisp: conses, simboli, numeri Lisp, stringhe eccetera, esattamente lo stesso materiale con cui il codice Lisp a livello utente dovrà occuparsi. Renderà il resto dell'implementazione sia più semplice che più istruttivo.

+0

Grazie per la risposta! Il mio obiettivo principale è quello di imparare di più sui compilatori e gli interpreti in generale, non necessariamente sui Lisps, ecco perché mi sto interrogando sull'approccio più corretto a questo.Ho scelto Scheme perché pensavo che fosse un linguaggio piuttosto semplice (almeno un sottoinsieme), ho lavorato con esso e, ultimo ma non meno importante, mi piace anche a me. :) Cosa intendi con la tua ultima riga? – ivanmp

+0

@ivanmp Intendevo dire che se stai provando a conoscere Lisp, è più * istruttivo * farlo in questo modo. Sì, * istruttivo * è la parola che stavo cercando. :) L'implementazione sarà anche più semplice perché non dovrai gestire strutture di dati diverse per fasi diverse e alcuni dati potranno semplicemente essere "passati" alle fasi senza doverli convertire in qualcos'altro (come nel caso di 'quote'). D'altra parte, se stai cercando di imparare la compilazione in generale, non in Lisp in particolare, in entrambi i casi è probabilmente soddisfacente. –

+1

Intendevo questa linea: _ È anche, per inciso, il modo in cui la maggior parte dei Lisps (** interessante, non includendo Scheme **) funziona tradizionalmente. – ivanmp

1

Si potrebbe dare un'occhiata a this c/c++ s-expr parser library per un esempio di come è stato fatto.

Sembra che la rappresentazione di base è:

struct elt { 
    int type; 
    char *val; 
    struct elt *list; 
    struct elt *next; 
}; 

e cito dai loro documenti:

Poiché un elemento può essere sia una lista o atomo, la struttura di elemento ha un indicatore del tipo che può essere LISTA o VALORE. Se l'indicatore di tipo è ELENCO, il membro della struttura "lista" sarà un puntatore alla testa dell'elenco rappresentato da questo elemento. Se l'indicatore del tipo è VALUE, il membro della struttura "val" conterrà l'atomo rappresentato dall'elemento come una stringa. In entrambi i casi, il puntatore "successivo" punterà sull'elemento successivo dell'espressione s corrente.

Inoltre here è un intero elenco di altre implementazioni di lettori s-expr in molte lingue che potrebbero essere di interesse.