Merge pull request #609 from gnembon/module_imports
better messaging on handling of unknown functions. fixes #606
This commit is contained in:
6 files changed
+71
-6
No files matched your search
@@ -444,6 +444,9 @@ algorithm has taken in to account last time mobs spawned.
|
|||||||
Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end
|
Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end
|
||||||
of the tick, and they will run in order they were scheduled.
|
of the tick, and they will run in order they were scheduled.
|
||||||
|
|
||||||
|
In case you want to schedule a function that is not defined in your module, please read the tips on
|
||||||
|
"Passing function references to other modules of your application" section in the `call(...)` section.
|
||||||
|
|
||||||
### `statistic(player, category, entry)`
|
### `statistic(player, category, entry)`
|
||||||
|
|
||||||
Queries in-game statistics for certain values. Categories include:
|
Queries in-game statistics for certain values. Categories include:
|
||||||
|
|||||||
@@ -746,6 +746,10 @@ Required arguments: `entity, amount, source, attacking_entity`
|
|||||||
It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error
|
It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error
|
||||||
when you attach an inapplicable event to it.
|
when you attach an inapplicable event to it.
|
||||||
|
|
||||||
|
In case you want to pass an event handler that is not defined in your module, please read the tips on
|
||||||
|
"Passing function references to other modules of your application" section in the `call(...)` section.
|
||||||
|
|
||||||
|
|
||||||
### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)`
|
### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)`
|
||||||
|
|
||||||
Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle
|
Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle
|
||||||
|
|||||||
@@ -263,6 +263,10 @@ handle_event('tick', _() -> foo('tick happened')); // built-in event
|
|||||||
handle_event('tick', null) // nah, ima good, kthxbai
|
handle_event('tick', null) // nah, ima good, kthxbai
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
In case you want to pass an event handler that is not defined in your module, please read the tips on
|
||||||
|
"Passing function references to other modules of your application" section in the `call(...)` section.
|
||||||
|
|
||||||
|
|
||||||
### `signal_event(event, target_player?, ... args?)`
|
### `signal_event(event, target_player?, ... args?)`
|
||||||
|
|
||||||
Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list
|
Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ foo(... x) -> ... # all arguments for foo are included in the list
|
|||||||
### `import(module_name, symbols ...)`
|
### `import(module_name, symbols ...)`
|
||||||
|
|
||||||
Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use
|
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,
|
them in the current app. This includes other symbols imported by these modules. Scarpet supports circular dependencies,
|
||||||
but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them.
|
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
|
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.
|
issues, and list contents of libraries.
|
||||||
@@ -144,9 +144,57 @@ issues, and list contents of libraries.
|
|||||||
|
|
||||||
calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly
|
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
|
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
|
functions as arguments and call them with `call` internally. Since function definitions return the defined
|
||||||
function, they can be defined in place as anonymous functions.
|
function, they can be defined in place as anonymous functions.
|
||||||
|
|
||||||
|
#### Passing function references to other modules of your application
|
||||||
|
|
||||||
|
In case a function is defined by its name, Scarpet will attempt to resolve its definition in the given module and its imports,
|
||||||
|
meaning if the call is in a imported library, and not in the main module of your app, and that function is not visible from the
|
||||||
|
library perspective, but in the app, it won't be call-able. In case you pass a function name to a separate module in your app,
|
||||||
|
it should import back that method from the main module for visibility.
|
||||||
|
|
||||||
|
Check an example of a problematic code of a library that expects a function value as a passed argument and how it is called in
|
||||||
|
the parent app:
|
||||||
|
```
|
||||||
|
//app.sc
|
||||||
|
import('lib', 'callme');
|
||||||
|
foo(x) -> x*x;
|
||||||
|
test() -> callme('foo' , 5);
|
||||||
|
```
|
||||||
|
```
|
||||||
|
//lib.scl
|
||||||
|
callme(fun, arg) -> call(fun, arg);
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case `'foo'` will fail to dereference in `lib` as it is not visible by name. In tightly coupled modules, where `lib` is just
|
||||||
|
a component of your `app` you can use circular import to acknowledge the symbol from the other module (pretty much like
|
||||||
|
imports in Java classes), and that solves the issue but makes the library dependent on the main app:
|
||||||
|
```
|
||||||
|
//lib.scl
|
||||||
|
import('app','foo');
|
||||||
|
callme(fun, arg) -> call(fun, arg);
|
||||||
|
```
|
||||||
|
You can circumvent that issue by explicitly dereferencing the local function where it is used as a lambda argument created
|
||||||
|
in the module in which the requested function is visible:
|
||||||
|
```
|
||||||
|
//app.sc
|
||||||
|
import('lib', 'callme');
|
||||||
|
foo(x) -> x*x;
|
||||||
|
test() -> callme(_(x) -> foo(x), 5);
|
||||||
|
```
|
||||||
|
```
|
||||||
|
//lib.scl
|
||||||
|
callme(fun, arg) -> call(fun, arg);
|
||||||
|
```
|
||||||
|
Or by passing an explicit reference to the function, instead of calling it by name:
|
||||||
|
```
|
||||||
|
//app.sc
|
||||||
|
import('lib', 'callme');
|
||||||
|
global_foohandler = (foo(x) -> x*x);
|
||||||
|
test() -> callme(global_foohandler, 5);
|
||||||
|
```
|
||||||
|
|
||||||
Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not
|
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
|
creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for
|
||||||
programmers with their own lambda arguments
|
programmers with their own lambda arguments
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ 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.
|
=> Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
In case you want to create a task based on a function that is not defined in your module, please read the tips on
|
||||||
|
"Passing function references to other modules of your application" section in the `call(...)` section.
|
||||||
|
|
||||||
### `sleep()` `sleep(timeout)`, `sleep(timeout, close_expr)`
|
### `sleep()` `sleep(timeout)`, `sleep(timeout, close_expr)`
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,10 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class ScriptHost
|
public abstract class ScriptHost
|
||||||
@@ -189,7 +186,13 @@ public abstract class ScriptHost
|
|||||||
public FunctionValue getAssertFunction(Module module, String name)
|
public FunctionValue getAssertFunction(Module module, String name)
|
||||||
{
|
{
|
||||||
FunctionValue ret = getFunction(module, name);
|
FunctionValue ret = getFunction(module, name);
|
||||||
if (ret == null) throw new InternalExpressionException("Function "+name+" is not defined yet");
|
if (ret == null)
|
||||||
|
{
|
||||||
|
if (module == main)
|
||||||
|
throw new InternalExpressionException("Function '"+name+"' is not defined yet");
|
||||||
|
else
|
||||||
|
throw new InternalExpressionException("Function '"+name+"' is not defined nor visible by its name in the imported module '"+module.getName()+"'");
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
private FunctionValue getFunction(Module module, String name)
|
private FunctionValue getFunction(Module module, String name)
|
||||||
|
|||||||
Reference in new issue
Block a user