Created 2 modules: parsley-grammar and parsley-examples
This commit is contained in:
parent
6be7f76372
commit
c9c1454f90
29 changed files with 278032 additions and 57 deletions
83
parsley-examples/pom.xml
Normal file
83
parsley-examples/pom.xml
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>parsley-examples</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>parsley-examples</name>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.albertoventurini.parsley</groupId>
|
||||||
|
<artifactId>parsley</artifactId>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<url>https://github.com/albertoventurini/parsley</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<antlr.version>4.7.1</antlr.version>
|
||||||
|
<junit.version>5.6.1</junit.version>
|
||||||
|
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||||
|
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||||
|
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||||
|
<maven-shade.plugin.version>3.2.2</maven-shade.plugin.version>
|
||||||
|
<mainClass>com.albertoventurini.parsley.Main</mainClass>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.albertoventurini.parsley</groupId>
|
||||||
|
<artifactId>parsley-grammar</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>11</source>
|
||||||
|
<target>11</target>
|
||||||
|
<release>11</release>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--enable-preview</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>${mainClass}</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Edge {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String source;
|
||||||
|
private final String target;
|
||||||
|
private final Map<String, String> data;
|
||||||
|
|
||||||
|
public Edge(
|
||||||
|
final String id,
|
||||||
|
final String source,
|
||||||
|
final String target,
|
||||||
|
final Map<String, String> data) {
|
||||||
|
this.id = id;
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String source() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String target() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> data() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
import com.albertoventurini.parsley.grammar.Grammar;
|
||||||
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
import com.albertoventurini.parsley.grammar.rules.Rule;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.*;
|
||||||
|
|
||||||
|
public class GrammarGraphMLParser implements GraphMLParser {
|
||||||
|
|
||||||
|
private final Grammar grammar;
|
||||||
|
|
||||||
|
public GrammarGraphMLParser() {
|
||||||
|
|
||||||
|
final Rule comment = sequence(string("<!--"), until("-->")).as("comment");
|
||||||
|
final Rule comments = zeroOrMore(comment).as("comments");
|
||||||
|
|
||||||
|
final Rule xmlHeader = string("<?xml version='1.0' ?>").as("header");
|
||||||
|
|
||||||
|
final Rule attribute = sequence(
|
||||||
|
takeWhile(c -> c != '=' && c != '>'),
|
||||||
|
character('='),
|
||||||
|
character('\''),
|
||||||
|
takeWhile(c -> c != '\''),
|
||||||
|
character('\'')).as("attribute");
|
||||||
|
|
||||||
|
final Function<String, Rule> tagFunc = (s) -> sequence(
|
||||||
|
character('<'),
|
||||||
|
string(s).as("tagName"),
|
||||||
|
zeroOrMore(attribute).as("attributes"),
|
||||||
|
character('>')).as("tag:" + s);
|
||||||
|
|
||||||
|
final Function<String, Rule> closingTagFunc = (s) -> sequence(
|
||||||
|
string("</"),
|
||||||
|
string(s),
|
||||||
|
character('>')).as("closingTag:" + s);
|
||||||
|
|
||||||
|
final BiFunction<String, Rule, Rule> elementFunc = (tagName, content) -> sequence(
|
||||||
|
tagFunc.apply(tagName).as("tag:" + tagName),
|
||||||
|
content.as("content"),
|
||||||
|
closingTagFunc.apply(tagName).as("closingTag:" + tagName));
|
||||||
|
|
||||||
|
final Rule data = elementFunc.apply("data", takeWhile(c -> c != '<')).as("dataElem");
|
||||||
|
|
||||||
|
final Rule node = elementFunc.apply("node", zeroOrMore(data)).as("nodeElem");
|
||||||
|
|
||||||
|
final Rule edge = elementFunc.apply("edge", zeroOrMore(data)).as("edgeElem");
|
||||||
|
|
||||||
|
final Rule graph = sequence(
|
||||||
|
tagFunc.apply("graph"),
|
||||||
|
oneOrMore(node).as("nodes"),
|
||||||
|
oneOrMore(edge).as("edges"),
|
||||||
|
closingTagFunc.apply("graph")).as("graphElem");
|
||||||
|
|
||||||
|
final Rule key = sequence(tagFunc.apply("key"), closingTagFunc.apply("key"))
|
||||||
|
.as("keyElem");
|
||||||
|
|
||||||
|
final Rule graphml = sequence(
|
||||||
|
tagFunc.apply("graphml"),
|
||||||
|
oneOrMore(key).as("keys"),
|
||||||
|
graph,
|
||||||
|
closingTagFunc.apply("graphml")).as("graphmlElem");
|
||||||
|
|
||||||
|
final Rule graphmlFile = sequence(
|
||||||
|
xmlHeader,
|
||||||
|
comments.discard(),
|
||||||
|
graphml).as("graphmlFile");
|
||||||
|
|
||||||
|
grammar = new Grammar(graphmlFile, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphMLParseResult parse(final String filePath) {
|
||||||
|
try {
|
||||||
|
return grammar
|
||||||
|
.parse(Files.readString(Path.of(filePath)))
|
||||||
|
.map(this::processParseTree)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Unable to parse graphml file: " + filePath));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GraphMLParseResult processParseTree(final ParseTree parseTree) {
|
||||||
|
|
||||||
|
final List<Node> nodes = parseTree.getFirstDescendantByName("nodes").map(n ->
|
||||||
|
n.getChildren()
|
||||||
|
.stream()
|
||||||
|
.map(this::processNodeElem)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
).orElseThrow();
|
||||||
|
|
||||||
|
final List<Edge> edges = parseTree.getFirstDescendantByName("edges").map(n ->
|
||||||
|
n.getChildren()
|
||||||
|
.stream()
|
||||||
|
.map(this::processEdgeElem)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
).orElseThrow();
|
||||||
|
|
||||||
|
return new GraphMLParseResult(nodes, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node processNodeElem(final ParseTree nodeElem) {
|
||||||
|
final String id = extractAttribute(nodeElem.child("tag:node"), "id");
|
||||||
|
|
||||||
|
final Map<String, String> data = nodeElem.child("content").getChildren()
|
||||||
|
.stream()
|
||||||
|
.map(this::extractDataElemContent)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
|
return new Node(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Map<String, String> data = edgeElem.child("content").getChildren()
|
||||||
|
.stream()
|
||||||
|
.map(this::extractDataElemContent)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
|
return new Edge(id, source, target, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
return Map.entry(attribute, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractAttribute(final ParseTree tag, final String attributeKey) {
|
||||||
|
return tag.child("attributes").getChildren()
|
||||||
|
.stream()
|
||||||
|
.filter(a -> a.child(0).getText().equals(attributeKey))
|
||||||
|
.map(a -> a.child(1).getText())
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class GraphMLParseResult {
|
||||||
|
|
||||||
|
private final List<Node> nodes;
|
||||||
|
private final List<Edge> edges;
|
||||||
|
|
||||||
|
public GraphMLParseResult(final List<Node> nodes, final List<Edge> edges) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
this.edges = edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Node> nodes() {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Edge> edges() {
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
public interface GraphMLParser {
|
||||||
|
GraphMLParseResult parse(String filePath);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Node {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final Map<String, String> data;
|
||||||
|
|
||||||
|
public Node(final String id, final Map<String, String> data) {
|
||||||
|
this.id = id;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> data() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.albertoventurini.parsley.examples.graphml;
|
||||||
|
|
||||||
|
import com.albertoventurini.parsley.grammar.Grammar;
|
||||||
|
import com.albertoventurini.parsley.grammar.rules.Rule;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.*;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class GraphMLGrammarTest {
|
||||||
|
|
||||||
|
private final Grammar grammar;
|
||||||
|
|
||||||
|
public GraphMLGrammarTest() {
|
||||||
|
final Rule comment = sequence(string("<!--"), until("-->"));
|
||||||
|
final Rule comments = zeroOrMore(comment);
|
||||||
|
|
||||||
|
final Rule xmlHeader = string("<?xml version='1.0' ?>").as("header");
|
||||||
|
|
||||||
|
final Rule attribute = sequence(
|
||||||
|
takeWhile(c -> c != '=' && c != '>'),
|
||||||
|
character('='),
|
||||||
|
character('\''),
|
||||||
|
takeWhile(c -> c != '\''),
|
||||||
|
character('\'')).as("attribute");
|
||||||
|
|
||||||
|
final Function<String, Rule> tagFunc = (s) -> sequence(
|
||||||
|
character('<'),
|
||||||
|
string(s),
|
||||||
|
zeroOrMore(attribute).as("attributes"),
|
||||||
|
character('>'));
|
||||||
|
|
||||||
|
final Function<String, Rule> closingTagFunc = (s) -> sequence(
|
||||||
|
string("</"),
|
||||||
|
string(s),
|
||||||
|
character('>'));
|
||||||
|
|
||||||
|
final BiFunction<String, Rule, Rule> elementFunc = (tagName, content) -> sequence(
|
||||||
|
tagFunc.apply(tagName).as("tag:" + tagName),
|
||||||
|
content.as("content"),
|
||||||
|
closingTagFunc.apply(tagName).as("closingTag:" + tagName));
|
||||||
|
|
||||||
|
final Rule data = elementFunc.apply("data", takeWhile(c -> c != '<')).as("dataElem");
|
||||||
|
|
||||||
|
final Rule node = elementFunc.apply("node", zeroOrMore(data)).as("nodeElem");
|
||||||
|
|
||||||
|
final Rule edge = elementFunc.apply("edge", zeroOrMore(data)).as("edgeElem");
|
||||||
|
|
||||||
|
final Rule graph = sequence(
|
||||||
|
tagFunc.apply("graph"),
|
||||||
|
oneOrMore(node).as("nodes"),
|
||||||
|
oneOrMore(edge).as("edges"),
|
||||||
|
closingTagFunc.apply("graph")).as("graphElem");
|
||||||
|
|
||||||
|
final Rule key = sequence(
|
||||||
|
tagFunc.apply("key"),
|
||||||
|
closingTagFunc.apply("key")).as("keyElem");
|
||||||
|
|
||||||
|
final Rule graphml = sequence(
|
||||||
|
tagFunc.apply("graphml"),
|
||||||
|
oneOrMore(key).as("keys"),
|
||||||
|
graph,
|
||||||
|
closingTagFunc.apply("graphml")).as("graphmlElem");
|
||||||
|
|
||||||
|
final Rule graphmlFile = sequence(xmlHeader, graphml).as("graphmlFile");
|
||||||
|
|
||||||
|
grammar = new Grammar(graphmlFile, comments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void graphmlGrammar_shouldParseGraphmlFile() throws Exception {
|
||||||
|
final var result =
|
||||||
|
grammar.parse(Files.readString(Path.of("src/test/resources/graphml/graphA.graphml")));
|
||||||
|
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void graphmlGrammar_shouldParseLargeGraphmlFile() throws Exception {
|
||||||
|
final var result =
|
||||||
|
grammar.parse(Files.readString(Path.of("src/test/resources/graphml/air-routes-latest.graphml")));
|
||||||
|
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
}
|
||||||
|
}
|
277256
parsley-examples/src/test/resources/graphml/air-routes-latest.graphml
Normal file
277256
parsley-examples/src/test/resources/graphml/air-routes-latest.graphml
Normal file
File diff suppressed because it is too large
Load diff
74
parsley-examples/src/test/resources/graphml/graphA.graphml
Normal file
74
parsley-examples/src/test/resources/graphml/graphA.graphml
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version='1.0' ?>
|
||||||
|
<!-- ******************************************************************************* -->
|
||||||
|
<!-- Sample file containing selected air routes between selected airports. -->
|
||||||
|
<graphml xmlns='http://graphml.graphdrawing.org/xmlns'>
|
||||||
|
<key id='type' for='node' attr.name='type' attr.type='string'></key>
|
||||||
|
<key id='code' for='node' attr.name='code' attr.type='string'></key>
|
||||||
|
<key id='icao' for='node' attr.name='icao' attr.type='string'></key>
|
||||||
|
<key id='desc' for='node' attr.name='desc' attr.type='string'></key>
|
||||||
|
<key id='region' for='node' attr.name='region' attr.type='string'></key>
|
||||||
|
<key id='runways' for='node' attr.name='runways' attr.type='int'></key>
|
||||||
|
<key id='longest' for='node' attr.name='longest' attr.type='int'></key>
|
||||||
|
<key id='elev' for='node' attr.name='elev' attr.type='int'></key>
|
||||||
|
<key id='country' for='node' attr.name='country' attr.type='string'></key>
|
||||||
|
<key id='city' for='node' attr.name='city' attr.type='string'></key>
|
||||||
|
<key id='lat' for='node' attr.name='lat' attr.type='double'></key>
|
||||||
|
<key id='lon' for='node' attr.name='lon' attr.type='double'></key>
|
||||||
|
<key id='author' for='node' attr.name='author' attr.type='string'></key>
|
||||||
|
<key id='date' for='node' attr.name='date' attr.type='string'></key>
|
||||||
|
<key id='dist' for='edge' attr.name='dist' attr.type='int'></key>
|
||||||
|
<key id='labelV' for='node' attr.name='labelV' attr.type='string'></key>
|
||||||
|
<key id='labelE' for='edge' attr.name='labelE' attr.type='string'></key>
|
||||||
|
|
||||||
|
<graph id='routes' edgedefault='directed'>
|
||||||
|
<node id='0'>
|
||||||
|
<data key='labelV'>version</data>
|
||||||
|
<data key='type'>version</data>
|
||||||
|
<data key='code'>0.84</data>
|
||||||
|
<data key='date'>2020-01-30 02:17:18 UTC</data>
|
||||||
|
<data key='author'>Kelvin R. Lawrence</data>
|
||||||
|
<data key='desc'>Air Routes Data - Version: 0.84 Generated: 2020-01-30 02:17:18 UTC; Graph created by Kelvin R. Lawrence; Please let me know of any errors you find in the graph.</data>
|
||||||
|
</node>
|
||||||
|
<node id='1'>
|
||||||
|
<data key='labelV'>airport</data>
|
||||||
|
<data key='type'>airport</data>
|
||||||
|
<data key='code'>ATL</data>
|
||||||
|
<data key='icao'>KATL</data>
|
||||||
|
<data key='city'>Atlanta</data>
|
||||||
|
<data key='desc'>Hartsfield - Jackson Atlanta International Airport</data>
|
||||||
|
<data key='region'>US-GA</data>
|
||||||
|
<data key='runways'>5</data>
|
||||||
|
<data key='longest'>12390</data>
|
||||||
|
<data key='elev'>1026</data>
|
||||||
|
<data key='country'>US</data>
|
||||||
|
<data key='lat'>33.6366996765137</data>
|
||||||
|
<data key='lon'>-84.4281005859375</data>
|
||||||
|
</node>
|
||||||
|
<node id='2'>
|
||||||
|
<data key='labelV'>airport</data>
|
||||||
|
<data key='type'>airport</data>
|
||||||
|
<data key='code'>ANC</data>
|
||||||
|
<data key='icao'>PANC</data>
|
||||||
|
<data key='city'>Anchorage</data>
|
||||||
|
<data key='desc'>Anchorage Ted Stevens</data>
|
||||||
|
<data key='region'>US-AK</data>
|
||||||
|
<data key='runways'>3</data>
|
||||||
|
<data key='longest'>12400</data>
|
||||||
|
<data key='elev'>151</data>
|
||||||
|
<data key='country'>US</data>
|
||||||
|
<data key='lat'>61.1744003295898</data>
|
||||||
|
<data key='lon'>-149.996002197266</data>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
|
||||||
|
<edge id='3742' source='1' target='3'>
|
||||||
|
<data key='labelE'>route</data>
|
||||||
|
<data key='dist'>811</data>
|
||||||
|
</edge>
|
||||||
|
<edge id='3743' source='1' target='4'>
|
||||||
|
<data key='labelE'>route</data>
|
||||||
|
<data key='dist'>214</data>
|
||||||
|
</edge>
|
||||||
|
|
||||||
|
</graph>
|
||||||
|
</graphml>
|
82
parsley-grammar/pom.xml
Normal file
82
parsley-grammar/pom.xml
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>parsley-grammar</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>parsley-grammar</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.albertoventurini.parsley</groupId>
|
||||||
|
<artifactId>parsley</artifactId>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<url>https://github.com/albertoventurini/parsley</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<antlr.version>4.7.1</antlr.version>
|
||||||
|
<junit.version>5.6.1</junit.version>
|
||||||
|
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||||
|
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
|
||||||
|
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||||
|
<maven-shade.plugin.version>3.2.2</maven-shade.plugin.version>
|
||||||
|
<mainClass>com.albertoventurini.parsley.Main</mainClass>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>11</source>
|
||||||
|
<target>11</target>
|
||||||
|
<release>11</release>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--enable-preview</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>${mainClass}</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -1,17 +1,17 @@
|
||||||
package com.albertoventurini.parsley;
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.rules.AnyCharacter;
|
import com.albertoventurini.parsley.grammar.rules.AnyCharacter;
|
||||||
import com.albertoventurini.parsley.rules.MatchCharacter;
|
import com.albertoventurini.parsley.grammar.rules.MatchCharacter;
|
||||||
import com.albertoventurini.parsley.rules.MatchString;
|
import com.albertoventurini.parsley.grammar.rules.MatchString;
|
||||||
import com.albertoventurini.parsley.rules.OneOf;
|
import com.albertoventurini.parsley.grammar.rules.OneOf;
|
||||||
import com.albertoventurini.parsley.rules.OneOrMore;
|
import com.albertoventurini.parsley.grammar.rules.OneOrMore;
|
||||||
import com.albertoventurini.parsley.rules.Rule;
|
import com.albertoventurini.parsley.grammar.rules.Rule;
|
||||||
import com.albertoventurini.parsley.rules.Sequence;
|
import com.albertoventurini.parsley.grammar.rules.Sequence;
|
||||||
import com.albertoventurini.parsley.rules.TakeWhileCharacter;
|
import com.albertoventurini.parsley.grammar.rules.TakeWhileCharacter;
|
||||||
import com.albertoventurini.parsley.rules.UntilString;
|
import com.albertoventurini.parsley.grammar.rules.UntilString;
|
||||||
import com.albertoventurini.parsley.rules.Wrapper;
|
import com.albertoventurini.parsley.grammar.rules.Wrapper;
|
||||||
import com.albertoventurini.parsley.rules.ZeroOrMore;
|
import com.albertoventurini.parsley.grammar.rules.ZeroOrMore;
|
||||||
import com.albertoventurini.parsley.rules.ZeroOrOne;
|
import com.albertoventurini.parsley.grammar.rules.ZeroOrOne;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
|
@ -1,6 +1,6 @@
|
||||||
package com.albertoventurini.parsley;
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.rules.Rule;
|
import com.albertoventurini.parsley.grammar.rules.Rule;
|
||||||
|
|
||||||
public class GrammarContext extends ParseContext {
|
public class GrammarContext extends ParseContext {
|
||||||
private final Rule commentRule;
|
private final Rule commentRule;
|
|
@ -1,4 +1,4 @@
|
||||||
package com.albertoventurini.parsley;
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.albertoventurini.parsley;
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
public class ParseException extends RuntimeException {
|
public class ParseException extends RuntimeException {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.albertoventurini.parsley;
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -20,7 +20,7 @@ public final class Sequence extends Rule {
|
||||||
final int start = ctx.getCursor();
|
final int start = ctx.getCursor();
|
||||||
final List<ParseTree> children = new ArrayList<>(rules.length);
|
final List<ParseTree> children = new ArrayList<>(rules.length);
|
||||||
|
|
||||||
for (final Rule rule : rules) {
|
for (final com.albertoventurini.parsley.grammar.rules.Rule rule : rules) {
|
||||||
if (!ctx.hasNext()) {
|
if (!ctx.hasNext()) {
|
||||||
ctx.setCursor(start);
|
ctx.setCursor(start);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -1,7 +1,7 @@
|
||||||
package com.albertoventurini.parsley.rules;
|
package com.albertoventurini.parsley.grammar.rules;
|
||||||
|
|
||||||
import com.albertoventurini.parsley.GrammarContext;
|
import com.albertoventurini.parsley.grammar.GrammarContext;
|
||||||
import com.albertoventurini.parsley.ParseTree;
|
import com.albertoventurini.parsley.grammar.ParseTree;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
package com.albertoventurini.parsley.grammar;
|
||||||
|
|
||||||
|
import com.albertoventurini.parsley.grammar.rules.Rule;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.character;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.oneOf;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.sequence;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.string;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.takeWhile;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.token;
|
||||||
|
import static com.albertoventurini.parsley.grammar.Grammar.zeroOrMore;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class GrammarTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void xmlTagWithoutAttributes() {
|
||||||
|
final Rule tag = sequence(
|
||||||
|
character('<'),
|
||||||
|
takeWhile(Character::isLetterOrDigit),
|
||||||
|
oneOf(string("/>"), character('>')));
|
||||||
|
|
||||||
|
final Grammar grammar = new Grammar(tag, null);
|
||||||
|
|
||||||
|
final var parseTree1 = grammar.parse("<tag1>");
|
||||||
|
|
||||||
|
assertTrue(parseTree1.isPresent());
|
||||||
|
assertEquals("tag1", parseTree1.get().child(0).getText());
|
||||||
|
|
||||||
|
final var parseTree2 = grammar.parse("<tag1 />");
|
||||||
|
|
||||||
|
assertTrue(parseTree2.isPresent());
|
||||||
|
assertEquals("tag1", parseTree2.get().child(0).getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void xmlTagWithOptionalAttributes() {
|
||||||
|
final Rule token = takeWhile(Character::isLetterOrDigit);
|
||||||
|
|
||||||
|
final Rule attribute = sequence(token, character('='), token);
|
||||||
|
|
||||||
|
final Rule tag = sequence(
|
||||||
|
character('<'),
|
||||||
|
token,
|
||||||
|
zeroOrMore(attribute),
|
||||||
|
oneOf(string("/>"), character('>')));
|
||||||
|
|
||||||
|
final Grammar grammar = new Grammar(tag, null);
|
||||||
|
|
||||||
|
assertTrue(grammar.parse("<tag1>").isPresent());
|
||||||
|
|
||||||
|
final var parseTree1 = grammar.parse("<tag1 id=123>");
|
||||||
|
|
||||||
|
assertTrue(parseTree1.isPresent());
|
||||||
|
assertEquals("tag1", parseTree1.get().child(0).getText());
|
||||||
|
assertEquals("id", parseTree1.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(0) // get the first attribute
|
||||||
|
.child(0) // get the first element in the sequence rule
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
assertEquals("123", parseTree1.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(0) // get the first attribute
|
||||||
|
.child(1) // get the second child in the sequence
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
|
||||||
|
final var parseTree2 = grammar.parse("<tag1 hello=world something=else/>");
|
||||||
|
|
||||||
|
assertTrue(parseTree2.isPresent());
|
||||||
|
assertEquals("tag1", parseTree2.get().child(0).getText());
|
||||||
|
|
||||||
|
assertEquals("hello", parseTree2.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(0) // get the first attribute
|
||||||
|
.child(0) // get the first child in the sequence
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
assertEquals("world", parseTree2.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(0) // get the first attribute
|
||||||
|
.child(1) // get the second child in the sequence
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals("something", parseTree2.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(1) // get the second attribute
|
||||||
|
.child(0) // get the first child in the sequence
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
assertEquals("else", parseTree2.get().child(1) // get the zeroOrMore rule
|
||||||
|
.child(1) // get the second attribute
|
||||||
|
.child(1) // get the second child in the sequence
|
||||||
|
.getText()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void xmlTagWithAttributeSurroundedByQuotes() {
|
||||||
|
final Rule attribute = sequence(
|
||||||
|
takeWhile(c -> c != '='),
|
||||||
|
character('='),
|
||||||
|
character('\''),
|
||||||
|
takeWhile(c -> c != '\''),
|
||||||
|
character('\''));
|
||||||
|
|
||||||
|
final Rule tag = sequence(
|
||||||
|
character('<'),
|
||||||
|
token(),
|
||||||
|
zeroOrMore(attribute),
|
||||||
|
oneOf(string("/>"), character('>')));
|
||||||
|
|
||||||
|
final Grammar grammar = new Grammar(tag, null);
|
||||||
|
|
||||||
|
assertTrue(grammar.parse("<graphml xmlns='http://graphml.graphdrawing.org/xmlns'>").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elementWithoutContent() {
|
||||||
|
final Rule attribute = sequence(
|
||||||
|
takeWhile(c -> c != '=' && c != '>'),
|
||||||
|
character('='),
|
||||||
|
character('\''),
|
||||||
|
takeWhile(c -> c != '\''),
|
||||||
|
character('\''));
|
||||||
|
|
||||||
|
final Function<String, Rule> tagFunc = (s) -> sequence(
|
||||||
|
character('<'),
|
||||||
|
string(s),
|
||||||
|
zeroOrMore(attribute),
|
||||||
|
character('>'));
|
||||||
|
|
||||||
|
final Function<String, Rule> closingTagFunc = (s) -> sequence(
|
||||||
|
string("</"),
|
||||||
|
string(s),
|
||||||
|
string(">"));
|
||||||
|
|
||||||
|
final Rule key = sequence(
|
||||||
|
tagFunc.apply("key"),
|
||||||
|
closingTagFunc.apply("key"));
|
||||||
|
|
||||||
|
final Grammar grammar = new Grammar(key, null);
|
||||||
|
|
||||||
|
assertTrue(grammar.parse("<key id='type' for='node' attr.name='type' attr.type='string'></key>").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9
pom.xml
9
pom.xml
|
@ -4,13 +4,18 @@
|
||||||
|
|
||||||
<groupId>com.albertoventurini.parsley</groupId>
|
<groupId>com.albertoventurini.parsley</groupId>
|
||||||
<artifactId>parsley</artifactId>
|
<artifactId>parsley</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>0.1.0-SNAPSHOT</version>
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>graphino</name>
|
<name>parsley</name>
|
||||||
|
|
||||||
<url>https://github.com/albertoventurini/parsley</url>
|
<url>https://github.com/albertoventurini/parsley</url>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>parsley-grammar</module>
|
||||||
|
<module>parsley-examples</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<antlr.version>4.7.1</antlr.version>
|
<antlr.version>4.7.1</antlr.version>
|
||||||
<junit.version>5.6.1</junit.version>
|
<junit.version>5.6.1</junit.version>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue