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

Рыбин Леонид #236

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
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>
32 changes: 32 additions & 0 deletions cs/Markdown/Md.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Markdown.Tokens;
using System.Text;

namespace Markdown;

public class Md
{
public string Render(string text)
{
var textLines = text.Split(Environment.NewLine).ToList();
var sb = new StringBuilder();

foreach (var line in textLines)
{
sb.Append(RenderCurrentString(line));
}

return sb.ToString();
}

private string RenderCurrentString(string line)
{
var tokenizer = new Tokenizer(line);

Choose a reason for hiding this comment

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

У тебя пока не написана логика, поэтому можешь сказать своё видение, какие токены вернуться для этой строки:

# Hello _world,_ Leo!

Copy link
Author

@JavaScriptHaters JavaScriptHaters Nov 25, 2024

Choose a reason for hiding this comment

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

В начале строки мы видим "# " поэтому добавляется HeaderToken(HtmlView = <h1>), потом "_" добавляет ItalicToken(HtmlView = <em>), после мы видим "_", но уже закрывающий, благодаря стэкам мы понимаем какой это тип(закрывающий или открывающий), поэтому добавиться ItalicToken(HtmlView = </em>) и наконец мы дойдём до конца строки, поскольку в стэке будет открывающий HeaderToken, то необходимо добавить HeaderToken(HtmlView = </h1>).

Copy link

@SlavikGh0st SlavikGh0st Nov 25, 2024

Choose a reason for hiding this comment

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

Так-с, т.е. в этой строке нам придет список токенов, условно говоря, "какой тэг и в какой позиции".
Я вижу тут 2 проблемы:

  1. Если вдруг понадобиться поддержать тэг <h2> (у него признак окончания тоже конец строки) - такой алгоритм позволит это сделать?
  2. Могут возникнуть сложности при сборке итоговой html-строки из-за смещения позиций: т.е. ты запомнил, что в позиции 9 должен начаться курсив, а в итоговой строке это уже не позиция 9, т.к. "# " мы заменили на "<h1>".

return GenerateHtml(line, tokenizer.TokenizeLine());
}

private string GenerateHtml(string line, List<ITokenPosition> Tokens)
{
// TODO some logic
throw new NotImplementedException();
}
}
4 changes: 4 additions & 0 deletions cs/Markdown/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using Markdown;

Md md = new Md();
md.Render("text \n\n\n text123");
21 changes: 21 additions & 0 deletions cs/Markdown/Tokenizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Markdown.Tokens;

namespace Markdown;

