I.4. Представим данные для работы с обратной польской записью - madwareru/drafting-interpreters GitHub Wiki

Давайте создадим новый проект консольного приложения на языке C#. Называем как нам нравится, я в моём случае выбрал название ReversePolishNotation. Так же создадим проект для юнит тестов основанный на NUnit, и добавим наш основной проект в его References.

Работу интерпретатора сложно представить без абстрактного синтаксического дерева или аналогичной ему структуры, хранящей данные о программе. Обычно эти данные имеют древовидную структуру и представляются с помощью типов-сумм, в случае C# мы обычно имеем ввиду под этим некоторую иерархию типов, унаследованную от общего интерфейса.

Для обратной польской записи мы имеем необходимость завести как раз такую иерархию для типов операций. Создадим новый файл IOperation.cs и напишем там следующий код:

public interface IOperation
{
    public class Put : IOperation
    {
        public readonly double Number;
        public Put(double number) => Number = number;
    }
    public class Add : IOperation {}
    public class Div : IOperation {}
    public class Sqrt : IOperation {}
}

Здесь мы имеем базовый тип интерфейса для операции, и внутри него вложена иерархия наследников, представляющих собой конкретные операции. Как можно заметить, все наследники содержат в себе только данные, и в случае если эти данные есть, они помечены как readonly. Это нам и нужно.

Упражнение I.4.1 Перегрузите у всех операций метод ToString() для того, чтобы иметь возможность получить строковое представление содержимого операций в красивом виде

Добавим так же вспомогательный класс для более приятной работы с операциями:

public static class Operation
{
    public static IOperation Put(double number) => new IOperation.Put(number);
    public static readonly IOperation Add = new IOperation.Add();
    public static readonly IOperation Div = new IOperation.Div();
    public static readonly IOperation Sqrt = new IOperation.Sqrt();
}

Как можно заметить, данный класс служит не только как способ избежать череды назойливых вызовов оператора new, но и для оптимизации. Операции, которые не содержат в себе данных -- пустые, и их можно спокойно уложить в статическое readonly поле для дальнейшего переиспользования везде где нам это понадобится.

Упражнение I.4.2 Подумайте, можно ли проделать такой же фокус с вызовом Operation.Put. Если это возможно, то что для этого потребуется?

Упражнение I.4.3 Замените код стандартного сгенерированного Hello World'а в консольном приложении на такой, который создаёт массив из операций, приведённый во вводном примере к разделу I.2 и печатает на экран его содержимое. Должно получиться что-то вроде [Put(3), Put(4), Put(9), Add, Div, Put(-7), Add, Sqrt]

Посмотреть на пример реализации актуальный для данной части туториала можно тут

В следующем разделе мы напишем наш первый код для интерпретатора обратной польской записи.