Лабораторная №3 — задание 3.2D #
Нужно реализовать разбор выражений в модуле синтаксического анализа, используя подход TDD.
Порядок выполнения #
В задании 3.1D вы сделали следующее:
- Создали модуль
src/Parser - Создали проект тестов
tests/Parser.UnitTests - Подготовили список тестов
tests/Parser.UnitTests/TESTLIST.md
Теперь вы должны реализовать разбор выражений и пройти все тесты из списка. В процессе разработки список тестов может дополняться и меняться.
TDD — цикл Red, Green, Refactor #
Цикл TDD содержит шаги Red, Green, Refactor:
- (Red) Напишите один тест из списка тестов
- старайтесь выбирать тест, который даёт новую информацию
- старайтесь не выбирать тест, для реализации которого нужно совершить много работы и принять много решений
- тест пишется до создания/изменения интерфейса тестируемого класса
- тест должен падать при выполнении либо вовсе не компилироваться (если указанные в нём символы из тестируемого кода ещё не объявлены)
- (Green) Реализуйте сценарий теста
- можно провести мелкий рефакторинг до реализации
- следуйте минимализму
- если в голову пришли новые идеи — запишите их в список тестов
- добейтесь прохождения теста
- (Refactor) Проведите рефакторинг
- можно пропустить этот шаг
- после рефакторинга тесты должны оставаться зелёными
- Если в списке тестов остались тесты — повторите цикл, начиная с шага №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#
- Парсер должен реализовать спецификацию, описанную аналитиком в файле
docs/specification/expressions-grammar.mdв вашем репозитории - Парсер должен быть разработан по TDD с применением шаблона «Список тестов»
- список тестов должен быть в формате markdown, описанном ниже
- при сдаче работы нужно будет показать этот список тестов
- тестировать следует именно парсер, а не его компоненты
- не нужно добавлять тривиальные тесты (тесты конструкторов и тому подобное)
- Парсер следует писать методом рекурсивного спуска и без генераторов синтаксических анализаторов
- Парсер должен следовать грамматике (см. ниже)
Парсер должен следовать грамматике #
Допустим, в грамматике есть правило: factor_expression = [ "+" | "-" ], exponentiation_expression ;
В классе Parser создаётся метод, соответствующий этому правилу:
- Название метода выводится из правила
- В комментарии к методу описывается правило в нотации EBNF (диалект ISO)
Пример:
/// <summary>
/// Выполняет синтаксический разбор.
/// Грамматика языка описана в файле `docs/specification/expressions-grammar.md`.
/// </summary>
public class Parser
{
/// <summary>
/// Разбирает один операнд сложения/вычитания.
/// Правила:
/// term_expression = factor_expression, { ("*" | "/"), factor_expression } ;
/// </summary>
private decimal ParseTermExpression()
{
// ... реализация разбора правила
}
}