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

Попов Захар #235

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
f53e022
Проведено начальное проектирование.
BlizPerfect Nov 24, 2024
f8768e2
В работе приложения больше не используются конвёртеры тегов.
BlizPerfect Dec 1, 2024
476d331
В работе приложения больше не используются теги в явном виде.
BlizPerfect Dec 1, 2024
36691f2
Реализация с помощью паттерна "Посетитель" получения визуального пред…
BlizPerfect Dec 1, 2024
f04e7fe
Рефакторинг токенов.
BlizPerfect Dec 1, 2024
a0e7f63
Реализация парсера.
BlizPerfect Dec 1, 2024
f99e193
Смена типа выходного файла на библиотеку.
BlizPerfect Dec 1, 2024
f0948eb
Написание тестов
BlizPerfect Dec 1, 2024
1bfdc4a
Исправил один int в цикле на var.
BlizPerfect Dec 1, 2024
058c67f
Выполнен рефакторинг тестов и изменено поведение парсера.
BlizPerfect Dec 2, 2024
c166721
Рефакторинг тестов в MdTests.
BlizPerfect Dec 2, 2024
1835148
Рефакторинг ParserMd
BlizPerfect Dec 2, 2024
7b04aad
Измнена логика работы теста по замеру алгоритмической сложности.
BlizPerfect Dec 10, 2024
e7e24a7
Токен теперь хранит в себе свой тип
BlizPerfect Dec 10, 2024
d7acd5a
Md теперь принимает конкеретный ParserMd.
BlizPerfect Dec 10, 2024
b176806
Рефакторинг кода
BlizPerfect Dec 10, 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
9 changes: 9 additions & 0 deletions cs/Markdown/Converters/IConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Markdown.Tokens;

namespace Markdown.Converters
{
internal interface IConverter
{
IList<Token> Convert(IList<Token> tokensToConvert);
}

Choose a reason for hiding this comment

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

Добавлю тут про второй пункт из описания.

Хочется услышать мнение о целесообразности приведения конкретных реализаций парсера, конвертера и врайтера в статические, а не динамические классы. Тут больше вопрос в том, как их тогда обязать реализовывать интерфейсы?

Если реализация у метода/класса одна, и в будущем не предвидится появление новых реализаций - то это можно реализовать статическим классом без дополнительного интерфейса.
Если же реализаций точно будет несколько, то в таком случае статика подойдёт тут в меньшей степени.
Нормально сделать интерфейс, который объявляет какие-либо методы и иметь динамические классы для него.

Что касается конкретно IConverter, IWriter и IParser, то наверное вопрос к тебе, что по твоему мнению никогда не потребует дополнительной реализации? А что может потребовать?

}
22 changes: 22 additions & 0 deletions cs/Markdown/Converters/MdToHTMLConverters/ConverterMdToHTML.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Markdown.Tags;
using Markdown.Tokens;
using Markdown.Tags.HTMLTags;

namespace Markdown.Converters.MdToHTMLConverters
{
internal class ConverterMdToHTML : IConverter
{
private readonly Dictionary<string, ITag> _mdTagNamesToHTMLTags
= new Dictionary<string, ITag>()
{
{"_", new HTMLTag("em") },
{"__", new HTMLTag("strong") },
{"# ", new HTMLTag("h1") },
};

public IList<Token> Convert(IList<Token> tokensToConvert)
{
throw new NotImplementedException();
}
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
}
}
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>
22 changes: 22 additions & 0 deletions cs/Markdown/Md.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Markdown.Converters;
using Markdown.Parsers;
using Markdown.Writers;

namespace Markdown
{
internal class Md(IParser parser, IConverter converter, IWriter writer)

Choose a reason for hiding this comment

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

Это хорошо, что класс Md конфигурируется.
Но некоторые части лучше реализовать внутри. Например, точно ли класс для markdown должен обязывать пользователя создавать для него правила?

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

Copy link

@Folleach Folleach Dec 2, 2024

Choose a reason for hiding this comment

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

Могу ли я создать класс Md и передать в него не Markdown парсер?
image

Получается класс Md это какая-то абстрактная машина конвертации форматов? Правильное ли тогда название Md?

{
private readonly IParser _parser = parser;
private readonly IConverter _converter = converter;
private readonly IWriter _writer = writer;
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved

public string Render(string text)
{
var mdTokens = _parser.ParseToTokens(text);

var HTMLTokens = _converter.Convert(mdTokens);
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved

return _writer.Write(HTMLTokens);
}
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Parsers/IParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Markdown.Tokens;

namespace Markdown.Parsers
{
internal interface IParser
{
IList<Token> ParseToTokens(string textToParse);
}
}
21 changes: 21 additions & 0 deletions cs/Markdown/Parsers/MdParsers/ParserMD.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Markdown.Tags;
using Markdown.Tags.MdTags;
using Markdown.Tokens;

namespace Markdown.Parsers.MdParsers
{
internal class ParserMD : IParser
{
private readonly Dictionary<string, ITag> _mdTags = new Dictionary<string, ITag>()
{
{"_", new PairedMdTag("_") },
{"__", new PairedMdTag("__") },
{"# ", new SingleMdTag("# ") }
};
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved

public IList<Token> ParseToTokens(string textToParse)
{
throw new NotImplementedException();
}
}
}
82 changes: 82 additions & 0 deletions cs/Markdown/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Markdown.Converters.MdToHTMLConverters;
using Markdown.Parsers.MdParsers;
using Markdown.Tags;
using Markdown.Tags.HTMLTags;
using Markdown.Tags.MdTags;
using Markdown.Tokens;
using Markdown.Writers;

namespace Markdown
{
internal class Program
{
static void Main(string[] args)
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
{
var simpleText = "Hello, Andrey";
var simpleTokenMD = new SimpleStringToken(simpleText);
var simpleTokenHTML = new SimpleStringToken("Hello, Andrey");
ShowEaxmple(simpleText, simpleTokenMD, simpleTokenHTML);
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved


var markdownText = "_Hello, Andrey_";
var markdownTokenMD = new MarkdownStringToken(new PairedMdTag("_"), "Hello, Andrey");
var markdownTokenHTML = new MarkdownStringToken(new HTMLTag("em"), "Hello, Andrey");
ShowEaxmple(markdownText, markdownTokenMD, markdownTokenHTML);


var complexText = "__с _разными_ символами__";
var complexInnerTokensMD = new List<Token>()
{
new SimpleStringToken("c "),
new MarkdownStringToken(new PairedMdTag("_"),"разными"),
new SimpleStringToken(" символами")
};
var complexInnerTokensHTML = new List<Token>()
{
new SimpleStringToken("c "),
new MarkdownStringToken(new HTMLTag("em"),"разными"),
new SimpleStringToken(" символами")
};
var complexTokenMD = new ComplexSringToken(new PairedMdTag("__"), complexInnerTokensMD);
var complexTokenHTML = new ComplexSringToken(new HTMLTag("strong"), complexInnerTokensHTML);
ShowEaxmple(complexText, complexTokenMD, complexTokenHTML);


var complexHeaderText = "# Заголовок __с _разными_ символами__";
var complexHeaderInnerTokensMD = new List<Token>()
{
new SimpleStringToken("Заголовок "),
new ComplexSringToken(new PairedMdTag("__"), complexInnerTokensMD)
};
var complexHeaderInnerTokensHTML = new List<Token>()
{
new SimpleStringToken("Заголовок "),
new ComplexSringToken(new HTMLTag("strong"), complexInnerTokensHTML)
};
var complexHeaderTokenMD = new ComplexSringToken(new SingleMdTag("# "), complexHeaderInnerTokensMD);
var complexHeaderTokenHTML = new ComplexSringToken(new HTMLTag("h1"), complexHeaderInnerTokensHTML);
ShowEaxmple(complexHeaderText, complexHeaderTokenMD, complexHeaderTokenHTML);

Console.ReadLine();

var exampleText = "# Заголовок __с _разными_ символами__";
var parser = new ParserMD();
var converter = new ConverterMdToHTML();
var writer = new WriterHTML();
var md = new Md(parser, converter, writer);
var htmlCode = md.Render(exampleText);
}

private static void ShowEaxmple(
string rawText,
Token tokenMD,
Token tokenHTML)
{
Console.WriteLine("Пример:");
Console.WriteLine($"Переданный текст: {rawText}");
Console.WriteLine($"Востановленный из токена MD текст: {tokenMD}");
Console.WriteLine($"Востановленный из токена HTML текст: {tokenHTML}");
Console.WriteLine();
}
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Tags/HTMLTags/HTMLTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Tags.HTMLTags
{
internal class HTMLTag(string tagName) : Tag(tagName), ITag
{
public string OpeningTagName => $"<{TagName}>";

public string ClosingTagName => $"</{TagName}>";
}
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tags/ITag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Markdown.Tags
{
internal interface ITag
{
string OpeningTagName { get; }
string ClosingTagName { get; }
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Tags/MdTags/PairedMdTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Tags.MdTags
{
internal class PairedMdTag(string tagName) : Tag(tagName), ITag
{
public string OpeningTagName => TagName;

public string ClosingTagName => TagName;
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Tags/MdTags/SingleMdTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Tags.MdTags
{
internal class SingleMdTag(string tagName) : Tag(tagName), ITag
{
public string OpeningTagName => TagName;

public string ClosingTagName => string.Empty;
}
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
}
16 changes: 16 additions & 0 deletions cs/Markdown/Tags/Tag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Markdown.Tags
{
internal abstract class Tag
{
protected Tag(string tagName)
{
if (string.IsNullOrWhiteSpace(tagName))
{
throw new ArgumentException(
"Имя тега не может быть null или пустой строкой");
}
TagName = tagName;
}
public string TagName { get; }
}
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
}
50 changes: 50 additions & 0 deletions cs/Markdown/Tokens/ComplexSringToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Markdown.Tags;
using System.Text;

namespace Markdown.Tokens
{
internal class ComplexSringToken : Token
{
public ITag Tag { get => _tag!; }
public Token[] InnerTokens { get => _innerTokens!.ToArray(); }

public ComplexSringToken(ITag tag, IList<Token> innerTokens)
: base(tag: tag, innerTokens: innerTokens)
{
if (tag is null)
{
throw new ArgumentException("Передаваемый токен не может быть null");
}

if (innerTokens is null)
{
throw new ArgumentException("Передаваемые внутренние токены не могут быть null");
}

if (innerTokens.Count == 0)
{
throw new ArgumentException(
"Передаваемые внутренние токены не могут быть пустыми");
}
}

public override string ToString()
{
var sb = new StringBuilder();

sb.Append(Tag.OpeningTagName);
foreach (var token in InnerTokens)
{
if (token is SimpleStringToken || token is MarkdownStringToken)
{
sb.Append(token.ToString());
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
continue;
}
sb.Append(token.ToString());
}
sb.Append(Tag.ClosingTagName);

return sb.ToString();
}
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
}
}
28 changes: 28 additions & 0 deletions cs/Markdown/Tokens/MarkdownStringToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Markdown.Tags;

namespace Markdown.Tokens
{
internal class MarkdownStringToken : Token
{
public ITag Tag { get => _tag!; }

public string Content { get => _content!; }
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved

public MarkdownStringToken(ITag tag, string content) : base(tag: tag, content: content)
{
if (tag is null)
{
throw new ArgumentException("Передаваемый токен не может быть null");
}

if (string.IsNullOrWhiteSpace(content))
{
throw new ArgumentException(
"Содержимое токена не может быть null или пустой строкой");
}
}

public override string ToString()
=> $"{Tag.OpeningTagName}{Content}{Tag.ClosingTagName}";
}
}
18 changes: 18 additions & 0 deletions cs/Markdown/Tokens/SimpleStringToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Markdown.Tokens
{
internal class SimpleStringToken : Token
{
public string Content { get => _content!; }

public SimpleStringToken(string content) : base(content: content)
{
if (string.IsNullOrWhiteSpace(content))
{
throw new ArgumentException(
"Содержимое токена не может быть null или пустой строкой");
}
}

public override string ToString() => Content;
}
}
15 changes: 15 additions & 0 deletions cs/Markdown/Tokens/Token.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Markdown.Tags;

namespace Markdown.Tokens
{
internal abstract class Token(
ITag? tag = null,
string? content = null,
IList<Token>? innerTokens = null)
{
protected readonly ITag? _tag = tag;
protected readonly string? _content = content;
protected readonly IList<Token>? _innerTokens = innerTokens;
}
}

9 changes: 9 additions & 0 deletions cs/Markdown/Writers/IWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Markdown.Tokens;

namespace Markdown.Writers
{
internal interface IWriter
{
string Write(IList<Token> tokensToWrite);
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
}
}
12 changes: 12 additions & 0 deletions cs/Markdown/Writers/WriterHTML.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Markdown.Tokens;

namespace Markdown.Writers
{
internal class WriterHTML : IWriter
{
public string Write(IList<Token> tokensToWrite)
{
throw new NotImplementedException();
}
}
}
Loading