ABNF++: um gerador de parser ABNF

De MediaWiki do Campus São José
Ir para: navegação, pesquisa


O compilador ABNF é um aplicativo desenvolvido no IFSC que gera um parser a partir de uma gramática. O parser se apresenta como uma classe C++ capaz de identificar e decompor texto de entrada de acordo com um conjunto de regras gerado com base na gramática especificada. Por exemplo, ao compilar esta especificação de gramática ABNF:

header="E-mail: " %d60 nome %d62 WSP email CRLF
email=1*16ALPHA %d64 1*8(1*16ALPHA ".") 1*16ALPHA
nome=*6(1*16ALPHA WSP) 1*16ALPHA

... são gerados estes arquivos:

  • parser_default.h: declaração da classe Parser_default
  • parser_default.cpp: implementação da classe Parser_default


A classe Parser_default contém a tabela de regras finais do parser. Uma regra final corresponde a uma linha completa de texto identificada pelo parser, enquanto uma regra intermediária corresponde a um trecho do texto identificado. O parser procura identificar uma regra final que corresponda integralmente a uma linha de texto. No exemplo acima existe uma regra final chamada header e duas regras intermediárias chamadas email e nome. Regras finais sempre terminam com CRLF (a sequência \r\n). Assim, o Parser_default do exemplo consegue identificar apenas linhas de texto que estejam no formato definido pela regra header. Além disso, ele consegue decompô-las em sequências de caracteres dadas pelas regras intermediárias email e nome.

Instalação

O compilador ABNF possui algumas dependências. Instale-as com este comando:

sudo apt-get install libpcre++-dev libstdc++-dev g++


O compilador ABNF pode ser instalado como um pacote de software Debian da seguinte forma:

  1. Faça o download do pacote de software abnfparser: abnfparser_1.0_all.deb
  2. Instale-o com o seguinte comando executado em um terminal:
    sudo dpkg -i abnfparser_1.1_all.deb
    
  3. Se a instalação ficar incompleta devido a dependências de software não resolvidas, execute isto:
    sudo apt -f install
    


Opcionalmente, pode-se instalar o compilador ABNF desta forma:

  1. Faça o download do arquivo .tgz que contém o abnfparser: abnfparser-1.0.tgz
  2. Descompacte-o em algum diretório com o seguinte comando executado em um terminal:
    tar xzf abnfparser-1.0.tgz
    
  3. Execute o script fix_lib.sh para criar um link simbólico para a versão da biblioteca apropriada para o seu sistema. Esse script está no diretório onde foi descompactado o abnfparser.

Uso do compilador

O programa gen_abnf compila uma gramática ABNF, gerando um parser sob medida para a especificação. A execução do compilador sem argumentos, ou com a opção -h, apresenta uma ajuda:

$ gen_abnf 
Uso: /usr/local/bin/gen_abnf [-n name][-d dir] rules_file | -h
-n:	nomeia arquivos de codigo e classe usando "name" (default="default")
-d:	grava arquivos de codigo no subdiretorio "dir"
-h:	mostra esta ajuda
rules_file:	arquivo com regras ABNF

As opções do compilador seguem descritas abaixo em maiores detalhes:

  • -n: especifica o nome do parser a ser criado. O compilador gera um parser chamado Parser_<nome>. Por exemplo, se o nome informado for teste, a classe gerada será Parser_teste.
  • -d: informa o diretório onde devem ser criados os arquivos de código-fonte do parser. Por default é o diretório atual.


A compilação de uma aplicação que use o parser deve seguir as orientações apresentadas pelo gerador do parser. Basicamente devem-se incluir opções de compilação para que o compilador g++ encontre a biblioteca ABNFParser. Essa biblioteca contém a API usada pelo parser. O exemplo a seguir mostra como compilar uma aplicação.

  1. Escreva um arquivo de texto com a gramática ABNF:
    header="E-mail: " nome WSP %d60 email %d62 CRLF
    email=1*16ALPHA %d64 1*8(1*16ALPHA ".") 1*16ALPHA
    nome=1*16ALPHA *3(WSP 1*16ALPHA)
    
  2. Gere o parser usando o programa gen_abnf. Neste exemplo, o parser tem o sufixo teste:
    $ gen_abnf -n teste abnf.txt 
    
    Criou arquivos de codigo "./parser_teste.h" e "./parser_teste.cpp"
    
    Para compilar seu projeto, inclua estas opcoes: "-I/usr/local/include -L/usr/local/lib -labnf -lpcre++"
    
  3. Escreva sua aplicação que usa o parser:
    #include <iostream>
    #include "parser_teste.h"
    
    int main() {
      Parser_teste parser;
    
      ABNFRule * r = (ABNFRule*)parser.match("E-mail: Dona Bilica <bilica@oioio.com.br>\r\n");
      cout << "Nome da regra ativada: " << r->get_name() << endl;
      cout << "Texto que ativou a regra: " << r->get_match() << endl;
    
      Token * email = r->get_token("email");
      Token * nome = r->get_token("nome");
      cout << "Nome: " << nome->get_match() << endl;
      cout << "E-mail: " << email->get_match() << endl;
    }
    
  4. Compile sua aplicação com o compilador g++, passando as opções indicadas no passo 2:
    g++ -o teste teste.cpp parser_teste.cpp -I/usr/local/include -L/usr/local/lib -labnf -lpcre++
    
  5. Execute sua aplicação:
    $ ./teste
    Nome da regra ativada: header
    Texto que ativou a regra: E-mail: Dona Bilica <bilica@oioio.com.br>
    
    Nome: Dona Bilica
    E-mail: bilica@oioio.com.br
    

API do parser

A API do parser ABNF possui as seguintes classes C++:

  • Parser_name: o parser gerado pelo compilador.
  • ABNFParser: classe base para o parser, contendo a interface para seu uso.
  • Token: classe abstrata ancestral de todas as classes de regras a seguir.
  • ABNFSimpleToken: classe que representa uma regra composta por uma string. Esse tipo de regra é ativado quando essa string for idêntica ao início do texto fornecido.
  • ABNFToken: classe que representa uma regra composta por uma expressão regular. Esse tipo de regra é ativado quando essa expressão regular corresponder ao início do texto fornecido.
  • ABNFRule: classe que representa uma sequência de uma ou mais regras quaisquer. Esse tipo de regra é ativado quando cada regra da sequência for ativada sucessivamente com o texto fornecido.
  • ABNFRepRule: classe que representa uma regra qualquer que pode repetir. Esse tipo de regra é ativado quando a regra embutida for ativada repetidamente com o texto fornecido, de acordo com a faixa de repetições especificada.
  • ABNFAltRule: classe que representa um conjunto de regras alternativas. Esse tipo de regra é ativado quando ao menos uma das regras for ativada, de acordo com a faixa de repetições especificada.


Abnf-class.jpg

Classe ABNFParser

A classe ABNFParser é a classe base para os parsers gerados.

Métodos:

  • ABNFParser(): construtor da classe
  • Token * match(const string & data): interpreta data, retorna a regra que a ele corresponde. Caso nenhuma regra seja ativada, retorna NULL.

Classe Token

A classe Token é a classe base abstrata para todas as regras usadas pelo parser.

Métodos:

  • int match(const string & data): verifica se a regra corresponde a data. Se corresponder, retorna a quantidade de caracteres contando do início de data identificados pela regra. Se não corresponder, dispara uma exceção do tipo inteiro.
  • string get_match(): retorna a string identificada pela regra
  • string get_name(): retorna o nome da regra
  • void reset(): limpa a regra, removendo o valor previamente identificado
  • Token * get_copy(): obtém uma cópia da regra
  • bool matched(): retorna verdadeiro se a regra identificou caracteres
  • vector<string> get_tokens(): retorna os nomes das regras que compõem esta regra
  • Token * start(): inicia uma iteração das regras contidas nesta regra, retornando a primeira regra.
  • Token * next(): retorna a próxima regra da iteração, ou NULL se a iteração chegou ao fim

Classe ABNFSimpleToken

Classe ABNFToken

Classe ABNFRule

A classe ABNFRule representa regras compostas por encadeamentos de outras regras. Esse tipo de regra é ativado quando cada regra da sequência for ativada sucessivamente com o texto fornecido.


Métodos:

  • Token * get_token(const string & name): retorna a regra identificada por name. O identificador da regra pode ser um caminho composto por uma sequência de nomes de regras. Por exemplo, email identifica uma regra chamada email, e email.dominio identifica uma regra chamada dominio e que está dentro da regra email. Esse esquema de nomeação das regras possibilita acessar qualquer elemento da árvore de parsing de uma regra.

Classe ABNFAltRule

A classe ABNFAltRule representa um conjunto de regras alternativas. Esse tipo de regra é ativado quando ao menos uma das regras for ativada, de acordo com a faixa de repetições especificada.


Métodos

  • Token * get_token(): retorna a regra que foi ativada, ou NULL caso contrário

Classe ABNFRepRule

A classe ABNFRepRule representa uma regra qualquer que pode repetir. Esse tipo de regra é ativado quando a regra embutida for ativada repetidamente com o texto fornecido, de acordo com a faixa de repetições especificada.


Métodos:

  • ABNFRepRule(const string & name, unsigned int n1, unsigned int n2 = 0): construtor da classe, em que especificam o nome da regra, e os limites de repetição. Se n1 e n2 forem nulos, a regra pode repetir um número arbitrário de vezes (incluindo nenhuma). Se n2 for nulo, a regra deve repetir pelo menos n1 vezes.

ABNF Core Rules

As regras core ABNF, conforme definidas na RFC 5234, estão predefinidas na API. Elas podem ser referidas diretamente na gramática ABNF:

  • SP: espaço em branco
  • CR: carriage-return (\r)
  • DQUOTE: aspas
  • HTAB: tab (\t)
  • LF: newline (\n)
  • DIGIT: dígito decimal (0 a 9)
  • ALPHA: caractere alfabético (a-z', A-Z)
  • CHAR: caractere ASCII (cód. 0 a 127)
  • CRLF: CR + LF (\r\n)
  • CTL: caractere ASCII de controle (cód. 0 a 31, e também 127)
  • OCTET: um byte qualquer (0 a 255)
  • VCHAR: caractere ASCII visível (cód. 33 a 126)
  • BIT: dígito 0 ou 1
  • HEXDIG: dígito hexadecimal (0-9', A-F)
  • WSP: SP ou HTAB
  • NSP: uma sequência de um ou mais WSP
  • LWSP: zero ou mais CRLF seguido de um WSP