public class Tokenizer
{
private Stack<BoldToken> boldTokens;
private Stack<ItalicToken> italicTokens;

Choose a reason for hiding this comment

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

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


public Tokenizer(string line)
{
// TODO some logic
throw new NotImplementedException();
}

public List<ITokenPosition> TokenizeLine()
{
// TODO some logic
throw new NotImplementedException();
}
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tokens/BoldToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Markdown.Tokens;

public class BoldToken : PairToken
{
public override string MdView => "__";
public override string HtmlTagOpen => "<strong>";
public override string HtmlTagClose => "</strong>";
}
7 changes: 7 additions & 0 deletions cs/Markdown/Tokens/EscapeToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Markdown.Tokens;

public class EscapeToken : SingleToken
{
public override string MdView => """\""";
public override string HtmlTagOpen => "";
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tokens/HeaderToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Markdown.Tokens;

public class HeaderToken : PairToken
{
public override string MdView => "# ";
public override string HtmlTagOpen => "<h1>";
public override string HtmlTagClose => "</h1>";
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tokens/ITokenPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Markdown.Tokens;

public interface ITokenPosition
{
public string MdView { get; }
public int Position { get; set; }
public string HtmlView { get; }
}
8 changes: 8 additions & 0 deletions cs/Markdown/Tokens/ItalicToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Markdown.Tokens;

public class ItalicToken : PairToken
{
public override string MdView => "_";
public override string HtmlTagOpen => "<em>";
public override string HtmlTagClose => "</em>";
}
12 changes: 12 additions & 0 deletions cs/Markdown/Tokens/PairToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Markdown.Tokens;

public abstract class PairToken : ITokenPosition
{
public abstract string MdView { get; }
public abstract string HtmlTagOpen { get; }
public abstract string HtmlTagClose { get; }
public string HtmlView => IsClosed ? HtmlTagOpen : HtmlTagClose;

public bool IsClosed { get; set; }
public int Position { get; set; }
}

Choose a reason for hiding this comment

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

Как мне кажется, у тебя перемешалась логика токена и тэга:
Класс тэга должен содержать в себе символ тэга, тип, открывающий/закрывающий элемент html.
Класс токена должен содержать в себе исходную строку, преобразованную строку, возможно, позицию.

Идея такая - ты создаешь парсер со списком тэгов, которые он должен обрабатывать/искать, а потом вызываешь метод, в котором передаешь текст, а он тебе возвращает найденные токены. В найденных токенах нас интересует уже готовый "преобразованный" текст.

Copy link
Author

Choose a reason for hiding this comment

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

Я специально не разделял тэг и токен, потому что я вижу работу парсера так:
Парсер идёт по строке, парсер увидел что символ или группа символов подходят под какой-то тип токена (заголовок, курсив и так далее), после этого он добавляет этот токен в общий список токенов(метод Tokenizer), а после при генерации HTML(метод GenerateHtml) токены будут заменяться на тэги, при этом каждый токен уже сам знает на что его заменить и где его заменить. Если же разделить тэги и токены, то процесс замены токена на тэг усложниться, так как появиться дополнительная логика в методе генерации HTML.

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

public abstract class SingleToken : ITokenPosition
{
public abstract string MdView { get; }
public abstract string HtmlTagOpen { get; }

public string HtmlView => HtmlTagOpen;
public int Position { get; set; }
}
16 changes: 16 additions & 0 deletions cs/MarkdownTests/MarkdownTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace MarkdownTests
{
public class Tests
{
[SetUp]
public void Setup()
{
}

[Test]
public void Test1()
{
Assert.Pass();
}
}
}
28 changes: 28 additions & 0 deletions cs/MarkdownTests/MarkdownTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<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="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>
<ProjectReference Include="..\Markdown\Markdown.csproj" />
</ItemGroup>

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

</Project>
19 changes: 17 additions & 2 deletions cs/clean-code.sln
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio Version 17
VisualStudioVersion = 17.12.35514.174 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chess", "Chess\Chess.csproj", "{DBFBE40E-EE0C-48F4-8763-EBD11C960081}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlDigit", "ControlDigit\ControlDigit.csproj", "{B06A4B35-9D61-4A63-9167-0673F20CA989}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdown", "Markdown\Markdown.csproj", "{2A88730A-1C3F-42B6-AD70-FF890AEAC047}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownTests", "MarkdownTests\MarkdownTests.csproj", "{5928AADB-6ACE-429E-B801-BE58A76299AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,5 +31,16 @@ Global
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.Build.0 = Release|Any CPU
{2A88730A-1C3F-42B6-AD70-FF890AEAC047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A88730A-1C3F-42B6-AD70-FF890AEAC047}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A88730A-1C3F-42B6-AD70-FF890AEAC047}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A88730A-1C3F-42B6-AD70-FF890AEAC047}.Release|Any CPU.Build.0 = Release|Any CPU
{5928AADB-6ACE-429E-B801-BE58A76299AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5928AADB-6ACE-429E-B801-BE58A76299AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5928AADB-6ACE-429E-B801-BE58A76299AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5928AADB-6ACE-429E-B801-BE58A76299AC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
3 changes: 3 additions & 0 deletions cs/clean-code.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=13FAEF5AD10371439A650EAFEB60E612/Categories/=Imported_002010_002E10_002E2016/@EntryIndexedValue">Imported 10.10.2016</s:String>
Expand Down