diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md
index 5c0a4f76..e1330d1c 100644
--- a/docs/scarpet/Full.md
+++ b/docs/scarpet/Full.md
@@ -146,7 +146,7 @@ around built-in function `outer`. It adds the specified value to the function ca
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.
+have memory and act like objects so to speak. Check `outer(var)` for details.
## Code delivery, line indicators
@@ -767,22 +767,21 @@ from other threads, just because the only possibility of a deadlock in this case
bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, can be
effectively parallelized.
-### `task(function, ?args...., ?executor)`
+### `task(function, ... args)`, `task_thread(executor, function, ... args)`
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.
+name, or value. `executor` identifier in `task_thread`, places the task in a specific queue identified by this value.
+The default thread value is the `null` thread. There are no limits on number of parallel tasks for any executor,
+so using different queues is solely for synchronization purposes.
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')
+task_thread('temp', 'foo',3,5); => runs function foo with a different thread executor, identified as 'temp'
+a = 3; task_thread('temp', _(outer(a), b) -> foo(a,b), 5)
=> Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
@@ -1274,10 +1273,11 @@ call creates new scope for variables inside `expr`, so all non-global variables
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
+The function returns itself as a first class object, which means it can be used to call it later with the `call` function
Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given
-a unique name, which you can pass somewhere else to get this function `call`ed.
+a unique name, which you can pass somewhere else to get this function `call`ed. Anonymous functions can only be called
+by their value and `call` method.
a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
@@ -1294,7 +1294,7 @@ arguments to a tuple which is used by map constructor as a key-value pair:
This means that it is not possible to define literally a set of inline function, however a set of functions can still
-be created by adding elements to an empty set, and building it this way.
+be created by adding elements to an empty set, and building it this way. That's a tradeoff for having a cool map initializer.
### `outer(arg)`
@@ -1319,6 +1319,23 @@ a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,
Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
scoping allow to organize even larger scripts
+### `Operator ...`
+
+Defines a function argument to represent a variable length argument list of whatever arguments are left
+from the argument call list, also known as a varargs. There can be only one defined vararg argument in a function signature.
+Technically, it doesn't matter where is it, but it looks best at the butt side of it.
+
+
+foo(a, b, c) -> ... # fixed argument function, call foo(1, 2, 3)
+foo(a, b, ... c) -> ... # c is now representing the variable argument part
+ foo(1, 2) # a:1, b:2, c:[]
+ foo(1, 2, 3) # a:1, b:2, c:[3]
+ foo(1, 2, 3, 4) # a:1, b:2, c:[3, 4]
+foo(... x) -> ... # all arguments for foo are included in the list
+
+
+
+
### `import(module_name, symbols ...)`
Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use
@@ -2155,13 +2172,13 @@ structures and features. List of available options and names that you can use de
with minecraft 1.16.1 and below or 1.16.2 and above since in 1.16.2 Mojang has added JSON support for worldgen features
meaning that since 1.16.2 - they have official names that can be used by datapacks and scarpet
-### Previous Structure Names, including variants (MC 1.16.1 and below)
+### Previous Structure Names, including variants (as of MC 1.16.3)
* `'monument'`: Ocean Monument. Generates at fixed Y coordinate, surrounds itself with water.
* `'fortress'`: Nether Fortress. Altitude varies, but its bounded by the code.
* `'mansion'`: Woodland Mansion
* `'jungle_temple'`: Jungle Temple
* `'desert_temple'`: Desert Temple. Generates at fixed Y altitude.
-* `'end_city'`: End City with Shulkers
+* `'endcity'`: End City with Shulkers (in 1.16.1- as `'end_city`)
* `'igloo'`: Igloo
* `'shipwreck'`: Shipwreck
* `'shipwreck2'`: Shipwreck, beached
@@ -2185,7 +2202,7 @@ meaning that since 1.16.2 - they have official names that can be used by datapac
* `'bastion_remnant_treasure'`: Treasure room version of a piglin bastion (1.16)
* `'bastion_remnant_bridge'` : Bridge version of a piglin bastion (1.16)
-### Feature Names (mc1.16.1-)
+### Feature Names (as of mc1.16.1)
* `'oak'`
* `'oak_beehive'`: oak with a hive (1.15+).
@@ -2249,13 +2266,13 @@ meaning that since 1.16.2 - they have official names that can be used by datapac
* `'twisting_vines'` (1.16)
* `'basalt_pillar'` (1.16)
-### Standard Structures (1.16.2+)
+### Standard Structures (as of 1.16.2+)
`'bastion_remnant'`, `'buried_treasure'`, `'desert_pyramid'`, `'endcity'`, `'fortress'`, `'igloo'`,
`'jungle_pyramid'`, `'mansion'`, `'mineshaft'`, `'monument'`, `'nether_fossil'`, `'ocean_ruin'`,
`'pillager_outpost'`, `'ruined_portal'`, `'shipwreck'`, `'stronghold'`, `'swamp_hut'`, `'village'`
-### Structure Variants (1.16.2+)
+### Structure Variants (as of 1.16.2+)
`'bastion_remnant'`, `'buried_treasure'`, `'desert_pyramid'`, `'end_city'`, `'fortress'`, `'igloo'`,
`'jungle_pyramid'`, `'mansion'`, `'mineshaft'`, `'mineshaft_mesa'`, `'monument'`, `'nether_fossil'`,
@@ -2264,7 +2281,7 @@ meaning that since 1.16.2 - they have official names that can be used by datapac
`'ruined_portal_swamp'`, `'shipwreck'`, `'shipwreck_beached'`, `'stronghold'`, `'swamp_hut'`,
`'village_desert'`, `'village_plains'`, `'village_savanna'`, `'village_snovy'`, `'village_taiga'`
-### World Generation Features (1.16.2+)
+### World Generation Features (as of 1.16.2+)
`'acacia'`, `'bamboo'`, `'bamboo_light'`, `'bamboo_vegetation'`, `'basalt_blobs'`, `'basalt_pillar'`,
`'birch'`, `'birch_bees_0002'`, `'birch_bees_002'`, `'birch_bees_005'`, `'birch_other'`, `'birch_tall'`,
@@ -3397,6 +3414,7 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
__on_player_interacts_with_entity(player, entity, hand)
+__on_player_collides_with_entity(player, entity)
__on_player_chooses_recipe(player, recipe, full_stack)
__on_player_switches_slot(player, from, to)
__on_player_swaps_hands(player)
diff --git a/docs/scarpet/language/FunctionsAndControlFlow.md b/docs/scarpet/language/FunctionsAndControlFlow.md
index 73d1de59..3fc9ded4 100644
--- a/docs/scarpet/language/FunctionsAndControlFlow.md
+++ b/docs/scarpet/language/FunctionsAndControlFlow.md
@@ -63,10 +63,11 @@ call creates new scope for variables inside `expr`, so all non-global variables
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
+The function returns itself as a first class object, which means it can be used to call it later with the `call` function
Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given
-a unique name, which you can pass somewhere else to get this function `call`ed.
+a unique name, which you can pass somewhere else to get this function `call`ed. Anonymous functions can only be called
+by their value and `call` method.
a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
@@ -83,7 +84,7 @@ arguments to a tuple which is used by map constructor as a key-value pair:
This means that it is not possible to define literally a set of inline function, however a set of functions can still
-be created by adding elements to an empty set, and building it this way.
+be created by adding elements to an empty set, and building it this way. That's a tradeoff for having a cool map initializer.
### `outer(arg)`
@@ -108,6 +109,23 @@ a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,
Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
scoping allow to organize even larger scripts
+### `Operator ...`
+
+Defines a function argument to represent a variable length argument list of whatever arguments are left
+from the argument call list, also known as a varargs. There can be only one defined vararg argument in a function signature.
+Technically, it doesn't matter where is it, but it looks best at the butt side of it.
+
+
+foo(a, b, c) -> ... # fixed argument function, call foo(1, 2, 3)
+foo(a, b, ... c) -> ... # c is now representing the variable argument part
+ foo(1, 2) # a:1, b:2, c:[]
+ foo(1, 2, 3) # a:1, b:2, c:[3]
+ foo(1, 2, 3, 4) # a:1, b:2, c:[3, 4]
+foo(... x) -> ... # all arguments for foo are included in the list
+
+
+
+
### `import(module_name, symbols ...)`
Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use
diff --git a/docs/scarpet/language/Overview.md b/docs/scarpet/language/Overview.md
index 0cacbd36..64c3d81d 100644
--- a/docs/scarpet/language/Overview.md
+++ b/docs/scarpet/language/Overview.md
@@ -146,7 +146,7 @@ around built-in function `outer`. It adds the specified value to the function ca
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.
+have memory and act like objects so to speak. Check `outer(var)` for details.
## Code delivery, line indicators
diff --git a/docs/scarpet/resources/editors/idea/1.txt b/docs/scarpet/resources/editors/idea/1.txt
index 8780d3f9..d69ced42 100644
--- a/docs/scarpet/resources/editors/idea/1.txt
+++ b/docs/scarpet/resources/editors/idea/1.txt
@@ -87,6 +87,7 @@ task_completed
task_count
task_dock
task_join
+task_thread
task_value
throw
time
diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml
index 202f0ca8..910d628c 100644
--- a/docs/scarpet/resources/editors/npp/scarpet.xml
+++ b/docs/scarpet/resources/editors/npp/scarpet.xml
@@ -13,7 +13,7 @@
- ; + - * / % ^ && || > >= < <= == != = += -> <> ! ~ , :
+ ; + - * / % ^ && || > >= < <= == != = += -> <> ! ~ , : ...
(
@@ -36,7 +36,7 @@
print return exit throw try
- task task_count task_value task_join task_completed synchronize task_dock
+ task task_thread task_count task_value task_join task_completed synchronize task_dock
system_variable_get system_variable_set
diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index 4c22c493..29c17140 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -309,7 +309,12 @@ public class CarpetScriptHost extends ScriptHost
{
if (CarpetServer.scriptServer.stopAll)
return Value.NULL;
- if (argv.size() != fun.getArguments().size())
+ try { // cause we can't throw checked exceptions in lambda. Left if be until need to handle these more gracefully
+ fun.assertArgsOk(argv.size(), (b) -> {
+ throw new InternalExpressionException("");
+ });
+ }
+ catch (InternalExpressionException ignored)
{
throw new InvalidCallbackException();
}
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 72310a9f..a042f822 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -339,7 +339,7 @@ public class Expression
}
});
}
- public FunctionValue addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List arguments, List outers, LazyValue code)
+ public FunctionValue addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List arguments, String varArgs, List outers, LazyValue code)
{
if (functions.containsKey(name))
throw new ExpressionException(context, expr, token, "Function "+name+" would mask a built-in function");
@@ -358,7 +358,7 @@ public class Expression
}
if (contextValues.isEmpty()) contextValues = null;
- FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues);
+ FunctionValue result = new FunctionValue(expr, token, name, code, arguments, varArgs, contextValues);
// do not store lambda definitions
if (!name.equals("_")) context.host.addUserDefinedFunction(context, module, name, result);
return result;
diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java
index accb93a7..0b8141f2 100644
--- a/src/main/java/carpet/script/api/Auxiliary.java
+++ b/src/main/java/carpet/script/api/Auxiliary.java
@@ -691,7 +691,7 @@ public class Auxiliary {
throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name");
long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
- FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, true);
+ FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true);
CarpetServer.scriptServer.events.scheduleCall(
(CarpetContext) c,
diff --git a/src/main/java/carpet/script/argument/FunctionArgument.java b/src/main/java/carpet/script/argument/FunctionArgument.java
index 7838a518..568d8411 100644
--- a/src/main/java/carpet/script/argument/FunctionArgument.java
+++ b/src/main/java/carpet/script/argument/FunctionArgument.java
@@ -1,6 +1,5 @@
package carpet.script.argument;
-import carpet.script.CarpetContext;
import carpet.script.Context;
import carpet.script.LazyValue;
import carpet.script.bundled.Module;
@@ -22,7 +21,7 @@ public class FunctionArgument extends Argument
this.function = function;
this.args = args;
}
- public static FunctionArgument findIn(Context c, Module module, List params, int offset, boolean prematureEvaluation, boolean strict)
+ public static FunctionArgument findIn(Context c, Module module, List params, int offset, boolean prematureEvaluation)
{
Value functionValue = params.get(offset).evalValue(c);
if (!(functionValue instanceof FunctionValue))
@@ -33,20 +32,22 @@ public class FunctionArgument extends Argument
FunctionValue fun = (FunctionValue)functionValue;
int argsize = fun.getArguments().size();
int extraargs = params.size() - argsize - offset - 1;
- if (extraargs < 0 || (extraargs > 0 && strict))
+ if (extraargs < 0)
+ throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires at least "+fun.getArguments().size()+" arguments");
+ if (extraargs > 0 && fun.getVarArgs()==null)
throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires "+fun.getArguments().size()+" arguments");
List lvargs = new ArrayList<>();
if (prematureEvaluation)
{
- for (int i = 0; i < argsize; i++)
+ for (int i = offset+1, mx = params.size(); i < mx; i++)
{
- Value arg = params.get(offset + 1 + i).evalValue(c);
+ Value arg = params.get(i).evalValue(c);
lvargs.add((cc, tt) -> arg);
}
}
else
{
- for (int i = 0; i < argsize; i++) lvargs.add(params.get(offset + 1 + i));
+ for (int i = offset+1, mx = params.size(); i < mx; i++) lvargs.add(params.get(i));
}
return new FunctionArgument(fun, offset+1+argsize, lvargs);
}
diff --git a/src/main/java/carpet/script/language/Functions.java b/src/main/java/carpet/script/language/Functions.java
index 5afa634d..0f4907f6 100644
--- a/src/main/java/carpet/script/language/Functions.java
+++ b/src/main/java/carpet/script/language/Functions.java
@@ -8,7 +8,7 @@ import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
import carpet.script.value.FunctionSignatureValue;
import carpet.script.value.FunctionValue;
-import carpet.script.value.GlobalValue;
+import carpet.script.value.FunctionAnnotationValue;
import carpet.script.value.ListValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
@@ -43,7 +43,7 @@ public class Functions {
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
if (t != Context.SIGNATURE) // just call the function
{
- FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
+ FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false);
FunctionValue fun = functionArgument.function;
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c);
return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
@@ -52,6 +52,7 @@ public class Functions {
String name = lv.get(0).evalValue(c).getString();
List args = new ArrayList<>();
List globals = new ArrayList<>();
+ String varArgs = null;
for (int i = 1; i < lv.size(); i++)
{
Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
@@ -59,27 +60,44 @@ public class Functions {
{
throw new InternalExpressionException("Only variables can be used in function signature, not " + v.getString());
}
- if (v instanceof GlobalValue)
+ if (v instanceof FunctionAnnotationValue)
{
- globals.add(v.boundVariable);
+ if (((FunctionAnnotationValue) v).type == 0)
+ {
+ globals.add(v.boundVariable);
+ }
+ else
+ {
+ if (varArgs != null)
+ throw new InternalExpressionException("Variable argument identifier is already defined as "+varArgs+", trying to overwrite with "+v.boundVariable);
+ varArgs = v.boundVariable;
+ }
}
else
{
args.add(v.boundVariable);
}
}
- Value retval = new FunctionSignatureValue(name, args, globals);
+ Value retval = new FunctionSignatureValue(name, args, varArgs, globals);
return (cc, tt) -> retval;
});
- expression.addLazyFunction("outer", 1, (c, t, lv) -> {
+ expression.addLazyFunction("outer", 1, (c, t, lv) ->
+ {
if (t != Context.LOCALIZATION)
throw new InternalExpressionException("Outer scoping of variables is only possible in function signatures");
- return (cc, tt) -> new GlobalValue(lv.get(0).evalValue(c));
+ return (cc, tt) -> new FunctionAnnotationValue(lv.get(0).evalValue(c), 0);
+ });
+
+ expression.addLazyUnaryOperator("...", Operators.precedence.get("def->..."), false, (c, t, lv) ->
+ {
+ if (t != Context.LOCALIZATION)
+ throw new InternalExpressionException("Outer scoping of variables is only possible in function signatures");
+ return (cc, tt) -> new FunctionAnnotationValue(lv.evalValue(c), 1);
});
//assigns const procedure to the lhs, returning its previous value
- expression.addLazyBinaryOperatorWithDelegation("->", Operators.precedence.get("def->"), false, (c, type, e, t, lv1, lv2) ->
+ expression.addLazyBinaryOperatorWithDelegation("->", Operators.precedence.get("def->..."), false, (c, type, e, t, lv1, lv2) ->
{
if (type == Context.MAPDEF)
{
@@ -90,7 +108,7 @@ public class Functions {
if (!(v1 instanceof FunctionSignatureValue))
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
FunctionSignatureValue sign = (FunctionSignatureValue) v1;
- Value result = expression.addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2);
+ Value result = expression.addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getVarArgs(), sign.getGlobals(), lv2);
return (cc, tt) -> result;
});
diff --git a/src/main/java/carpet/script/language/Operators.java b/src/main/java/carpet/script/language/Operators.java
index a5f5a146..906001ff 100644
--- a/src/main/java/carpet/script/language/Operators.java
+++ b/src/main/java/carpet/script/language/Operators.java
@@ -29,7 +29,7 @@ public class Operators {
put("and&&", 5);
put("or||", 4);
put("assign=<>", 3);
- put("def->", 2);
+ put("def->...", 2);
put("nextop;", 1);
}};
diff --git a/src/main/java/carpet/script/language/Threading.java b/src/main/java/carpet/script/language/Threading.java
index 781d093e..938c72ca 100644
--- a/src/main/java/carpet/script/language/Threading.java
+++ b/src/main/java/carpet/script/language/Threading.java
@@ -15,7 +15,7 @@ public class Threading
{
if (lv.size() == 0)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
- FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true, true);
+ FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true);
ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.args);
Thread.yield();
return (cc, tt) -> thread;
@@ -26,7 +26,7 @@ public class Threading
if (lv.size() < 2)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
Value queue = lv.get(0).evalValue(c);
- FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, true);
+ FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true);
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args);
Thread.yield();
return (cc, tt) -> thread;
diff --git a/src/main/java/carpet/script/value/GlobalValue.java b/src/main/java/carpet/script/value/FunctionAnnotationValue.java
similarity index 80%
rename from src/main/java/carpet/script/value/GlobalValue.java
rename to src/main/java/carpet/script/value/FunctionAnnotationValue.java
index c38afb68..c15ab396 100644
--- a/src/main/java/carpet/script/value/GlobalValue.java
+++ b/src/main/java/carpet/script/value/FunctionAnnotationValue.java
@@ -3,13 +3,17 @@ package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.Tag;
-public class GlobalValue extends Value
+public class FunctionAnnotationValue extends Value
{
- public GlobalValue(Value variable)
+ // 0 global
+ // 1 vararg
+ public int type;
+ public FunctionAnnotationValue(Value variable, int type)
{
if (variable.boundVariable == null)
throw new InternalExpressionException("You can only borrow variables from the outer scope");
this.boundVariable = variable.boundVariable;
+ this.type = type;
}
@Override
diff --git a/src/main/java/carpet/script/value/FunctionSignatureValue.java b/src/main/java/carpet/script/value/FunctionSignatureValue.java
index c5a4aa8b..e1c2f148 100644
--- a/src/main/java/carpet/script/value/FunctionSignatureValue.java
+++ b/src/main/java/carpet/script/value/FunctionSignatureValue.java
@@ -7,11 +7,13 @@ public class FunctionSignatureValue extends FrameworkValue
private String identifier;
private List arguments;
private List globals;
+ private String varArgs;
- public FunctionSignatureValue(String name, List args, List globals)
+ public FunctionSignatureValue(String name, List args, String varArgs, List globals)
{
this.identifier = name;
this.arguments = args;
+ this.varArgs = varArgs;
this.globals = globals;
}
public String getName()
@@ -25,6 +27,5 @@ public class FunctionSignatureValue extends FrameworkValue
public List getGlobals() {return globals;}
-
-
+ public String getVarArgs() { return varArgs;}
}
diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java
index 3762508c..88c2440a 100644
--- a/src/main/java/carpet/script/value/FunctionValue.java
+++ b/src/main/java/carpet/script/value/FunctionValue.java
@@ -8,7 +8,6 @@ import carpet.script.Tokenizer;
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.ReturnStatement;
@@ -20,6 +19,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
public class FunctionValue extends Value implements Fluff.ILazyFunction
@@ -30,27 +30,30 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
private LazyValue body;
private Map outerState;
private List args;
+ private String varArgs;
private static long variantCounter = 1;
private long variant;
- private FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args)
+ private FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args, String varArgs)
{
this.expression = expression;
this.token = token;
this.name = name;
this.body = body;
this.args = args;
+ this.varArgs = varArgs;
this.outerState = null;
variant = 0L;
}
- public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args, Map outerState)
+ public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args, String varArgs, Map outerState)
{
this.expression = expression;
this.token = token;
this.name = name;
this.body = body;
this.args = args;
+ this.varArgs = varArgs;
this.outerState = outerState;
variant = variantCounter++;
}
@@ -84,7 +87,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
@Override
protected Value clone()
{
- FunctionValue ret = new FunctionValue(expression, token, name, body, args);
+ FunctionValue ret = new FunctionValue(expression, token, name, body, args, varArgs);
ret.outerState = this.outerState;
ret.variant = this.variant;
return ret;
@@ -146,7 +149,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
@Override
public boolean numParamsVaries()
{
- return false;
+ return varArgs != null;
}
public LazyValue callInContext(Expression callingExpression, Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams)
@@ -175,23 +178,44 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
@Override
public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams)
{
- if (args.size() != lazyParams.size()) // something that might be subject to change in the future
- // in case we add variable arguments
- {
- throw new ExpressionException(c, e, t,
- "Incorrect number of arguments for function "+name+
- ". Should be "+args.size()+", not "+lazyParams.size()+" like "+args
- );
- }
+ assertArgsOk(lazyParams.size(), (fixedArgs) ->{
+ if (fixedArgs) // wrong number of args for fixed args
+ {
+ throw new ExpressionException(c, e, t,
+ "Incorrect number of arguments for function "+name+
+ ". Should be "+args.size()+", not "+lazyParams.size()+" like "+args
+ );
+ }
+ else // too few args for varargs
+ {
+ List argList = new ArrayList<>(args);
+ argList.add("... "+varArgs);
+ throw new ExpressionException(c, e, t,
+ "Incorrect number of arguments for function "+name+
+ ". Should be at least "+args.size()+", not "+lazyParams.size()+" like "+argList
+ );
+ }
+ });
Context newFrame = c.recreate();
if (outerState != null) outerState.forEach(newFrame::setVariable);
for (int i=0; i val);
}
+ if (varArgs != null)
+ {
+ List extraParams = new ArrayList<>();
+ for (int i = args.size(), mx = lazyParams.size(); i < mx; i++)
+ {
+ extraParams.add(lazyParams.get(i).evalValue(c).reboundedTo(null)); // copy by value I guess
+ }
+ Value rest = ListValue.wrap(extraParams).bindTo(varArgs); // didn't we just copied that?
+ newFrame.setVariable(varArgs, (cc, tt) -> rest);
+
+ }
Value retVal;
boolean rethrow = false;
try
@@ -211,10 +235,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
retVal = throwStatement.retval;
rethrow = true;
}
- //catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc )
- //{
- // throw exc; // rethrow so could be contextualized if needed
- //}
if (rethrow)
{
throw new ThrowStatement(retVal);
@@ -235,6 +255,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
{
return args;
}
+ public String getVarArgs() {return varArgs; }
@Override
public Tag toTag(boolean force)
@@ -242,4 +263,17 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return StringTag.of(getString());
}
+
+ public void assertArgsOk(int size, Consumer feedback)
+ {
+ if (varArgs == null && args.size() != size) // wrong number of args for fixed args
+ {
+ feedback.accept(true);
+
+ }
+ else if (varArgs != null && args.size() > size) // too few args for varargs
+ {
+ feedback.accept(false);
+ }
+ }
}