Skip to content

Commit

Permalink
docs: add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
yusshu committed Dec 28, 2023
1 parent 6389dc5 commit aeb70f0
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 0 deletions.
83 changes: 83 additions & 0 deletions docs/bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
## Bindings

There are two ways to bind a external function/property to the Mocha environment so that
it can be used from the used expressions.

### 1. Function interface

The way is faster for interpreted expressions, but slower for compiled expressions.

<!--@formatter:off-->
```java
MochaEngine<?> mocha = MochaEngine.createStandard();

// query.get_age()
mocha.scope().query().set("get_age", (ctx, args) -> {
return NumberValue.of(18);
});

mocha.eval("query.get_age()"); // evaluates to 18
```
<!--@formatter:on-->

### 2. Java methods

This way is faster for compiled expressions, since they can directly call the methods, but
it is a lot slower for interpreted expressions, since they have to use reflection to call
them.

<!--@formatter:off-->
```java
@Binding("random")
public class RandomBinding {
// random.select(a, b)
@Binding("select")
public static double select(double a, double b) {
return Math.random() < 0.5 ? a : b;
}
}

// ...
MochaEngine<?> mocha = MochaEngine.createStandard();

mocha.scope().forceSet("random", JavaObjectBinding.of(RandomBinding.class, new RandomBinding()));

mocha.compile("random.select(1, 2)").evaluate(); // evaluates to either 1 or 2
// generates the following code:
// return RandomBinding.select(1, 2);
```
<!--@formatter:on-->

### 3. Use both

Mocha allows you to use both approaches so that you can use the best of both worlds.
With this approach, compiled expressions will prefer the Java methods, while interpreted
expressions will prefer the function interfaces.

<!--@formatter:off-->
```java
@Binding("random")
public class RandomBinding implements ObjectValue {
// random.select(a, b)
@Binding("select")
public static double select(double a, double b) {
return Math.random() < 0.5 ? a : b;
}

@Override
public @NotNull Value get(String name) {
if (name.equals("select")) {
return (Function<?>) (ctx, args) ->
NumberValue.of(select(args[0].asDouble(), args[1].asDouble()));
}
return Value.nil();
}
}

mocha.scope().forceSet("random", JavaObjectBinding.of(RandomBinding.class, new RandomBinding()));

mocha.compile("random.select(1, 2)").evaluate(); // uses Java method

mocha.evaluate("random.select(1, 2)"); // uses Function
```
<!--@formatter:on-->
37 changes: 37 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Getting Started

Welcome to the `mocha` documentation

`mocha` is a lightweight, fast and efficient Molang lexer, parser, interpreter
and compiler for Java 8+. Molang is a simple **expression-based** language designed
for fast and **data-driven** calculation of values at run-time.

For more information about the Molang language, check their official documentation
at [learn.microsoft.com](https://learn.microsoft.com/en-us/minecraft/creator/reference/content/molangreference/examples/molangconcepts/molangintroduction?view=minecraft-bedrock-stable).

### Features

- Fast computing. mocha can compile Molang expressions directly to JVM bytecode,
no need to parse and interpret the expression every time, zero overhead.
- Extensive API, you can bind objects and functions to the Molang expression
environment, so that they can be called from the evaluated expressions.
- Lightweight, mocha is a small library with a single dependency: javassist, it's easy to
integrate in your project.

### mocha vs other Molang libraries

Here are some (reduced) results from a benchmark comparing mocha with other Molang
libraries, ordered from fastest to slowest. *(To run the benchmark yourself, run
`./gradlew jmh`)*

<!--@formatter:off-->
```python
Benchmark Mode Cnt Score Error Units
1. unnamed's mocha sample 8874677 0.035 ± 0.006 us/op
2. MoonflowerTeam's molang-compiler sample 7507288 0.041 ± 0.007 us/op
3. bedrockk's MoLang sample 5404802 6.449 ± 0.158 us/op
```
<!--@formatter:on-->

*To run the benchmark yourself, run `./gradlew jmh`. You can also check the benchmark
source code [here](https://github.com/unnamed/mocha/blob/main/src/jmh/java/team/unnamed/mocha/CompareBenchmark.java)*.
4 changes: 4 additions & 0 deletions docs/index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
getting-started.md
installation.md
usage.md
bindings.md
27 changes: 27 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Installation

You can add `mocha` to your project using [Gradle](https://gradle.org/)
*(recommended)*, [Maven](https://maven.apache.org/) or manually downloading the
JAR files from [GitHub Releases](https://github.com/unnamed/mocha/releases).

Note that `mocha` is available in the Maven Central Repository.

### Gradle

```kotlin
dependencies {
implementation("team.unnamed:mocha:%%REPLACE_latestRelease{team.unnamed:mocha}%%")
}
```

### Maven

<!--@formatter:off-->
```xml
<dependency>
<groupId>team.unnamed</groupId>
<artifactId>mocha</artifactId>
<version>%%REPLACE_latestRelease{team.unnamed:mocha}%%</version>
</dependency>
```
<!--@formatter:on-->
85 changes: 85 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
## Usage

Create a new MochaEngine instance with the static factory method `MochaEngine.createStandard()`,
which will create the instance with the standard configuration, where everything tries to be
as spec-compliant as possible.

<!--@formatter:off-->
```java
MochaEngine<?> mocha = MochaEngine.createStandard();
```
<!--@formatter:on-->

Then we can either evaluate (interpret) or compile Molang code.

### Evaluate

To evaluate expressions we can just use the `eval` method.

<!--@formatter:off-->
```java
double result = mocha.eval("math.sqrt(3 * 3 + 4 * 4)");
// evaluates to 5.0

double result2 = mocha.eval("math.abs(-5) + 5");
// evaluates to 10.0
```
<!--@formatter:on-->

Or if we might want to evaluate the same expression multiple times,
we can cache the parsed expressions.

<!--@formatter:off-->
```java
MochaFunction function = mocha.prepareEval("math.sqrt(3 * 3 + 4 * 4)");

function.evaluate();
// evaluates to 5.0

function.evaluate();
// evaluates to 5.0
```
<!--@formatter:on-->

### Compile

Compiling expressions is pretty similar to preparing them and evaluating them
later, but faster.

<!--@formatter:off-->
```java
MochaFunction function = mocha.compile("math.sqrt(3 * 3 + 4 * 4)");
// will compile a class that implements MochaFunction, with the following
// code:
// return Math.sqrt(25);
// note that the compiler can optimize constant expressions like 3*3+4*4 to just 25

function.evaluate();
// evaluates to 5.0, no interpretation overhead

function.evaluate();
// evaluates to 5.0, no interpretation overhead
```
<!--@formatter:on-->

We could also specify the function type we would like to get.

<!--@formatter:off-->
```java
interface CompareFunction extends MochaCompiledFunction {
boolean compare(@Named("a") double a, @Named("b") double b);
}

// ...
CompareFunction gt = mocha.compile("a > b", CompareFunction.class);

gt.compare(5, 4);
// true

gt.compare(4, 5);
// false

gt.compare(5, 5);
// false
```
<!--@formatter:on-->

0 comments on commit aeb70f0

Please sign in to comment.