-
-
Notifications
You must be signed in to change notification settings - Fork 414
Multiline function calling #8117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Efnilite
wants to merge
6
commits into
dev/feature
Choose a base branch
from
feature/multiline-function-args
base: dev/feature
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
125d115
init commit
Efnilite 18ba815
use FunctionRegistry
Efnilite e1dcf5d
init commit
Efnilite d82ac87
requested changes
Efnilite 29dd1a1
fix variables
Efnilite 17e0431
Merge branch 'dev/feature' into feature/multiline-function-args
Efnilite File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
224 changes: 224 additions & 0 deletions
224
src/main/java/ch/njol/skript/sections/ExprSecFunction.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
package ch.njol.skript.sections; | ||
|
||
import ch.njol.skript.Skript; | ||
import ch.njol.skript.config.Node; | ||
import ch.njol.skript.config.SectionNode; | ||
import ch.njol.skript.config.SimpleNode; | ||
import ch.njol.skript.doc.Description; | ||
import ch.njol.skript.doc.Name; | ||
import ch.njol.skript.expressions.base.SectionExpression; | ||
import ch.njol.skript.lang.*; | ||
import ch.njol.skript.lang.SkriptParser.ParseResult; | ||
import ch.njol.skript.lang.function.Function; | ||
import ch.njol.skript.lang.function.FunctionRegistry; | ||
import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; | ||
import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; | ||
import ch.njol.skript.lang.function.Parameter; | ||
import ch.njol.skript.lang.function.Signature; | ||
import ch.njol.skript.lang.parser.ParserInstance; | ||
import ch.njol.skript.registrations.Classes; | ||
import ch.njol.skript.util.LiteralUtils; | ||
import ch.njol.util.Kleenean; | ||
import org.bukkit.event.Event; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.*; | ||
import java.util.Map.Entry; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Collectors; | ||
|
||
@Name("Function Section") | ||
@Description(""" | ||
Runs a function with the specified arguments. | ||
""") | ||
public class ExprSecFunction extends SectionExpression<Object> { | ||
|
||
private static final String AMBIGUOUS_ERROR = | ||
"Skript cannot determine which function named '%s' to call. " + | ||
"The following functions were matched: %s. " + | ||
"Try clarifying the type of the arguments using the 'value within' expression."; | ||
|
||
/** | ||
* The pattern for a valid function name. | ||
* Functions must start with a letter or underscore and can only contain letters, numbers, and underscores. | ||
*/ | ||
private final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile("[A-z_][A-z_0-9]*"); | ||
|
||
/** | ||
* The pattern for an argument that can be passed in the children of this section. | ||
*/ | ||
private static final Pattern ARGUMENT_PATTERN = Pattern.compile("(?<name>%s) set to (?<value>.+)".formatted(FUNCTION_NAME_PATTERN.toString())); | ||
Efnilite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
static { | ||
Skript.registerExpression(ExprSecFunction.class, Object.class, ExpressionType.SIMPLE, "function <.+> with argument[s]"); | ||
|
||
} | ||
|
||
private Function<?> function; | ||
private LinkedHashMap<String, Expression<?>> arguments = null; | ||
|
||
@Override | ||
public boolean init(Expression<?>[] expressions, int pattern, Kleenean delayed, ParseResult result, | ||
@Nullable SectionNode node, @Nullable List<TriggerItem> triggerItems) { | ||
Efnilite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (node == null) { | ||
Efnilite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
Skript.error("A section must follow this expression."); | ||
return false; | ||
} else if (node.isEmpty()) { | ||
Skript.error("A function section must contain code."); | ||
Efnilite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
return false; | ||
} | ||
|
||
LinkedHashMap<String, String> args = new LinkedHashMap<>(); | ||
for (Node n : node) { | ||
if (!(n instanceof SimpleNode) || n.getKey() == null) { | ||
Skript.error("Invalid argument declaration for a function section: ", n.getKey()); | ||
return false; | ||
} | ||
|
||
Matcher matcher = ARGUMENT_PATTERN.matcher(n.getKey()); | ||
if (!matcher.matches()) { | ||
Skript.error("Invalid argument declaration for a function section: ", n.getKey()); | ||
return false; | ||
} | ||
|
||
args.put(matcher.group("name"), matcher.group("value")); | ||
} | ||
|
||
String namespace = ParserInstance.get().getCurrentScript().getConfig().getFileName(); | ||
String name = result.regexes.get(0).group(); | ||
|
||
if (!FUNCTION_NAME_PATTERN.matcher(name).matches()) { | ||
Skript.error("The function %s() does not exist.".formatted(name)); | ||
return false; | ||
} | ||
|
||
// todo use FunctionParser | ||
function = findFunction(namespace, name, args); | ||
|
||
if (function == null || arguments == null || arguments.isEmpty()) { | ||
doesNotExist(name, args); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Attempts to find the function to execute given the arguments. | ||
* | ||
* @param namespace The current script. | ||
* @param name The name of the function. | ||
* @param args The passed arguments. | ||
* @return The function given the arguments, or null if no function is found. | ||
*/ | ||
private Function<?> findFunction(String namespace, String name, LinkedHashMap<String, String> args) { | ||
signatures: | ||
for (Signature<?> signature : FunctionRegistry.getRegistry().getSignatures(namespace, name)) { | ||
LinkedHashMap<String, Expression<?>> arguments = new LinkedHashMap<>(); | ||
|
||
LinkedHashMap<String, Parameter<?>> parameters = Arrays.stream(signature.getParameters()) | ||
.collect(Collectors.toMap(Parameter::getName, p -> p, (a, b) -> b, LinkedHashMap::new)); | ||
for (Entry<String, String> entry : args.entrySet()) { | ||
Parameter<?> parameter = parameters.get(entry.getKey()); | ||
|
||
if (parameter == null) { | ||
continue signatures; | ||
} | ||
|
||
//noinspection unchecked | ||
Expression<?> expression = new SkriptParser(entry.getValue(), SkriptParser.ALL_FLAGS, ParseContext.DEFAULT) | ||
.parseExpression(parameter.getType().getC()); | ||
Efnilite marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
if (expression == null) { | ||
continue signatures; | ||
} | ||
|
||
arguments.put(entry.getKey(), expression); | ||
} | ||
|
||
Class<?>[] signatureArgs = Arrays.stream(signature.getParameters()) | ||
.map(it -> { | ||
if (it.isSingleValue()) { | ||
return it.getType().getC(); | ||
} else { | ||
return it.getType().getC().arrayType(); | ||
} | ||
}) | ||
.toArray(Class<?>[]::new); | ||
|
||
Retrieval<Function<?>> retrieval = FunctionRegistry.getRegistry().getFunction(namespace, name, signatureArgs); | ||
if (retrieval.result() == RetrievalResult.EXACT) { | ||
this.arguments = arguments; | ||
return retrieval.retrieved(); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Prints the error for when a function does not exist. | ||
* | ||
* @param name The function name. | ||
* @param arguments The passed arguments to the function call. | ||
*/ | ||
private void doesNotExist(String name, LinkedHashMap<String, String> arguments) { | ||
StringJoiner joiner = new StringJoiner(", "); | ||
|
||
for (Map.Entry<String, String> entry : arguments.entrySet()) { | ||
SkriptParser parser = new SkriptParser(entry.getValue(), SkriptParser.ALL_FLAGS, ParseContext.DEFAULT); | ||
|
||
Expression<?> expression = LiteralUtils.defendExpression(parser.parseExpression(Object.class)); | ||
|
||
if (expression == null) { | ||
joiner.add("?"); | ||
continue; | ||
} | ||
|
||
if (expression.isSingle()) { | ||
joiner.add(entry.getKey() + ": " + Classes.getSuperClassInfo(expression.getReturnType()).getName().getSingular()); | ||
} else { | ||
joiner.add(entry.getKey() + ": " + Classes.getSuperClassInfo(expression.getReturnType()).getName().getPlural()); | ||
} | ||
} | ||
|
||
Skript.error("The function %s(%s) does not exist.", name, joiner); | ||
} | ||
|
||
@Override | ||
protected Object @Nullable [] get(Event event) { | ||
Object[][] args = new Object[arguments.size()][]; | ||
int i = 0; | ||
for (Expression<?> value : arguments.values()) { | ||
args[i] = value.getArray(event); | ||
i++; | ||
} | ||
|
||
return function.execute(args); | ||
} | ||
|
||
@Override | ||
public boolean isSingle() { | ||
return function.isSingle(); | ||
} | ||
|
||
@Override | ||
public boolean isSectionOnly() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Class<?> getReturnType() { | ||
return function.getReturnType() != null ? function.getReturnType().getC() : null; | ||
} | ||
|
||
@Override | ||
public String toString(@Nullable Event event, boolean debug) { | ||
return new SyntaxStringBuilder(event, debug) | ||
.append("run function") | ||
.append(function.getName()) | ||
.append("with arguments") | ||
.toString(); | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
src/test/skript/tests/syntaxes/sections/ExprSecFunction.sk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
local function esf(x: int):: int: | ||
return 1 | ||
|
||
#local function esf(x: string):: int: | ||
# return 2 | ||
|
||
#local function esf(x: ints):: int: | ||
# return 3 | ||
|
||
local function esf_two(x: int, y: int):: int: | ||
return 4 | ||
|
||
test "function section": | ||
set {_x} to function esf with arguments: | ||
x set to 1 | ||
assert {_x} = 1 | ||
|
||
#set {_x} to function esf with arguments: | ||
# x set to "hey" | ||
#assert {_x} = 2 | ||
|
||
#set {_x} to function esf with arguments: | ||
# x set to 1 and 2 | ||
#assert {_x} = 3 | ||
|
||
parse: | ||
function esf with arguments: | ||
x set to firework | ||
assert first element of last parse logs is set | ||
|
||
#parse: | ||
# function esf with arguments: | ||
# x set to {_y} | ||
#assert first element of last parse logs is set |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.