Pergunta

Eu wounder como implementar recuo como delimitadores de bloco em bisonte + flex. Assim como em python. Eu estou escrevendo minha própria linguagem de programação (principalmente para se divertir, mas pretendo usá-lo em conjunto com um motor de jogo), eu vou tentar chegar a algo especial que minimiza clichê e maximiza dev velocidade.

Eu já escrevi um compilador (na verdade um `langToy ' para Nasm tradutor) em C, mas não conseguiu. Por alguma razão ele só foi capaz de lidar com uma corda em todo o arquivo de origem (bem, eu tinha sido acordado por mais de 48 horas - por isso ... Você sabe, colapso cerebral)

.

Eu não sei se chaves e / ou começar -.> End são mais fáceis de implementar (Eu não tenho problema em fazer isso) ou se é apenas meu cérebro que bloqueia

Agradecemos antecipadamente!


Update: Ok, eu não tenho nenhuma pista sobre como fazê-lo com flex. Tenho problemas com retorno de vários DEDENTs para o analisador. Flex / Bison são relativamente novo para mim.


Update 2: Este é o flex-arquivo que eu vim acima com a medida; não bastante obtê-lo:

%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();
}

Você pode tentar brincar com ele, talvez você vê o que eu estou sentindo falta. retorno de vários dedents seria uma facilidade na Haxe (t_dedent retorno (num);).

Este código não sempre coincidir com os travessões / dedents corretamente.


Update 3:. Eu acho que vou desistir da esperança em flex e fazer do meu próprio caminho, Se alguém sabe como fazê-lo em flex eu ficaria feliz em ouvir isso de qualquer maneira

Foi útil?

Solução

O que você precisa fazer é ter Flex contagem da quantidade de espaços em branco no início de cada linha e insira um número apropriado de TRAVESSÃO / Unindent fichas para o analisador de uso para agrupar as coisas. Uma questão é o que você quer fazer sobre guias vs espaços - você só quer tê-los ser equivalente com tabulações fixos, ou você quer para exigir o recuo para ser consistente (por isso, se uma linha começa com um guia e no próximo com um espaço, você sinalizar um erro, que é provavelmente um pouco mais).

Assumindo que você quer tabstops 8 colunas fixas, você pode usar algo como

%{
/* 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 ...

Você tem que ter certeza que você iniciar o parse no modo travessão (para obter o recuo na primeira linha).

Outras dicas

resposta Chris' vai um longo caminho no sentido de uma solução utilizável, graças um bando para este! Infelizmente, está faltando alguns aspectos mais importantes que eu precisava:

  • Vários outdents (iniciais remove a indentação) de uma só vez. Considere o seguinte código deve emitir dois outdents após a chamada para baz:

    def foo():
      if bar:
        baz()
    
  • outdents Emit quando o final do arquivo é alcançado e ainda está em algum nível de recuo.

  • níveis de recuo de tamanho diferente. código atual Chris' só funciona corretamente para travessões 1-espaço.

Com base no código de Chris, eu vim com uma solução que funciona em todos os casos que se deparam até agora. Eu criei um projeto de modelo para analisar texto baseado recuo usando o flex (e bisonte) no github: https://github.com/lucasb-eyer/flex-bison-indentation . É um projecto completamente funcional (à base de CCertifique) que também controla a posição da linha e a gama do sinal de corrente coluna.

Apenas no caso do link deve quebrar por qualquer razão, aqui é a carne do 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); }

chaves (e tal) são apenas simples se você usar um tokenizer que retira todos os espaços (usando é apenas para os tokens separados). Consulte desta página (a seção "Como é que o compilador analisar o recuo?" ) para algumas idéias sobre tokenizing python.

Se você não está fazendo tokenizing antes da análise, então pode haver trabalho adicional a fazer, isso depende de como você está construindo o analisador.

Você precisa de uma regra que parece análoga a esta (supondo que você usar guias para os seus travessões):

\ t: {return TABDENT; }

Francamente, eu tenho sempre encontrou chaves (ou começar / fim) para ser mais fácil de escrever e de ler, tanto como um ser humano e como um escritor lexer / analisador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top