Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Смышляев Дмитрий #231

Open
wants to merge 83 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
e26d15e
Создал новый проект
vafle228 Nov 17, 2024
459f8f4
Описал структуру классов для токенайзеров
vafle228 Nov 24, 2024
350c559
Добавил парсеры
vafle228 Nov 24, 2024
4a2abdb
Добавил генератор html кода
vafle228 Nov 24, 2024
7889590
Реализация алгоритма
vafle228 Nov 24, 2024
8a31839
Merge pull request #1 from vafle228/architecture
vafle228 Nov 24, 2024
9900175
Пока не в тдд (забыл)
vafle228 Nov 26, 2024
65fc929
Довожу тесты 1.0
vafle228 Nov 26, 2024
9b750df
Довожу тесты 2.0
vafle228 Nov 26, 2024
7f664cd
Поправил токены
vafle228 Nov 26, 2024
c8fcea8
Добавил пробел как отдельный токен
vafle228 Nov 26, 2024
302aa2f
Новый сканер
vafle228 Nov 26, 2024
620fb70
Чуть поправил токены
vafle228 Nov 26, 2024
cbb6061
Довожу тесты 3.0
vafle228 Nov 26, 2024
110c4d2
Поправил ААА концепцию в тесте
vafle228 Nov 26, 2024
5408bec
Merge pull request #2 from vafle228/tokenizer
vafle228 Nov 26, 2024
2727bca
Добавил матч тулы
vafle228 Nov 26, 2024
7fc6935
Простейший италик
vafle228 Nov 26, 2024
8446c91
Улучшил апи паттерн билдера
vafle228 Nov 26, 2024
d22da48
Небольшой ренейминг
vafle228 Nov 26, 2024
d1d8413
Ввел паттерн как отдельную сущность
vafle228 Nov 26, 2024
5d586ba
Новые экстеншены
vafle228 Nov 26, 2024
fec4199
Переосмысление метча
vafle228 Nov 26, 2024
fdd4a28
Добавил правило для текста
vafle228 Dec 1, 2024
676fd39
Паттерн рул
vafle228 Dec 1, 2024
680e394
Обалдеть всего
vafle228 Dec 1, 2024
25288d6
Поддержка дабл андерскора
vafle228 Dec 1, 2024
df27307
Небольшая правка логики
vafle228 Dec 1, 2024
43b7116
Новые тесты
vafle228 Dec 1, 2024
3771ebd
Непарный тег тест
vafle228 Dec 1, 2024
647e1ff
Merge pull request #3 from vafle228/parser
vafle228 Dec 1, 2024
fac2a80
Реорганизация кода
vafle228 Dec 1, 2024
816efd4
Заготовки под жирный тег
vafle228 Dec 1, 2024
fec2be8
Болд тег
vafle228 Dec 2, 2024
7664345
Фикс италик тега
vafle228 Dec 2, 2024
d0ff733
Уточнил правило
vafle228 Dec 2, 2024
89f2a8f
Уточнил болд тег
vafle228 Dec 2, 2024
5ad9ed2
Подготовка к новыйм тегам
vafle228 Dec 2, 2024
945a4a9
Тег параграфа
vafle228 Dec 2, 2024
c33f29b
Движение к будущему
vafle228 Dec 3, 2024
fdd61ec
Новый бордер тег
vafle228 Dec 3, 2024
9cf741a
Нафиг эту бордер рулу вообщем
vafle228 Dec 3, 2024
d902ab6
Убрал тупую нал проверку
vafle228 Dec 3, 2024
69653bc
Отрефакторил параграф рул
vafle228 Dec 3, 2024
2aee82e
Отрефакторил хедлайн
vafle228 Dec 3, 2024
d36920c
Поделил ответсвенность тестов
vafle228 Dec 3, 2024
a4dae45
Обалдеть изменений
vafle228 Dec 3, 2024
34760cc
Тесты для специальных рулзов
vafle228 Dec 3, 2024
81b9907
Ор рул тесты
vafle228 Dec 3, 2024
05b2a1a
Тесты для кондитионал рулы
vafle228 Dec 8, 2024
992df7a
Крупное переосмысление
vafle228 Dec 8, 2024
88d2842
Переделка италик тега
vafle228 Dec 8, 2024
b3a605c
Переписал болд тег на новый лад
vafle228 Dec 8, 2024
de49690
Дофикс оставшихся тегов
vafle228 Dec 8, 2024
ed1043c
Избавился от лишних экстеншенов
vafle228 Dec 8, 2024
245b5c6
Тесты для звезды клини
vafle228 Dec 8, 2024
81b7300
Тесты для континюс рулы
vafle228 Dec 8, 2024
7650424
Тесты для заголовка
vafle228 Dec 8, 2024
03f7791
Ескейп тег
vafle228 Dec 9, 2024
764838a
Узнал про крутые строки
vafle228 Dec 9, 2024
c78f04d
Правки по ескейп тегу
vafle228 Dec 9, 2024
0b33846
Бади рул
vafle228 Dec 9, 2024
c946f8d
Merge pull request #4 from vafle228/parser
vafle228 Dec 9, 2024
2b6337a
Переместил тулзы на уровень выше
vafle228 Dec 9, 2024
af53027
Добавил интерфейс генератора
vafle228 Dec 9, 2024
36db829
Реализовал хтмл генератор
vafle228 Dec 9, 2024
7177671
Оч строгие тесты
vafle228 Dec 9, 2024
1bb8d65
Тесты на генератор хтмл
vafle228 Dec 10, 2024
de2fa80
Тест на скорость работы
vafle228 Dec 10, 2024
b1c321f
Починил бади тесты
vafle228 Dec 10, 2024
de913c7
Переписал токены на мемори спаны
vafle228 Dec 16, 2024
0cfad9e
Добавил уровни для хедлайна
vafle228 Dec 16, 2024
ff0d4c1
Уточнил болд и италик рулу
vafle228 Dec 16, 2024
06f5619
Merge pull request #5 from vafle228/generator
vafle228 Dec 16, 2024
1f3cd85
Хочу открыть мр
vafle228 Dec 16, 2024
e462015
Добавил новые токены
vafle228 Dec 16, 2024
6d1b752
Добавил href рулзу
vafle228 Dec 16, 2024
d9f6d32
Описал спецификацию
vafle228 Dec 16, 2024
1f5429a
Внедрил хрефу в параграф
vafle228 Dec 16, 2024
c36f843
Добавил хрефу в генератор хтмл
vafle228 Dec 16, 2024
6323061
Перенес спеку в маркадаун файл
vafle228 Dec 16, 2024
d51a496
Чуть прибрался
vafle228 Dec 16, 2024
b8c92f5
Merge pull request #6 from vafle228/custom-tag
vafle228 Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cs/Markdown/Generator/HTMLGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Markdown.Parser.Nodes;

