-
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
Смышляев Дмитрий #231
base: master
Are you sure you want to change the base?
Смышляев Дмитрий #231
Changes from 31 commits
e26d15e
459f8f4
350c559
4a2abdb
7889590
8a31839
9900175
65fc929
9b750df
7f664cd
c8fcea8
302aa2f
620fb70
cbb6061
110c4d2
5408bec
2727bca
7fc6935
8446c91
d22da48
d1d8413
5d586ba
fec4199
fdd4a28
676fd39
680e394
25288d6
df27307
43b7116
3771ebd
647e1ff
fac2a80
816efd4
fec2be8
7664345
d0ff733
89f2a8f
5ad9ed2
945a4a9
c33f29b
fdd61ec
9cf741a
d902ab6
69653bc
2aee82e
d36920c
a4dae45
34760cc
81b9907
05b2a1a
992df7a
88d2842
b3a605c
de49690
ed1043c
245b5c6
81b7300
7650424
03f7791
764838a
c78f04d
0b33846
c946f8d
2b6337a
af53027
36db829
7177671
1bb8d65
de2fa80
b1c321f
de913c7
0cfad9e
ff0d4c1
06f5619
1f3cd85
e462015
6d1b752
d9f6d32
1f5429a
c36f843
6323061
d51a496
b8c92f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Markdown.Parser.Nodes; | ||
|
||
namespace Markdown.Generator; | ||
|
||
public class HTMLGenerator | ||
{ | ||
|
||
public string GenerateHTML(Node astRoot) | ||
{ | ||
/* Do magic with ast root */ | ||
return "<h1>Hello world</h1>"; | ||
} | ||
} |
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Markdown.Parser.Nodes; | ||
|
||
public class Node(NodeType nodeType, int consumed) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Опять Data class) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Не понимаю, за что отвечает |
||
{ | ||
public int Consumed { get; } = consumed; | ||
public NodeType NodeType { get; } = nodeType; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Markdown.Parser.Nodes; | ||
|
||
public enum NodeType | ||
{ | ||
ITALIC, | ||
BOLD, | ||
TEXT | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace Markdown.Parser.Nodes; | ||
|
||
public class TagNode(NodeType nodeType, List<Node> children, int consumed) : Node(nodeType, consumed) | ||
{ | ||
public List<Node> Children { get; } = children; | ||
|
||
public TagNode(NodeType nodeType, Node child, int consumed) | ||
:this(nodeType, [child], consumed) | ||
{ } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using Markdown.Tokenizer.Tokens; | ||
|
||
namespace Markdown.Parser.Nodes; | ||
|
||
public class TextNode(int start, int consumed, List<Token> source) : Node(NodeType.TEXT, consumed) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А вот тут точно нарушение SRP и всего вместе взятого (или я не понимаю). Почему сюда весь source закидывается?.. Почему не только нужные токены? |
||
{ | ||
private readonly Lazy<Token> firstToken = new(source.Skip(start).First); | ||
private readonly Lazy<Token> lastToken = new(source.Skip(start).Take(consumed).Last); | ||
private readonly Lazy<List<Token>> tokens = new(source.Skip(start).Take(consumed).ToList); | ||
|
||
public Token Last => lastToken.Value; | ||
public Token First => firstToken.Value; | ||
public List<Token> Tokens => tokens.Value; | ||
public string Text => Tokens.ToText(); | ||
} |
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, int begin = 0) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
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, int begin = 0) | ||
{ | ||
throw new NotImplementedException(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А хде... |
||
} | ||
} |
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, int begin = 0) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
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, int begin = 0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Markdown.Parser.Nodes; | ||
using Markdown.Parser.Rules.Tools; | ||
using Markdown.Tokenizer.Tokens; | ||
|
||
namespace Markdown.Parser.Rules; | ||
|
||
public class ItalicRule : IParsingRule | ||
{ | ||
private readonly List<IParsingRule> defaultPattern = | ||
[ | ||
new PatternRule(TokenType.UNDERSCORE), | ||
new TextRule(), | ||
new PatternRule(TokenType.UNDERSCORE), | ||
]; | ||
|
||
private readonly List<IParsingRule> innerTagPattern = | ||
[ | ||
new PatternRule(TokenType.UNDERSCORE), | ||
new PatternRule(TokenType.WORD), | ||
new PatternRule(TokenType.UNDERSCORE), | ||
]; | ||
|
||
public Node? Match(List<Token> tokens, int begin = 0) | ||
{ | ||
var pattern = ChoosePattern(tokens, begin); | ||
var match = tokens.MatchPattern(pattern, begin); | ||
|
||
if (match.Count != pattern.Count) return null; | ||
if (match.Second() is not TextNode textNode) return null; | ||
|
||
var endWithWord = textNode.Last.TokenType == TokenType.WORD; | ||
var startWithWord = textNode.First.TokenType == TokenType.WORD; | ||
|
||
return startWithWord && endWithWord ? BuildNode(textNode) : null; | ||
} | ||
|
||
private List<IParsingRule> ChoosePattern(List<Token> tokens, int begin = 0) | ||
{ | ||
if (begin != 0 && tokens[begin - 1].TokenType == TokenType.WORD) | ||
return innerTagPattern; | ||
return defaultPattern; | ||
} | ||
|
||
private static TagNode BuildNode(TextNode textNode) | ||
=> new(NodeType.ITALIC, textNode, textNode.Consumed + 2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я бы не завязывался на There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. И как будто есть смысл вынести такой Factory метод в сами ноды, как думаешь? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using Markdown.Parser.Nodes; | ||
using Markdown.Tokenizer.Tokens; | ||
|
||
namespace Markdown.Parser.Rules; | ||
|
||
public class PatternRule(List<TokenType> pattern) : IParsingRule | ||
{ | ||
public PatternRule(TokenType tokenType) | ||
: this([tokenType]) | ||
{ } | ||
|
||
public Node? Match(List<Token> tokens, int begin = 0) | ||
{ | ||
if (pattern.Count == 0) return null; | ||
if (tokens.Count - begin < pattern.Count) return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: почему думаешь, что лучше вернуть null, чем выкинуть Exception? |
||
|
||
var isMatched = tokens | ||
.Skip(begin).Take(pattern.Count).Zip(pattern) | ||
.All(pair => pair.First.TokenType == pair.Second); | ||
return !isMatched ? null : new TextNode(begin, pattern.Count, tokens); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Markdown.Parser.Nodes; | ||
using Markdown.Tokenizer.Tokens; | ||
|
||
namespace Markdown.Parser.Rules; | ||
|
||
public class TextRule : IParsingRule | ||
{ | ||
public Node? Match(List<Token> tokens, int begin = 0) | ||
{ | ||
var textLength = tokens.Skip(begin).TakeWhile(IsText).Count(); | ||
return textLength == 0 ? null : new TextNode(begin, textLength, tokens); | ||
} | ||
|
||
private static bool IsText(Token token) | ||
=> token.TokenType is TokenType.WORD or TokenType.SPACE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Number? New Line? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Markdown.Parser.Nodes; | ||
using Markdown.Tokenizer.Tokens; | ||
|
||
namespace Markdown.Parser.Rules.Tools; | ||
|
||
public static class ListMatchExtensions | ||
{ | ||
public static List<Node> MatchPattern(this List<Token> tokens, List<IParsingRule> pattern, int begin = 0) | ||
{ | ||
List<Node> nodes = []; | ||
|
||
foreach (var patternRule in pattern) | ||
{ | ||
var node = patternRule.Match(tokens, begin); | ||
if (node is null) return []; | ||
nodes.Add(node); begin += node.Consumed; | ||
} | ||
return nodes; | ||
} | ||
|
||
public static List<Node> KleenStarMatch(this List<Token> tokens, IParsingRule pattern, int begin = 0) | ||
{ | ||
List<Node> nodes = []; | ||
while (true) | ||
{ | ||
var node = pattern.Match(tokens, begin); | ||
if (node is null) return nodes; | ||
begin += node.Consumed; nodes.Add(node); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. перенос |
||
} | ||
} | ||
|
||
public static Node? FirstMatch(this List<Token> tokens, List<IParsingRule> patterns, int begin = 0) | ||
{ | ||
var match = patterns | ||
.Select(rule => rule.Match(tokens, begin)) | ||
.FirstOrDefault(node => node is not null, null); | ||
return match; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace Markdown.Parser.Rules.Tools; | ||
|
||
public static class ListOrderExtensions | ||
{ | ||
public static T? Second<T>(this List<T> list) | ||
=> list.Count < 2 ? default : list[1]; | ||
|
||
public static T? Third<T>(this List<T> list) | ||
=> list.Count < 3 ? default : list[2]; | ||
} |
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); | ||
} | ||
} |
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"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Советы категории Б: можешь вынести эту строчку в константу, чтобы потом, при добавлении новых примеров, можно было легко между ними переключаться :)
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
} | ||
} |
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 WordScanner() | ||
]; | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Перенеси на новую строчку плиз, глазкам больно ахаха There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ахахахаха, ладно перенесу |
||
} | ||
return tokenList; | ||
} | ||
} |
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); | ||
} |
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); | ||
} |
This comment was marked as resolved.
Sorry, something went wrong.
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.
Согласен: сделаю, как начну генераторы пилить )