2017-05-17 2 views
2

Ich versuche, eine Syntax Analyzer in Java (mit CUP) zu erstellen, die dieses Stück Code erkennen konnte:Wie definiert man eine Syntax, die mehrere Gedankenstriche für verschachtelte "if" Anweisungen verwendet?

if ¿b? then 
~ a = 2; 
~ if ¿b && c? then 
~ ~ a = 3; 
else 
~ a = 4; 

verwendet Meine Produktionen der "if" Anweisung das sind die folgen:

Instr ::= ... 
     | IF CONOP Exp:e CONCL THEN CondInstrList:l 
     ... 
     ; 
... 
CondInstrList ::= CondInstrList CondInstr 
     | /*empty*/ 
     ; 
... 
CondInstr ::= CONTROLD Instr 
     | CONTROLD CondInstr 
     ; 

wobei Instr für Anweisung/Anweisung steht, CondInstrList für Bedingte Anweisungsliste steht und CONTROLD für Control Dash (~) steht. (CONOP und CONCL bedeuten Zustand Öffnen/Schließen)

Das Problem ist, dass mit dieser Grammatik, die erzeugte AST ist wie folgt:

if 
|-condition b 
|-condInstrListT 
    |---asig a = 2 
    |---if 
     |---condition b and c 
     |---condInstrListT 
     | |---asig a = 2 
     |---condInstrListF 
      |---asig a = 4 

und so wird die „else“ Teil in Verbindung mit dem inneren " ob".

Ich weiß einfach nicht, wie man eine Grammatik schreibt, die respektiert, wie ich meine Sprache haben möchte.

Jede Hilfe wird geschätzt.

Ich kann mehr Details geben, wenn nötig.

Antwort

1

Ich glaube nicht, dass Sie tun können, was Sie im Sinn haben durch die Grammatik allein. Aber es ist möglich mit einer etwas anderen Grammatik und etwas Hilfe von Ihrem lexikalischen Analysator.

Hier ist was zu tun: Anstatt die ~ Markierungen als individuelle Grammatiksymbole zu behandeln, lassen Sie den lexikalischen Analysator Sequenzen von ~ am Anfang einer Zeile in Indent und OUTDENT Tokens umwandeln, die in Ihrer Grammatik genauso funktionieren wie { und} arbeite in Java. Sie verfolgen eine "aktuelle Einzugsebene", die bei Null beginnt. Am Anfang jeder Zeile zählen Sie die ~ Zeichen. Für jedes über die aktuelle Einzugsebene hinausgehende Objekt generieren Sie ein INDENT-Token und erhöhen die aktuelle Einzugsebene. Für jedes Zeichen unterhalb der aktuellen Einzugsebene generieren Sie ein OUTDENT-Token und verringern die aktuelle Einzugsebene.

So Ihr Beispieltext von

if ¿b? then 
~ a = 2; 
~ if ¿b && c? then 
~ ~ a = 3; 
else 
~ a = 4; 

würde Token versehen wie:

// Indent level = 0 and no ~, so no INDENT here 
[IF] [CONOP] [ID b] [CONCL] [THEN] 
// Indent level = 0, one ~, so one INDENT 
[INDENT] 
    // Indent level = 1 
    [ID a] [OP =] [CONST 2] [SEMICOLON] 
    // Indent level = 1, one ~, so no INDENT here 
    [IF] [CONOP] [ID b] [OP &&] [ID c] [CONCL] [THEN] 
    // Indent level = 1, two ~, so one INDENT 
    [INDENT] 
     // Indent level = 2 
     [ID a] [ASSIGN] [CONST 3] [SEMICOLON] 
     // Indent level = 2, lines starts with no ~, two OUTDENTs 
    [OUTDENT] 
    // Indent level = 1 
[OUTDENT] 
//Indent level = 0 
[ELSE] // No ~ at start of this line, so no INDENT 
// Indent level = 0; one ~, so one INDENT 
[INDENT] 
    // Indent level = 1 
    [ID a] [ASSIGN] [CONST 4] [SEMICOLON] 
// End-of-input. Indent level = 1, so 1 OUTDENT 
[OUTDENT] 
// Done; indent level = 0; 

Die Einrücken und Ausrücken Token in Ihrer Grammatik handeln würde wie linke und rechte Klammern in Java zu tun, so Ihre Grammatik könnte so etwas wie aussehen:

Instr ::= ... 
     | IF CONOP Exp:e CONCL THEN INDENT CondInstrList:l OUTDENT 
     ... 
     ; 
... 
CondInstrList ::= CondInstrList Instr 
     | /*empty*/ 
     ; 
... 

Die Sprache Python macht das gleiche, aber wi th nur weißer Raum statt ~. Sie können Python-Quelle here herunterladen, wenn Sie interessiert sind. Suchen Sie nach den Dateien Grammar\Grammar und Parser\tokenizer.c.

+0

Das ist genau die Lösung. Jetzt habe ich ein Problem mit JLex. Ich speichere die Einrückungsebene in einem Attribut mit dem Namen "Einzug" und jedes Ende der Zeile setze den Zähler der tatsächlichen Einrückungsebene (Attribut "Ist") auf 0. Dies ist der Fall, wenn ein Strich mehr als die tatsächliche Einrückungsebene ist Ich gebe ein INDENT-Symbol zurück. Das Problem tritt auf, wenn ich die OUTDENT zurückgeben muss. Ich kann einfach nicht herausfinden, wie es geht, da JLex nur einen Wert zurückgibt. Ich kann herausfinden, wie viele "Einrückungslevel" ich subtrahieren muss, aber ich habe keine Möglichkeit so viele OUTDENT-Symbole wie nötig zurückzugeben. –

+0

Ich denke, Sie müssten in der Lage sein, Eingaben erneut zu lesen, um die korrekten OUTDENT-Tokens direkt im lexikalischen Analysator zu generieren, und ich habe keinen Beweis gesehen, dass JLex das tun kann. Sie werden wahrscheinlich eine Zwischenschicht zwischen dem Parser und dem lexikalischen Analysator haben müssen, die Informationen über die Einrückungslevel jeder Zeile vom lexikalischen Analysator erhalten und die richtige Anzahl von IDENT- oder OUTDENT-Tokens herausfinden kann, die an den Scanner zurückgegeben werden. –

+0

Dieser letzte Satz sollte "... Token lesen, um zum ** Parser ** zurückzukehren". –

Verwandte Themen