Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 13 additions & 0 deletions Markdown/Block.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Markdown
{
internal class Block(List<Token> tokens)
{
public List<Token> tokens = tokens;
}
}
215 changes: 215 additions & 0 deletions Markdown/EmphasisProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
using System.Collections.Generic;

namespace Markdown
{
internal sealed class EmphasisProcessor : ITokenProcessor
{
public List<Token> Process(List<Token> tokens)
{

int n = tokens.Count;
var pair = new int[n];
for (int i = 0; i < n; i++) pair[i] = -1;

int lastOpenEm = -1;
int lastOpenStrong = -1;

bool isEmOpenInsideWord = false;
bool isStrongOpenInsideWord = false;
bool isInsideWord = false;

int lastDigit = -1;

for(int i = 0; i < n; i++) // первый проход по массиву токенов, делаем первоначальный матчинг пар
{
if (tokens[i].Type == TokenType.Text ||
tokens[i].Type == TokenType.Digit)
{
isInsideWord = true;
}
if (tokens[i].Type == TokenType.Digit)
{
lastDigit = i;
lastOpenEm = -1;
lastOpenStrong = -1;
isEmOpenInsideWord = false;
isStrongOpenInsideWord = false;
}
if (tokens[i].Type == TokenType.Whitespace)
{
isInsideWord = false;
if (isEmOpenInsideWord)
{
isEmOpenInsideWord = false;
lastOpenEm = -1;
}
if (isStrongOpenInsideWord)
{
isStrongOpenInsideWord = false;
lastOpenStrong = -1;
}
continue;
}
if (tokens[i].Type == TokenType.Digit)
{
lastOpenEm = -1;
lastOpenStrong = -1;
}
if (tokens[i].Type == TokenType.Underscore)
{
if (lastOpenEm != -1 && tokens[i].CanClose)
{
pair[lastOpenEm] = i;
pair[i] = lastOpenEm;
lastOpenEm = -1;
continue;
}
if(tokens[i].CanOpen)
{
lastOpenEm = i;
isEmOpenInsideWord = isInsideWord;
continue;
}
}
if (tokens[i].Type == TokenType.DoubleUnderscore)
{
if (lastOpenStrong != -1 && tokens[i].CanClose)
{
pair[lastOpenStrong] = i;
pair[i] = lastOpenStrong;
lastOpenStrong = -1;
continue;
}
if (tokens[i].CanOpen)
{
lastOpenStrong = i;
isStrongOpenInsideWord = isInsideWord;
continue;
}
}
}
// обрабатывает различные неучтённые при первом проходе ситуации
bool isEmOpen = false;
bool isEmInInterval = false;
bool isStrongInInterval = false;
bool isStrongOpen = false;
int lastEmPair = -1;
int lastStrongPair = -1;
for (int i = 0; i < n; i++)
{
if (pair[i] == -1)
{
isEmInInterval |= tokens[i].Type == TokenType.Underscore &&
isStrongOpen;
isStrongInInterval |= tokens[i].Type == TokenType.DoubleUnderscore &&
isEmOpen;
continue;
}
if (tokens[i].Type == TokenType.Underscore)
{
bool isOpening = pair[i] > i;
isEmOpen = isOpening;
if (isOpening)
{
lastEmPair = i;
isStrongInInterval = false;
}
else if (isStrongOpen && lastEmPair < lastStrongPair )
{

pair[pair[lastStrongPair]] = -1;
pair[lastStrongPair] = -1;
pair[pair[i]] = -1;
pair[i] = -1;
isEmOpen = false;
isStrongOpen = false;
}
else if (isStrongInInterval || Math.Abs(i - pair[i]) == 1)
{
pair[pair[i]] = -1;
pair[i] = -1;
isEmOpen = false;
}
}
else
{
bool isOpening = pair[i] > i;
isStrongOpen = isOpening;
if (isOpening)
{
lastStrongPair = i;
isEmInInterval = false;
}
else if (isEmOpen && lastEmPair > lastStrongPair)
{
pair[pair[lastEmPair]] = -1;
pair[lastEmPair] = -1;
pair[pair[i]] = -1;
pair[i] = -1;
isEmOpen = false;
isStrongOpen = false;
}
else if (isEmInInterval || Math.Abs(i - pair[i]) == 1)
{
pair[pair[i]] = -1;
pair[i] = -1;
isStrongOpen = false;
}
}
}

// обрабатывает ситуацию "_пример __теста__ пример_" -> "<em>пример __теста__ пример</em>"
isEmOpen = false;
for(int i = 0; i < n; i++)
{
if (pair[i] == -1)
{
continue;
}
if (tokens[i].Type == TokenType.Underscore)
{
isEmOpen = pair[i] > i;
}
else
{
if (isEmOpen)
{
pair[pair[i]] = -1;
pair[i] = -1;
}
}
}


var result = new List<Token>(n);
for (int i = 0; i < n; i++)
{
var t = tokens[i];

if (t.Type == TokenType.Underscore || t.Type == TokenType.DoubleUnderscore)
{

if (pair[i] != -1)
{
bool isOpening = pair[i] > i;
if (t.Type == TokenType.DoubleUnderscore)
result.Add(new Token(TokenType.Tag, false, false, isOpening ? "<strong>" : "</strong>"));
else
result.Add(new Token(TokenType.Tag, false, false, isOpening ? "<em>" : "</em>"));
continue;
}
else
{
result.Add(new Token(TokenType.Text, false, false, t.Text));
continue;
}

}

result.Add(t);
}

return result;
}
}
}
37 changes: 37 additions & 0 deletions Markdown/EscapeProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Markdown
{
internal sealed class EscapeProcessor : ITokenProcessor
{
public List<Token> Process(List<Token> tokens)
{
var result = new List<Token>();
for (int i = 0; i < tokens.Count; i++)
{
var t = tokens[i];
if (t.Type != TokenType.Backslash)
{
result.Add(t);
continue;
}
if(i == tokens.Count - 1 ||
(tokens[i + 1].Type != TokenType.DoubleUnderscore &&
tokens[i + 1].Type != TokenType.Underscore &&
tokens[i + 1].Type != TokenType.Backslash &&
tokens[i+1].Type != TokenType.Grid))
{
result.Add(new Token(TokenType.Text, false, false, "\\"));
continue;
}

result.Add(new Token(TokenType.Text, false, false, tokens[i+1].Text));
i += 1;
}

return result;
}
}
}
41 changes: 41 additions & 0 deletions Markdown/HeaderProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Markdown
{
internal class HeaderProcessor : IBlockProcessor
{
public List<Block> Process(List<Block> blocks)
{
var result = new List<Block>();
foreach(var block in blocks)
{
result.Add(new Block(new List<Token>()));
for (int i = 0; i < block.tokens.Count; i++) {
if (block.tokens[i].Type == TokenType.NewLine)
{
result.Add(new Block(new List<Token>()));
}
else if(block.tokens[i].Type == TokenType.Grid &&
i != block.tokens.Count -1 &&
block.tokens[i+1].Type == TokenType.Whitespace &&
result[result.Count - 1].tokens.Count == 0)
{
result[result.Count - 1].tokens.Add(new Token(TokenType.Tag, false, false, "<h1>"));
i++; // пропуск пробела
}
else
{
result[result.Count - 1].tokens.Add(block.tokens[i]);
}
}
}
for(int block = 0; block < result.Count; block++)
{
if (result[block].tokens[0].Type == TokenType.Tag &&
result[block].tokens[0].Text == "<h1>")
{
result[block].tokens.Add(new Token(TokenType.Tag, false, false, "</h1>"));
}
}
return result;
}
}
}
9 changes: 9 additions & 0 deletions Markdown/IBlockProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace Markdown
{
internal interface IBlockProcessor
{
List<Block> Process(List<Block> blocks);
}
}
9 changes: 9 additions & 0 deletions Markdown/ITokenProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace Markdown
{
internal interface ITokenProcessor
{
List<Token> Process(List<Token> tokens);
}
}
40 changes: 40 additions & 0 deletions Markdown/Md.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;