namespace Markdown.Generator;

public class HTMLGenerator

This comment was marked as resolved.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Согласен: сделаю, как начну генераторы пилить )

{

public string GenerateHTML(Node astRoot)
{
/* Do magic with ast root */
return "<h1>Hello world</h1>";
}
}
10 changes: 10 additions & 0 deletions cs/Markdown/Markdown.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
6 changes: 6 additions & 0 deletions cs/Markdown/Parser/Nodes/Node.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Markdown.Parser.Nodes;

public class Node
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Допиши, что тут должно быть плиз, не совсем понимаю, чем от токена отличается(

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По существу, токен - язык автомата. Вместо того чтобы читать посимвольно, я объединяю группы каких-то символов в токены, даю им тайпы и уже работаю с ними. Ноды же это результаты работы рулов. По сути в нодах будет лежать непрерывная последовательность токенов, которая будет являться каким либо тегом. А древовидность нод позволит описывать вложение одного тега в другой

{

}
12 changes: 12 additions & 0 deletions cs/Markdown/Parser/Rules/BodyRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Markdown.Parser.Nodes;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser.Rules;

public class BodyRule : IParsingRule
{
public Node Match(List<Token> tokens)
{
throw new NotImplementedException();
}
}
12 changes: 12 additions & 0 deletions cs/Markdown/Parser/Rules/BoldRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Markdown.Parser.Nodes;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser.Rules;

public class BoldRule : IParsingRule
{
public Node Match(List<Token> tokens)
{
throw new NotImplementedException();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А хде...

}
}
12 changes: 12 additions & 0 deletions cs/Markdown/Parser/Rules/HeadlineRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Markdown.Parser.Nodes;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser.Rules;

public class HeadlineRule : IParsingRule
{
public Node Match(List<Token> tokens)
{
throw new NotImplementedException();
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Parser/Rules/IParsingRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Markdown.Parser.Nodes;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser.Rules;

public interface IParsingRule
{
public Node Match(List<Token> tokens);
}
12 changes: 12 additions & 0 deletions cs/Markdown/Parser/Rules/ItalicRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Markdown.Parser.Nodes;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser.Rules;

public class ItalicRule : IParsingRule
{
public Node Match(List<Token> tokens)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Из этого как-то должен получиться автомат 🤔
А точно достаточно входных параметром для этого?
В общем, сорян, не совсем понимаю, как оно будет работать, буду рад, если опишешь)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Из этого действительно не получится автомат: параметров уже больше. На данный момент основная задача рулов - найти свой тег в листе токенов. Я начну описание с примитивных тегов, типо текста и италика. Далее, при описании тега выделения, я хочу переиспользовать ранее написанные теги, чтобы получить типо такого __ ITALIC | TEXT __. Продолжая так делать, я хочу определить "корневой" тег, который будет запускать парсинг всего текста.

Говоря иначе, я хочу написать автомат, который строится на регвырах. Сами же регвыры я буду переиспользовать, чтобы написать еще более сложный регвыр. Финальный же автомат будет выглядить как or по всем получившимся регвырам.

{
throw new NotImplementedException();
}
}
13 changes: 13 additions & 0 deletions cs/Markdown/Parser/TokenParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Markdown.Parser.Nodes;
using Markdown.Parser.Rules;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Parser;

public class TokenParser
{
public Node Parse(List<Token> tokens)
{
return new BodyRule().Match(tokens);
}
}
20 changes: 20 additions & 0 deletions cs/Markdown/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// See https://aka.ms/new-console-template for more information

using Markdown.Generator;
using Markdown.Parser;
using Markdown.Tokenizer;

namespace Markdown;

internal class Program
{
public static void Main(string[] args)
{
var markdown = "This _is_ a __sample__ markdown _file_.\n";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Советы категории Б: можешь вынести эту строчку в константу, чтобы потом, при добавлении новых примеров, можно было легко между ними переключаться :)

        const string firstExample = "This _is_ a __sample__ markdown _file_.\n";
        const string secondExample = "#This is another __sample__ markdown _file_";
        var markdown = firstExample;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Окей, сделаю :)


var tokens = new MarkdownTokenizer().Tokenize(markdown);
var astRoot = new TokenParser().Parse(tokens);

Console.WriteLine(new HTMLGenerator().GenerateHTML(astRoot));
}
}
26 changes: 26 additions & 0 deletions cs/Markdown/Tokenizer/MarkdownTokenizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Markdown.Tokenizer.Scanners;
using Markdown.Tokenizer.Tokens;

