Support Markdown lists

This commit is contained in:
Alberto Venturini 2021-07-11 14:58:58 +02:00
parent 301eaaf98d
commit 90f84950d2
24 changed files with 307 additions and 88 deletions

View file

@ -46,9 +46,9 @@ public class GrammarGraphMLParser implements GraphMLParser {
}
private Node processNodeElem(final ParseTree nodeElem) {
final String id = extractAttribute(nodeElem.child("tag:node"), "id");
final String id = extractAttribute(nodeElem.getChild("tag:node"), "id");
final Map<String, String> data = nodeElem.child("content").getChildren()
final Map<String, String> data = nodeElem.getChild("content").getChildren()
.stream()
.map(this::extractDataElemContent)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
@ -57,11 +57,11 @@ public class GrammarGraphMLParser implements GraphMLParser {
}
private Edge processEdgeElem(final ParseTree edgeElem) {
final String id = extractAttribute(edgeElem.child("tag:edge"), "id");
final String source = extractAttribute(edgeElem.child("tag:edge"), "source");
final String target = extractAttribute(edgeElem.child("tag:edge"), "target");
final String id = extractAttribute(edgeElem.getChild("tag:edge"), "id");
final String source = extractAttribute(edgeElem.getChild("tag:edge"), "source");
final String target = extractAttribute(edgeElem.getChild("tag:edge"), "target");
final Map<String, String> data = edgeElem.child("content").getChildren()
final Map<String, String> data = edgeElem.getChild("content").getChildren()
.stream()
.map(this::extractDataElemContent)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
@ -70,16 +70,16 @@ public class GrammarGraphMLParser implements GraphMLParser {
}
private Map.Entry<String, String> extractDataElemContent(final ParseTree dataElem) {
final var attribute = extractAttribute(dataElem.child("tag:data"), "key");
final var content = dataElem.child(1).getText();
final var attribute = extractAttribute(dataElem.getChild("tag:data"), "key");
final var content = dataElem.getChild(1).getText();
return Map.entry(attribute, content);
}
private String extractAttribute(final ParseTree tag, final String attributeKey) {
return tag.child("attributes").getChildren()
return tag.getChild("attributes").getChildren()
.stream()
.filter(a -> a.child(0).getText().equals(attributeKey))
.map(a -> a.child(1).getText())
.filter(a -> a.getChild(0).getText().equals(attributeKey))
.map(a -> a.getChild(1).getText())
.findFirst()
.orElseThrow();
}

View file

@ -7,21 +7,68 @@ import static com.albertoventurini.parsley.grammar.rules.Rules.*;
public class MarkdownGrammar extends Grammar {
private final char NEWLINE = '\n';
private final char BOLD_DELIMITER = '*';
private final char ITALIC_DELIMITER = '_';
private final Rule h1 = sequence(
character('#'),
takeWhile(c -> c != '\n')
);
takeWhile(c -> c != NEWLINE).as("text")
).as("h1");
private final Rule h2 = sequence(
string("##"),
takeWhile(c -> c != NEWLINE).as("text")
).as("h2");
private final Rule h3 = sequence(
string("###"),
takeWhile(c -> c != NEWLINE).as("text")
).as("h3");
private final Rule headers = sequence(oneOf(h3, h2, h1), zeroOrMore(character(NEWLINE)));
private final Rule plainText = takeWhile(c ->
c != NEWLINE
&& c != BOLD_DELIMITER
&& c != ITALIC_DELIMITER
).as("text");
private final Rule bold = sequence(
character(BOLD_DELIMITER),
takeWhile(c -> c != NEWLINE && c != BOLD_DELIMITER).as("bold"),
character(BOLD_DELIMITER)
).as("boldWrapper");
private final Rule italic = sequence(
character(ITALIC_DELIMITER),
takeWhile(c -> c != NEWLINE && c != ITALIC_DELIMITER).as("italic"),
character(ITALIC_DELIMITER)
).as("italicWrapper");
private final Rule text = oneOf(
bold,
italic,
plainText);
private final Rule paragraph = sequence(
character('\n'),
takeWhile(c -> c != '\n')
);
oneOrMore(text).as("paragraph"),
zeroOrMore(character(NEWLINE))
).as("paragraphWrapper");
private final Rule element = oneOf(h1, paragraph);
private final Rule listItem = sequence(
oneOf(string("* "), string("- ")),
takeWhile(c -> c != NEWLINE).as("listItem"),
character(NEWLINE)
).as("listItemWrapper");
private final Rule elements = sequence(element);
private final Rule list = sequence(oneOrMore(listItem).as("list"), zeroOrMore(character(NEWLINE)));
private final Rule document = zeroOrMore(elements);
private final Rule element = oneOf(headers, list, paragraph);
private final Rule document = zeroOrMore(element);
@Override
public Rule startRule() {
@ -32,4 +79,9 @@ public class MarkdownGrammar extends Grammar {
protected Rule commentRule() {
return null;
}
@Override
protected boolean whitespace(final char c) {
return c != '\n' && Character.isWhitespace(c);
}
}

View file

@ -1,13 +0,0 @@
package com.albertoventurini.parsley.examples.graphml;
import com.albertoventurini.parsley.examples.markdown.MarkdownGrammar;
import org.junit.jupiter.api.Test;
public class MarkdownGrammarTest {
@Test
public void markdownGrammar_withH1AndParagraph_shouldParse() {
final MarkdownGrammar grammar = new MarkdownGrammar();
}
}

View file

@ -0,0 +1,73 @@
package com.albertoventurini.parsley.examples.markdown;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MarkdownGrammarTest {
private final MarkdownGrammar grammar = new MarkdownGrammar();
@Test
public void shouldParseH1() {
final var parseResult = grammar.parse("# hello world");
assertTrue(parseResult.isPresent());
assertEquals("# hello world", parseResult.get().getText());
assertTrue(parseResult.get().getFirstDescendantByName("h1").isPresent());
assertEquals("# hello world", parseResult.get().getFirstDescendantByName("h1").get().getText());
assertTrue(parseResult.get().getFirstDescendantByName("text").isPresent());
assertEquals("hello world", parseResult.get().getFirstDescendantByName("text").get().getText());
}
@Test
public void shouldParseH2() {
final var parseResult = grammar.parse("## hello world");
assertTrue(parseResult.isPresent());
assertEquals("## hello world", parseResult.get().getText());
assertTrue(parseResult.get().getFirstDescendantByName("h2").isPresent());
assertEquals("## hello world", parseResult.get().getFirstDescendantByName("h2").get().getText());
assertNotNull(parseResult.get().getFirstDescendantByName("text"));
assertTrue(parseResult.get().getFirstDescendantByName("text").isPresent());
assertEquals("hello world", parseResult.get().getFirstDescendantByName("text").get().getText());
}
@Test
public void shouldParseParagraph() {
final var parseResult = grammar.parse("hello world");
assertTrue(parseResult.isPresent());
assertEquals("hello world", parseResult.get().getText());
assertTrue(parseResult.get().getFirstDescendantByName("paragraph").isPresent());
assertEquals("hello world", parseResult.get().getFirstDescendantByName("paragraph").get().getText());
}
@Test
public void shouldParseParagraphWithBoldText() {
final var parseResult = grammar.parse("hello world *bold text* normal text");
assertTrue(parseResult.isPresent());
assertTrue(parseResult.get().getFirstDescendantByName("paragraph").isPresent());
assertEquals(3, parseResult.get().getFirstDescendantByName("paragraph").get().getChildren().size());
assertEquals("bold text", parseResult.get().getFirstDescendantByName("bold").get().getText());
}
@Test
public void shouldParseList() {
final var parseResult = grammar.parse("hello world\n" +
"\n" +
"* first item\n" +
"* second item\n" +
"");
assertTrue(parseResult.isPresent());
assertTrue(parseResult.get().getFirstDescendantByName("paragraph").isPresent());
assertEquals("hello world", parseResult.get().getFirstDescendantByName("paragraph").get().getText());
assertTrue(parseResult.get().getFirstDescendantByName("list").isPresent());
assertEquals(2, parseResult.get().getFirstDescendantByName("list").get().getChildren().size());
assertEquals("first item", parseResult.get().getFirstDescendantByName("list").get().getChildren().get(0).getFirstDescendantByName("listItem").get().getText());
}
}