lunes, 4 de mayo de 2015

Interpreter

Interpreter 


Propósito

Dado un lenguaje, define una representación para esta gramática junto con un intérprete que utiliza la representación para interpretar sentencias en el lenguaje.

Motivación

A veces es conveniente representar un problema como palabras de algún lenguaje sencillo, como por ejemplo, evaluar expresiones booleanas. El patrón interpreter describe como definir una gramática, representar palabras del lenguaje y cómo interpretarlas, se utiliza una clase para representar cada regla de la gramática.

Aplicabilidad

El uso de este patrón es indicado cuando hay un lenguaje que interpretar y se pueden representar sus palabras como arboles sintácticos abstractos, funciona mejor si:
  • La gramática es simple. Para gramáticas complejas la jerarquía de clases se vuelve inmanejable.
  • La eficiencia no es importante. Los intérpretes más eficientes no se consiguen interpretando arboles; normalmente se hace con maquinas de estados.
Estructura


Participantes

AbstractExpression Declara una operación abstracta Interpret que es común a todos los nodos en el árbol de sintaxis abstracto.

TerminalExpresion
  • Implementa una operación Interpret asociada con símbolos terminales en la gramática.
  • Una instancia es requerida para todo símbolo terminal en una sentencia.
NonterminalExpression
  • Una de estas clases es requerida para toda regla R::R1 R2…..Rn en la gramática.
  • Mantiene instancias variables del tipo AbstractExpression para cada uno de los símbolos de R1 a Rn.
Context
  • Contiene información que es global al intérprete.
Cliente
  • Se basa (o se da) una sintaxis abstracta que representa un árbol en el idioma en que se define la gramática.
  • Invoca la operación de Intérprete.

Colaboraciones

  • El cliente construye las sentencias es un árbol de sintaxis abstracto de NonterminalExpression e instancias de TerminalExpression. Luego el cliente inicializa el contexto e invoca el intérprete de la operación.
  • Cada nodo NonterminalExpression define un intérprete en términos de cada subexpresión. La operación del intérprete de cada TerminalExpression define el caso recursivamente.
  • Las operaciones del intérprete para cada nodo usan el contexto para crear y acceder el estado del intérprete.
Consecuencias

El patrón Interpreter tienes los siguientes beneficios y desventajas:

  1. Hace fácil el cambiar y extender la gramática. Porque el patrón usa clases que representan reglas gramaticales, podemos usar herencia para cambiar o extender la gramática.
  2. Implementar la gramática también es fácil. La definición de clases en nodos del l árbol abstracto de sintaxis tienen implementaciones similares.
  3. Gramáticas complejas son difíciles de mantener. El patrón Interpreter define al menos una clase para toda regla en la gramática. Por lo tanto gramáticas que tienen muchas reglas son difíciles de manejar y mantener.
  4. Adicionar nuevas formas de interpretar expresiones. El patrón Intérprete hace fácil el evaluar una expresión de una forma nueva.
Implementación

Los patrones Interpreter y Composite comparten muchos de los problemas en la implementación. Los siguientes problemas son específicos de Interpreter.

  1. Creando el árbol de sintaxis abstracto. EL patrón Interpreter no explica cómo crear el árbol abstracto de sintaxis, no explica cómo hacer parsing.
  2. Definiendo las operaciones de Interpreter. No hay que definir la operación Interpreter en cada clase de expresión. Si un lenguaje tiene varias interpretaciones, es mejor colocarlos en instancias del patrón Visitor.
  3. Compartir símbolos terminales con el patrón Flyweight. Las gramáticas con un gran número de símbolos terminales pueden beneficiarse compartiendo una copia única de cada símbolo terminal con el patrón Flyweight.
Código de ejemplo c#

using System;
using System.Collections;

class MainApp
{
  static void Main()
  {
    Context context = new Context();

    // Usually a tree
    ArrayList list = new ArrayList();

    // Populate 'abstract syntax tree'
    list.Add(new TerminalExpression());
    list.Add(new NonterminalExpression());
    list.Add(new TerminalExpression());
    list.Add(new TerminalExpression());

    // Interpret
    foreach (AbstractExpression exp in list)
    {
      exp.Interpret(context);
    }

    // Wait for user
    Console.Read();
  }
}

// "Context"
class Context
{
}

// "AbstractExpression"
abstract class AbstractExpression
{
  public abstract void Interpret(Context context);
}

// "TerminalExpression"
class TerminalExpression : AbstractExpression
{
  public override void Interpret(Context context) 
  {
    Console.WriteLine("Called Terminal.Interpret()");
  }
}

// "NonterminalExpression"
class NonterminalExpression : AbstractExpression
{
  public override void Interpret(Context context) 
  {
    Console.WriteLine("Called Nonterminal.Interpret()");
  } 
}

Usos conocidos

Este patrón se utiliza ampliamente en compiladores implementados con lenguajes orientados a objetos.

Patrones relacionados

  • El árbol de sintaxis abstracto es una instancia del patrónComposite.
  • Flyweight muestra como compartir símbolos terminales en el árbol.
  • Se puede usar un Iterator para recorrer el árbol.
  • Se puede implementar Visitor para mantener el comportamiento de cada nodo en una clase aparte.
Referencias

  • Design Patterns Elements of Reusable Object-Oriented Software, GoF.
  • http://www.ldc.usb.ve/~mgoncalves/IS2/sd07/grupo6.pdf
  • http://kybele.escet.urjc.es/documentos/SI/Patrones/12_Interpreter.pdf

No hay comentarios:

Publicar un comentario