Frage

I wounder wie Einzug als Blocktrennzeichen in Bison + flex zu implementieren. Genau wie in Python. Ich bin meine eigene Programmiersprache (meist zum Spaß, aber ich beabsichtige, es mit einem Spiel-Engine zusammen zu verwenden) zu schreiben, ich werde versuchen, speziell mit etwas zu entwickeln, den vorformulierten minimiert und maximiert dev Geschwindigkeit.

Ich habe bereits einen Compiler (eigentlich ein `langToy ' NASM Übersetzer) in C geschrieben, scheiterte aber. Aus irgendeinem Grunde war es nur in der Lage eine Zeichenfolge in der gesamten Quelldatei zu handhaben (na ja, ich hatte für mehr als 48 Stunden wach - so ... Du weißt schon, Gehirn Einschmelzen)

.

Ich weiß nicht, ob geschweiften Klammern und / oder beginnen -> Ende ist einfacher zu implementieren (Ich habe kein Problem zu tun, dass) oder wenn es nur mein Gehirn, das abstürzt

.

Vielen Dank im Voraus!


Update: Okay, ich habe keine Ahnung, wie es mit Flex zu tun. Ich habe Probleme mit mehreren DEDENTs an den Parser zurück. Flex / Bison ist relativ neu für mich.


Update 2: Dies ist die Flex-Datei, die ich mit so weit habe kommen; es ist ganz es nicht bekommen:

%x t
%option noyywrap

%{
  int lineno = 0, ntab = 0, ltab = 0, dedent = 0;
%}

%%

<*>\n  { ntab = 0; BEGIN(t); }
<t>\t  { ++ntab; }
<t>.   { int i; /* my compiler complains not c99 if i use for( int i=0... */
         if( ntab > ltab )
           printf("> indent >\n");
         else if( ntab < ltab )
           for( i = 0; i < ltab - ntab; i++ )
             printf("< dedent <\n");
         else
           printf("=        =\n");

         ltab = ntab; ntab = 0;
         BEGIN(INITIAL);
         /* move to next rule */
         REJECT;}
.    /* ignore everything else for now */

%%

main()
{
  yyin = fopen( "test", "r" );
  yylex();
}

Sie können versuchen, mit ihm zu spielen, um, vielleicht sieht man, was mir fehlt. mehr dedents Rückkehr wäre eine Leichtigkeit in Haxe (return t_dedent (num);).

Dieser Code entspricht nicht immer die Einzüge / dedents richtig.


Update 3: Ich denke, dass ich die Hoffnung auf flex geben und tun es meine eigene Art und Weise, wenn jemand weiß, wie es in Flex zu tun wäre ich glücklich, es zu hören sowieso

War es hilfreich?

Lösung

Was Sie tun müssen, ist bereits flex Zahl die Menge von Leerzeichen am Anfang jeder Zeile und fügen Sie eine entsprechende Anzahl von SPIEGELSTRICH / Unindent Tokens für den Parser zu gruppieren Dinge zu verwenden. Eine Frage ist, was Sie über Registerkarten vs Räume tun wollen - wollen Sie einfach nur haben sie mit festen Tab entsprechen nicht mehr, oder möchten Sie einrücken verlangen, konsequent zu sein (also, wenn eine Zeile beginnt mit einer Lasche und dem nächsten mit einem Raum, ein Fehlersignal, das wahrscheinlich ein wenig härter ist).

Angenommen, Sie wollen feste 8-Säule tabstops, können Sie so etwas wie

verwenden
%{
/* globals to track current indentation */
int current_line_indent = 0;   /* indentation of the current line */
int indent_level = 0;          /* indentation level passed to the parser */
%}

%x indent /* start state for parsing the indentation */
%s normal /* normal start state for everything else */

%%
<indent>" "      { current_line_indent++; }
<indent>"\t"     { current_line_indent = (current_line_indent + 8) & ~7; }
<indent>"\n"     { current_line_indent = 0; /*ignoring blank line */ }
<indent>.        {
                   unput(*yytext);
                   if (current_line_indent > indent_level) {
                       indent_level++;
                       return INDENT;
                   } else if (current_line_indent < indent_level) {
                       indent_level--;
                       return UNINDENT;
                   } else {
                       BEGIN normal;
                   }
                 }

<normal>"\n"     { current_line_indent = 0; BEGIN indent; }
... other flex rules ...

Sie müssen Sie den Parsing in indent-Modus stellen Sie sicher starten (um die Vertiefung zu erhalten in der ersten Zeile).

Andere Tipps

Chris' Antwort geht ein langer Weg in Richtung auf eine brauchbare Lösung, dank ein Haufen dafür! Leider ist es noch ein paar wichtige Aspekte fehlen, die ich brauchte:

  • Mehrere outdents (unindents) auf einmal. Betrachten Sie den folgenden Code sollte emittieren zwei outdents nach dem Aufruf von baz:

    def foo():
      if bar:
        baz()
    
  • outdents auszusenden, wenn das Ende der Datei erreicht ist, und ist immer noch in einem gewissen Einrückungsebene.

  • Einrückungen von unterschiedlicher Größe. Chris' aktuelle Code funktioniert nur richtig für 1-Raum-Einzüge.

Basierend auf Chris' Code, kam ich mit einer Lösung auf, die in allen Fällen funktioniert die ich bislang gekommen sind. Ich habe für das Parsen von Einbuchtung-basierten Textes mit flex (und Bison) auf Github ein Template-Projekt erstellt: https://github.com/lucasb-eyer/flex-bison-indentation . Es ist ein voll funktionsfähiges (CMake-based) Projekt, das auch die Zeilenposition und den Spaltenbereich des aktuellen Token verfolgt.

Für den Fall der Link aus irgendeinem Grund brechen sollte, hier ist das Fleisch der Lexer:

#include <stack>

int g_current_line_indent = 0;
std::stack<size_t> g_indent_levels;
int g_is_fake_outdent_symbol = 0;

static const unsigned int TAB_WIDTH = 2;

#define YY_USER_INIT { \
    g_indent_levels.push(0); \
    BEGIN(initial); \
}
#include "parser.hh"

%}

%x initial
%x indent
%s normal

%%
    int indent_caller = normal;

 /* Everything runs in the <normal> mode and enters the <indent> mode
    when a newline symbol is encountered.
    There is no newline symbol before the first line, so we need to go
    into the <indent> mode by hand there.
 */
<initial>.  { set_yycolumn(yycolumn-1); indent_caller = normal; yyless(0); BEGIN(indent); }
<initial>\n { indent_caller = normal; yyless(0); BEGIN(indent); }    

<indent>" "     { g_current_line_indent++; }
<indent>\t      { g_current_line_indent = (g_current_line_indent + TAB_WIDTH) & ~(TAB_WIDTH-1); }
<indent>\n      { g_current_line_indent = 0; /* ignoring blank line */ }
<indent><<EOF>> {
                    // When encountering the end of file, we want to emit an
                    // outdent for all indents currently left.
                    if(g_indent_levels.top() != 0) {
                        g_indent_levels.pop();

                        // See the same code below (<indent>.) for a rationale.
                        if(g_current_line_indent != g_indent_levels.top()) {
                            unput('\n');
                            for(size_t i = 0 ; i < g_indent_levels.top() ; ++i) {
                                unput(' ');
                            }
                        } else {
                            BEGIN(indent_caller);
                        }

                        return TOK_OUTDENT;
                    } else {
                        yyterminate();
                    }
                }

<indent>.       {
                    if(!g_is_fake_outdent_symbol) {
                        unput(*yytext);
                    }
                    g_is_fake_outdent_symbol = 0;
                    // -2: -1 for putting it back and -1 for ending at the last space.
                    set_yycolumn(yycolumn-1);

                    // Indentation level has increased. It can only ever
                    // increase by one level at a time. Remember how many
                    // spaces this level has and emit an indentation token.
                    if(g_current_line_indent > g_indent_levels.top()) {
                        g_indent_levels.push(g_current_line_indent);
                        BEGIN(indent_caller);
                        return TOK_INDENT;
                    } else if(g_current_line_indent < g_indent_levels.top()) {
                        // Outdenting is the most difficult, as we might need to
                        // outdent multiple times at once, but flex doesn't allow
                        // emitting multiple tokens at once! So we fake this by
                        // 'unput'ting fake lines which will give us the next
                        // outdent.
                        g_indent_levels.pop();

                        if(g_current_line_indent != g_indent_levels.top()) {
                            // Unput the rest of the current line, including the newline.
                            // We want to keep it untouched.
                            for(size_t i = 0 ; i < g_current_line_indent ; ++i) {
                                unput(' ');
                            }
                            unput('\n');
                            // Now, insert a fake character indented just so
                            // that we get a correct outdent the next time.
                            unput('.');
                            // Though we need to remember that it's a fake one
                            // so we can ignore the symbol.
                            g_is_fake_outdent_symbol = 1;
                            for(size_t i = 0 ; i < g_indent_levels.top() ; ++i) {
                                unput(' ');
                            }
                            unput('\n');
                        } else {
                            BEGIN(indent_caller);
                        }

                        return TOK_OUTDENT;
                    } else {
                        // No change in indentation, not much to do here...
                        BEGIN(indent_caller);
                    }
                }

<normal>\n    { g_current_line_indent = 0; indent_caller = YY_START; BEGIN(indent); }

geschweiften Klammern (und so weiter) sind nur einfacher, wenn Sie einen tokenizer verwenden, die alle Leerzeichen abstreift (mit nur zu getrenntem Tokens). Siehe dieser Seite (der Abschnitt „Wie funktioniert der Compiler die Vertiefung analysieren?“ ) für einige Ideen auf python Tokenisieren.

Wenn Sie nicht Tokenisieren tun, bevor das Parsen, dann kann es zusätzliche Arbeit zu tun, es hängt davon ab, wie Sie die Parser bauen.

Sie müssen eine Regel, die auf diese analog sieht (angenommen Sie Registerkarten für Ihre Einzüge verwenden):

\ t: {return TABDENT; }

Ehrlich gesagt, ich habe immer gefunden Klammern (oder zu Beginn / Ende) leichter sein, zu schreiben und zu lesen, sowohl als Mensch als auch als Lexer / Parser Schriftsteller.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top