Creación de un analizador en C

Antes de empezar

Una biblioteca de implementación cargable o LIL es el módulo de implementación para un analizador C (o un nodo). LIL es un objeto compartido de UNIX o una biblioteca de enlace dinámico (DLL) que no tiene la extensión de archivo .dll, sino .lil.

Las funciones de implementación que el desarrollador tenga que escribir aparecen listadas en la sección Funciones de implementación de analizador. Las funciones de programa de utilidad que WebSphere Business Integration Message Broker proporciona para ayudar a este proceso aparecen listadas en la sección Funciones de programa de utilidad de analizador.

WebSphere Business Integration Message Broker proporciona el origen para un analizador definido por el usuario de ejemplo que se denomina BipSampPluginParser.c. Es un analizador pseudo XML que se puede utilizar en el estado actual o se puede modificar.

La tarea de escribir un analizador varía considerablemente de acuerdo con la complejidad de la corriente de bits que se va a analizar. Aquí sólo se describen los pasos básicos. Se describen en las secciones siguientes:
  1. Definición del analizador durante la incialización del intermediario
  2. Creación de una instancia del analizador
  3. Implmentación de las funciones del analizador
  4. Supresión de una instancia del analizador definido por el usuario

Implementación de las funciones de entrada

Las funciones de entrada (por ejemplo, cpiParseBuffer) las invoca el intermediario cuando se solicita a un analizador que analice un mensaje de entrada. El analizador debe indicar al intermediario qué cantidad de almacenamiento intermedio de corriente de bits de entrada reclama como suyo. En el caso de una cabecera de tamaño fijo, el analizador reclama el tamaño de la cabecera. Si el analizador tiene la intención de manejar todo el mensaje, reclama el resto del almacenamiento intermedio.

Por ejemplo:
  1. Invoque la función cpiParseBufferEncoded:
    int cpiParseBufferEncoded(
      CciParser*  parser,
      CciContext* context,
      int         encoding,
      int         ccsid
    ){
      PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
      int                rc;
    
  2. Obtenga un puntero al almacenamiento intermedio de mensajes y establezca el desplazamiento utilizando la función cpiBufferPointer:
      pc->iBuffer = (void *)cpiBufferPointer(&rc, parser);
      pc->iIndex = 0;
  3. Guarde el formato del almacenamiento intermedio:
      pc->iEncoding = encoding;
      pc->iCcsid = ccsid;
  4. Guarde el tamaño del almacenamiento intermedio utilizando la función cpiBufferSize:
      pc->iSize = cpiBufferSize(&rc, parser);
  5. Prepare el primer byte de la corriente de datos utilizando la funcióncpiBufferByte:
      pc->iCurrentCharacter = cpiBufferByte(&rc, parser, pc->iIndex);
  6. Establezca el elemento actual en el elemento raíz utilizando la función cpiRootElement:
      pc->iCurrentElement = cpiRootElement(&rc, parser);
  7. Restablezca el distintivo para asegurarse de que se restablece correctamente:
      pc->iInTag = 0;
    
  8. Solicite la propiedad del resto del almacenamiento intermedio:
      return(pc->iSize);
    }

Implementación de funciones de análisis

Las funciones de análisis general (por ejemplo, cpiParseFirstChild) son las que invoca el intermediario cuando tiene que crearse el árbol de elementos de sintaxis para poder evaluar una expresión ESQL. Por ejemplo, un nodo de filtro utiliza una referencia de campo ESQL en una expresión ESQL. Esta referencia de campo debe resolverse para poder evaluar la expresión. Se invoca la función de análisis general del analizador, quizás de forma reiterada, hasta que se cree el elemento solicitado o bien hasta que el analizador se cerciore de que no existe.

Por ejemplo:
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;
}

Implementación de las funciones de salida

Las funciones de salida (por ejemplo, cpiWriteBuffer) las invoca el intermediario cuando se solicita que un analizador ejecute en serie un árbol de elementos de sintaxis en una corriente de bits de salida. Por ejemplo, un nodo Compute podría haber creado un árbol en el dominio del analizador definido por el usuario. Cuando este árbol necesita generarse por ejemplo, mediante un nodo MQOutput, el analizador es responsable de añadir el almacenamiento intermedio de corriente de bits de salida con datos que representan el árbol que se ha creado.

Por ejemplo:
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;
}

Mensajes con múltiples formatos de mensajes

Normalmente, los datos del mensaje entrada pertenecen a un formato de mensaje individual, con lo cual el analizador es responsable de analizar todo el contenido del mensaje. El nombre de clase del analizador que es necesario está definido en el campo Format (formato) de la cabecera MQMD o MQRFH2 del mensaje de entrada.

Sin embargo, el mensaje puede constar de múltiples formatos, por ejemplo, el caso en que hay una cabecera en un formato seguido por datos en otro formato. En este caso, el analizador tiene que identificar el nombre de clase del analizador que es responsable del siguiente formato de la cadena, y así sucesivamente. En un analizador definido por el usuario, la función de implementación cpiNextParserClassName la invoca el intermediario cuando necesita navegar hacia abajo de la cadena de clases de analizadores de un mensaje que está compuesto por formatos de mensajes múltiples.

