Skip to content

Commit

Permalink
add Calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
janlely committed May 22, 2023
1 parent 8f81c1d commit d19d548
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 22 deletions.
64 changes: 42 additions & 22 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,52 @@ Just like the usage of Parser Combinator in Haskell, jparser does not generate A
</dependency>
```

## hello world
## hello world: Simple Calculator
```java
public class Calculator {

@Test
public void testHelloWorld() {
String time = "2023-05-01 12:59:59";
Parser timeParser = NumberParsers.anyIntStr() //year
.chain(() -> TextParsers.one('-').ignore()) //-
.chain(() -> NumberParsers.anyIntStr()) //mon
.chain(() -> TextParsers.one('-').ignore()) //-
.chain(() -> NumberParsers.anyIntStr()) //day
.chain(() -> TextParsers.one(' ').ignore())// ' '
.chain(() -> NumberParsers.anyIntStr()) //hour
.chain(() -> TextParsers.one(':').ignore()) // ':'
.chain(() -> NumberParsers.anyIntStr()) //minute
.chain(() -> TextParsers.one(':').ignore()) // ':'
.chain(() -> NumberParsers.anyIntStr()) //second
.chain(() -> TextParsers.eof());
Result result = timeParser.runParser(Buffer.builder().data(time.getBytes()).build());
assert result.<Integer>get(0) == 2023;
assert result.<Integer>get(1) == 5;
assert result.<Integer>get(2) == 1;
assert result.<Integer>get(3) == 12;
assert result.<Integer>get(4) == 59;
assert result.<Integer>get(5) == 59;
public void testCalc() {
Result result = expr().parse(Buffer.builder().data("(1+2)*3-(4*2)".getBytes()).build());
assert result.<Double>get(0).compareTo(1.0) == 0;
result = expr().parse(Buffer.builder().data("1+2*3-(4*2)".getBytes()).build());
assert result.<Double>get(0).compareTo(-1.0) == 0;
}

public Parser expr() {
return Parser.choose(
() -> term().chain(TextParsers.one('+').ignore())
.chain(() -> expr()).map(s -> (double)s.get(0) + (double)s.get(1)),
() -> term().chain(TextParsers.one('-').ignore())
.chain(() -> expr()).map(s -> (double)s.get(0) - (double)s.get(1)),
() -> term()
);
}

public Parser term() {
return Parser.choose(
() -> factor().chain(TextParsers.one('*').trim(false).ignore())
.chain(() -> term()).map(s -> (double)s.get(0) * (double)s.get(1)),
() -> factor().chain(TextParsers.one('/').trim(false).ignore())
.chain(() -> term()).map(s -> (double)s.get(0) / (double)s.get(1)),
() -> factor()
);
}

public Parser factor() {
return Parser.choose(
TextParsers.one('(').ignore()
.chain(() -> expr())
.chain(TextParsers.one(')').ignore()),
number()
);
}

public Parser number() {
return NumberParsers.anyDoubleStr();
}
}

```

## basic parsers
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/io/github/janlely/jparser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,31 @@ public Result parse(IBuffer buffer) {
};
}

/**
* Same as Combinator Choose
* @param parser A Parser generator
* @return A new Parser that is composed of the specific Parser generator
*/
public Parser or(Parser parser) {
return new Parser() {
@Override
public Result parse(IBuffer buffer) {
Result result = Parser.this.runParser(buffer);
if (result.isSuccess()) {
return result;
}
Result result2 = parser.runParser(buffer);
if (result2.isSuccess()) {
return result2;
}
return Result.builder()
.pos(buffer.getPos())
.errorMsg("No suitable Parser to choose")
.build();
}
};
}

/**
* make this Parser optional
* @return A new Parser
Expand Down Expand Up @@ -594,6 +619,19 @@ public static Parser choose(Supplier<Parser> ...parsers) {
return parser;
}

/**
* choose a Parser from array of Parser
* @param parsers Parsers candidates
* @return A new Parser that is composed of the parsers
*/
public static Parser choose(Parser ...parsers) {
Parser parser = Parser.broken();
for (Parser p : parsers) {
parser = parser.or(p);
}
return parser;
}

/**
* choose a Parser from array of Parser
* @param parsers Parsers candidates
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/io/github/janlely/jparser/parsers/NumberParsers.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ public static Parser anyIntStr() {
.map(s -> Integer.parseInt((String) s.get(0)));
}

/**
* Parse any double encoded as a string.
* @return A new Parser
*/
public static Parser anyDoubleStr() {
return TextParsers.one('-').optional().chain(NumberParsers.anyIntStr())
.chain(TextParsers.one('.').chain(NumberParsers::anyDoubleStr).optional())
.map(Mapper.toStr())
.map(s -> Double.parseDouble((String) s.get(0)));
}

/**
* Parse an arbitrary long integer encoded in big-endian format.
* @return A new Parser
Expand Down
51 changes: 51 additions & 0 deletions src/test/java/io/github/janlely/jparser/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.github.janlely.jparser;

import io.github.janlely.jparser.parsers.NumberParsers;
import io.github.janlely.jparser.parsers.TextParsers;
import io.github.janlely.jparser.util.Buffer;
import org.junit.Test;

public class Calculator {

@Test
public void testCalc() {
Result result = expr().parse(Buffer.builder().data("(1+2)*3-(4*2)".getBytes()).build());
assert result.<Double>get(0).compareTo(1.0) == 0;
result = expr().parse(Buffer.builder().data("1+2*3-(4*2)".getBytes()).build());
assert result.<Double>get(0).compareTo(-1.0) == 0;
}

public Parser expr() {
return Parser.choose(
() -> term().chain(TextParsers.one('+').ignore())
.chain(() -> expr()).map(s -> (double)s.get(0) + (double)s.get(1)),
() -> term().chain(TextParsers.one('-').ignore())
.chain(() -> expr()).map(s -> (double)s.get(0) - (double)s.get(1)),
() -> term()
);
}

public Parser term() {
return Parser.choose(
() -> factor().chain(TextParsers.one('*').trim(false).ignore())
.chain(() -> term()).map(s -> (double)s.get(0) * (double)s.get(1)),
() -> factor().chain(TextParsers.one('/').trim(false).ignore())
.chain(() -> term()).map(s -> (double)s.get(0) / (double)s.get(1)),
() -> factor()
);
}

public Parser factor() {
return Parser.choose(
TextParsers.one('(').ignore()
.chain(() -> expr())
.chain(TextParsers.one(')').ignore()),
number()
);
}

public Parser number() {
return NumberParsers.anyDoubleStr();
}
}

0 comments on commit d19d548

Please sign in to comment.