Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
129 changes: 129 additions & 0 deletions cs/MarkdownTests/Entities/Builders/HtmlBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Text;
using Markdown.Enums;
using Markdown.Models;
using Markdown.Models.SyntaxTreeModels;

namespace Markdown.Entities.Builders
{
/// <summary>
/// Преобразует абстрактное синтаксическое дерево (AST) в HTML-код.
/// Выполняет обход дерева в глубину и генерирует соответствующие HTML-теги для каждого узла.
/// </summary>
/// <param name="tree">Абстрактное синтаксическое дерево для преобразования</param>
/// <returns>HTML-код, соответствующий структуре исходного дерева</returns>
/// <remarks>
/// Для каждого типа узла AST генерирует соответствующий HTML-тег:
/// - Заголовки → h1
/// - Жирный текст → strong
/// - Курсив → em
/// - Параграфы → p
/// </remarks>
public class HtmlBuilder : IBuilder
{
private readonly StringBuilder _htmlBuilder;
private readonly Dictionary<NodeType, string> _tagMapping;

public HtmlBuilder()
{
_htmlBuilder = new StringBuilder();
_tagMapping = new Dictionary<NodeType, string>
{
{ NodeType.Document, "" },
{ NodeType.Paragraph, "p" },
{ NodeType.Header, "h1" },
{ NodeType.Bold, "strong" },
{ NodeType.Italic, "em" },
{ NodeType.Text, "" }
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Может вынести куда-нибудь в отдельный файл или в поля класса, всё равно он не изменяется

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ok

}

public string Build(ISyntaxTree tree)
{
if (tree == null)
return string.Empty;

_htmlBuilder.Clear();
BuildNodes(tree.Tree);
return _htmlBuilder.ToString();
}

private void BuildNodes(List<Node> nodes)
{
foreach (var node in nodes)
{
BuildNode(node);
}
}

private void BuildNode(Node node)
{
switch (node.Type)
{
case NodeType.Text:
BuildTextNode(node);
break;
case NodeType.Document:
BuildDocumentNode(node);
break;
default:
BuildFormattedNode(node);
break;
}
}

private void BuildTextNode(Node node)
{
if (!string.IsNullOrEmpty(node.Value))
{
var escapedText = EscapeHtml(node.Value);
_htmlBuilder.Append(escapedText);
}
}

private void BuildDocumentNode(Node node)
{
if (node.ChildrenNodes != null)
{
BuildNodes(node.ChildrenNodes);
}
}

private void BuildFormattedNode(Node node)
{
var tagName = _tagMapping[node.Type];

if (!string.IsNullOrEmpty(tagName))
{
_htmlBuilder.Append($"<{tagName}>");
}

if (node.ChildrenNodes != null && node.ChildrenNodes.Count > 0)
{
BuildNodes(node.ChildrenNodes);
}
else if (!string.IsNullOrEmpty(node.Value))
{
BuildTextNode(node);
}

if (!string.IsNullOrEmpty(tagName))
{
_htmlBuilder.Append($"</{tagName}>");
}
}

private string EscapeHtml(string text)
{
if (string.IsNullOrEmpty(text))
return text;

// Эскейпинг HTML-символов для безопасности
return text
.Replace("&", "&amp;")
.Replace("<", "&lt;")
.Replace(">", "&gt;")
.Replace("\"", "&quot;")
.Replace("'", "&#39;");
}
}
}
20 changes: 20 additions & 0 deletions cs/MarkdownTests/Entities/Builders/IBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Markdown.Models.SyntaxTree;
using Markdown.Models.SyntaxTreeModels;

namespace Markdown.Entities.Builders
{
/// <summary>
/// Преобразует абстрактное синтаксическое дерево (AST) в текст с новой разметкой.
/// </summary>
/// <param name="tree">Абстрактное синтаксическое дерево для преобразования</param>
/// <returns>Текст с измененной разметкой</returns>
public interface IBuilder
{
string Build(ISyntaxTree tree);
}
}
46 changes: 46 additions & 0 deletions cs/MarkdownTests/Entities/Converters/Converter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Markdown.Entities.Builders;
using Markdown.Entities.Parsers;
using Markdown.Entities.Renderers;
using Markdown.Models.SyntaxTree;

namespace Markdown.Entities.Converters
{
/// <summary>
/// Выполняет полный цикл преобразования одного языка разметки в другой.
/// Координирует работу токенизатора и билдера.
/// Конструктор позволяет определить желаемый билдер и токенизатор под текущую задачу, например
/// в нашем случае токенизатор умеющий работать с markdown-разметкой а buidler который умеет строить
/// по синтаксическому дереву исходный текст с html-разметкой
/// </summary>
/// <param name="text">Исходный Markdown-текст</param>
/// <returns>Строка с HTML-разметкой</returns>
/// <remarks>
/// Последовательность преобразований:
/// 1. Токенизация исходного текста
/// 2. Построение абстрактного синтаксического дерева (AST)
/// 3. Рендеринг AST в HTML-формат
/// </remarks>
public class Converter : IConverter
{
public IBuilder Builder { get; }
public ITokenizer Tokenizer { get; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно сделать приватными readonly полями, думаю конвертер не подразумевает отдельное их использование в обход своей логики

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ok


public Converter(IBuilder builder, ITokenizer tokenizer)
{
Builder = builder;
Tokenizer = tokenizer;
}

public string Convert(string text)
{
if (Builder != null && Tokenizer != null)
{
var tokens = Tokenizer.Tokenize(text);
var ast = new SyntaxTree(tokens);
var convertedText = Builder.Build(ast);
return convertedText;
}
throw new NullReferenceException();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно более явную ошибку об отсутствии билдера или токенайзера

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ok

}
}
}
12 changes: 12 additions & 0 deletions cs/MarkdownTests/Entities/Converters/IConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

namespace Markdown.Entities.Renderers
{
/// <summary>
/// Интерфейс для класса-конвертера который умеет конвертировать один язык разметки в другой, например конвертер из Markdown в HTML
/// <param name="text">Исходный текст с определенным языком разметки который хотим преобразовать</param>
/// </summary>
public interface IConverter
{
string Convert(string text);
}
}
19 changes: 19 additions & 0 deletions cs/MarkdownTests/Entities/Tokenizers/ITokenizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Markdown.Models;

namespace Markdown.Entities.Parsers
{
/// <summary>
/// Интерфейс для класса который будет заниматься преобразованием исходного текста в список токенов для дальнейшего построения синтаксического дерева.
/// Умеет преобразовывать как целый текст в набор токенов, так и обрабатывать коллекции отдельных строк превращая их в список токенов
/// </summary>
public interface ITokenizer
{
List<Token> Tokenize(string text);

List<string> TextToLines(string text);

List<Token> TokenizeLines(IEnumerable<string> lines);

List<Token> TokenizeLine(string line);
Comment on lines +13 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Методы нигде не используются, думаю они не должны торчать наружу

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

++

}
}
Loading