java-commons/muscript/src/test/resources/example.md

6.9 KiB

μ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.

Types

μScript supports a couple of basic types, namely:

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

Example
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.

Example
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

Example
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.

Example
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.

Example
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.

Example
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]