Si el analizador definido por el usuario da soporte al análisis de un formato de mensaje que forma parte de un formato de mensajes múltiples, el analizador definido por el usuario debe implementar la función cpiNextParserClassName.

Por ejemplo:
  1. Invoque la función cpiNextParserClassName:
    void cpiNextParserClassName(
      CciParser*  parser,
      CciContext* context,
      CciChar*    buffer,
      int         size
    ){
      PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
      int                rc = 0;
    
  2. Copie el nombre de la siguiente clase de analizador en el intermediario:
      CciCharNCpy(buffer, pc->iNextParserClassName, size);
    
      return;
    }

Definición del analizador durante la incialización del intermediario

La función de inicialización del analizador definido por el usuario se invoca automáticamente durante la inicialización del intermediario. El analizador definido por el usuario es responsable de:
  • Crear y nombrar la fábrica del analizador de mensajes que el analizador definido por el usuario ha implementado. La fábrica de analizador es un contenedor de implementaciones relacionadas con analizadores. Los nombres de fábricas de analizadores deben ser únicos dentro de un intermediario.
  • Definir los nombres de clases de analizadores de mensajes soportados y suministrar un puntero a una tabla de funciones virtual que contiene punteros a funciones de implementación de analizadores definidos por el usuario. Los nombres de clases de analizadores deben ser únicos dentro de un intermediario.

Cada LIL que implementa un analizador definido por el usuario debe exportar una función denominada bipGetParserFactory como la función de inicialización. La función de inicialización define el nombre de la fábrica que el analizador definido por el usuario soporta y las clases de objetos o el objeto compartido, soportados por la fábrica.

La función de inicialización también debe crear el objeto de fábrica y definir los nombres de todos los analizadores soportados por LIL. Una fábrica puede dar soporte a cualquier número de clases de objetos (analizadores). Cuando se define un analizador, se transfiere una lista de punteros a las funciones de implementación de dicho analizador. Si ya existe un analizador que tenga el mismo nombre, se rechaza la petición.

Por ejemplo, para definir el analizador:
  1. Exporte la función de inicialización bipGetParserFactory:
    void LilFactoryExportPrefix * LilFactoryExportSuffix bipGetParserFactory()
    {
  2. Declare las variables:
     CciFactory*     factoryObject;
      int             rc;
      static CPI_VFT  vftable = {CPI_VFT_DEFAULT};
  3. Inicialice todas las constantes estáticas:
      initParserConstants();
  4. Tabla de funciones de configuración con punteros a funciones de implementación de analizador:
      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 continuación, la función de inicialización debe crear una fábrica de analizador invocando cpiCreateParserFactory. Las clases de analizadores soportados por la fábrica se definen invocando cpiDefineParserClass. La dirección del objeto de fábrica (devuelta por la función cpiCreateParserFactory) debe devolverse al intermediario como el valor de retorno de la función de inicialización.

Por ejemplo:
  1. Cree la fábrica de analizador utilizando la función cpiCreateParserFactory:
      factoryObject = cpiCreateParserFactory(&rc, constParserFactory);
      
  2. Defina las clases de mensajes soportados por la fábrica utilizando la función cpiDefineParserClass:
    if (factoryObject) {
       cpiDefineParserClass(&rc, factoryObject, constPXML, &vftable);
      }
    else {
        /* Error: No se ha podido crear fábrica de analizador */
      }
  3. Devuelve la dirección del objeto de esta fábrica al intermediario:
      return(factoryObject);
    }

Creación de una instancia del analizador

Siempre que se crea una instancia de un objeto de analizador definido por el usuario, el intermediario de mensajes invoca la función de implementación de creación de contexto cpiCreateContext. Esto permite al analizador definido por el usuario asignar datos de instancia asociados al analizador.

Por ejemplo:
  1. Invoque cpiCreateContext:
    CciContext* cpiCreateContext(
      CciParser* parser
    ){
      PARSER_CONTEXT_ST *p;
    
  2. Asigne un puntero al contexto local:
      p = (PARSER_CONTEXT_ST *)malloc(sizeof(PARSER_CONTEXT_ST));
    
  3. Borre el área de contexto:
      if (p) {
         memset(p, 0, sizeof(PARSER_CONTEXT_ST));
        }
      else {
        /* Error: No se ha podido asignar memoria */
      }
  4. Devuelva el puntero al contexto local:
      return((CciContext*)p);
    }

Implmentación de las funciones del analizador

Un analizador necesita implementar los siguientes tipos de funciones de implementación:
  1. funciones de entrada
  2. funciones de análisis
  3. funciones de salida

Cada tipo de función se describe a continuación.

Supresión de una instancia del analizador definido por el usuario

Para suprimir una instancia de un analizador, debe utilizar la función cpiDeleteContext. Por ejemplo:
void cpiDeleteContext(
  CciParser*  parser,
  CciContext* context
){
  PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
  int                rc = 0;

  return;
}

Conceptos relacionados
Planificación de extensiones definidas por el usuario
Extensiones definidas por el usuario en el entorno de ejecución
Diseño de extensiones definidas por el usuario
Analizadores definidos por el usuario

Tareas relacionadas
Desarrollo de extensiones definidas por el usuario
Implementación de los ejemplos proporcionados
Compilación de una extensión definida por el usuario en C

Referencia relacionada
API de analizador C definida por el usuario