Domanda

Dopo aver creato l'albero di analisi, devo popolare la tabella dei simboli ora.

Devo memorizzare informazioni come

Tipo, portata, offset ecc. Per gli identificatori.

Ora come faccio a conoscere il tipo, ambito degli identificatori, poiché tutto ciò che conosco è il valore lexeme e il numero di riga per quel particolare ID (dopo l'analisi lessicale).

Come faccio a raggiungere il tutto.Grazie.

È stato utile?

Soluzione

.

Ora come faccio a conoscere il tipo, portata degli identificatori, dal momento che tutto I sapere è il valore del lexeme e il numero di linea per quel particolare ID (dopo Analisi lessicale).

Come accennato EJP, è necessario fare un passo attraverso l'albero di analisi. Il tuo albero avrebbe dovuto essere creato in modo da poter eseguire un traversale in ordine, visitando ogni nodo nello stesso ordine in cui vengono valutate le istruzioni e le espressioni del codice sorgente. I tuoi nodi dell'albero dovrebbero corrispondere anche a un costrutto linguistico specifico, ad es. WhileStmtNode, MethodDeclNode, ecc.

Supponiamo che io stia costruendo la tabella dei simboli, passando ricorsivamente attraverso l'albero e ho appena inserito un nodo del corpo del metodo. Potrei avere qualcosa come il seguente:

public void doAction(MethodBodyNode methodBody) {
    currScope = 2;
    methodBody.getExpr().applyAction(this);
    currScope = 2;
}
.

Conservare una variabile globale per gestire l'ambito. Ogni volta che inserisco un blocco in cui cambiano l'ambito, incrementato currScope. Allo stesso modo, mantenerei currClass e currMethod Variabili per memorizzare con il nome del simbolo, il tipo, l'offset, ecc. Per fasi successive.

Aggiornamento:

.

Ora dì, sto attraversando l'albero, ogni volta che incontro un ID I dovrebbe inserire il valore alla tabella simbolo insieme al tipo, Scope e altri, dicono per lo scopo che controllo se mi imbatto a '{' o Nome della funzione, ma come faccio a sapere che tipo di ID è questo.

Ogni nodo dell'albero dovrebbe contenere tutte le informazioni necessarie per l'intero costrutto. Se stai usando un generatore di parser, come Cup o Bison, puoi specificare come costruire l'albero nelle azioni di grammatica. Ad esempio.

variableDeclaration::= identifier:i identifier:i2 SEMICOLON {: RESULT = new VarDeclNode(i, i2, null); :};
identifier::= ID:i {: RESULT = new IdNode(i.getLineNum(), i.getCharNum(), i.getStringValue()); :};
.

Queste produzioni corrispondono alla generazione di Foo f; e appendono un nodo di dichiarazione variabile all'albero. Quel nodo incapsula due nodi identificativi che contengono il numero di riga, il numero di caratteri e il valore stringa del Lexeme. Il primo nodo identificatore è il tipo e il secondo è il nome della variabile. ID è un simbolo del terminale restituito dal Lexer al momento della corrispondenza di un identificatore. Suppongo che tu abbia familiarità con questo in una certa misura.

public class VarDeclNode extends StmtNode {

    private IdNode id;
    private IdNode type;
    private ExprNode expr;

    public VarDeclNode(IdNode id, IdNode type, ExprNode expr) {
        super();
        this.id = id;
        this.type = type;
        this.expr = expr;
    }

}
.

Quando hai un albero di sintassi con nodi come questo, hai tutte le informazioni di cui hai bisogno.

2nd Update:

Non importa se stai usando un parser personalizzato o uno generato, c'è un punto distinto in cui aggiungi un nodo nell'albero alzato abbinamento a una produzione. E non importa quale lingua stai usando. C Strisce andrà bene.

.

Se è un non terminale ha le informazioni come nome nonterminali, e se la è un terminale I.e. Un token, quindi le informazioni in token I.e. Valore Lexeme, Il nome del token e il numero di riga sono memorizzati

Devi avere nodi specializzati nell'albero, ad es. Classnode, typonode, metoddeclnode, ifstmtnode, exprnode. Non è possibile memorizzare un tipo di nodo e mettere i terminali e i terminali in esso. Un non-terminale è rappresentato come un nodo dell'albero, non ci sono altre informazioni per archiviarlo accanto alle parti che lo compongono, che sono generalmente non-terminali stessi. Non memorizzeresti alcuna informazione token. Ci sono solo un paio di casi in cui effettivamente conservi il valore della stringa di un Lexeme: per un identificatore e per una stringa / booleana / integer letterale.

Dai un'occhiata a Questo esempio . Durante la prima riduzione quando S viene ridotto a (S + F), si aggiungono un ParenExprNode alla radice dell'albero. Avresti anche un AddExprNode come figlio del ParenExprNode. Quella logica deve essere codificata in modo difficile nel tuo parser quando si applica una riduzione della regola 2 della grammatica.

L'albero:

    ExprNode (root)
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode
.

Il codice:

struct ExprNode { void* exprNode; };
struct ParenExprNode { void* exprNode; };
struct AddExprNode { void* op1, * op2; };
struct IdNode { char* val; int line; int charNum; };
struct IntLiteralNode { int val; int line; int charNum; };

void reduce_rule_2(ExprNode* expr) {

    //remove stack symbols

    //create nodes
    struct ParenExprNode* parenExpr = malloc(sizeof(struct ParenExprNode));
    struct AddExprNode* addExpr = malloc(sizeof(struct AddExprNode));
    addExpr->op1 = malloc(sizeof(struct ExprNode));
    addExpr->op2 = malloc(sizeof(struct ExprNode));

    //link them
    parenExpr->exprNode = (void*)addExpr;
    expr->exprNode = (void*)parenExpr;
}
.

Nel passaggio successivo, la parentesi sinistra viene rimossa dall'ingresso. In seguito, S è in cima allo stack e viene ridotto a F per regola 1. Poiché F è il non-terminale per un identificatore, è rappresentato da IdNode.

L'albero:

    ExprNode
       |
  ParenExprNode
       |
   AddExprNode
   /         \
ExprNode   ExprNode
   |
 IdNode
.

Il codice:

reduce_rule_2(addExpr->op1);

void reduce_rule_1(ExprNode* expr) {
    //reduce stack symbols
    struct IdNode* id = malloc(sizeof(struct IdNode));
    id->val = parser_matched_text();
    id->lineNum = parser_line_num();
    id->charNum = parser_char_num();
    expr->exprNode = (void*)id;
}
.

E così via ...

Altri suggerimenti

.

Tutto quello che so è il valore del Lexeme e il numero di riga per quel particolare ID

Non è vero.Sai dove viene dichiarato nell'albero Parse, il che ti dice tutto ciò di cui hai bisogno.Fai questo passo da Elaborazione L'albero Parse.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top