Padrão de intérprete C#

Resumo : neste tutorial, você aprenderá como usar o padrão de interpretador C# para construir gramática para interpretar uma linguagem simples.

Introdução ao padrão de interpretador C#

O padrão de intérprete é um padrão de comportamento que permite construir um intérprete para uma linguagem simples e especifica como avaliar sentenças ou expressões nessa linguagem.

O padrão de intérprete define a gramática da linguagem e fornece uma maneira de avaliar a gramática.

Normalmente, a gramática consiste em um conjunto de classes , cada uma das quais representa uma regra da gramática. O padrão interpretador combina as regras para formar uma hierarquia com as regras mais básicas na parte inferior e as regras mais complexas no topo,

Por exemplo, a imagem a seguir ilustra uma expressão 10 + 20 * (2 + 3) como uma árvore de regras:

Estrutura da árvore do interpretador C#

Para avaliar uma frase ou expressão, o intérprete recebe informações e aplica as regras gramaticais a elas. Mais especificamente, o intérprete avalia recursivamente as regras, aplica primeiro as regras mais básicas e combina o resultado de cada avaliação até que toda a frase ou expressão seja avaliada.

O resultado da avaliação é normalmente um valor ou um conjunto de valores, dependendo da aplicação específica do intérprete.

O diagrama UML a seguir ilustra o padrão Interpreter:

Padrão de intérprete C#

O padrão de intérprete envolve os seguintes participantes:

  • AbstractExpressioné uma classe abstrata ou uma interface que possui interpret()um método para avaliar a expressão.
  • TerminalExpressioné uma implementação concreta da expressão abstrata. O TerminalExpressionrepresenta os elementos mais simples da gramática, como variáveis, constantes ou literais. Uma expressão terminal não pode ser decomposta em expressões menores. Por exemplo, em uma gramática de expressão aritmética, uma expressão terminal pode ser um número ou uma variável.
  • NonTerminalExpressionrepresenta os elementos mais complexos da gramática da linguagem construídos a partir de expressões mais simples. Ao contrário de uma expressão terminal, uma expressão não terminal pode ser decomposta em expressões menores até atingir expressões terminais. Por exemplo, em uma gramática de expressão aritmética, uma expressão não terminal poderia representar uma operação binária como a adição, que é construída a partir de duas expressões terminais.
  • Contexté o contexto em que o intérprete avalia a expressão. Contém as informações necessárias para a avaliação da expressão.
  • Clienté uma classe responsável por construir uma expressão usando TerminalExpressionand NonterminalExpression. Ele passa a expressão ao intérprete para avaliação.

Por exemplo, a imagem a seguir mostra as expressões terminais e não terminais:

Intérprete C# - TerminalExpression e NonTerminal Expression

Exemplo de padrão de interpretador C#

O programa a seguir demonstra como usar o padrão de interpretador C#:

namespace IterpreterPattern;

// Abstract Expression
public abstract class Expression
{
    public abstract int Interpret(Context context);
}

// Terminal Expression
public class NumberExpression : Expression
{
    private readonly int number;
    public NumberExpression(int number)
    {
        this.number = number;
    }
    public override int Interpret(Context context)
    {
        return number;
    }
}

// Non-terminal Expression
public  class AddExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public AddExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) + _right.Interpret(context);
    }
}
public class SubtractExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public SubtractExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) - _right.Interpret(context);
    }
}

public class MultiplyExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public MultiplyExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) * _right.Interpret(context);
    }
}
public class DivideExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public DivideExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) / _right.Interpret(context);
    }
}

// Context
public class Context
{
    private readonly Dictionary<string, int> _variables;

    public Context()
    {
        _variables = new Dictionary<string, int>();
    }

    public void SetVariable(string name, int value)
    {
        _variables[name] = value;
    }

    public int GetVariable(string name)
    {
        if (_variables.TryGetValue(name, out var value))
        {
            return value;
        }
        else
        {
            throw new ArgumentException($"Variable '{name}' not found.");
        }
    }
}

// Client
public class Client
{
    public static void Main(string[] args)
    {
        var context = new Context();
        context.SetVariable("x", 2);
        context.SetVariable("y", 3);

        // -> 10 + 20*(x + y)
        var expression = new AddExpression(
            new NumberExpression(10),
            new MultiplyExpression(  
                new NumberExpression(20),
                new AddExpression(
                    new NumberExpression(context.GetVariable("x")),
                    new NumberExpression(context.GetVariable("y"))
                )
            )
        );

        var result = expression.Interpret(context);

        Console.WriteLine("Result: " + result); 
    }
}Linguagem de código:  C#  ( cs )

Saída:

Result: 110Linguagem de código:  C#  ( cs )

Como funciona.

Primeiro, defina a classe abstrata Expressão:

public abstract class Expression
{
    public abstract int Interpret(Context context);
}Linguagem de código:  C#  ( cs )

Segundo, defina a NumberExpressionclasse que herda da Expressionclasse:

public class NumberExpression : Expression
{
    private readonly int number;
    public NumberExpression(int number)
    {
        this.number = number;
    }
    public override int Interpret(Context context)
    {
        return number;
    }
}Linguagem de código:  C#  ( cs )

O NumberExpressionserve como um TerminalExpression.

Terceiro, defina a AddExpressionclasse que resume duas expressões:

public class AddExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public AddExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) + _right.Interpret(context);
    }
}Linguagem de código:  C#  ( cs )

O AddExpressionserve como um NonterminalExpression.

Quarto, defina as classes SubtractExpression, MultiplyExpressione DivideExpressioncomo a AddExpressionclasse, mas subtraia, multiplique e divida dois números respectivamente.

public class SubtractExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public SubtractExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) - _right.Interpret(context);
    }
}

public class MultiplyExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public MultiplyExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) * _right.Interpret(context);
    }
}
public class DivideExpression : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;
    public DivideExpression(Expression leftExpression, Expression rightExpression)
    {
        _left = leftExpression;
        _right = rightExpression;
    }
    public override int Interpret(Context context)
    {
        return _left.Interpret(context) / _right.Interpret(context);
    }
}Linguagem de código:  C#  ( cs )

Quinto, defina a classe Context que mantém um dicionário de variáveis ​​para a expressão:

public class Context
{
    private readonly Dictionary<string, int> _variables;

    public Context()
    {
        _variables = new Dictionary<string, int>();
    }

    public void SetVariable(string name, int value)
    {
        _variables[name] = value;
    }

    public int GetVariable(string name)
    {
        if (_variables.TryGetValue(name, out var value))
        {
            return value;
        }
        else
        {
            throw new ArgumentException($"Variable '{name}' not found.");
        }
    }
}Linguagem de código:  C#  ( cs )

Por fim, defina o Cliente que compõe a expressão: 10 + 20 * (x + y), onde x é 2 e y é 3:

public class Client
{
    public static void Main(string[] args)
    {
        var context = new Context();
        context.SetVariable("x", 2);
        context.SetVariable("y", 3);

        // -> 10 + 20*(x + y)
        var expression = new AddExpression(
            new NumberExpression(10),
            new MultiplyExpression(  
                new NumberExpression(20),
                new AddExpression(
                    new NumberExpression(context.GetVariable("x")),
                    new NumberExpression(context.GetVariable("y"))
                )
            )
        );

        var result = expression.Interpret(context);

        Console.WriteLine("Result: " + result); 
    }
}Linguagem de código:  C#  ( cs )

Resumo

  • Use o padrão de intérprete para construir um intérprete para uma linguagem simples.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *