-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)*. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
getting-started.md | ||
installation.md | ||
usage.md | ||
bindings.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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--> |