Skip to content

Commit

Permalink
Update version: Introduce lazy parsers, JSON example, and separate pr…
Browse files Browse the repository at this point in the history
…oject in different submodules
  • Loading branch information
BjoernLoetters committed Jan 28, 2025
1 parent a9e38e2 commit d1c4c86
Show file tree
Hide file tree
Showing 54 changed files with 234 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
title: "Release ${{ github.ref_name }}"
files: |
LICENSE
target/*.jar
core/target/*.jar
34 changes: 34 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.bjoernloetters</groupId>
<artifactId>jcombinators</artifactId>
<version>1.5.0-alpha</version>
</parent>

<artifactId>jcombinators-core</artifactId>
<packaging>jar</packaging>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -43,11 +44,11 @@ default <U> Parser<U> flatMap(final Function<T, Parser<U>> function) {
return new FlatMapParser<>(this, function);
}

default <U> Parser<U> andr(final Parser<U> parser) {
default <U> Parser<U> keepRight(final Parser<U> parser) {
return this.flatMap(ignore -> parser);
}

default <U> Parser<T> andl(final Parser<U> parser) {
default <U> Parser<T> keepLeft(final Parser<U> parser) {
return this.flatMap(result -> parser.map(ignore -> result));
}

Expand Down Expand Up @@ -98,7 +99,11 @@ default Parser<T> or(final Parser<T> alternative) {
return or(this, alternative);
}

static <T> Parser<T> chainl1(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator) {
default Parser<T> between(final Parser<?> left, final Parser<?> right) {
return left.keepRight(this).keepLeft(right);
}

static <T> Parser<T> chainLeft1(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator) {
return element.and(separator.and(element).repeat()).map(tuple -> {
T result = tuple.first();

Expand All @@ -110,11 +115,11 @@ static <T> Parser<T> chainl1(final Parser<T> element, final Parser<BiFunction<T,
});
}

static <T> Parser<T> chainl(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator, final T otherwise) {
return chainl1(element, separator).optional().map(result -> result.orElse(otherwise));
static <T> Parser<T> chainLeft(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator, final T otherwise) {
return chainLeft1(element, separator).optional().map(result -> result.orElse(otherwise));
}

static <T> Parser<T> chainr1(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator) {
static <T> Parser<T> chainRight1(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator) {
return element.and(separator.and(element).repeat()).map(tuple -> {
if (tuple.second().isEmpty()) {
return tuple.first();
Expand All @@ -138,8 +143,8 @@ static <T> Parser<T> chainr1(final Parser<T> element, final Parser<BiFunction<T,
});
}

static <T> Parser<T> chainr(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator, final T otherwise) {
return chainl1(element, separator).optional().map(result -> result.orElse(otherwise));
static <T> Parser<T> chainRight(final Parser<T> element, final Parser<BiFunction<T, T, T>> separator, final T otherwise) {
return chainLeft1(element, separator).optional().map(result -> result.orElse(otherwise));
}

static <T> Parser<T> success(final T value) {
Expand Down Expand Up @@ -187,4 +192,8 @@ static <T> Parser<T> position(final Parser<Function<Position, T>> parser) {
};
}

static <T> Parser<T> lazy(final Supplier<Parser<T>> supplier) {
return new LazyParser<>(supplier);
}

}
File renamed without changes.
File renamed without changes.
28 changes: 28 additions & 0 deletions core/src/main/java/jcombinators/primitive/LazyParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package jcombinators.primitive;

import jcombinators.Parser;
import jcombinators.input.Input;
import jcombinators.result.Result;

import java.util.function.Supplier;

public final class LazyParser<T> implements Parser<T> {

private final Supplier<Parser<T>> supplier;

private Parser<T> parser = null;

public LazyParser(final Supplier<Parser<T>> supplier) {
this.supplier = supplier;
}

@Override
public Result<T> apply(final Input input) {
if (parser == null) {
parser = supplier.get();
}

return parser.apply(input);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public final Optional<T> get() {
return Optional.empty();
}

@Override
public T getOrThrow() throws RuntimeException {
throw new RuntimeException(message);
}

@Override
public final boolean isFailure() {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public Result(final Input rest) {

public abstract Optional<T> get();

public abstract T getOrThrow() throws RuntimeException;

public abstract boolean isFailure();

public abstract boolean isSuccess();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public Optional<T> get() {
return Optional.of(value);
}

@Override
public T getOrThrow() throws RuntimeException {
return value;
}

@Override
public boolean isFailure() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import jcombinators.input.Input;
import jcombinators.position.Position;
import jcombinators.result.Result;
import jcombinators.result.Success;
import org.junit.Test;

import java.util.List;
Expand Down Expand Up @@ -54,7 +53,7 @@ public void repeat1EmptyInputTest() {
public void keepRightSuccessTest() {
Parser<Character> first = character('a');
Parser<Character> second = character('b');
Parser<Character> parser = first.andr(second);
Parser<Character> parser = first.keepRight(second);

assertSuccess(parser, 'b', "ab");
}
Expand All @@ -63,7 +62,7 @@ public void keepRightSuccessTest() {
public void keepRightFailureTest() {
Parser<Character> first = character('a');
Parser<Character> second = character('b');
Parser<Character> parser = first.andr(second);
Parser<Character> parser = first.keepRight(second);

assertFailure(parser, "syntax error in Test 'keepRightFailureTest' at line 1 and character 2: unexpected character 'c', expected the literal 'b'", "ac");
}
Expand All @@ -72,7 +71,7 @@ public void keepRightFailureTest() {
public void keepLeftSuccessTest() {
Parser<Character> first = character('a');
Parser<Character> second = character('b');
Parser<Character> parser = first.andl(second);
Parser<Character> parser = first.keepLeft(second);

assertSuccess(parser, 'a', "ab");
}
Expand All @@ -81,7 +80,7 @@ public void keepLeftSuccessTest() {
public void keepLeftFailureTest() {
Parser<Character> first = character('a');
Parser<Character> second = character('b');
Parser<Character> parser = first.andl(second);
Parser<Character> parser = first.keepLeft(second);

assertFailure(parser, "syntax error in Test 'keepLeftFailureTest' at line 1 and character 2: unexpected character 'c', expected the literal 'b'", "ac");
}
Expand Down Expand Up @@ -170,7 +169,7 @@ public void separate1FailureTest() {
public void chainLeftSuccessTest() {
final Parser<Integer> number = regex("[0-9]").map(Integer::parseInt);
final Parser<BiFunction<Integer, Integer, Integer>> plus = character('+').map(op -> Integer::sum);
final Parser<Integer> parser = Parser.chainl1(number, plus);
final Parser<Integer> parser = Parser.chainLeft1(number, plus);

assertSuccess(parser, 6, "1+2+3");
}
Expand All @@ -179,7 +178,7 @@ public void chainLeftSuccessTest() {
public void chainRightSuccessTest() {
final Parser<Integer> number = regex("[0-9]").map(Integer::parseInt);
final Parser<BiFunction<Integer, Integer, Integer>> exponent = character('^').map(op -> (a, b) -> (int) Math.pow(a, b));
final Parser<Integer> parser = Parser.chainr1(number, exponent);
final Parser<Integer> parser = Parser.chainRight1(number, exponent);

assertSuccess(parser, 2, "2^3^0");
}
Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions documentation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.bjoernloetters</groupId>
<artifactId>jcombinators</artifactId>
<version>1.5.0-alpha</version>
</parent>

<artifactId>jcombinators-documentation</artifactId>
</project>
22 changes: 22 additions & 0 deletions examples/json/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.bjoernloetters</groupId>
<artifactId>jcombinators-examples</artifactId>
<version>1.5.0-alpha</version>
</parent>

<artifactId>jcombinators-example-json</artifactId>

<dependencies>
<dependency>
<groupId>io.github.bjoernloetters</groupId>
<artifactId>jcombinators-core</artifactId>
<version>1.5.0-alpha</version>
</dependency>
</dependencies>
</project>
42 changes: 42 additions & 0 deletions examples/json/src/main/java/json/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package json;

import jcombinators.Parser;
import jcombinators.data.Tuple;
import jcombinators.input.Input;

import java.util.stream.Collectors;

import static jcombinators.Parser.lazy;
import static jcombinators.Parser.or;
import static jcombinators.common.StringParser.literal;
import static jcombinators.common.StringParser.regex;

public final class Example {

private Example() {}

public static Parser<JsonValue> jsonValue() {
return lazy(() -> or(jsonNull, jsonNumber, jsonBoolean, jsonArray, jsonObject, jsonString));
}

private static final Parser<String> string = regex("\"([^\"\\\\]|\\\\.)*\"").map(string -> string.substring(1, string.length() - 1));

public static final Parser<JsonNumber> jsonNumber = regex("[+-]?(?:\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?")
.map(Double::parseDouble).map(JsonNumber::new);

public static final Parser<JsonBoolean> jsonBoolean = literal("true").map(ignore -> new JsonBoolean(true))
.or(literal("false").map(ignore -> new JsonBoolean(false)));

public static final Parser<JsonNull> jsonNull = literal("null").map(ignore -> new JsonNull());

public static final Parser<JsonString> jsonString = string.map(JsonString::new);

public static final Parser<JsonArray> jsonArray = jsonValue().separate(literal(",")).between(literal("["), literal("]"))
.map(values -> values.toArray(new JsonValue[0])).map(JsonArray::new);

private static final Parser<Tuple<String, JsonValue>> jsonObjectMember = string.keepLeft(literal(":")).and(jsonValue());

public static final Parser<JsonObject> jsonObject = jsonObjectMember.separate(literal(",")).between(literal("{"), literal("}"))
.map(tuples -> new JsonObject(tuples.stream().collect(Collectors.toMap(Tuple::first, Tuple::second))));

}
4 changes: 4 additions & 0 deletions examples/json/src/main/java/json/JsonArray.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package json;

public record JsonArray(JsonValue[] values) implements JsonValue {
}
4 changes: 4 additions & 0 deletions examples/json/src/main/java/json/JsonBoolean.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package json;

public record JsonBoolean(boolean value) implements JsonValue {
}
4 changes: 4 additions & 0 deletions examples/json/src/main/java/json/JsonNull.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package json;

public record JsonNull() implements JsonValue {
}
4 changes: 4 additions & 0 deletions examples/json/src/main/java/json/JsonNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package json;

public record JsonNumber(double value) implements JsonValue {
}
6 changes: 6 additions & 0 deletions examples/json/src/main/java/json/JsonObject.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package json;

import java.util.Map;

public record JsonObject(Map<String, JsonValue> values) implements JsonValue {
}
4 changes: 4 additions & 0 deletions examples/json/src/main/java/json/JsonString.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package json;

public record JsonString(String value) implements JsonValue {
}
6 changes: 6 additions & 0 deletions examples/json/src/main/java/json/JsonValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package json;

public sealed interface JsonValue permits JsonObject, JsonString, JsonArray, JsonNull, JsonNumber, JsonBoolean {


}
19 changes: 19 additions & 0 deletions examples/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.bjoernloetters</groupId>
<artifactId>jcombinators</artifactId>
<version>1.5.0-alpha</version>
</parent>

<artifactId>jcombinators-examples</artifactId>
<packaging>pom</packaging>

<modules>
<module>json</module>
</modules>
</project>
Loading

0 comments on commit d1c4c86

Please sign in to comment.