java-commons/muscript
2023-03-11 13:33:45 +01:00
..
src muScript: remove namespace support and fix bind 2023-03-11 13:33:45 +01:00
build.gradle.kts Attempt to fix doc 2023-02-23 14:37:45 +01:00
README.md Fix example 2023-01-24 14:10:42 +01:00

μScript

μ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

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.

Embedding μScript

μScript is available as a maven package which you can add to your project. To use it, first parse an expression via Parser.parse(String script) and convert the returned generic expression to a typed one by calling as(Bool|String|Number|Dynamic)Expr. This process may throw a ParseException. You may also use Parser.parseScript(String script) for multi-expression scripts. You can call get(Dynamic<?> dataRoot) on the result to execute the script on the provided data, which should be a Scope which you created with StandardLib.createScope() to add standard methods. This is also where you can add custom data to be accessed by your script. The execution of a script can throw a LocationalException which may be converted to a LocationalError for printing using the source of the expression if available. You may also call StarScriptIngester.starScriptToMu() to generate μScript code from StarScript code.

A full example could look as follows:

public class Example {
    public static void main(String[] args) {
        String source = String.join(" ", args);
        Expr<?> parsed;
        try {
            parsed = Parser.parse(source); // or Parser.parse(StarScriptIngester.starScriptToMu(source))
        } catch (Parser.ParseException e) { // Could not parse
            System.err.println(e.error);
            return;
        }
        BoolExpr typed;
        try {
            typed = parsed.asBoolExpr();
        } catch (LocationalException e) {
            System.err.println(e.asPrintable(source));
            return;
        }
        Scope scope = StandardLib.createScope()
                .set("someValue", 15)
                .set("someOther", Map.of(
                        "subValue", DFinal.of(true)
                ));
        boolean result;
        try {
            result = typed.get(scope);
        } catch (LocationalException e) {
            System.err.println(e.asPrintable(source));
            return;
        }
        System.out.println("Result: " + result);
    }
}