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.SexFunction; import carpet.script.Fluff.TriFunction; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import carpet.script.value.AbstractListValue; import carpet.script.value.ContainerValueInterface; import carpet.script.value.FunctionSignatureValue; import carpet.script.value.GlobalValue; 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.Value; 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.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';
* 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. This means that once defined functions
* are saved with the world for further use. There are two types of variables,
* 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. What this does is
* it borrows the reference to that variable from the outer scope to be used inside the function and any modifications to that outer
* variable would result in changes of that value in the outer scope as well. Its like passing the parameters by reference,
* except the calling function itself decides what variables its borrowing and what its name. This can be used to
* return more than one result from a function call, although its a very ugly way of doing it -
* I would still recommend returning a list of values instead.
* Variables from outer scopes have a similar behaviour to, for example, nonlocal variables from python.
Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing your
* code in packages (files with .sc extension that can be placed inside "/scripts" folder in the world files
* and loaded as a module with command /script load [package_name]. Scarpet modules loaded from disk should only
* contain code, no need to start with "/script run" prefix
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(10));
* 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 Infinity at pos 112
* run_program() -> ( loop( 10, foo = floor(rand(10)); 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 (112) 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(10));
* $ 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 Infinity at line 7, pos 5
* 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) **
.. or scope foo from the outer function - in this case the inner function has to determine what it is accessing
** foo = floor(rand(10)); * check_foo_not_zero(); * ... * check_foo_not_zero(outer(foo)) -> if(foo == 0, foo = 1) **
outer scope can only be used in
* function signatures to indicate outer variables. They are not arguments, but still you would want to use
* locally without affecting other uses of foo in your program.
*
For the most part - passing arguments as values, and using returned values .
* The main usecase of Scarpet would rather be simpler scripts, default scope for all variables is global, unless variable
* is declared with local scope explicitly.
*
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. The
* usefulness of preceding all the lines of the script with $ is explained in the preamble
* 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.
In general expr; expr; expr; expr is equivalent to
* (((expr ; expr) ; expr) ; expr). In case the programmer forgets that it should be warned with a
* helpful error at compile time.
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
* 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 borrows the reference to that variable from the outer scope and allows
* its modification in the inner scope. Any modification of outer variable will result in change of them in
* the outer function. In case the variable was not set yet in the outer scope - it will be created. This construct
* is similar to nonlocal scoping from python
* a(outer(list)) -> list+=1; list = l(1,2,3); 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
*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 operators, but behave like them:
*
~ .+ - !^* / %+ -== != > >= <= <&&||= += <>->;,( )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
*
*
* Get, Accessor Operator .Operator version of the get(...) function to access elements of lists, nbts, and potentially other
* containers. It is important to distinguish from ~ operator, which is a matching operator, meaning it
* is expected to perform some extra computations to retrieve the result, while . should be
* straightforward and immediate.
Matching Operator ~This operator should be understood as 'matches', 'contains' or 'is_in'. For strings it matches the right operand as a regular
* expression to the left one, returning the first match. This can be used to extract information from unparsed nbt's
* in a more efficient way. 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 * (despite obvious lack of support for json NBT's):
*
* 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')
*
* 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 other 100.
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 sortraphical 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 cangreatly 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
*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]
*
*
* get(value, address), element(value, address)(deprecated)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
[Minecraft specific usecase]: In case value is of nbt type, uses addess 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
* get(l(range(10)), 5) => 5
* get(l(range(10)), -1) => 9
* get(l(range(10)), 10) => 0
* get(l(range(10)), 93) => 3
* get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health
*
*
* put(list, index, values ...), put(list, null, values ...)Modifies the list by replacing values starting from index with values.
* use negative numbers to reach elements from the end of the list. put
* call will always be able to find the index. In case there is few items, it will loop over. In case end
* of the list is reached before values run out, list is extended to accomodate for more values. in case you
* want to append at the end of the list, use null as index. returns number of elements inserted.
* a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
* a = l(1, 2, 3); put(a, null, 4, 5, 6); a => [1, 2, 3, 4, 5, 6]
* a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(element(a, 1), 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
*
* while(cond, limit, expr)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. Optionally,
* if exit condition is present, stops the execution if the condition becomes true.
* Both expr and exit receive _ system variable indicating the iteration
* loop(5, 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 += _), length(primes) >= 10 );
* primes
*
* // outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
*
* map(list,expr(_,_i), exit(_,_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, optional exit condition is evaluated after each mapping and if it is false,
* the map is terminated and returned whatever got collected so far
* map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
* map(players('*'), _+' is stupid') [gnembon is stupid, herobrine is stupid]
*
*
* filter(list,expr(_,_i),exit(_,_i))filters list elements returning only these that return positive result of the expr.
* Like with map if the optional condition exit gets true, the list is returned up to this point
* 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
* 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), but works with generators. expr
* also receives bound _ and _i variables
* 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
*
*
*
* for(list,expr(_,_i),exit(_,_i)?)Evaluates expression over list of items from the list, and can optionally stop early if
* exit expression is specified and evaluates true for a given iteration. Supplies
* _(value) and _i(iteration number) to both expr and exit.
* Returns the number of times expr 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
*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
* reduce([1,2,3,4],_a+_,0) => 10
* reduce([1,2,3,4],_a*_,1) => 24
*
* 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'
*
*
* lower(expr), uppper(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'
*
*
* 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 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))
*
*
* 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())
*
*
*