package carpet.script; import carpet.script.Fluff.AbstractFunction; import carpet.script.Fluff.AbstractLazyFunction; import carpet.script.Fluff.AbstractLazyOperator; import carpet.script.Fluff.AbstractOperator; import carpet.script.Fluff.AbstractUnaryOperator; import carpet.script.Fluff.ILazyFunction; import carpet.script.Fluff.ILazyOperator; import carpet.script.Fluff.QuadFunction; import carpet.script.Fluff.QuinnFunction; import carpet.script.Fluff.SexFunction; import carpet.script.Fluff.TriFunction; import carpet.script.bundled.Module; import carpet.script.exception.BreakStatement; import carpet.script.exception.ContinueStatement; import carpet.script.exception.ExitStatement; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import carpet.script.exception.ResolvedException; import carpet.script.exception.ReturnStatement; import carpet.script.exception.ThrowStatement; import carpet.script.value.AbstractListValue; import carpet.script.value.ContainerValueInterface; import carpet.script.value.FunctionSignatureValue; import carpet.script.value.FunctionValue; import carpet.script.value.GlobalValue; import carpet.script.value.LContainerValue; import carpet.script.value.LazyListValue; import carpet.script.value.ListValue; import carpet.script.value.MapValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.ThreadValue; import carpet.script.value.Value; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.apache.commons.lang3.text.WordUtils; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.IllegalFormatException; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.Stack; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; /** *
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
* *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.
* *
* script run print('Hello World!')
*
* or an OVERLY complex example:
*
* /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'
*
*
* or simply
*
* script run print('There is'+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
*
* It definitely pays to check what higher level scarpet functions have to offer.
* 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
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.
* 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:
*
* 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))
*
* Would you prefer to read
*
* 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)
* )
*
* Or rather:
*
* 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)
* )
*
* 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
* *
* 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'.
*
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.
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
* 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)
* )
*
* Which we then call in-game with:
** /script load foo * /script in foo invoke run_program **
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:
*
* /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)
* )
*
*
* 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:
*
* 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))
*
*
* 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 $:
*
* /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)
* $)
*
*
* Then we get the following error message
* *
* Your math is wrong, Incorrect number format for NaN at line 7, pos 2
* print(_+' - foo: '+foo);
* HERE>> print(' reciprocal: '+ _/foo )
* )
*
*
*
* 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:
*
* foo = check_not_zero(foo); * ... * check_not_zero(foo) -> if(foo == 0, 1, foo) **
.. or convert it to a global variable, which in this case passing as an argument is not required
** global_foo = floor(rand(10)); * check_foo_not_zero(); * ... * check_foo_not_zero() -> if(global_foo == 0, global_foo = 1) ** */ public class Expression { private static final Map
scarpet provides a number of constants that can be used literally in scripts
null: nothing, zilch, not even falsetrue: pure true, or just 1 (one)false: false truth, or true falsth, 0 (zero) actuallypi: for the fans of perimeters, its a perimeter of an apple pi of diameter 1. About 3.14euler: clever guy. Derivative of its exponent is goto 1. About 2.72Apart 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.
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
* 'foo'
* print('This doesn\'t work')
* nbt ~ '\\.foo' // matching '.' as a '.', not 'any character match'
*
* 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 $ signs are all treats as
* whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved.
* expr;
* expr;
* expr;
* expr
*
* 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 sideeffects
*
* expr1 ; expr2 => expr2 // with expr1 as a sideeffect
*
* 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
* a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2,3,1,1]
*
* Operator ->To organize code better than a flat sequence of operations, one can define functions. Definition is correct if * has the following form
*
* fun(args, ...) -> expr
*
* 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 called.
* a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
*
* 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
* list = l(1,2,3); a(outer(list)) -> list+=1; a(); a(); list // => [1,2,3,1,1]
*
* 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
*
* a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,3,1,1]
*
* 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
* 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
*
*
* 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.
*
* def() -> (
* expr1;
* expr2;
* return(expr3); // function terminates returning expr3
* expr4; // skipped
* expr5 // skipped
* )
*
* 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
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}
* '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)
*
* 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
* 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:
*
~ :+ - !^* / %+ -== != > >= <= <&&||= += <>->;,( )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.
* string
in Minecraft API portion entity ~ feature is a shortcode for query(entity,feature)
* for queries that do not take any extra arguments.
* 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
*
* Or a longer example of an ineffective way to searching for a squid
*
* 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') ) )
*
* 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(...)):
* 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')
*
*
* 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 - elementwise addition/subtraction
Examples:
*
* 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)
*
*
* Just Operators % ^The modulo and exponent (power) operators work only if both operands are numbers
*
* pi^pi%euler => 1.124....
* -9 % 4 => -1
* 9 % -4 => 0 ¯\_(ツ)_/¯ Java
* -3 ^ 2 => 9
* -3 ^ pi => // Error
*
*
* 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:
*
* null == null
* null != false
* 0 == false
* 1 == true
* null < 0
* null < -1000
* 1000 < 'a'
* 'bar' < 'foo'
* 3 == 3.0
*
*
* 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 (&&).
* 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
*
*
* 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.
* 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]
*
* Unary Operators - +Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg.
*
* -4 => -4
* +4 => 4
* +'4' // Error message
*
*
* Negation Operator !flips boolean condition of the expression. Equivalent of bool(expr)==false
* !true => 0
* !false => 1
* !null => 1
* !5 => 0
* !l() => 1
* !l(null) => 0
*
* 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).
*
* 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]
*
* 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.
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
* *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
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.
* check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
* for(range(1000000,1100000),check_prime(_)) => 7216
*
* 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.
* 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
*
*
* loop(num,expr(_),exit(_)?)Evaluates expression expr, num number of times.
*expr receives _ system variable indicating the iteration.
* 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]]
*
* In this small example we will search for first 10 primes, apparently including 0:
*
* 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]
*
*
* 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.
* map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
* map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
*
*
* 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.
* filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
* map(filter(entities_list('all'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
*
*
* 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.
* first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 // first prime after 1000
*
* 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.
* 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
*
*
* 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.
* reduce([1,2,3,4],_a+_,0) => 10
* reduce([1,2,3,4],_a*_,1) => 24
*
* Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..)
*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), ':' operatorReturns 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.
* 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
*
*
* 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)Lists
*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 endinsert: 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 putextend: 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 listDue 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.
* *Maps
*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.
*NBT Tags
*
* 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
* 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]}]}
*
*
* 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
* 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]
*
* 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
* join('-',range(10)) => 0-1-2-3-4-5-6-7-8-9
* join('-','foo') => foo
* join('-', 'foo', 'bar') => foo-bar
*
*
* split(delim, expr)Splits a string undr expr by delim which can be a regular expression
* split('',foo) => [f, o, o]
* split('.','foo.bar') => []
* split('\\.','foo.bar') => [foo, bar]
*
*
* 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
* 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]
*
*
* 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
*
* 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]
*
*
* 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
* 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]
*
*
* 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
* 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]
*
*
* 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.
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
*
* 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}
*
* 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
* *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
* 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
*
*
* number(expr)Returns a numeric context of the expression. * Can be used to read numbers from strings
*
* 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
*
*
* str(expr, params? ... ), str(expr, param_list)Returns a formatted string representing expression.
* Accepts formatting style accepted by String.format.
* Supported types (with "%?" syntax):
* 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'
*
*
* 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.
*
* 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.
*
* 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.
lower(expr), upper(expr), title(expr)Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression
*
* lower('aBc') => 'abc'
* upper('aBc') => 'ABC'
* title('aBc') => 'Abc'
*
*
* 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
* replace('abbccddebfg','b+','z') // => azccddezfg
* replace_first('abbccddebfg','b+','z') // => azccddebfg
*
*
* 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
*
* 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
*
*
* rand(expr)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
* 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]
*
*
* 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
* 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
*
*
* 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
sleep(50)*
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)
* start_time = time();
* flip_my_world_upside_down();
* print(str('this took %d milliseconds',time()-start_time))
*
*
* 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.
* *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
*
* a = 1; var('a') = 'foo'; a => a == 'foo'
*
*
* 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 which will become invalid ........,
* 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
*
* 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
* /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())
*
*
* 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