namespace Markdown.Tokenizer;

public class MarkdownTokenizer
{
private readonly ITokenScanner[] scanners = [
new SpecScanner(), new NumberScanner(), new TextScanner()
];

public List<Token> Tokenize(string markdown)
{
var begin = 0;
var tokenList = new List<Token>();

while (begin < markdown.Length)
{
var token = scanners
.Select(sc => sc.Scan(markdown, begin))
.First(token => token is not null);
begin += token!.Length; tokenList.Add(token);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Перенеси на новую строчку плиз, глазкам больно ахаха

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ахахахаха, ладно перенесу

}
return tokenList;
}
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tokenizer/Scanners/ITokenScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Markdown.Tokenizer.Tokens;

namespace Markdown.Tokenizer.Scanners;

public interface ITokenScanner
{
public Token? Scan(string markdown, int begin = 0);
}
17 changes: 17 additions & 0 deletions cs/Markdown/Tokenizer/Scanners/NumberScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Markdown.Tokenizer.Tokens;

namespace Markdown.Tokenizer.Scanners;

public class NumberScanner : ITokenScanner
{
public Token? Scan(string markdown, int begin = 0)
{
var numberIterator = markdown
.Skip(begin)
.TakeWhile(CanScan);
var numberLen = numberIterator.Count();
return numberLen == 0 ? null : new Token(TokenType.NUMBER, begin, numberLen, markdown);
}

public static bool CanScan(char symbol) => char.IsDigit(symbol);
}
26 changes: 26 additions & 0 deletions cs/Markdown/Tokenizer/Scanners/SpecScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Markdown.Tokenizer.Tokens;

namespace Markdown.Tokenizer.Scanners;

public class SpecScanner : ITokenScanner
{
public Token? Scan(string markdown, int begin = 0)
{
var tokenType = GetTokenType(markdown[begin]);
if (tokenType is null) return null;

var notNullType = (TokenType)tokenType;
return new Token(notNullType, begin, 1, markdown);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А как будешь разбираться с bold? Это, если что, __. Тут уже два символа. К GetTokenType тот же вопрос)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как я описывал выше: токены это язык, который мой автомат собирается распознавать. Поэтому болд для меня это +- такая вещь __ TEXT __

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не. я к тому, что сейчас все заточено под 1 символ (абсолютно все). bold в 2 символа может добавить проблем, если сразу не подумать над ним.
Если в следующих коммитах уже есть решение, то гуд)

}

