-
Notifications
You must be signed in to change notification settings - Fork 300
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
Минеев Максим #243
base: master
Are you sure you want to change the base?
Минеев Максим #243
Conversation
|
||
public class MarkdownToHtmlConverter(ILexer lexer, IParser parser) | ||
{ | ||
public ILexer Lexer { get; } = lexer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
можно сделать private
cs/Markdown/Program.cs
Outdated
{ | ||
Console.WriteLine("Hello, World!"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
кажется нет необходимости и можно убрать
cs/Markdown/Tokens/Token.cs
Outdated
|
||
namespace Markdown.Tokens; | ||
|
||
public abstract class Token(int position) : IToken |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А зачем интерфейс для абстрактного класса? Интерфейс нужен для того чтобы описать поведение, но ведь абстрактный класс тоже это делает
cs/Markdown/Tokens/Token.cs
Outdated
public abstract string Value { get; } | ||
public int Position => position; | ||
public int Length => Value.Length; | ||
public int GetIndexToNextToken() => Position + Length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
нет использований
public List<IToken> Tokenize(string input) | ||
{ | ||
position = 0; | ||
var nestingStack = new Stack<string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Лучше position сделать локальной переменной чтобы была одинаковая логика работы с position, nestingStack, input
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Если position сделать локальной переменной, то возникнет несколько неудобств:
- Придется прокидывать текущую позицию практически во все функции, увеличится список параметров
- Сейчас логика сдвига позиции находится максимально близко к самим "действиям", т.е. как только мы совершили какое-то действие (например, спарсили текст), мы сразу сдвигаем позицию в том же методе парсинга. Если будем передавать position в метод параметром, то нам придется либо передавать его ссылкой (ref), либо возвращать из метода необходимый сдвиг или еще как-то ухищряться, чтобы правильно сдвинуть position
Поэтому мне кажется, что более красиво и аккуратно будет оставить position полем класса, а чтобы сделать логику работы более одинаковой, можно вынести стек тоже полем класса и не передавать его каждый раз параметром (с инпутом так поступить не можем, потому что получем его параметром в методе, вынести инициализацию в конструктор тоже не можем, потому что нарушим контракт метода Tokenize и не сможем получать в конвертере не конкретную реализацию лексера, а интерфейс)
cs/Markdown/MarkdownLexer.cs
Outdated
position += 2; | ||
} | ||
|
||
private bool NextIsDoubleGround(string input) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Здесь напрашивается сделать инпут valueObject'ом и все эти методы перетащить туда
cs/Markdown/MarkdownLexer.cs
Outdated
{ | ||
private int position; | ||
private readonly List<IToken> tokens = []; | ||
private const string DoubleGround = "__"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
заметила что некоторые символы инициализированы дважды (например, DoubleGround в MarkdownLexer и в MarkdownParser). Исходя из этого предложение вынести эти константы в отдельный класс и использовать везде его
stack.Push(Ground); | ||
} | ||
|
||
private void ParseItalicOrBoldAndAdvanceWhenStackHasOne(bool isSingleGround, bool isDoubleGround, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
сейчас реализована такая логика, что мы кладем токен в стек и в зависимости от последующих токенов мы токен из стека можем убрать, что привело к усложнению кода (ParseItalicOrBoldAndAdvanceWhenStackHasOne, ParseItalicOrBoldAndAdvanceWhenStackHasTwo и т.д.).
можем ли мы анализировать последующие символы чтобы сразу добавлять корректные токены?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
На самом деле в стек всегда сразу добавляются корректные токены (открывающие токены в соответствии с разрешенной вложенностью), но трудность заключается в следующем: в процессе лексического анализа нам надо правильно (хотя бы частично) обрабатывать вложенность, чтобы на этапе парсинга получать уже "правильный" набор токенов (___text___
должен парситься лексером в ['__', '_', 'text', '_', '__']
, а не, например, в ['_', '__', 'text', '_', '__']
). Чтобы решать проблемы с вложенностью, я, по аналогии с популярной задачей про валидность скобочной последовательности, собирался просто закидывать в стек открывающие теги, а когда встречаем закрывающий, то достаем открывающий из стека, но из-за того, что тег курсива явялется частью тега полужирного, добавлять и доставать открывающие теги оказалось не так уж "просто" и приходится проверять очень много условий.
Изначально вся логика обработки этой вложенности была написана в ParseItalicOrBoldAndAdvance, но метод вырос до +-150 строк кода и очень глубокой вложенностью if-ов, и, чтобы хоть немного его уменьшить и сделать более декларативным я разнес эту логику по разным функциям, но получились такие страшные названия (называл прямо "в лоб", ровно то, что делает функция, возможно можно было назвать более удачно). В итоге я так и не придумал что-то лучше, чтобы код был более понятным и не нагруженным, мне показалось, что в текущем состоянии функции получились в достаточной мере декларативными, но с очень длинными названиями (от этого избавиться мне не удалось)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется большинство проблем бы решилось и код упростился, если бы в стек клали не строки (двойное или одинарное подчеркивание), а char (только одинарные подчеркивания)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется большинство проблем бы решилось и код упростился, если бы в стек клали не строки (двойное или одинарное подчеркивание), а char (только одинарные подчеркивания)
Возможно, подумаю над этим
cs/Markdown/MarkdownLexer.cs
Outdated
private void ParseHeadingAndAdvance(string input) | ||
{ | ||
if (NextIsSpace(input) && IsStartOfParagraph(input)) tokens.Add(new HeadingToken(position++)); | ||
else tokens.Add(new TextToken(position, "#")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
лучше константу заиспользовать
} | ||
|
||
private void ParseItalicOrBoldAndAdvanceWhenStackHasOne(bool isSingleGround, bool isDoubleGround, | ||
bool isTripleGround, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
вместо булевых флагов можно передавать int
var isDoubleGround = input.NextIsGround(position); | ||
var isTripleGround = input.NextIsDoubleGround(position); | ||
var isSingleGround = !isTripleGround && !isDoubleGround; | ||
if (stack.Count == 0) ParseItalicOrBoldAndAdvanceWhenStackEmpty(isSingleGround, isTripleGround, stack); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
можно заменить на switch
@masssha1308