moved scarpet docs to markdown files
This commit is contained in:
1 parent
c7603038da
commit
d93a0dbdf4
19 files changed
+3154
-2712
No files matched your search
@@ -0,0 +1,241 @@
|
||||
# Lists, Maps and API support for Containers
|
||||
|
||||
Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..)
|
||||
|
||||
## Container manipulation
|
||||
|
||||
Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft specific
|
||||
modifyable containers, like NBTs
|
||||
|
||||
### `get(container, address, ...), get(lvalue), ':' operator`
|
||||
|
||||
Returns the value at `address` element from the `value`. For lists it indicates an index, use negative numbers to
|
||||
reach elements from the end of the list. `get` call will always be able to find the index. In case there is few
|
||||
items, it will loop over
|
||||
|
||||
for maps, retrieves the value under the key specified in the `address` or null otherwise
|
||||
|
||||
[Minecraft specific usecase]: In case `value` is of `nbt` type, uses address as the nbt path to query, returning null,
|
||||
if path is not found, one value if there was one match, or list of values if result is a list. Returned elements can
|
||||
be of numerical type, string texts, or another compound nbt tags
|
||||
|
||||
In case to simplify the access with nested objects, you can add chain of addresses to the arguments of `get` rather
|
||||
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
||||
|
||||
<pre>
|
||||
get(l(range(10)), 5) => 5
|
||||
get(l(range(10)), -1) => 9
|
||||
get(l(range(10)), 10) => 0
|
||||
l(range(10)):93 => 3
|
||||
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
|
||||
get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar') => 3
|
||||
</pre>
|
||||
|
||||
### `has(container, address, ...), has(lvalue)`
|
||||
|
||||
Similar to `get`, but returns boolean value indicating if the given index / key / path is in the container.
|
||||
Can be used to determine if `get(...)==null` means the element doesn't exist, or the stored value for this
|
||||
address is `null`, and is cheaper to run than `get`.
|
||||
|
||||
Like get, it can accept multiple addresses for chains in nested containers. In this case `has(foo:a:b)` is
|
||||
equivalent to `has(get(foo,a), b)` or `has(foo, a, b)`
|
||||
|
||||
### `delete(container, address, ...), delete(lvalue)`
|
||||
|
||||
Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it
|
||||
removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous
|
||||
entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.
|
||||
|
||||
Like with the `get` and `has`, `delete` can accept chained addresses, as well as l-value container access, removing
|
||||
the value from the leaf of the path provided, so `delete(foo, a, b)` is the
|
||||
same as `delete(get(foo,a),b)` or `delete(foo:a:b)`
|
||||
|
||||
Returns true, if container was changed, false, if it was left unchanged, and null if operation was invalid.
|
||||
|
||||
### `put(container, address, value), put(container, address, value, mode), put(lvalue, value)`
|
||||
|
||||
<u>**Lists**</u>
|
||||
|
||||
Modifies the container by replacing the value under the address with the supplied `value`. For lists, a valid
|
||||
index is required, but can be negative as well to indicate positions from the end of the list. If `null` is
|
||||
supplied as the address, it always means - add to the end of the list.
|
||||
|
||||
There are three modes that lists can have items added to them:
|
||||
|
||||
* `replace`(default): Replaces item under given index(address). Doesn't change the size of the array
|
||||
unless `null` address is used, which is an exception and then it appends to the end
|
||||
* `insert`: Inserts given element at a specified index, shifting the rest of the array to make space for the item.
|
||||
Note that index of -1 points to the last element of the list, thus inserting at that position and moving the previous
|
||||
last element to the new last element position. To insert at the end, use `+=` operator, or `null` address in put
|
||||
* `extend`: treats the supplied value as an iterable set of values to insert at a given index, extending the list
|
||||
by this amount of items. Again use `null` address/index to point to the end of the list
|
||||
|
||||
Due to the extra mode parameter, there is no chaining for `put`, but you can still use l-value container access to
|
||||
indicate container and address, so `put(foo, key, value)` is the same as `put(foo:key, value)` or `foo:key=value`
|
||||
|
||||
Returns true, if container got modified, false otherwise, and null if operation was invalid.
|
||||
|
||||
<u>**Maps**</u>
|
||||
|
||||
For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied
|
||||
key (address), or sets it if not currently present.
|
||||
|
||||
<u>**NBT Tags**</u>
|
||||
|
||||
The address for nbt values is a valid nbt path that you would use with `/data` command, and tag is any tag that
|
||||
would be applicable for a given insert operation. Note that to distinguish between proper types (like integer types,
|
||||
you need to use command notation, i.e. regular ints is `123`, while byte size int would be `123b` and an explicit
|
||||
string would be `"5"`, so it helps that scarpet uses single quotes in his strings. Unlike for lists and maps, it
|
||||
returns the number of affected nodes, or 0 if none were affected.
|
||||
|
||||
There are three modes that NBT tags can have items added to them:
|
||||
|
||||
* `replace`(default): Replaces item under given path(address). Removes them first if possible, and then adds given
|
||||
element to the supplied position. The target path can indicate compound tag keys, lists, or individual elements
|
||||
of the lists.
|
||||
* `<N>`: Index for list insertions. Inserts given element at a specified index, inside a list specified with the
|
||||
path address. Fails if list is not specified. It behaves like `insert` mode for lists, i.e. it is not removing any
|
||||
of the existing elements. Use `replace` to remove and replace existing element.
|
||||
* `merge`: assumes that both path and replacement target are of compound type (dictionaries, maps, `{}` types),
|
||||
and merges keys from `value` with the compound tag under the path
|
||||
|
||||
<pre>
|
||||
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
|
||||
a = l(1, 2, 3); put(a, null, 4); a => [1, 2, 3, 4]
|
||||
a = l(1, 2, 3); put(a, 1, 4, 'insert'); a => [1, 4, 2, 3]
|
||||
a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a => [1, 2, 3, 4, 5, 6]
|
||||
a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a => [1, 4, 5, 6, 2, 3]
|
||||
a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
|
||||
a = m(1,2,3,4); put(a, 5, null); a => {1: null, 2: null, 3: null, 4: null, 5: null}
|
||||
tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag => {BlockData:{Properties:[1,2,3,4]}}
|
||||
tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag => {a:[{lvl:1},{lvl:1},{lvl:1}]}
|
||||
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
|
||||
=> {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
|
||||
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
|
||||
=> {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
|
||||
</pre>
|
||||
|
||||
## List operations
|
||||
|
||||
### `l(values ...), l(iterator)`
|
||||
|
||||
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
||||
elements are variables, you coujld use it to return multiple results from one function call, if that
|
||||
function returns a list of results with the same size as the `l` call uses. In case there is only one
|
||||
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
||||
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
||||
functions, and are treated as empty lists, unless unrolled with `l`
|
||||
|
||||
<pre>
|
||||
l(1,2,'foo') => [1, 2, foo]
|
||||
l() => [] (empty list)
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
||||
l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
|
||||
l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz) => [3, 4, 5]
|
||||
</pre>
|
||||
|
||||
In the last example `l(min(foo, bar), baz)` creates a valid L-value, as min(foo, bar) finds the lower of the
|
||||
variables (in this case `foo`) creating a valid assignable L-list of [foo, baz], and these values
|
||||
will be assigned new values
|
||||
|
||||
### `join(delim, list), join(delim, values ...)`
|
||||
|
||||
Returns a string that contains joined elements of the list, iterator, or all values,
|
||||
concatenated with `delim` delimiter
|
||||
|
||||
<pre>
|
||||
join('-',range(10)) => 0-1-2-3-4-5-6-7-8-9
|
||||
join('-','foo') => foo
|
||||
join('-', 'foo', 'bar') => foo-bar
|
||||
</pre>
|
||||
|
||||
### `split(delim, expr)`
|
||||
|
||||
Splits a string undr `expr` by `delim` which can be a regular expression
|
||||
|
||||
<pre>
|
||||
split('',foo) => [f, o, o]
|
||||
split('.','foo.bar') => []
|
||||
split('\\.','foo.bar') => [foo, bar]
|
||||
</pre>
|
||||
|
||||
### `slice(expr, from, to?)`
|
||||
|
||||
extracts a substring, or sublist (based on the type of the result of the expression under expr with
|
||||
starting index of `from`, and ending at `to` if provided, or the end, if omitted
|
||||
|
||||
<pre>
|
||||
slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3]
|
||||
slice('foobar', 0, 1) => 'f'
|
||||
slice('foobar', 3) => 'bar'
|
||||
slice(range(10), 3, 5) => [3, 4, 5]
|
||||
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
||||
</pre>
|
||||
|
||||
### `sort(list), sort(values ...)`
|
||||
|
||||
Sorts in the default sortographical order either all arguments, or a list if its the only argument.
|
||||
It returns a new sorted list, not affecting the list passed to the argument
|
||||
|
||||
<pre>sort(3,2,1) => [1, 2, 3]
|
||||
sort('a',3,11,1) => [1, 3, 11, 'a']
|
||||
list = l(4,3,2,1); sort(list) => [1, 2, 3, 4]
|
||||
</pre>
|
||||
|
||||
### `sort_key(list, key_expr)`
|
||||
|
||||
Sorts a copy of the list in the order or keys as defined by the `key_expr` for each element
|
||||
|
||||
<pre>
|
||||
sort_key([1,3,2],_) => [1, 2, 3]
|
||||
sort_key([1,3,2],-_) => [3, 2, 1]
|
||||
sort_key(l(range(10)),rand(1)) => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
|
||||
sort_key(l(range(20)),str(_)) => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
</pre>
|
||||
|
||||
### `range(to), range(from, to), range(from, to, step)`
|
||||
|
||||
Creates a range of numbers from `from`, no greater/larger than `to`. The `step` parameter dictates not only the
|
||||
increment size, but also direction (can be negative). The returned value is not a proper list, just the iterator
|
||||
but if for whatever reason you need a proper list with all items evaluated, use `l(range(to))`.
|
||||
Primarily to be used in higher order functions
|
||||
|
||||
<pre>
|
||||
range(10) => [...]
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||
reduce(range(10),_a+_, 0) => 45
|
||||
range(5,10) => [5, 6, 7, 8, 9]
|
||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||
</pre>
|
||||
|
||||
### `element(list, index)(deprecated)`
|
||||
|
||||
Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.`
|
||||
operator. Also previous unique behaviours with `put` on lists has been removed to support all type of
|
||||
container types.
|
||||
|
||||
## Map operations
|
||||
|
||||
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
||||
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
||||
|
||||
### `m(values ...), m(iterator), m(key_value_pairs)`
|
||||
|
||||
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||
|
||||
<pre>
|
||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
||||
m() => {} (empty map)
|
||||
m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||
</pre>
|
||||
|
||||
### `keys(map), values(map), pairs(map)`
|
||||
|
||||
Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map
|
||||
@@ -0,0 +1,170 @@
|
||||
# User-defined functions and program control flow
|
||||
|
||||
## Writing programs with more than 1 line
|
||||
|
||||
### `Operator ;`
|
||||
|
||||
To effectively write programs that have more than one line, a programmer needs way to specify a sequence of commands
|
||||
that execute one after another. In `scarpet` this can be achieved with `;`. Its an operator, and by separating
|
||||
statements with semicolons. And since whitespaces and
|
||||
`$` (commandline visible newline separator)
|
||||
sign are all treats as whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved.
|
||||
|
||||
<pre>
|
||||
expr;
|
||||
expr;
|
||||
expr;
|
||||
expr
|
||||
</pre>
|
||||
|
||||
Notice that the last expression is not followed by a semicolon. Since instruction separation is functional
|
||||
in `scarpet`, and not barely an instruction delimiter, terminating the code with a dangling operator wouldn't
|
||||
be valid. Having said that, since many programming languages don't care about the number of op terminators
|
||||
programmers use, carpet preprocessor will remove all unnecessary semicolons from scripts when compiled.
|
||||
|
||||
In general `expr; expr; expr; expr` is equivalent to `(((expr ; expr) ; expr) ; expr)`.
|
||||
|
||||
Result of the evaluated expression is the same as the result of the second expression, but first expression
|
||||
is also evaluated for side-effects
|
||||
|
||||
<pre>
|
||||
expr1 ; expr2 => expr2 // with expr1 as a side-effect
|
||||
</pre>
|
||||
|
||||
## Global variables
|
||||
|
||||
All defined functions are compiled, stored persistently, and available globally - accessible to all other scripts.
|
||||
Functions can only be undefined via call to `undef('fun')`, which would erase global entry for function `fun`.
|
||||
Since all variables have local scope inside each function, one way to share large objects is via global variables
|
||||
|
||||
Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not,
|
||||
inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it
|
||||
needs to define it, or initialize it explicitly, or undefine it via `undef`
|
||||
|
||||
<pre>
|
||||
a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
### `Operator ->`
|
||||
|
||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||
if has the following form
|
||||
|
||||
<pre>
|
||||
fun(args, ...) -> expr
|
||||
</pre>
|
||||
|
||||
Where `fun(args, ...)` is a function signature indicating function name, number of arguments, and their names,
|
||||
and expr is an expression (can be complex) that is evaluated when `fun` is called. Names in the signature don't
|
||||
need to be used anywhere else, other occurrences of these names will be masked in this function scope. Function
|
||||
call creates new scope for variables inside `expr`, so all non-global variables are not visible from the caller
|
||||
scope. All parameters are passed by value to the new scope, including lists and other containers, however their
|
||||
copy will be shallow.
|
||||
|
||||
The function returns its name as a string, which means it can be used to call it later with the `call` function
|
||||
|
||||
Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given
|
||||
a unique name, which you can pass somewhere else to get this function `call`ed.
|
||||
|
||||
<pre>
|
||||
a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
||||
</pre>
|
||||
|
||||
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
||||
it needs to use `outer` function in function signature
|
||||
|
||||
### `outer(arg)`
|
||||
|
||||
`outer` function can only be used in the function signature, and it will cause an error everywhere else. It
|
||||
saves the value of that variable from the outer scope and allows its use in the inner scope. This is a similar
|
||||
behaviour to using outer variables in lambda function definitions from Java, except here you have to specify
|
||||
which variables you want to use, and borrow
|
||||
|
||||
This mechanism can be used to use static mutable objects without the need of using `global_...` variables
|
||||
|
||||
<pre>
|
||||
list = l(1,2,3); a(outer(list)) -> list+=1; a(); a(); list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
The return value of a function is the value of the last expression. This as the same effect as using outer or
|
||||
global lists, but is more expensive
|
||||
|
||||
<pre>
|
||||
a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
|
||||
scoping allow to organize even larger scripts
|
||||
|
||||
### `import(module_name, symbols ...)`
|
||||
|
||||
Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use
|
||||
them in the current app. This include other symbols imported by these modules. Scarpet supports cicular dependencies,
|
||||
but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them.
|
||||
Returns full list of available symbols that could be imported from this module, which can be used to debug import
|
||||
issues, and list contents of libraries.
|
||||
|
||||
### `call(function, args.....)`
|
||||
|
||||
calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly
|
||||
except you can use it with function value, or name instead. This means you can pass functions to other user defined
|
||||
functions as arguments and call them with `call` internally. And since function definitions return the defined
|
||||
function, they can be defined in place as anonymous functions.
|
||||
|
||||
Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not
|
||||
creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for
|
||||
programmers with their own lambda arguments
|
||||
|
||||
<pre>
|
||||
my_map(list, function) -> map(list, call(function, _));
|
||||
my_map(l(1,2,3), _(x) -> x*x); // => [1,4,9]
|
||||
profile_expr(my_map(l(1,2,3), _(x) -> x*x)); // => ~32000
|
||||
sq(x) -> x*x; profile_expr(my_map(l(1,2,3), 'sq')); // => ~36000
|
||||
sq = (_(x) -> x*x); profile_expr(my_map(l(1,2,3), sq)); // => ~36000
|
||||
profile_expr(map(l(1,2,3), _*_)); // => ~80000
|
||||
</pre>
|
||||
|
||||
## Control flow
|
||||
|
||||
### `return(expr?)`
|
||||
|
||||
Sometimes its convenient to break the organized control flow, or it is not practical to pass the final result value of
|
||||
a function to the last statement, in this case a return statement can be used
|
||||
|
||||
If no argument is provided - returns null value.
|
||||
|
||||
<pre>
|
||||
def() -> (
|
||||
expr1;
|
||||
expr2;
|
||||
return(expr3); // function terminates returning expr3
|
||||
expr4; // skipped
|
||||
expr5 // skipped
|
||||
)
|
||||
</pre>
|
||||
|
||||
In general its cheaper to leave the last expression as a return value, rather than calling
|
||||
returns everywhere, but it would often lead to a messy code.
|
||||
|
||||
### `exit(expr?)`
|
||||
|
||||
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
||||
|
||||
### `try(expr, catch_expr(_)?) ... throw(value?)`
|
||||
|
||||
`try` function evaluates expression, and continues further unless `throw` function is called anywhere
|
||||
inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with.
|
||||
This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns
|
||||
null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program
|
||||
execution. There is only one level of exceptions currently in carpet, so if the inner function also defines
|
||||
the `try` catchment area, it will received the exception first, but it can technically rethrow the value its
|
||||
getting for the outer scope. Unhandled throw acts like an exit statement.
|
||||
|
||||
### `if(cond, expr, cond?, expr?, ..., default?)`
|
||||
|
||||
If statement is a function that takes a number of conditions that are evaluated one after another and if any of
|
||||
them turns out true, its `expr` gets returned, otherwise, if all conditions fail, the return value is `default`
|
||||
expression, or `null` if default is skipped
|
||||
|
||||
`if` function is equivalent to `if (cond) expr; else if (cond) expr; else default;` from Java,
|
||||
just in a functional form
|
||||
@@ -0,0 +1,132 @@
|
||||
# Loops, and higher order functions
|
||||
|
||||
Efficient use of these functions can greatly simplify your programs and speed them up, as these functions will
|
||||
internalize most of the operations that need to be applied on multiple values at the same time. Most of them take
|
||||
a `list` argument which can be any iterable structure in scarpet, including generators, like `rect`, or `range`,
|
||||
and maps, where the iterator returns all the map keys
|
||||
|
||||
## Loops
|
||||
|
||||
### `break(), break(expr), continue(), continue(expr)`
|
||||
|
||||
These allow to control execution of a loop either skipping current iteration code, using `continue`, or finishing the
|
||||
current loop, using `break`. `break` and `continue` can only be used inside `for, while, loop, map, filter and reduce`
|
||||
functions, while `break` can be used in `first` as well. Outside of the internal expressions of these functions,
|
||||
calling `break` or `continue` will cause an error. In case of the nexted loops, and more complex setups, use
|
||||
custom `try` and `throw` setup.
|
||||
|
||||
Please check corresponding loop function description what `continue` and `break` do in their contexts, but in
|
||||
general case, passed values to `break` and `continue` will be used in place of the return value of the internal
|
||||
iteration expression.
|
||||
|
||||
### `for(list,expr(_,_i))`
|
||||
|
||||
Evaluates expression over list of items from the `list`. Supplies `_`(value) and `_i`(iteration number) to the `expr`.
|
||||
|
||||
Returns the number of times `expr` was successful. Uses `continue` and `break` argument in place of the returned
|
||||
value from the `expr`(if supplied), to determine if the iteration was successful.
|
||||
|
||||
<pre>
|
||||
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
|
||||
for(range(1000000,1100000),check_prime(_)) => 7216
|
||||
</pre>
|
||||
|
||||
From which we can learn that there is 7216 primes between 1M and 1.1M
|
||||
|
||||
### `while(cond, limit)`
|
||||
|
||||
Evaluates expression `expr` repeatedly until condition `cond` becomes false, but not more than `limit` times.
|
||||
Returns the result of the last `expr` evaluation, or `null` if nothing was successful. Both `expr` and `cond` will
|
||||
recveived a bound variable `_` indicating current iteration, so its a number.
|
||||
|
||||
<pre>
|
||||
while(a<100,10,a=_*_) => 81 // loop exhausted via limit
|
||||
while(a<100,20,a=_*_) => 100 // loop stopped at condition, but a has already been assigned
|
||||
while(_*_<100,20,a=_*_) => 81 // loop stopped at condition, before a was assigned a value
|
||||
</pre>
|
||||
|
||||
### `loop(num,expr(_),exit(_)?)`
|
||||
|
||||
Evaluates expression `expr`, `num` number of times. code>expr receives `_` system variable indicating the iteration.
|
||||
|
||||
<pre>
|
||||
loop(5, game_tick()) => repeat tick 5 times
|
||||
list = l(); loop(5, x = _; loop(5, list += l(x, _) ) ); list
|
||||
// double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]
|
||||
</pre>
|
||||
|
||||
In this small example we will search for first 10 primes, apparently including 0:
|
||||
|
||||
<pre>
|
||||
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
|
||||
primes = l();
|
||||
loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break())));
|
||||
primes
|
||||
// outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
|
||||
</pre>
|
||||
|
||||
## Higher Order Functions
|
||||
|
||||
### `map(list,expr(_,_i))`
|
||||
|
||||
Converts a `list` of values, to another list where each value is result of an expression `v = expr(_, _i)`
|
||||
where `_` is passed as each element of the list, and `_i` is the index of such element. If `break` is called the
|
||||
map returns whatever collected thus far. If `continue` and `break` are used with supplied argument, it is used in
|
||||
place of the resulting map element, otherwise current element is skipped.
|
||||
|
||||
<pre>
|
||||
map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||
map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
|
||||
</pre>
|
||||
|
||||
### `filter(list,expr(_,_i))`
|
||||
|
||||
filters `list` elements returning only these that return positive result of the `expr`. With `break` and `continue`
|
||||
statements, the supplied value can be used as a boolean check instead.
|
||||
|
||||
<pre>
|
||||
filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
|
||||
map(filter(entity_list('*'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
|
||||
</pre>
|
||||
|
||||
### `first(list,expr(_,_i))`
|
||||
|
||||
Finds and returns the first item in the list that satisfies `expr`. It sets `_` for current element value,
|
||||
and `_i` for index of that element. `break` can be called inside the iteration code, using its argument value
|
||||
instead of the current item. `continue` has no sense and cannot be called inside `first` call.
|
||||
|
||||
<pre>
|
||||
first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 // first prime after 1000
|
||||
</pre>
|
||||
|
||||
Notice in the example above, that we needed to rename the outer `_` to be albe to use in in the inner `first` call
|
||||
|
||||
### `all(list,expr(_,_i))`
|
||||
|
||||
Returns `true` if all elements on the list satisfy the condition. Its roughly equivalent
|
||||
to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound `_` and `_i` variables. `break`
|
||||
and `continue` have no sense and cannot be used inside of `expr` body.
|
||||
|
||||
<pre>
|
||||
all([1,2,3], check_prime(_)) => 1
|
||||
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
|
||||
map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
|
||||
=> [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
|
||||
// find all 3x3 bedrock structures in the top bedrock layer
|
||||
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
|
||||
all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
|
||||
pos(_) ) => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
|
||||
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
|
||||
</pre>
|
||||
|
||||
### `reduce(list,expr(_a,_,_i), initial)`
|
||||
|
||||
Applies `expr` for each element of the list and saves the result in `_a` accumulator. Consecutive calls to `expr`
|
||||
can access that value to apply more values. You also need to specify the initial value to apply for the
|
||||
accumulator. `break` can be used to terminate reduction prematurely. If a value is provided to `break` or `continue`,
|
||||
it will be used from now on as a new value for the accumulator.
|
||||
|
||||
<pre>
|
||||
reduce([1,2,3,4],_a+_,0) => 10
|
||||
reduce([1,2,3,4],_a*_,1) => 24
|
||||
</pre>
|
||||
@@ -0,0 +1,120 @@
|
||||
# Arithmetic operations
|
||||
|
||||
## Basic Arithmetic Functions
|
||||
|
||||
There is bunch of them - they require a number and spit out a number, doing what you would expect them to do.
|
||||
|
||||
### `fact(n)`
|
||||
|
||||
Factorial of a number, a.k.a `n!`, just not in `scarpet`. Gets big... quick...
|
||||
|
||||
### `sqrt(n)`
|
||||
|
||||
Square root. For other fancy roots, use `^`, math and yo noggin. Imagine square roots on a tree...
|
||||
|
||||
### `abs(n)`
|
||||
|
||||
Absolut value.
|
||||
|
||||
### `round(n)`
|
||||
|
||||
Closest integer value. Did you know the earth is also round?
|
||||
|
||||
### `floor(n)`
|
||||
|
||||
Highest integer that is still no larger then `n`. Insert a floor pun here.
|
||||
|
||||
### `ceil(n)`
|
||||
|
||||
First lucky integer that is not smalller than `n`. As you would expect, ceiling is typically right above the floor.
|
||||
|
||||
### `ln(n)`
|
||||
|
||||
Natural logarithm of `n`. Naturally.
|
||||
|
||||
### `ln1p(n)`
|
||||
|
||||
Natural logarithm of `n+1`. Very optimistic.
|
||||
|
||||
### `log10(n)`
|
||||
|
||||
Decimal logarithm of `n`. Its ceiling is the length of its floor.
|
||||
|
||||
### `log(n)`
|
||||
|
||||
Binary logarithm of `n`. Finally, a proper one, not like the previous 11.
|
||||
|
||||
### `log1p(n)`
|
||||
|
||||
Binary logarithm of `n+1`. Also always positive.
|
||||
|
||||
### `mandelbrot(a, b, limit)`
|
||||
|
||||
Computes the value of the mandelbrot set, for set `a` and `b` spot. Spot the beetle. Why not.
|
||||
|
||||
### `min(arg, ...), min(list), max(arg, ...), max(list)`
|
||||
|
||||
Compute minimum or maximum of supplied arguments assuming default sorthoraphical order.
|
||||
In case you are missing `argmax`, just use `a ~ max(a)`, little less efficient, but still fun.
|
||||
|
||||
Interesting bit - `min` and `max` don't remove variable associations from arguments, which means can be used as
|
||||
LHS of assignments (obvious case), or argument spec in function definitions (far less obvious).
|
||||
|
||||
<pre>
|
||||
a = 1; b = 2; min(a,b) = 3; l(a,b) => [3, 2]
|
||||
a = 1; b = 2; fun(x, min(a,b)) -> l(a,b); fun(3,5) => [5, 0]
|
||||
</pre>
|
||||
|
||||
Absolutely no idea, how the latter might be useful in practice. But since it compiles, can ship it.
|
||||
|
||||
### `relu(n)`
|
||||
|
||||
Linear rectifier of `n`. 0 below 0, n above. Why not. `max(0,n)` with less moral repercussions.
|
||||
|
||||
## Trigonometric / Geometric Functions
|
||||
|
||||
### `sin(x)`
|
||||
|
||||
### `cos(x)`
|
||||
|
||||
### `tan(x)`
|
||||
|
||||
### `asin(x)`
|
||||
|
||||
### `acos(x)`
|
||||
|
||||
### `atan(x)`
|
||||
|
||||
### `atan2(x,y)`
|
||||
|
||||
### `sinh(x)`
|
||||
|
||||
### `cosh(x)`
|
||||
|
||||
### `tanh(x)`
|
||||
|
||||
### `sec(x)`
|
||||
|
||||
### `csc(x)`
|
||||
|
||||
### `sech(x)`
|
||||
|
||||
### `csch(x)`
|
||||
|
||||
### `cot(x)`
|
||||
|
||||
### `acot(x)`
|
||||
|
||||
### `coth(x)`
|
||||
|
||||
### `asinh(x)`
|
||||
|
||||
### `acosh(x)`
|
||||
|
||||
### `atanh(x)`
|
||||
|
||||
### `rad(deg)`
|
||||
|
||||
### `deg(rad)`
|
||||
|
||||
Use as you wish
|
||||
@@ -0,0 +1,215 @@
|
||||
# Operators
|
||||
|
||||
There is a number of operators you can use inside the expressions. Those could be considered generic type operators
|
||||
that apply to most data types. They also follow standard operator precedence, i.e. `2+2*2` is understood
|
||||
as `2+(2*2)`, not `(2+2)*2`, otherwise they are applied from left to right, i.e. `2+4-3` is interpreted
|
||||
as `(2+4)-3`, which in case of numbers doesn't matter, but since `scarpet` allows for mixing all value types
|
||||
the associativity would matter, and may lead to unintended effects:
|
||||
|
||||
Important operator is function definition `->` operator. It will be covered
|
||||
in {@link carpet.script.Expression#UserDefinedFunctionsAndControlFlow}
|
||||
|
||||
<pre>
|
||||
'123'+4-2 => ('123'+4)-2 => '1234'-2 => '134'
|
||||
'123'+(4-2) => '123'+2 => '1232'
|
||||
3*'foo' => 'foofoofoo'
|
||||
1357-5 => 1352
|
||||
1357-'5' => 137
|
||||
3*'foo'-'o' => 'fff'
|
||||
l(1,3,5)+7 => l(8,10,12)
|
||||
</pre>
|
||||
|
||||
As you can see, values can behave differently when mixed with other types in the same expression.
|
||||
In case values are of the same types, the result tends to be obvious, but `Scarpet` tries to make sense of whatever
|
||||
it has to deal with
|
||||
|
||||
## Operator Precedence
|
||||
|
||||
Here is the complete list of operators in `scarpet` including control flow operators. Note, that commas and brackets
|
||||
are not technically operators, but part of the language, even if they look like them:
|
||||
|
||||
* Match, Get `~ :`
|
||||
* Unary `+ - !`
|
||||
* Exponent `^`
|
||||
* Multiplication `* / %`
|
||||
* Addition `+ -`
|
||||
* Comparison `== != > >= <= <`
|
||||
* Logical And`&&`
|
||||
* Logical Or `||`
|
||||
* Assignment `= += <>`
|
||||
* Definition `->`
|
||||
* Next statement`;`
|
||||
* Comma `,`
|
||||
* Bracket `( )`
|
||||
|
||||
### `Get, Accessor Operator :`
|
||||
|
||||
Operator version of the `get(...)` function to access elements of lists, maps, and potentially other containers
|
||||
(i.e. NBTs). It is important to distinguish from `~` operator, which is a matching operator, which is expected to
|
||||
perform some extra computations to retrieve the result, while `:` should be straightforward and immediate, and
|
||||
the source object should behave like a container and support full container API,
|
||||
meaning `get(...)`, `put(...)`, `delete(...)`, and `has(...)` functions
|
||||
|
||||
For certain operators and functions (get, put, delete, has, =, +=) objects can use `:` annotated fields as l-values,
|
||||
meaning construct like `foo:0 = 5`, would act like `put(foo, 0, 5)`, rather than `get(foo, 0) = 5`,
|
||||
which would result in an error.
|
||||
|
||||
TODO: add more information about l-value behaviour.
|
||||
|
||||
### `Matching Operator ~`
|
||||
|
||||
This operator should be understood as 'matches', 'contains', 'is_in', or 'find me some stuff about something else.
|
||||
For strings it matches the right operand as a regular expression to the left, returning the first match.
|
||||
This can be used to extract information from unparsed nbt's in a more convoluted way (use `get(...)` for
|
||||
more appropriate way of doing it). For lists it checks if an element is in the list, and returns the
|
||||
index of that element, or `null` if no such element was found, especially that the use of `first(...)`
|
||||
function will not return the index. Currently it doesn't have any special behaviour for numbers - it checks for
|
||||
existence of characters in string representation of the left operand with respect of the regular expression on
|
||||
the right hand side.
|
||||
|
||||
In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,feature)` for queries that do not take
|
||||
any extra arguments.
|
||||
|
||||
<pre>
|
||||
l(1,2,3) ~ 2 => 1
|
||||
l(1,2,3) ~ 4 => null
|
||||
'foobar' ~ '.b' => 'ob'
|
||||
player('*') ~ 'gnembon' // null unless player gnembon is logged in (better to use player('gnembon') instead
|
||||
p ~ 'sneaking' // if p is an entity returns whether p is sneaking
|
||||
</pre>
|
||||
|
||||
Or a longer example of an ineffective way to searching for a squid
|
||||
|
||||
<pre>
|
||||
entities = entities_area('all',x,y,z,100,10,100);
|
||||
sid = entities ~ 'Squid';
|
||||
if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
|
||||
</pre>
|
||||
|
||||
Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level
|
||||
(not using proper NBTs query support via `get(...)`):
|
||||
|
||||
<pre>
|
||||
global_get_enchantment(p, ench) -> (
|
||||
$ for(l('main','offhand'),
|
||||
$ holds = query(p, 'holds', _);
|
||||
$ if( holds,
|
||||
$ l(what, count, nbt) = holds;
|
||||
$ if( what ~ '_axe' && nbt ~ ench,
|
||||
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
||||
$ )
|
||||
$ )
|
||||
$ );
|
||||
$ lvl
|
||||
$);
|
||||
/script run global_get_enchantment(players(), 'sharpness')
|
||||
</pre>
|
||||
|
||||
### `Basic Arithmetic Operators + - * /`
|
||||
|
||||
Allows to add the results of two expressions. If the operands resolve to numbers, the result is arithmetic operation.
|
||||
In case of strings, adding or subtracting from a string results in string concatenation and removal of substrings
|
||||
from that string. Multiplication of strings and numbers results in repeating the string N times and division results
|
||||
in taking the first k'th part of the string, so that `str*n/n ~ str` In case first operand is a list, either it
|
||||
results in a new list with all elements modified one by one with the other operand, or if the operand is a list
|
||||
with the same number of items - element-wise addition/subtraction
|
||||
|
||||
Examples:
|
||||
|
||||
<pre>
|
||||
2+3 => 5
|
||||
'foo'+3+2 => 'foo32'
|
||||
'foo'+(3+2) => 'foo5'
|
||||
3+2+'bar' => '5bar'
|
||||
'foo'*3 => 'foofoofoo'
|
||||
'foofoofoo' / 3 => 'foo'
|
||||
'foofoofoo'-'o' => 'fff'
|
||||
l(1,2,3)+1 => l(2,3,4)
|
||||
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
|
||||
</pre>
|
||||
|
||||
### `Just Operators % ^`
|
||||
|
||||
The modulo and exponent (power) operators work only if both operands are numbers
|
||||
|
||||
<pre>pi^pi%euler => 1.124....
|
||||
-9 % 4 => -1
|
||||
9 % -4 => 0 ¯\_(ツ)_/¯ Java
|
||||
-3 ^ 2 => 9
|
||||
-3 ^ pi => // Error
|
||||
</pre>
|
||||
|
||||
### `Comparison Operators == != < > <= >=`
|
||||
|
||||
Allows to compare the results of two expressions. For numbers it is considers arithmetic order of numbers, for
|
||||
strings - lexicographical, nulls are always 'less' than everything else, and lists check their elements -
|
||||
if the sizes are different, the size matters, otherwise, pairwise comparisons for each elements are performed.
|
||||
The same order rules than with all these operators are used with the default sortographical order as used by `sort`
|
||||
function. All of these are true:
|
||||
|
||||
<pre>
|
||||
null == null
|
||||
null != false
|
||||
0 == false
|
||||
1 == true
|
||||
null < 0
|
||||
null < -1000
|
||||
1000 < 'a'
|
||||
'bar' < 'foo'
|
||||
3 == 3.0
|
||||
</pre>
|
||||
|
||||
### `Logical Operators && ||`
|
||||
|
||||
These operator compute respective boolean operation on the operands. What it important is that if calculating of the
|
||||
second operand is not necessary, it won't be evaluated, which means one can use them as conditional statements. In
|
||||
case of success returns first positive operand (`||`) or last one (`&&`).
|
||||
|
||||
<pre>
|
||||
true || false => 1
|
||||
null || false => 0
|
||||
null != false || run('kill gnembon') => 1 // gnembon survives
|
||||
null != false && run('kill gnembon') => 0 // when cheats not allowed
|
||||
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
|
||||
</pre>
|
||||
|
||||
### `Assignment Operators = <> +=`
|
||||
|
||||
A set of assignment operators. All require bounded variable on the LHS, `<>` requires bounded arguments on the
|
||||
right hand side as well (bounded, meaning being variables). Additionally they can also handle list constructors
|
||||
with all bounded variables, and work then as list assignment operators. When `+=` is used on a list, it extends
|
||||
that list of that element, and returns the list (old == new). `scarpet` doesn't support currently removal of items.
|
||||
Removal of items can be obtaine via `filter` command, and reassigning it fo the same variable. Both operations would
|
||||
require rewriting of the array anyways.
|
||||
|
||||
<pre>
|
||||
a = 5 => a == 5
|
||||
l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
|
||||
l(minx,maxx) = sort(xi,xj); // minx assumes min(xi, xj) and maxx, max(xi, xj)
|
||||
l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f) => [3,4,5,0,1,2]
|
||||
a = l(1,2,3); a += 4 => [1,2,3,4]
|
||||
a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]
|
||||
</pre>
|
||||
|
||||
### `Unary Operators - +`
|
||||
|
||||
Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg.
|
||||
|
||||
<pre>
|
||||
-4 => -4
|
||||
+4 => 4
|
||||
+'4' // Error message
|
||||
</pre>
|
||||
|
||||
### `Negation Operator !`
|
||||
|
||||
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
||||
|
||||
<pre>
|
||||
!true => 0
|
||||
!false => 1
|
||||
!null => 1
|
||||
!5 => 0
|
||||
!l() => 1
|
||||
!l(null) => 0
|
||||
</pre>
|
||||
@@ -0,0 +1,276 @@
|
||||
# Fundamental components of `scarpet` programming language (towards version 1.7).
|
||||
|
||||
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
||||
designed to provide the ability to write custom programs to run within Minecraft
|
||||
and interact with the world.
|
||||
|
||||
The project was initially built based on the EvalEx project, however it now diverged
|
||||
so far away from the original implementation, it would be hard to tell it without
|
||||
this mention. EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
||||
simple mathematical and boolean expressions. For more information,
|
||||
see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
||||
|
||||
This specification is divided into two sections: this one is agnostic to any
|
||||
Minecraft related features and could function on its own, and CarpetExpression
|
||||
for Minecraft specific routines and world manipulation functions.
|
||||
|
||||
# Synopsis
|
||||
|
||||
<pre>
|
||||
script run print('Hello World!')
|
||||
</pre>
|
||||
|
||||
or an OVERLY complex example:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
|
||||
(
|
||||
l(minx, maxx) = sort(l(x1, x2));
|
||||
l(miny, maxy) = sort(l(y1, y2));
|
||||
l(minz, maxz) = sort(l(z1, z2));
|
||||
'Need to compute the size of the area of course';
|
||||
'Cause this language doesn\'t support comments in the command mode';
|
||||
xsize = maxx - minx + 1;
|
||||
ysize = maxy - miny + 1;
|
||||
zsize = maxz - minz + 1;
|
||||
total_count = 0;
|
||||
loop(xsize,
|
||||
xx = minx + _ ;
|
||||
loop(ysize,
|
||||
yy = miny + _ ;
|
||||
loop(zsize,
|
||||
zz = minz + _ ;
|
||||
if ( block(xx,yy,zz) == block_to_check,
|
||||
total_count += ceil(rand(1))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
total_count
|
||||
);
|
||||
check_area_around_closest_player_for_block(block_to_check) ->
|
||||
(
|
||||
closest_player = player();
|
||||
l(posx, posy, posz) = query(closest_player, 'pos');
|
||||
total_count = block_check( posx-8,1,posz-8, posx+8,17,posz+8, block_to_check);
|
||||
print('There is '+total_count+' of '+block_to_check+' around you')
|
||||
)
|
||||
/script invoke check_area_around_closest_player_for_block 'diamond_ore'
|
||||
</pre>
|
||||
|
||||
or simply
|
||||
|
||||
<pre>
|
||||
/script run print('There is'+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
|
||||
</pre>
|
||||
|
||||
It definitely pays to check what higher level `scarpet` functions have to offer.
|
||||
|
||||
# Programs
|
||||
|
||||
You can think of an program like a mathematical expression, like `"2.4*sin(45)/(2-4)"` or `"sin(y)>0 & max(z, 3)>3"`.
|
||||
Writing a program, is like writing a `2+3`, just a bit longer.
|
||||
|
||||
## Basic language components
|
||||
|
||||
Programs consist of constants, like `2`, `3.14`, `pi`, or `'foo'`, operators like `+`, `/`, `->`, variables which you
|
||||
can define, like `foo` or special ones that will be defined for you, like `_x`, or `_` , which I specific to a each
|
||||
built in function, and functions with name, and arguments in the form of `f(a,b,c)`, where `f` is the function name,
|
||||
and `a, b, c` are the arguments which can be any other expression. And that's all the parts of the language, so all
|
||||
in all - sounds quite simple.
|
||||
|
||||
## Code flow
|
||||
|
||||
Like any other proper programming language, `scarpet` needs brackets, basically to identify where stuff begins and
|
||||
where it ends. In the languages that uses much more complicated constructs, like Java, they tend to use all sort of
|
||||
them, round ones to indicate function calls, curly to indicate section of code, square to access lists, pointy for
|
||||
generic types etc... I mean - there is no etc, cause they have exhausted all the bracket options...
|
||||
|
||||
`Scarpet` is different, since it runs everything based on functions (although its not per se a functional
|
||||
language like lisp) only needs the round brackets for everything, and it is up to the programmer to organize
|
||||
its code so its readable, as adding more brackets does not have any effect on the performance of the programs
|
||||
as they are compiled before they are executed. Look at the following example usage of `if()` function:
|
||||
|
||||
<pre>
|
||||
if(x<y+6,set(x,8+y,z,'air');plop(x,top('surface',x,z),z,'birch'),sin(query(player(),'yaw'))>0.5,plop(0,0,0,'boulder'),particle('fire',x,y,z))
|
||||
</pre>
|
||||
|
||||
Would you prefer to read
|
||||
|
||||
<pre>
|
||||
if( x<y+6,
|
||||
set(x,8+y,z,'air');
|
||||
plop(x,top('surface',x,z),z,'birch'),
|
||||
sin(query(player(),'yaw'))>0.5,
|
||||
plop(0,0,0,'boulder'),
|
||||
particle('fire',x,y,z)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Or rather:
|
||||
|
||||
<pre>
|
||||
if
|
||||
(
|
||||
x<y+6,
|
||||
(
|
||||
set(x,8+y,z,'air');
|
||||
plop(x,top('surface',x,z),z,'birch')
|
||||
),
|
||||
sin(query(player(),'yaw'))>0.5,
|
||||
(
|
||||
plop(0,0,0,'boulder')
|
||||
),
|
||||
particle('fire',x,y,z)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Whichever style you prefer it doesn't matter. It typically depends on the situation and the complexity of the
|
||||
subcomponents. No matter how many whitespaces and extra brackets you add - the code will evaluate to exactly the
|
||||
same expression, and will run exactly the same, so make sure your programs are nice and clean so others don't
|
||||
have problems with them
|
||||
|
||||
## Functions and scoping
|
||||
|
||||
Users can define functions in the form `fun(args....) -> expression` and they are compiled and saved for further
|
||||
execution in this but also subsequent calls of /script command. Functions can also be assigned to variables,
|
||||
passed as arguments, called with `call` function, but in most cases you would want to call them directly by
|
||||
name, in the form of `fun(args...)`. This means that once defined functions are saved with the world for
|
||||
further use. For variables, there are two types of them, global - which are shared anywhere in the code,
|
||||
and those are all which name starts with 'global_', and local variables which is everything else and those
|
||||
are only visible inside each function. This also means that all the parameters in functions are
|
||||
passed 'by value', not 'by reference'.
|
||||
|
||||
## Outer variables
|
||||
|
||||
Functions can still 'borrow' variables from the outer scope, by adding them to the function signature wrapped
|
||||
around built-in function `outer`. It adds the specified value to the function call stack so they behave exactly
|
||||
like capturing lambdas in Java, but unlike java captured variables don't need to be final. Scarpet will just
|
||||
attach their new values at the time of the function definition, even if they change later. Most value will be
|
||||
copied, but mutable values, like maps or lists, allow to keep the 'state' with the function, allowing them to
|
||||
have memory and act like objects so to speak. . Check `outer(var)` for details.
|
||||
|
||||
## Code delivery, line indicators
|
||||
|
||||
Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing
|
||||
your code in apps (files with `.sc` extension that can be placed inside "/scripts" folder in the world files and
|
||||
loaded as a scarpet app with command `/script load [app_name]`. Scarpet apps loaded from disk should only
|
||||
contain code, no need to start with "/script run" prefix
|
||||
|
||||
The following is the code that could be provided in a `foo.sc` app file located in world `/scripts` folder
|
||||
|
||||
<pre>
|
||||
run_program() -> (
|
||||
loop( 10,
|
||||
// looping 10 times
|
||||
// comments are allowed in scripts located in world files
|
||||
// since we can tell where that line ends
|
||||
foo = floor(rand(10));
|
||||
check_not_zero(foo);
|
||||
print(_+' - foo: '+foo);
|
||||
print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
);
|
||||
check_not_zero(foo) -> (
|
||||
if (foo==0, foo = 1)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Which we then call in-game with:
|
||||
|
||||
<pre>
|
||||
/script load foo
|
||||
/script in foo invoke run_program
|
||||
</pre>
|
||||
|
||||
However the following code can also be input as a command, or in a command block.
|
||||
|
||||
Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your
|
||||
programs by pasting them to command blocks or reading from world files, however pasting to command blocks will
|
||||
remove some whitespaces and squish your newlines making the code not readable. If you are pasting a program that
|
||||
is perfect and will never cause an error, I salute you, but for the most part it is quite likely that your program
|
||||
might break, either at compile time, when its initially analyzed, or at execute time, when you suddenly attempt to
|
||||
divide something by zero. In these cases you would want to get a meaningful error message, but for that you would
|
||||
need to indicate for the compiler where did you put these new lines, since command block would squish them. For that,
|
||||
place at the beginning of the line to let the compiler know where are you. This makes so that `$` is the only
|
||||
character that is illegal in programs, since it will be replaced with new lines. As far as I know, `$` is not
|
||||
used anywhere inside Minecraft identifiers, so this shouldn't hinder the abilities of your programs.
|
||||
|
||||
Consider the following program executed as command block command:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
run_program() -> (
|
||||
loop( 10,
|
||||
foo = floor(rand(_));
|
||||
check_not_zero(foo);
|
||||
print(_+' - foo: '+foo);
|
||||
print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
);
|
||||
check_not_zero(foo) -> (
|
||||
if (foo==0, foo = 1)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Lets say that the intention was to check if the bar is zero and prevent division by zero in print, but because
|
||||
the `foo` is passed as a variable, it never changes the original foo value. Because of the inevitable division
|
||||
by zero, we get the following message:
|
||||
|
||||
<pre>
|
||||
Your math is wrong, Incorrect number format for NaN at pos 98
|
||||
run_program() -> ( loop( 10, foo = floor(rand(_)); check_not_zero(foo); print(_+' - foo: '+foo);
|
||||
HERE>> print(' reciprocal: '+ _/foo ) ));check_not_zero(foo) -> ( if (foo==0, foo = 1))
|
||||
</pre>
|
||||
|
||||
As we can see, we got our problem where the result of the mathematical operation was not a number
|
||||
(infinity, so not a number), however by pasting our program into the command made it squish the newlines so
|
||||
while it is clear where the error happened and we still can track the error down, the position of the error (98)
|
||||
is not very helpful and wouldn't be useful if the program gets significantly longer. To combat this issue we can
|
||||
precede every line of the script with dollar signs `$`:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
$run_program() -> (
|
||||
$ loop( 10,
|
||||
$ foo = floor(rand(_));
|
||||
$ check_not_zero(foo);
|
||||
$ print(_+' - foo: '+foo);
|
||||
$ print(' reciprocal: '+ _/foo )
|
||||
$ )
|
||||
$);
|
||||
$check_not_zero(foo) -> (
|
||||
$ if (foo==0, foo = 1)
|
||||
$)
|
||||
</pre>
|
||||
|
||||
Then we get the following error message
|
||||
|
||||
<pre>
|
||||
Your math is wrong, Incorrect number format for NaN at line 7, pos 2
|
||||
print(_+' - foo: '+foo);
|
||||
HERE>> print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
</pre>
|
||||
|
||||
As we can note not only we get much more concise snippet, but also information about the line number and position,
|
||||
so means its way easier to locate the potential problems problem
|
||||
|
||||
Obviously that's not the way we intended this program to work. To get it `foo` modified via a function call,
|
||||
we would either return it as a result and assign it to the new variable:
|
||||
|
||||
<pre>
|
||||
foo = check_not_zero(foo);
|
||||
...
|
||||
check_not_zero(foo) -> if(foo == 0, 1, foo)
|
||||
</pre>
|
||||
|
||||
.. or convert it to a global variable, which in this case passing as an argument is not required
|
||||
|
||||
<pre>
|
||||
global_foo = floor(rand(10));
|
||||
check_foo_not_zero();
|
||||
...
|
||||
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
||||
</pre>
|
||||
@@ -0,0 +1,324 @@
|
||||
# System functions
|
||||
|
||||
## Type conversion functions
|
||||
|
||||
### `copy(expr)`
|
||||
|
||||
Returns the deep copy of the expression. Can be used to copy mutable objects, like maps and lists
|
||||
|
||||
### `type(expr)`
|
||||
|
||||
Returns the string value indicating type of the expression. Possible outcomes
|
||||
are `null, number, string, list, iterator`, as well as minecraft related concepts like `block, entity, nbt`
|
||||
|
||||
### `bool(expr)`
|
||||
|
||||
Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is
|
||||
alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other
|
||||
places where boolean context can be used. This can be used in places where API functions return string values to
|
||||
represent binary values
|
||||
|
||||
<pre>
|
||||
bool(pi) => 1
|
||||
bool(false) => 0
|
||||
bool('') => 0
|
||||
bool(l()) => 0
|
||||
bool(l('')) => 1
|
||||
bool('foo') => 1
|
||||
bool('false') => 0
|
||||
bool('nulL') => 0
|
||||
if('false',1,0) => 1
|
||||
</pre>
|
||||
|
||||
### `number(expr)`
|
||||
|
||||
Returns a numeric context of the expression. Can be used to read numbers from strings
|
||||
|
||||
<pre>
|
||||
number(null) => null
|
||||
number(false) => 0
|
||||
number('') => null
|
||||
number('3.14') => 3.14
|
||||
number(l()) => 0
|
||||
number(l('')) => 1
|
||||
number('foo') => null
|
||||
number('3bar') => null
|
||||
number('2')+number('2') => 4
|
||||
</pre>
|
||||
|
||||
### `str(expr, params? ... ), str(expr, param_list)`
|
||||
|
||||
Returns a formatted string representing expression. Accepts formatting style accepted by `String.format`.
|
||||
Supported types (with `"%?"` syntax):
|
||||
|
||||
* d, o, x: integers
|
||||
* a, e, f, g: floats
|
||||
* b: booleans
|
||||
* s: strings
|
||||
|
||||
<pre>
|
||||
str(null) => null
|
||||
str(false) => 0
|
||||
str('') => null
|
||||
str('3.14') => 3.14
|
||||
str(l()) => 0
|
||||
str(l('')) => 1
|
||||
str('foo') => null
|
||||
str('3bar') => null
|
||||
str(2)+str(2) => 22
|
||||
str('pi: %.2f',pi) => 'pi: 3.14'
|
||||
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
||||
</pre>
|
||||
|
||||
* * *
|
||||
## Threading and Parallel Execution
|
||||
|
||||
Scarpet allows to run threads of execution in parallel to the main script execution thread. In Minecraft, the main
|
||||
thread scripts are executed on the main server thread. Since Minecraft is inherently NOT thread safe, it is not that
|
||||
beneficial to parallel execution in order to access world resources faster. Both `GetBlockState` and `setBlockState`
|
||||
are not thread safe and require the execution to park on the server thread, where these requests can be executed in
|
||||
the off-tick time in between ticks that didn't take 50ms. There are however benefits of running things in parallel,
|
||||
like fine time control not relying on the tick clock, or running things independent on each other. You can still run
|
||||
your actions on tick-by-tick basis, either taking control of the execution using `game_tick()` API function
|
||||
(nasty solution), or scheduling tick using `schedule()` function (much nicer solution), but threading often gives
|
||||
the neatest solution to solve problems in parallel (see scarpet camera).
|
||||
|
||||
Due to limitations with the game, there are some limits to the threading as well. You cannot for
|
||||
instance `join_task()` at all from the main script and server thread, because any use of Minecraft specific
|
||||
function that require any world access, will require to park and join on the main thread to get world access,
|
||||
meaning that calling join on that task would inevitably lead to a typical deadlock. You can still join tasks
|
||||
from other threads, just because the only possibility of a deadlock in this case would come explicitly from your
|
||||
bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, can be
|
||||
effectively parallelized.
|
||||
|
||||
### `task(function, ?args...., ?executor)`
|
||||
|
||||
Creates and runs a parallel task, returning the handle to the task object. Task will return the return value of the
|
||||
function when its completed, or will return `null` immediately if task is still in progress, so grabbing a value of
|
||||
a task object is non-blocking. Function can be either function value, or function lambda, or a name of an existing
|
||||
defined function. In case function needs arguments to be called with, they should be supplied after the function
|
||||
name, or value. Optional `executor` identifier is to place the task in a specific queue identified by this value.
|
||||
The default thread value is the `null` thread. There is no limits on number of parallel tasks for any executor,
|
||||
so using different queues is solely for synchronization purposes. Also, since `task` function knows how many extra
|
||||
arguments it requires, the use of tailing optional parameter for the custom executor thread pool is not ambiguous.
|
||||
|
||||
<pre>
|
||||
task( _() -> print('Hello Other World') ) => Runs print command on a separate thread
|
||||
foo(a, b) -> print(a+b); task('foo',2,2) => Uses existing function definition to start a task
|
||||
task('foo',3,5,'temp'); => runs function foo with a different thread executor, identified as 'temp'
|
||||
a = 3; task( _(outer(a), b) -> foo(a,b), 5, 'temp')
|
||||
=> Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
|
||||
</pre>
|
||||
|
||||
### `task_count(executor?)`
|
||||
|
||||
If no argument provided, returns total number of tasks being executed in parallel at this moment using scarpet
|
||||
threading system. If the executor is provided, returns number of active tasks for that provider. Use `task_count(null)`
|
||||
to get the task count of the default executor only.
|
||||
|
||||
### `task_value(task)`
|
||||
|
||||
Returns the task return value, or `null` if task hasn't finished yet. Its a non-blocking operation. Unlike `join_task`,
|
||||
can be called on any task at any point
|
||||
|
||||
### `task_join(task)`
|
||||
|
||||
Waits for the task completion and returns its computed value. If the task has already finished returns it immediately.
|
||||
Unless taking the task value directly, i.e. via `task_value`, this operation is blocking. Since Minecraft has a
|
||||
limitation that all world access operations have to be performed on the main game thread in the off-tick time,
|
||||
joining any tasks that use Minecraft API from the main thread would mean automatic lock, so joining from the main
|
||||
thread is not allowed. Join tasks from other threads, if you really need to, or communicate asynchronously with
|
||||
the task via globals or function data / arguments to monitor its progress, communicate, get partial results,
|
||||
or signal termination.
|
||||
|
||||
### `task_completed(task)`
|
||||
|
||||
Returns true if task has completed, or false otherwise.
|
||||
|
||||
### `synchronize(lock, expression)`
|
||||
|
||||
Evaluates `expression` synchronized with respect to the lock `lock`. Returns the value of the expression.
|
||||
|
||||
* * *
|
||||
|
||||
## Auxiliary functions
|
||||
|
||||
### `lower(expr), upper(expr), title(expr)`
|
||||
|
||||
Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression
|
||||
|
||||
<pre>
|
||||
lower('aBc') => 'abc'
|
||||
upper('aBc') => 'ABC'
|
||||
title('aBc') => 'Abc'
|
||||
</pre>
|
||||
|
||||
### `replace(string, regex, repl?); replace_first(string, regex, repl?)`
|
||||
|
||||
Replaces all, or first occurence of a regular expression in the string with `repl` expression,
|
||||
or nothing, if not specified
|
||||
|
||||
<pre>
|
||||
replace('abbccddebfg','b+','z') // => azccddezfg
|
||||
replace_first('abbccddebfg','b+','z') // => azccddebfg
|
||||
</pre>
|
||||
|
||||
### `length(expr)`
|
||||
|
||||
Returns length of the expression, the length of the string, the length of the integer part of the number,
|
||||
or length of the list
|
||||
|
||||
<pre>
|
||||
length(pi) => 1
|
||||
length(pi*pi) => 1
|
||||
length(pi^pi) => 2
|
||||
length(l()) => 0
|
||||
length(l(1,2,3)) => 3
|
||||
length('') => 0
|
||||
length('foo') => 3
|
||||
</pre>
|
||||
|
||||
### `rand(expr), rand(expr, seed)`
|
||||
|
||||
returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions,
|
||||
boolean functions, or `bool`), returns false if the randomly selected value is less than 1\. This means
|
||||
that `rand(2)` returns true half of the time and `rand(5)` returns true for 80% (4/5) of the time. If seed is not
|
||||
provided, uses a random seed. If seed is provided, each consecutive call to rand() will act like 'next' call to the
|
||||
same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number
|
||||
(per app), then your random sequence will revert to the beginning.
|
||||
|
||||
<pre>
|
||||
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
|
||||
map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
|
||||
map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
|
||||
</pre>
|
||||
|
||||
### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)`
|
||||
|
||||
returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples
|
||||
from is `0`, but seed can be specified as a 4th argument as well. In case you need 1D or 2D noise values with custom
|
||||
seed, use `null` for `z`, or `y` and `z` arguments respectively.
|
||||
|
||||
Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier.
|
||||
Querying for lower-dimensional result, rather than affixing unused dimensions to constants has a speed benefit,
|
||||
|
||||
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds
|
||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||
|
||||
### `simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)`
|
||||
|
||||
returns a noise value from `0.0` to `1.0` (roughly) for 2 or 3 dimensional coordinate. The default seed it samples
|
||||
from is `0`, but seed can be specified as a 4th argument as well. In case you need 2D noise values with custom seed,
|
||||
use `null` for `z` argument.
|
||||
|
||||
Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. To sample 1D simplex
|
||||
noise, affix other coordinate to a constant.
|
||||
|
||||
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds
|
||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||
|
||||
### `print(expr)`
|
||||
|
||||
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
||||
so `print`-statements can be weaved in code to debug programming issues
|
||||
|
||||
<pre>
|
||||
print('foo') => results in foo, prints: foo
|
||||
a = 1; print(a = 5) => results in 5, prints: 5
|
||||
a = 1; print(a) = 5 => results in 5, prints: 1
|
||||
print('pi = '+pi) => prints: pi = 3.141592653589793
|
||||
print(str('pi = %.2f',pi)) => prints: pi = 3.14
|
||||
</pre>
|
||||
|
||||
### `sleep(expr)`
|
||||
|
||||
Halts the execution of the program (and the game itself) for `expr` milliseconds. All in all, its better to
|
||||
use `game_tick(expr)` to let the game do its job while the program waits
|
||||
|
||||
<pre>
|
||||
sleep(50)
|
||||
</pre>
|
||||
|
||||
### `time()`
|
||||
|
||||
Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`.
|
||||
It returns a float, which has 1 microsecond precision (0.001 ms)
|
||||
|
||||
<pre>
|
||||
start_time = time();
|
||||
flip_my_world_upside_down();
|
||||
print(str('this took %d milliseconds',time()-start_time))
|
||||
</pre>
|
||||
|
||||
### `profile_expr(expression)`
|
||||
|
||||
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
||||
Note that, even if its only a number, it WILL run these commands, so if they are destructive, you need to be careful.
|
||||
|
||||
* * *
|
||||
|
||||
## Access to variables and stored functions (use with caution)
|
||||
|
||||
### `var(expr)`
|
||||
|
||||
Returns the variable under the name of the string value of the expression. Allows to manipulate variables in more
|
||||
programmatic manner, which allows to use local variable set with a hash map type key-value access,
|
||||
can also be used with global variables
|
||||
|
||||
<pre>
|
||||
a = 1; var('a') = 'foo'; a => a == 'foo'
|
||||
</pre>
|
||||
|
||||
### `undef(expr)`
|
||||
|
||||
Removes all bindings of a variable with a name of `expr`. Removes also all function definitions with that name.
|
||||
It can affect global variable pool, and local variable set for a particular function.
|
||||
|
||||
<pre>
|
||||
inc(i) -> i+1; foo = 5; inc(foo) => 6
|
||||
inc(i) -> i+1; foo = 5; undef('foo'); inc(foo) => 1
|
||||
inc(i) -> i+1; foo = 5; undef('inc'); undef('foo'); inc(foo) => Error: Function inc is not defined yet at pos 53
|
||||
undef('pi') => bad idea - removes hidden variable holding the pi value
|
||||
undef('true') => even worse idea, unbinds global true value, all references to true would now refer to the default 0
|
||||
</pre>
|
||||
|
||||
### `vars(prefix)`
|
||||
|
||||
It returns all names of variables from local scope (if prefix does not start with 'global') or global variables
|
||||
(otherwise). Here is a larger example that uses combination of `vars` and `var` functions to be
|
||||
used for object counting
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
$ count_blocks(ent) -> (
|
||||
$ l(cx, cy, cz) = query(ent, 'pos');
|
||||
$ scan(cx, cy, cz, 16, 16, 16, var('count_'+_) += 1);
|
||||
$ for ( sort_key( vars('count_'), -var(_)),
|
||||
$ print(str( '%s: %d', slice(_,6), var(_) ))
|
||||
$ )
|
||||
$ )
|
||||
/script run count_blocks(player())
|
||||
</pre>
|
||||
|
||||
* * *
|
||||
|
||||
## System key-value storage
|
||||
|
||||
Scarpet runs apps in isolation. The can share code via use of shared libraries, but each library that is imported to
|
||||
each app is specific to that app. Apps can store and fetch state from disk, but its restricted to specific locations
|
||||
meaning apps cannot interact via disk either. To facilitate communication for interappperability, scarpet hosts its
|
||||
own key-value storage that is shared between all apps currently running on the host, providing methods for getting an
|
||||
associated value with optional setting it if not present, and an operation of modifying a content of a system
|
||||
global value.
|
||||
|
||||
### `system_variable_get(key, default_value ?)`
|
||||
|
||||
Returns the variable from the system shared key-value storage keyed with a `key` value, optionally if value is
|
||||
not present, and default expression is provided, sets a new value to be associated with that key
|
||||
|
||||
### `system_variable_set(key, new_value)`
|
||||
|
||||
Returns the variable from the system shared key-value storage keyed with a `key` value, and sets a new
|
||||
mapping for the key
|
||||
@@ -0,0 +1,29 @@
|
||||
# Variables and Constants
|
||||
|
||||
`scarpet` provides a number of constants that can be used literally in scripts
|
||||
|
||||
* `null`: nothing, zilch, not even false
|
||||
* `true`: pure true, or just 1 (one)
|
||||
* `false`: false truth, or true falsth, 0 (zero) actually
|
||||
* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14
|
||||
* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72
|
||||
|
||||
Apart from that, there is a bunch of system variables, that start with `_` that are set by `scarpet` built-ins,
|
||||
like `_`, typically each consecutive value in loops, `_i` indicating iteration, or `_a` like an accumulator
|
||||
for `reduce` function. Certain calls to Minecraft specific calls would also set `_x`, `_y`, `_z`, indicating
|
||||
block positions. All variables starting with `_` are read-only, and cannot be declared and modified in client code.
|
||||
|
||||
## Literals
|
||||
|
||||
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
||||
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
||||
use them as intergers, you can. Strings using single quoting, for multiple reasons, but primarily to allow for
|
||||
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
||||
for example, or when typing in jsons inside scarpet to feed back into a `/data merge` command for example.
|
||||
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
||||
|
||||
<pre>
|
||||
'foo'
|
||||
print('This doesn\'t work')
|
||||
nbt ~ '\\.foo' // matching '.' as a '.', not 'any character match'
|
||||
</pre>
|
||||
Reference in new issue
Block a user