public static bool CanScan(char symbol)
=> GetTokenType(symbol) != null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если у нас будет текст вида Всем_привет!?
Потом будешь разбирать в автомате? Кажется, все сломается. Ну, у меня ощущение такое.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Оно не должно сломаться, так как автомат не найдет закрывающей _ и просто проскочит на следующий символ


private static TokenType? GetTokenType(char symbol) => symbol switch
{
' ' => TokenType.SPACE,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: мб словарем сделать? Удобнее будет :)
https://marcduerst.com/2019/10/30/mapping-with-dictionary-instead-switch/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Словарь так словарь: ща поправлю :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не, NIT - это буквально "докопаться". Такие треды можешь не исправлять, если не хочешь/считаешь свое решение лучше. Я тут просто варианты накидываю)

'\n' => TokenType.NEW_LINE,
'_' => TokenType.UNDERSCORE,
_ => null
};
}
18 changes: 18 additions & 0 deletions cs/Markdown/Tokenizer/Scanners/TextScanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Markdown.Tokenizer.Tokens;

namespace Markdown.Tokenizer.Scanners;

public class TextScanner : ITokenScanner
{
public Token? Scan(string markdown, int begin = 0)
{
var valueIterator = markdown
.Skip(begin)
.TakeWhile(CanScan);
var valueLen = valueIterator.Count();
return valueLen == 0 ? null : new Token(TokenType.TEXT, begin, valueLen, markdown);
}

private static bool CanScan(char symbol)
=> !SpecScanner.CanScan(symbol) && !NumberScanner.CanScan(symbol);
}
20 changes: 20 additions & 0 deletions cs/Markdown/Tokenizer/Tokens/Token.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Markdown.Tokenizer.Tokens;

public class Token(TokenType tokenType, int begin, int length, string sourceText)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это DataClass, в нем нет логики (совсем чуть-чуть :D), только данные.
Давай тогда заиспользуем Record?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И для чего сюда передавать сразу весь текст sourceText?
Не говорю, что это плохо, просто мне не совсем понятно.
И еще на подумать, в aspnet есть такой концепт - Do not retrieve more data than is necessary. Он говорит про минимизацию запросов при http взаимодействии, но в т.ч. красиво перекладывается на код и доменные модели.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Рекод сделаю :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceText сюда передается с мыслью, чтобы токен мог достать свое значение из текста, в котором он существует. Соглашусь, что может выглядеть как переизбыток данных, но пока я еще не дописал полностью логику генератора хтмл, пусть он полежит тут (хотя бы для дебаг целей)

{
private string? value;

public int Begin { get; } = begin;
public int Length { get; } = length;
public TokenType TokenType { get; } = tokenType;

public string GetValue()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мб свойством? Тут логики не особо много)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Будет свойство, согл

{
return value ??= sourceText.Substring(Begin, Length);
}

public override string ToString()
{
return $"Token {TokenType} | Value \"{GetValue()}\"";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Лучше не переопределять .ToString без лишней потребности. Не совсем очевидное поведение. В плане, без углубления в код нельзя понять, что .ToString делает именно это (я бы первым делом подумал, что он возвращает value, это стандартное поведение таких методов)

p.s. а если хочешь оставить что-то подобное для дебага, то можно заюзать Externsion метод по типу .ToDebugString

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

}
}
10 changes: 10 additions & 0 deletions cs/Markdown/Tokenizer/Tokens/TokenType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Markdown.Tokenizer.Tokens;

