Como o suporte flexível bisonte-local exatamente?
-
19-08-2019 - |
Pergunta
Eu estou tentando usar bisonte flex e para criar um filtro, porque eu quero ficar certos elementos da gramática de uma linguagem complexa. Meu plano é usar Flex + bisonte reconhecer a gramática, e despejar a localização de elementos de interesse. (Em seguida, use um script para texto grab acordo com os locais despejado.)
Eu encontrei flexível pode suportar um recurso bisonte chamado bisonte-locais, mas como ele funciona exatamente. Eu tentei o exemplo no documento flexível, parece que o yylloc não é definida automaticamente pelo Flex, eu sempre chegar (1,0)-(1,0)
. Poderia flexionar calcular a localização de cada token automaticamente? Se não, qual função de interface é definida por mim para implementar? Existe algum exemplo?
Qualquer solução melhor em relação às ferramentas?
Atenciosamente, Kevin
Editar:
Agora, a interface para turn yylex a:
int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );
manual do bisonte não especifica como lexer deve implementar para corretamente conjunto yylloc_param. Para mim é difícil de número de coluna trace manualmente de cada token.
Solução
Dê uma olhada na seção 3.6 do o Bison Manual - que parece cobrir locais em algum detalhe. Combinado com o que você encontra no manual do Flex, que poderá ser suficiente.
Outras dicas
A declaração yylex provavelmente mudou porque você usou uma reentrada ou puro-parser. Parece que muitos documentos em toda a web sugerem que é necessário se você quiser locais bisonte para trabalhar, mas isso não é obrigatório.
Eu precisava de números de linha também e encontrou a documentação Bison confundir a esse respeito. A solução mais simples (usando o var yylloc global): Em seu arquivo Bison basta adicionar as localizações% directiva:
%{
...
%}
%locations
...
%%
...
no seu lexer:
%{
...
#include "yourprser.tab.h" /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...
A macro YY_USER_ACTION é "chamado" antes de cada uma de suas ações simbólicas e yylloc atualizações. Agora você pode usar o @N / @ $ regras como esta:
statement : error ';' { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }
, ou utilize o yylloc var mundial:
void yyerror(char *s)
{
fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}
Eu gosto de resposta de Shlomi.
Além disso, eu estava olhando para atualizar localização coluna também. Encontrado http://oreilly.com/linux/excerpts/9780596155971/error- reporte-recovery.html que fazia mais sentido depois de ler a resposta de Shlomi.
Infelizmente não há um erro de digitação na página para yylloc. Eu simplificado-lo abaixo um pouco.
Em seu analisador add:
%locations
no seu lexer:
%{
#include "parser.tab.h"
int yycolumn = 1;
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
yycolumn += yyleng; \
yylval.str = strdup(yytext);
%}
%option yylineno
Pode haver algo acontecendo com localização coluna que não mantém estritamente o controle de colunas mas apenas continua aumentando. Isso é apenas a minha ignorância e appologize se confunde ninguém. Atualmente estou usando coluna para manter a contagem de caracteres arquivo que no meu caso é mais benéfico do que a localização da coluna.
Espero que ajude.
Nem bison
nem flex
atualizações yylloc
automaticamente, mas é, na verdade, não é difícil de fazê-lo sozinho, se você sabe o truque.
O truque para implementar o suporte yylloc
é que, embora yyparse()
declara yylloc
, nunca muda. Isso significa que se você modificar yylloc
em uma chamada para o lexer, você encontrará os mesmos valores em que na próxima chamada. Assim, yylloc
conterá a posição do último token. Desde o final do último token é o mesmo que o atual token começar, você pode usar o valor yylloc
de idade para ajudar a determinar o novo valor.
Em outras palavras, yylex()
não deve calcular yylloc
; deveria Atualização yylloc
.
Para atualizar yylloc
, é preciso primeiro copiar os valores last_
para first_
, e depois atualizar os valores last_
para refletir a duração do token just-correspondido. (Este não é o strlen()
do token, é o comprimento linhas-e-colunas.) Podemos fazer isso na macro YY_USER_ACTION
, que é chamado pouco antes é realizada qualquer ação lexer; que garante que, se um jogos de regras, mas ele não retorna um valor (por exemplo, uma regra ignorar espaços em branco ou comentários), a localização do que é não-simbólicos ignorados, em vez de ser incluído no início do token real, ou perdido em uma maneira que faz o rastreamento de localização imprecisa.
Aqui está uma versão significou para um analisador de reentrada; você pode modificá-lo para um analisador não reentrante trocando os operadores ->
para .
:
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
for(int i = 0; yytext[i] != '\0'; i++) { \
if(yytext[i] == '\n') { \
yylloc->last_line++; \
yylloc->last_column = 0; \
} \
else { \
yylloc->last_column++; \
} \
}
Se você preferir, você poderia, em vez colocar esse código em uma função e fazer a chamada macro a função, mas as duas técnicas são equivalentes.
A resposta de Shomi é a solução mais simples se você só se preocupam com mantendo o número da linha. No entanto, se você também quiser números de coluna, então você precisa manter o controle deles.
Uma maneira de fazer isso é adicionar regras yycolumn = 1
em todos os lugares uma nova linha aparece (como sugerido na resposta de David Elson), mas se você não quiser manter o controle de todos os lugares uma nova linha poderia mostrar-se (espaços em branco, comentários, etc. ..) uma alternativa está a inspeccionar o tampão yytext
no início de cada acção:
static void update_loc(){
static int curr_line = 1;
static int curr_col = 1;
yylloc.first_line = curr_line;
yylloc.first_column = curr_col;
{char * s; for(s = yytext; *s != '\0'; s++){
if(*s == '\n'){
curr_line++;
curr_col = 1;
}else{
curr_col++;
}
}}
yylloc.last_line = curr_line;
yylloc.last_column = curr_col-1;
}
#define YY_USER_ACTION update_loc();
Finalmente, uma coisa a notar é que quando você começar a manter o controle de números de coluna com a mão assim como você pode também acompanhar os números de linha no mesmo lugar e não se preocupar com o uso de opção yylineno
do Flex.
Então, eu tenho essa a "trabalhar", mas com um par de medidas adicionais (I pode ter negligenciado-los aqui ... desculpas nesse caso):
-
Em parser.y , eu tinha a dizer:
#define YYLEX_PARAM &yylval, &yylloc
mesmo com
%locations
ebison --locations
, para obtê-lo para passar os dados. -
Em lexer.l eu tive que usar
->
vez de.
parayylloc
-
Também em lexer.l , eu redefinir a coluna na ação:
[\n] { yycolumn = 1; }
Obviamente um pouco mais complexa, por \r
etc, mas pelo menos eu tenho que trabalhar.
Eu acho que eu consegui fazê-lo funcionar (crédito vai para o escritor do manual bisonte ltcalc lexical analisador ). Por padrão, bisontes cria yylloc que contém
{ first_line, first_column , last_line , last_column }
Nós só precisa atualizar esses valores em nossa analisador léxico. Ex:
[ \t] { ++yylloc.last_column; }
[\n] { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ {
yylloc.last_column += strlen(yytext);
return IDENTIFIER;
}
Agora, bisonte, para recuperar esses campos:
statement : IDENTIFIER '=' expression
{ printf("%d - %d\n", @1.last_line, @1.last_column); }
Por padrão, esses campos são inicializados para um, devemos inicializar os campos da coluna para zero caso contrário eles vão relatar a coluna errada.
Uma adição à resposta de Shlomi:
Se você estiver usando% definir api.pure em bisões para criar um analisador de reentrada, você também precisa especificar a opção% bisonte-locais em flex. Isto porque, em um yylloc analisador de reentrada não é uma variável global, e precisa de ser passado para o lexer.
Assim, no analisador:
%define api.pure
%locations
no lexer:
#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno