Criando um Analisador em C

Antes de começar

Uma biblioteca de implementação carregável, ou uma LIL, é o módulo de implementação para um analisador (ou nó) em C.Uma LIL é um objeto compartilhado do UNIX ou uma DLL (Dynamic Link Library) do Windows que não tem a extensão de arquivo .dll, mas .lil.

As funções de implementação que precisam ser escritas pelo desenvolvedor estão relacionadas em Funções de Implementação de Analisador.As funções utilitárias fornecidas pelo para auxiliar esse processo estão relacionadas em Funções Utilitárias de Analisador.

O fornece a origem para um analisador de amostra definido pelo usuário chamado BipSampPluginParser.c. Esse é um analisador pseudo XML simples que pode ser utilizado em seu estado atual ou pode ser modificado.

A tarefa de escrever um analisador varia consideravelmente de acordo com a complexidade do fluxo de bits a ser analisado. Somente as etapas básicas são descritas aqui. Elas são descritas nas seguintes seções:
  1. Definindo o Analisador durante a Inicialização do Intermediário
  2. Criando uma Instância do Analisador
  3. Implementando a Funcionalidade do Analisador
  4. Excluindo uma Instância do Analisador Definido pelo Usuário

Implementando Funções de Entrada

As funções de entrada (por exemplo, cpiParseBuffer) são chamadas pelo intermediário quando um analisador é requerido para analisar uma mensagem de entrada. O analisador deve instruir ao intermediário de quanto do buffer do fluxo de bits de entrada ele reclama para si. No caso de um cabeçalho de comprimento fixo, o analisador reclama o tamanho do cabeçalho. Caso o analisador deva manipular a mensagem inteira, ele reclamará o restante do buffer.

Por exemplo:
  1. Chamar a função cpiParseBufferEncoded:
    int cpiParseBufferEncoded(
      CciParser*  parser,
      CciContext* context,
      int         encoding,
      int         ccsid
    ){
      PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
      int             rc;
    
  2. Obter um ponteiro para o buffer de mensagem e definir o deslocamento utilizando a função cpiBufferPointer:
      pc->iBuffer = (void *)cpiBufferPointer(&rc, parser);
      pc->iIndex = 0;
  3. Salvar o formato do buffer:
      pc->iEncoding = encoding;
      pc->iCcsid = ccsid;
  4. Salvar o tamanho do buffer utilizando a função cpiBufferSize:
      pc->iSize = cpiBufferSize(&rc, parser);
  5. Obter o primeiro byte no fluxo utilizando a função cpiBufferByte:
      pc->iCurrentCharacter = cpiBufferByte(&rc, parser, pc->iIndex);
  6. Definir o elemento atual para o elemento raiz utilizando a função cpiRootElement:
      pc->iCurrentElement = cpiRootElement(&rc, parser);
  7. Reconfigurar o sinalizador para assegurar-se de que a análise seja redefinida corretamente:
      pc->iInTag = 0;
    
  8. Reclamar a propriedade do restante do buffer:
      return(pc->iSize);
    }

Implementando Funções de Análise

Funções gerais de análise (por exemplo, cpiParseFirstChild) são aquelas chamadas pelo intermediário quando a árvore de elementos de sintaxe precisa ser criada para avaliar uma expressão ESQL. Por exemplo, um nó de filtro utiliza uma referência de campo ESQL em uma expressão ESQL. Essa referência de campo precisa ser resolvida para avaliar a expressão. A função geral de análise do analisador é chamada, talvez repetidamente, até que o elemento pedido seja criado ou seja reconhecido pelo analisador como não existente.

Por exemplo:
void cpiParseFirstChild(
  CciParser*  parser,
  CciContext* context,
  CciElement* element
){
  PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
  int             rc;

  if ((!cpiElementCompleteNext(&rc, element)) &&
      (cpiElementType(&rc, element) == CCI_ELEMENT_TYPE_NAME)) {

    while ((!cpiElementCompleteNext(&rc, element))     &&
           (!cpiFirstChild(&rc, element)) &&
           (pc->iCurrentElement))
    {
      pc->iCurrentElement = parseNextItem(parser, context, pc->iCurrentElement);
    }
  }
  return;
}

Implementando Funções de Saída

As funções de saída (por exemplo, cpiWriteBuffer) são chamadas pelo intermediário quando um analisador é requerido para serializar uma árvore de elementos de sintaxe para um fluxo de bits de saída. Por exemplo, um nó Compute pode ter criado uma árvore no domínio do analisador definido pelo usuário. Quando essa árvore precisar ser enviada para a saída, por exemplo, um nó MQOutput, o analisador é responsável por anexar o buffer do fluxo de bits de saída com dados que representam a árvore que foi construída.

Por exemplo:
int cpiWriteBufferEncoded(
  CciParser*  parser,
  CciContext* context,
  int         encoding,
  int         ccsid
){
  PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
  int                initialSize = 0;
  int                rc = 0;
  const void* a;
  CciByte b;

  initialSize = cpiBufferSize(&rc, parser);
  a = cpiBufferPointer(&rc, parser);
  b = cpiBufferByte(&rc, parser, 0);

  cpiAppendToBuffer(&rc, parser, (char *)"Some test data", 14);

  return cpiBufferSize(0, parser) - initialSize;
}

Mensagens com Vários Formatos de Mensagem

Normalmente, os dados da mensagem de entrada são de um único formato da mensagem, e portanto um analisador é responsável por analisar todo o conteúdo da mensagem. O nome da classe do analisador que é necessário é definido no campo Format no cabeçalho MQMD ou MQRFH2 da mensagem de entrada.

Entretanto, a mensagem pode consistir em vários formatos, por exemplo, onde exista um cabeçalho em um formato seguido por dados em outro formato. Nesse caso, o primeiro analisador tem que identificar o nome da classe do analisador que é responsável pelo próximo formato na cadeia, e assim por diante. Em um analisador definido pelo usuário, a função de implementação cpiNextParserClassName é chamada pelo intermediário quando ele precisa navegar por uma cadeia de classes de analisador para uma mensagem composta de vários formatos da mensagem.

Se o analisador definido pelo usuário suportar a análise de um formato de mensagem que faz parte de um formato de várias mensagens, o analisador definido pelo usuário deve implementar a função cpiNextParserClassName.

Por exemplo:
  1. Chamar a função cpiNextParserClassName:
    void cpiNextParserClassName(
      CciParser*  parser,
      CciContext* context,
      CciChar*    buffer,
      int         size
    ){
      PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
      int                rc = 0;
    
  2. Copiar o nome da próxima classe de analisador para o intermediário:
      CciCharNCpy(buffer, pc->iNextParserClassName, size);
    
      return;
    }

Definindo o Analisador durante a Inicialização do Intermediário

A função de inicialização do analisador definido pelo usuário é chamada automaticamente durante a inicialização do intermediário. O analisador definido pelo usuário é responsável por:
  • Criar e nomear a fábrica de analisador de mensagem que é implementada pelo analisador definido pelo usuário. A fábrica de analisador é um contêiner para implementações de analisadores relacionados. Os nomes de fábricas de analisador precisam ser exclusivos dentro de um intermediário.
  • Definir os nomes de classes de analisador de mensagem suportadas e fornecer um ponteiro para uma tabela de funções virtuais que contém ponteiros para as funções de implementação do analisador definido pelo usuário. Os nomes de classes de analisador precisam ser exclusivos dentro de um intermediário.

Cada LIL que implementa um analisador definido pelo usuário deve exportar uma função chamada bipGetParserFactory como sua função de inicialização. A função de inicialização define o nome da fábrica que o analisador definido pelo usuário suporta e as classes de objetos ou o objeto compartilhado suportado pela fábrica.

A função de inicialização precisa também criar o objeto de fábrica e definir os nomes de todos os analisadores suportados pela LIL. Uma fábrica pode suportar qualquer número de classes de objetos (analisadores). Quando um analisador é definido, uma lista de ponteiros para as funções de implementação para esse analisador é transmitida ao intermediário. Se já existir um analisador com o mesmo nome, o pedido será rejeitado.

Por exemplo, para definir o analisador:
  1. Exportar a função de inicialização bipGetParserFactory:
    void LilFactoryExportPrefix * LilFactoryExportSuffix bipGetParserFactory()
    {
  2. Declarar as variáveis:
     CciFactory*     factoryObject;
      int             rc;
      static CPI_VFT  vftable = {CPI_VFT_DEFAULT};
  3. Inicializar todas as constantes estáticas:
      initParserConstants();
  4. Configurar a tabela de funções com ponteiros para funções de implementação do analisador:
      vftable.iFpCreateContext            = cpiCreateContext;
      vftable.iFpParseBufferEncoded       = cpiParseBufferEncoded;
      vftable.iFpParseFirstChild          = cpiParseFirstChild;
      vftable.iFpParseLastChild           = cpiParseLastChild;
      vftable.iFpParsePreviousSibling     = cpiParsePreviousSibling;
      vftable.iFpParseNextSibling         = cpiParseNextSibling;
      vftable.iFpWriteBufferEncoded       = cpiWriteBufferEncoded;
      vftable.iFpDeleteContext            = cpiDeleteContext;
      vftable.iFpSetElementValue          = cpiSetElementValue;
      vftable.iFpElementValue             = cpiElementValue;
      vftable.iFpNextParserClassName      = cpiNextParserClassName;
      vftable.iFpSetNextParserClassName   = cpiSetNextParserClassName;
      vftable.iFpNextParserEncoding       = cpiNextParserEncoding;
      vftable.iFpNextParserCodedCharSetId = cpiNextParserCodedCharSetId;

A função de inicialização deve então criar uma fábrica de analisador chamando cpiCreateParserFactory. As classes de analisador suportadas pela fábrica são definidas chamando cpiDefineParserClass. O endereço do objeto fábrica (retornado por cpiCreateParserFactory) deve ser retornado ao intermediário como o valor de retorno da função de inicialização.

Por exemplo:
  1. Criar a fábrica de analisador utilizando a função cpiCreateParserFactory:
      factoryObject = cpiCreateParserFactory(&rc, constParserFactory);
      
  2. Definir as classes de mensagem suportadas pela fábrica utilizando a função cpiDefineParserClass:
    if (factoryObject) {
       cpiDefineParserClass(&rc, factoryObject, constPXML, &vftable);
      }
    else {
        /* Erro: Incapaz de criar a fábrica de analisador */
      }
  3. Retorne o endereço desse objeto fábrica ao intermediário:
      return(factoryObject);
    }

Criando uma Instância do Analisador

Sempre que uma instância de um objeto do analisador definido pelo usuário é criada, a função de implementação de criação de contexto cpiCreateContext é chamada pelo intermediário de mensagens. Isso permite ao analisador definido pelo usuário alocar dados da instância associados ao analisador.

Por exemplo:
  1. Chamar cpiCreateContext:
    CciContext* cpiCreateContext(
      CciParser* parser
    ){
      PARSER_CONTEXT_ST *p;
    
  2. Alocar um ponteiro para o contexto local:
      p = (PARSER_CONTEXT_ST *)malloc(sizeof(PARSER_CONTEXT_ST));
    
  3. Limpar a área do contexto:
      if (p) {
         memset(p, 0, sizeof(PARSER_CONTEXT_ST));
        }
      else {
            /* Erro: Incapaz de alocar memória */
      }
  4. Retornar o ponteiro para o contexto local:
      return((CciContext*)p);
    }

Implementando a Funcionalidade do Analisador

Um analisador precisa implementar os seguintes tipos de funções de implementação:
  1. funções de entrada
  2. funções de análise
  3. funções de saída

Cada tipo de função é descrito a seguir.

Excluindo uma Instância do Analisador Definido pelo Usuário

Para excluir uma instância de um analisador, é utilizada a função cpiDeleteContext. Por exemplo:
void cpiDeleteContext(
  CciParser*  parser,
  CciContext* context
){
  PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
  int                rc = 0;

  return;
}

Conceitos relacionados
Planejando Extensões Definidas pelo Usuário
Extensões Definidas pelo Usuário no Ambiente de Tempo de Execução
Projetando Extensões Definidas pelo Usuário
Analisadores Definidos pelo Usuário

Tarefas relacionadas
Desenvolvendo Extensões Definidas pelo Usuário
Implementando as Amostras Fornecidas
Compilando uma Extensão Definida pelo Usuário em C

Referências relacionadas
API de Nó C Definido pelo Usuário