public enum TokenType
{
TEXT,
UNDERSCORE,
SPACE,
NEW_LINE,
NUMBER,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А чем NUMBER отличается от TEXT с точки зрения обработки?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И похожий вопрос на засыпку: для чего хочешь использовать SPACE?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NUMBER и TEXT отличаются тем, что число с _ воспринимается как число, а текст - как тег. SPACE же нужен, чтобы хендлить вариант, когда у меня будет разрыв тегов между слов wor_d hel_lo. Если не определить пробел, то сканер вернет мне последовательность TEXT _ TEXT _ TEXT которую легко спутать с тегом

}
29 changes: 29 additions & 0 deletions cs/MarkdownTests/MarkdownTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="NUnit" Version="3.14.0"/>
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Markdown\Markdown.csproj" />
</ItemGroup>

</Project>
54 changes: 54 additions & 0 deletions cs/MarkdownTests/Tokenizer/MarkdownTokenizerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Text;
using FluentAssertions;
using Markdown.Tokenizer;

namespace MarkdownTests.Tokenizer;

[TestFixture]
public class MarkdownTokenizerTest
{
[TestCase("Text with numbers 321")]
[TestCase("Some not specific text")]
[TestCase("Text with __markdown__ characters")]
[TestCase("_A_ __lot__ of _characters_ in _markdown_\n")]
public void MarkdownTokenizer_Tokenize_TransformAllTextToTokens(string markdown)
{
var tokenizer = new MarkdownTokenizer();

var tokens = tokenizer.Tokenize(markdown);

var totalLength = tokens.Sum(token => token.Length);
totalLength.Should().Be(markdown.Length);
}

[TestCase("Hello world!")]
[TestCase("0123456789 - this is all digits")]
[TestCase("Some _wonderful __text with_ intersects__")]
public void MarkdownTokenizer_Tokenize_AllTokensAreNotIntersect(string markdown)
{
var tokenizer = new MarkdownTokenizer();

var tokens = tokenizer.Tokenize(markdown);

var pairs = Enumerable
.Range(0, tokens.Count - 1)
.Select(i => tokens[i + 1]).Zip(tokens)
.Select(pair => (next : pair.First, prev : pair.Second));
pairs.Should().OnlyContain(pair => pair.next.Begin - pair.prev.Begin == pair.prev.Length);
}

[TestCase("Text with numbers 321")]
[TestCase("Some not specific text")]
[TestCase("Text with __markdown__ characters")]
[TestCase("_A_ __lot__ of _characters_ in _markdown_\n")]
public void MarkdownTokenizer_Tokenize_TokensPresentInCorrectOrder(string markdown)
{
var tokenizer = new MarkdownTokenizer();

var tokens = tokenizer.Tokenize(markdown);

var resultStringBuilder = tokens
.Aggregate(new StringBuilder(), (sb, token) => sb.Append(token.GetValue()));
resultStringBuilder.ToString().Should().Be(markdown);
}
}
31 changes: 31 additions & 0 deletions cs/MarkdownTests/Tokenizer/Scanners/NumberScannerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using FluentAssertions;
using Markdown.Tokenizer.Scanners;
using Markdown.Tokenizer.Tokens;

namespace MarkdownTests.Tokenizer.Scanners;

[TestFixture]
public class NumberScannerTest
{
[TestCase("1", 0)]
[TestCase("12345", 0)]
public void NumberScanner_Scan_TokenShouldHaveNumberType(string text, int begin)
{
var scanner = new NumberScanner();

var token = scanner.Scan(text, begin);

token.Should().NotBeNull();
token.TokenType.Should().Be(TokenType.NUMBER);
}

[TestCase(" 123", 0)]
[TestCase("_\n ", 0)]
[TestCase("abcdifgh", 0)]
public void NumberScanner_Scan_ShouldScanNullFromText(string text, int begin)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: можно поставить begin значение по умолчанию. Тогда не потеряется гибкость тестов, но код станет чуть чище :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я с этим уже разобрался в своей "приватной" ветке ))

{
var scanner = new NumberScanner();
var token = scanner.Scan(text, begin);
token.Should().BeNull();
}
}
Loading