diff --git a/muscript/README.md b/muscript/README.md
index faa585c..b50a7dd 100644
--- a/muscript/README.md
+++ b/muscript/README.md
@@ -2,83 +2,14 @@
μScript was created to allow respackopts pack authors to specify conditions for loading resources
in a more human-friendly manner than the previous json tree-based system.
+
It is intended to be vaguely like java in its syntax, though it deviates in some aspects.
+
The language is parsed into an AST representation which is executed in-place, no compilation is performed.
-μScript supports outputting data using various types, not just strings or booleans
+μScript supports outputting data using various types, not just strings or booleans.
-## Value types
-
-This DSL supports numbers (double), booleans, strings, objects, lists and functions.
-The topmost operator of a condition must always return a boolean for it to be valid in the context of respackopts.
-The values of input data are according to the pack config.
-String literals may be written with quotation marks as follows: `"some text"` or `'some text'`
-Numbers may be written as follows: `103` or `10.15`
-Booleans may be written either as `true` or `false`
-Lists may be created with the `listOf` function (part of StandardLib): `listOf(1, 2, 3, 4)`
-Functions may be created with the syntax explained below (--> Closures).
-You may assign any of these to a variable, including closures.
-Objects cannot be created manually but may be provided to the script as parameters or from functions defined in Java.
-
-Please ensure you use proper whitespace, as this might behave unexpectedly otherwise.
-
-## Operators
-
-Numbers support the following operators (x and y are numbers):
-
-- Addition: `x + y`
-- Subtraction: `x - y`
-- Multiplication: `x * y`
-- Division: `x / y`
-- Modulo: `x % y`
-- Power: `x ^ y`
-- Inversion: `-x`
-- Greater than: `x > y`
-- Greater than or equal: `x >= y`
-- Less than: `x < y`
-- Less than or equal: `x <= y`
-- Equality: `x == y`
-- Inequality: `x != y`
-
-Strings support the following operators (x and y are strings, a is any value):
-
-- Equality: `x == y`
-- Inequality: `x != y`
-- Concatenation: `x || a` or `a || x`
-
-Booleans support the following operators (x and y are booleans, a and b are values of the same type):
-
-- Conditional: `x ? a : b`
-- Inversion: `!x`
-- And: `x & y`
-- Or: `x | y`
-- Equality (=XNOR): `x == y`
-- Inequality (=XOR): `x != y`
-
-Objects support the following operators (x is an object with an entry called `entry`):
-
-- Value access via `.`: `x.entry`
-- Value access via square brackets: `x["entry"]`
-
-Lists support the following operators (x is a list, someFunc is a method taking variadic arguments):
-
-- Value access via square brackets: `x[0]`
-- Expanding via `...` (only in calls): `someFunc(x...)`
-
-Parentheses (`()`) may be used to indicate order of operation.
-
-## Closures (functions)
-One may define a closure as follows: `{list, of, arguments -> body}`.
-The body of a closure may contain multiple expressions, which can be (but don't have to be) seperated by semicolons.
-In that case, the return value of the last expression will be the return value of the closure.
-The arrow (`->`) must always be present, even if the closure takes no arguments.
-The amount of arguments is checked at runtime, so the amount used to call the function MUST be correct.
-If you don't know the amount of arguments upfront, you may declare a variadic closure with `...`: `{some, arguments... -> body}`
-where `arguments` would be the variadic argument, which is always a list of excess parameters.
-In closures (or multi-expression scripts for that matter), you may assign variables as follows: `name = value`.
-Please note that you cannot assign values to fields of objects.
-You may also assign a closure to a variable to use it like a named function.
-This could look as follows: `someFunction = {arg -> arg * arg}`
-Please also be aware that μScript does NOT allow you to modify variables of outer scopes from inner scopes.
+The purpose of this document is to be a guide on embedding μScript in your application or library.
+For details on how to use the language, look [here](src/test/resources/example.md).
## Embedding μScript
diff --git a/muscript/src/test/java/io/gitlab/jfronny/muscript/test/ValidExampleTest.java b/muscript/src/test/java/io/gitlab/jfronny/muscript/test/ValidExampleTest.java
new file mode 100644
index 0000000..f06f6e5
--- /dev/null
+++ b/muscript/src/test/java/io/gitlab/jfronny/muscript/test/ValidExampleTest.java
@@ -0,0 +1,58 @@
+package io.gitlab.jfronny.muscript.test;
+
+import io.gitlab.jfronny.commons.StringFormatter;
+import io.gitlab.jfronny.muscript.compiler.Parser;
+import io.gitlab.jfronny.muscript.error.LocationalException;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
+import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.makeArgs;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ValidExampleTest {
+ @Test
+ void assertValidExample() throws IOException {
+ final String source;
+ try (InputStream is = Objects.requireNonNull(ValidExampleTest.class.getClassLoader().getResourceAsStream("example.md"))) {
+ source = new String(is.readAllBytes());
+ }
+ String[] split = source.lines().toArray(String[]::new);
+ for (int i = 0; i < split.length; i++) {
+ String s = split[i];
+ if (s.equals("```mu")) {
+ StringBuilder blockBuilder = new StringBuilder();
+ while (!split[++i].equals("```")) {
+ blockBuilder.append('\n').append(split[i]);
+ }
+ assertEquals("```", split[i++]);
+ assertEquals("", split[i++]);
+ assertEquals("Result
", split[i++]);
+ assertEquals("
", split[i++]);
+ assertEquals("", split[i++]);
+ assertEquals("```", split[i]);
+ StringBuilder resultBuilder = new StringBuilder();
+ while (!split[++i].equals("```")) {
+ resultBuilder.append('\n').append(split[i]);
+ }
+ i++;
+ assertEquals(" ", split[i++]);
+ final String block = blockBuilder.substring(1);
+ final String expectedResult = resultBuilder.substring(1);
+ String result = null;
+ try {
+ result = Parser.parseScript(block).asExpr().asStringExpr().get(makeArgs());
+ } catch (Throwable t) {
+ assertEquals(expectedResult, StringFormatter.toString(t, e ->
+ e instanceof LocationalException le
+ ? le.asPrintable(block).toString()
+ : e.toString()
+ ));
+ }
+ if (result != null) assertEquals(expectedResult, result);
+ }
+ }
+ }
+}
diff --git a/muscript/src/test/resources/example.md b/muscript/src/test/resources/example.md
new file mode 100644
index 0000000..94a2ee9
--- /dev/null
+++ b/muscript/src/test/resources/example.md
@@ -0,0 +1,218 @@
+# μScript
+
+The purpose of this document is to be a guide to using the μScript language.
+It is not intended to explain any history of the language, implementation details or details on how to embed μScript in programs.
+For information on that, look [here](../../../README.md).
+
+## Types
+μScript supports a couple of basic types, namely:
+- [Numbers](#numbers) (double-precision floating-point)
+- [Booleans](#booleans)
+- [Strings](#strings)
+- [Objects](#objects)
+- [Lists](#lists)
+- [Functions](#functions)
+
+Numbers, booleans and strings can be created just like in java.
+
+## Numbers
+
+You can use the most common math operations on numbers.
+That includes addition, subtraction, multiplication, division, modulo, pow, and your standard comparison operators.
+
+Order of operation is also supported.
+
+The StdLib comes with additional constants and functions, namely: `PI`, `E`, `round`, `floor`, `ceil`, `abs`, `random`
+
+For example:
+
+```mu
+listOf(
+ 2 + 5,
+ 2 - 5,
+ 0.5 * 4,
+ 2 + 2 * 3,
+ (2 + 2) * 3,
+ 4 / 2,
+ 5 % 3, // this is modulo
+ 2 ^ 10, // two to the power of ten
+ 12 > 4,
+ 5 < 3,
+ 4 >= 4,
+ 5 != 2,
+ 8 == 8,
+ round(PI, 2) + floor(E) // round also accepts a second argument: precision, but it can be ommitted to round to a whole number
+)
+```
+
+Result
+
+
+```
+[7, -3, 2, 8, 12, 2, 2, 1024, true, false, true, true, true, 5.1400]
+```
+
+
+## Booleans
+
+Your standard logic operators are supported.
+XOR/XNOR are just your normal `!=`/`==` operators.
+Make sure not to use `||`, as that is the string concatenation operator, not OR.
+You can also use ternary conditional operators as you would in java.
+
+```mu
+listOf(
+ 1 < 3 ? "this is correct" : "it is not", // if the condition is true, do the first thing. Else, do the second
+ !true, // not
+ true & false, // and
+ true & true,
+ true | false, // or
+ false | false
+)
+```
+
+Result
+
+
+```
+[this is correct, false, false, true, true, false]
+```
+
+
+## Strings
+
+Strings only support concatenation via `||`.
+You can also concatenate other things to a string.
+Values of all other types can also be coerced into strings.
+
+Equality operations are supported.
+
+The StdLib comes with some additional functions, namely: `toUpper`, `toLower`, `contains` and `replace`
+
+```mu
+15 || "string one"::toUpper() || 'string Two'::toLower() || "example"::contains("thing") || "yxamply"::replace("y", "e")
+```
+
+Result
+
+
+```
+15STRING ONEstring twofalseexample
+```
+
+
+## Objects
+
+You cannot create your own objects, but objects may be passed to your script from functions or as parameters.
+You can read (but not write) their fields via `.` or `[]`.
+
+In the following example, the objects `object` and `object2` is passed to the script.
+
+The StdLib also contains two objects, namely `date` and `time` which allow reading the current date and time.
+They also allow creating date/time objects and comparing them.
+
+```mu
+listOf(
+ object2.valuename, // This is how you would normally do this
+ object2['valuename'], // You can also use []
+ this['object2']['valuename'], // 'this' is always an object representing the current scope
+ object2['value' || 'name'], // [] can contain any expression
+ object[object2['sub'].val / 10], // [] even references to other field
+ object2.sub['val'] * 2, // You can do anything with the value of a field
+ object.subvalue / (object2.sub.val + 6),
+ object2.stringfunc("some parameter"), // Objects can also contain functions
+ date(2023, 5, 13),
+ time(23, 55, 10),
+ date(2020, 5, 10) > date.today
+)
+```
+
+Result
+
+
+```
+[subvalue, subvalue, subvalue, subvalue, One, 20, 64, some parameter, 2023-05-13, 23:55:10, false]
+```
+
+
+## Lists
+
+Lists can be created with the stdlib function `listOf` (as seen in other examples).
+You can access their entries with `[]`, just like you would access fields for objects.
+In function calls, you can use the spread operator (`...`) to use all elements of the list as parameters.
+The StdLib also comes with some additional functions, namely `len`, `isEmpty`, `concat`, `filter`, `map`, `flatMap`, `fold` and `forEach`.
+
+```mu
+listOf(
+ len(listOf(1, 2, 3)),
+ isEmpty(listOf()),
+ concat(listOf(1, 2), listOf(3, 4)),
+ listOf(1, 2)::concat(listOf(3, 4))::len(), // Don't forget using the bind operator for readability in longer chains
+ time(listOf(23, 55)..., 10), // You can use the spread operator in any call, not just variadic ones
+ listOf(1, 2, 3)[1], // Lists are 0-indexed
+ listOf(1, 2, 3, 4)::filter({n->n%2==0})::map({n->n/2}) // you can chain the additional functions for proper functional programming
+)
+```
+
+Result
+
+
+```
+[3, true, [1, 2, 3, 4], 4, 23:55:10, 2, [1, 2]]
+```
+
+
+## Functions
+
+μScript is functional as in functions being treated like any other data type.
+This means you can pass them to methods as arguments, store them in variables or do whatever else you feel like.
+A closure consists of a list of arguments (of which the last may be variadic) and some instructions.
+
+```mu
+someFunction = {n -> n * 2} // By assigning a closure to a variable, you get an equivalent to a normal java function
+
+someVal = 1
+$someVal = 1
+
+{->someVal = 12}() // If you try to change a variable from inside a closure, you create a copy of it. The outer scope doesn't change
+{->$someVal = 12}() // The only exception to that are variables prefixed with a dollar sign
+ // These work like mutable variables in other languages
+/*
+ Use `...` to mark the last argument of a closure as variadic
+ All arguments that don't fit in the previous parameters will be packed in it as a list
+ Note that the length of that list may be 0
+*/
+someFunction2 = { a, b... ->
+ isEmpty(b) ? a : a * someFunction2(b...) // you can use the spread operator on that list like any other
+}
+
+listOf(
+ someFunction(1), // Closures are ran with parentheses
+ {->"some expression(s)"}(), // You don't have to store them to call them once...
+ {f1, f2 -> f1(f2(5))}(someFunction, {n->n}), // ... or to pass them to a higher order function
+ {->
+ 12 + 3
+ 10 + 2
+ 5 + 2 // The last expression in a closure is its result. This is the same behavior as scripts (like the one this runs in)
+ }(),
+ {->
+ 12 + 3; // You can (and probably should) use semicolons to seperate expressions
+ 10 + 2;
+ 5 + 2;
+ }(),
+ 1::someFunction(), // You can use the bind operator '::' to create a new closure using the value to the left as the first argument
+ // In this case, this new closure is executed immediately using the parentheses, but doing that isn't necessary
+ 1::({n->n})(), // Instead of a name, you can also use any expression as the right part of a bind operator (if it is in parentheses)
+ someVal,
+ $someVal,
+ someFunction2(1, 2, 3, 4)
+)
+```
+
+Result
+
+
+```
+[2, some expression(s), 10, 7, 7, 2, 1, 1, 12, 24]
+```
+