namespace Markdown
{
public class Md
{
private Tokenizer tokenizer = new Tokenizer();
private List<IBlockProcessor> blockProcessors = new List<IBlockProcessor>
{
new HeaderProcessor()
};
private List<ITokenProcessor> tokenProcessors = new List<ITokenProcessor>
{
new EscapeProcessor(),
new EmphasisProcessor()
};
private RenderProcess renderProcess = new RenderProcess();

public string Render(string input)
{
if (input == null) return null;
var tokens = tokenizer.Tokenize(input);
var blocks = new List<Block>();
blocks.Add(new Block(tokens));
foreach (var blockProcesor in blockProcessors)
{
blocks = blockProcesor.Process(blocks);
}
foreach (var tokenProcesor in tokenProcessors)
{
for (int i = 0; i < blocks.Count; i++)
{
blocks[i] = new Block(tokenProcesor.Process(blocks[i].tokens));
}
}

return renderProcess.Render(blocks);
}
}
}
24 changes: 24 additions & 0 deletions Markdown/RenderProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text;

namespace Markdown
{
internal class RenderProcess
{
internal string Render(List<Block> blocks)
{
var sb = new StringBuilder();
for (int i = 0; i < blocks.Count; i++)
{
foreach (var token in blocks[i].tokens)
{
sb.Append(token.Text);
}
if (i != blocks.Count - 1)
{
sb.Append('\n');
}
}
return sb.ToString();
}
}
}
Loading