Задание 3.2D

Лабораторная №3 — задание 3.2D #

Нужно реализовать разбор выражений в модуле синтаксического анализа, используя подход TDD.

Порядок выполнения #

В задании 3.1D вы сделали следующее:

  1. Создали модуль src/Parser
  2. Создали проект тестов tests/Parser.UnitTests
  3. Подготовили список тестов tests/Parser.UnitTests/TESTLIST.md

Теперь вы должны реализовать разбор выражений и пройти все тесты из списка. В процессе разработки список тестов может дополняться и меняться.

TDD — цикл Red, Green, Refactor #

Цикл TDD содержит шаги Red, Green, Refactor:

  1. (Red) Напишите один тест из списка тестов
    • старайтесь выбирать тест, который даёт новую информацию
    • старайтесь не выбирать тест, для реализации которого нужно совершить много работы и принять много решений
    • тест пишется до создания/изменения интерфейса тестируемого класса
    • тест должен падать при выполнении либо вовсе не компилироваться (если указанные в нём символы из тестируемого кода ещё не объявлены)
  2. (Green) Реализуйте сценарий теста
    • можно провести мелкий рефакторинг до реализации
    • следуйте минимализму
    • если в голову пришли новые идеи — запишите их в список тестов
    • добейтесь прохождения теста
  3. (Refactor) Проведите рефакторинг
    • можно пропустить этот шаг
    • после рефакторинга тесты должны оставаться зелёными
  4. Если в списке тестов остались тесты — повторите цикл, начиная с шага №1 (Red).

Фиксация в Git (git commit) #

Вы сами выбираете частоту фиксаций.

Рекомендации:

  • Фиксируйте (git commit) каждый успешно пройденный тест
  • Если вам нужно прервать работу — закончите шаг Red и оставьте работу с падающим тестов. Это поможет вернуться к работе позже.

Пример MemSql #

Можно ориентироваться на Пример MemSql

  • Список тестов: tests/SqlParser.UnitTests/TESTLIST.md
  • Тесты: tests/SqlParser.UnitTests
  • Реализация: src/SqlParser

Структура модуля синтаксического анализа #

Предлагаемая диаграмма классов показана ниже. Вы можете реализовать иной вариант на своё усмотрение.

classDiagram
    class BuiltinFunctions {
        +Invoke(string name, List~decimal~ arguments) decimal
    }

    class TokenStream {
        +TokenStream(string code)
        +Peek() Token
        +Advance() void
    }

    class Parser {
        +EvaluateExpression(string code) int
    }

    class Lexer {
        <<external>>
        +ParseToken() Token
    }

    Parser --o TokenStream : содержит
    Parser --> BuiltinFunctions : вызывает
    TokenStream --o Lexer : содержит

Требования к парсеру (синтаксическому анализатору) #

Учитывайте Требования к коду на C#

  1. Парсер должен реализовать спецификацию, описанную аналитиком в файле docs/specification/expressions-grammar.md в вашем репозитории
  2. Парсер должен быть разработан по TDD с применением шаблона «Список тестов»
    • список тестов должен быть в формате markdown, описанном ниже
    • при сдаче работы нужно будет показать этот список тестов
    • тестировать следует именно парсер, а не его компоненты
    • не нужно добавлять тривиальные тесты (тесты конструкторов и тому подобное)
  3. Парсер следует писать методом рекурсивного спуска и без генераторов синтаксических анализаторов
  4. Парсер должен следовать грамматике (см. ниже)

Парсер должен следовать грамматике #

Допустим, в грамматике есть правило: factor_expression = [ "+" | "-" ], exponentiation_expression ;

В классе Parser создаётся метод, соответствующий этому правилу:

  1. Название метода выводится из правила
  2. В комментарии к методу описывается правило в нотации EBNF (диалект ISO)

Пример:

/// <summary>
/// Выполняет синтаксический разбор.
/// Грамматика языка описана в файле `docs/specification/expressions-grammar.md`.
/// </summary>
public class Parser
{
    /// <summary>
    ///  Разбирает один операнд сложения/вычитания.
    ///  Правила:
    ///     term_expression = factor_expression, { ("*" | "/"), factor_expression } ;
    /// </summary>
    private decimal ParseTermExpression()
    {
        // ... реализация разбора правила
    }
}