1313import ch .njol .skript .lang .DefaultExpressionUtils .DefaultExpressionError ;
1414import ch .njol .skript .lang .function .ExprFunctionCall ;
1515import ch .njol .skript .lang .function .FunctionReference ;
16- import ch .njol .skript .lang .function .FunctionRegistry ;
17- import ch .njol .skript .lang .function .Functions ;
1816import ch .njol .skript .lang .parser .DefaultValueData ;
1917import ch .njol .skript .lang .parser .ParseStackOverflowException ;
2018import ch .njol .skript .lang .parser .ParserInstance ;
4543import org .bukkit .plugin .java .JavaPlugin ;
4644import org .jetbrains .annotations .NotNull ;
4745import org .jetbrains .annotations .Nullable ;
46+ import org .skriptlang .skript .common .function .FunctionReferenceParser ;
4847import org .skriptlang .skript .lang .converter .Converters ;
4948import org .skriptlang .skript .lang .experiment .ExperimentSet ;
5049import org .skriptlang .skript .lang .experiment .ExperimentalSyntax ;
5857import java .util .Map .Entry ;
5958import java .util .concurrent .ConcurrentHashMap ;
6059import java .util .concurrent .atomic .AtomicBoolean ;
61- import java .util .function .Supplier ;
6260import java .util .logging .Level ;
6361import java .util .regex .MatchResult ;
6462import java .util .regex .Matcher ;
@@ -519,11 +517,10 @@ private static <T extends SyntaxElement> boolean checkExperimentalSyntax(T eleme
519517 log .printError ();
520518 return null ;
521519 }
522- FunctionReference <T > functionReference = parseFunction ( types );
520+ org . skriptlang . skript . common . function . FunctionReference <T > functionReference = parseFunctionReference ( );
523521 if (functionReference != null ) {
524522 log .printLog ();
525- //noinspection rawtypes
526- return new ExprFunctionCall (functionReference );
523+ return new ExprFunctionCall <>(functionReference , types );
527524 } else if (log .hasError ()) {
528525 log .printError ();
529526 return null ;
@@ -675,9 +672,8 @@ private static <T extends SyntaxElement> boolean checkExperimentalSyntax(T eleme
675672 }
676673
677674 // If it wasn't variable, do same for function call
678- FunctionReference <?> functionReference = parseFunction ( types );
675+ org . skriptlang . skript . common . function . FunctionReference <?> functionReference = parseFunctionReference ( );
679676 if (functionReference != null ) {
680-
681677 if (onlySingular && !functionReference .isSingle ()) {
682678 Skript .error ("'" + expr + "' can only be a single "
683679 + Classes .toString (Stream .of (exprInfo .classes ).map (classInfo -> classInfo .getName ().toString ()).toArray (), false )
@@ -687,7 +683,7 @@ private static <T extends SyntaxElement> boolean checkExperimentalSyntax(T eleme
687683 }
688684
689685 log .printLog ();
690- return new ExprFunctionCall <>(functionReference );
686+ return new ExprFunctionCall <>(functionReference , types );
691687 } else if (log .hasError ()) {
692688 log .printError ();
693689 return null ;
@@ -907,12 +903,7 @@ private boolean checkAcceptedType(Class<?> clazz, Class<?> ... types) {
907903 private final static String MULTIPLE_AND_OR = "List has multiple 'and' or 'or', will default to 'and'. Use brackets if you want to define multiple lists." ;
908904 private final static String MISSING_AND_OR = "List is missing 'and' or 'or', defaulting to 'and'" ;
909905
910- private boolean suppressMissingAndOrWarnings = SkriptConfig .disableMissingAndOrWarnings .value ();
911-
912- private SkriptParser suppressMissingAndOrWarnings () {
913- suppressMissingAndOrWarnings = true ;
914- return this ;
915- }
906+ private final boolean suppressMissingAndOrWarnings = SkriptConfig .disableMissingAndOrWarnings .value ();
916907
917908 @ SafeVarargs
918909 public final <T > @ Nullable Expression <? extends T > parseExpression (Class <? extends T >... types ) {
@@ -1185,173 +1176,38 @@ private record OrderedExprInfo(ExprInfo[] infos) { }
11851176 * Function parsing
11861177 */
11871178
1188- private final static Pattern FUNCTION_CALL_PATTERN = Pattern .compile ("(" + Functions .functionNamePattern + ")\\ ((.*)\\ )" );
1189-
1179+
11901180 /**
1191- * @param types The required return type or null if it is not used (e.g. when calling a void function)
1192- * @return The parsed function, or null if the given expression is not a function call or is an invalid function call (check for an error to differentiate these two)
1181+ * Attempts to parse {@link SkriptParser#expr} as a function reference.
1182+ *
1183+ * @param <T> The return type of the function.
1184+ * @return A {@link FunctionReference} if a function is found, or {@code null} if none is found.
11931185 */
1194- @ SuppressWarnings ("unchecked" )
1195- public <T > @ Nullable FunctionReference <T > parseFunction (@ Nullable Class <? extends T >... types ) {
1196- if (context != ParseContext .DEFAULT && context != ParseContext .EVENT )
1197- return null ;
1198- try (ParseLogHandler log = SkriptLogger .startParseLogHandler ()) {
1199- Matcher matcher = FUNCTION_CALL_PATTERN .matcher (expr );
1200- if (!matcher .matches ()) {
1201- log .printLog ();
1202- return null ;
1203- }
1204-
1205- String functionName = matcher .group (1 );
1206- String args = matcher .group (2 );
1207-
1208- // Check for incorrect quotes, e.g. "myFunction() + otherFunction()" being parsed as one function
1209- // See https://github.com/SkriptLang/Skript/issues/1532
1210- for (int i = 0 ; i < args .length (); i = next (args , i , context )) {
1211- if (i == -1 ) {
1212- log .printLog ();
1213- return null ;
1214- }
1215- }
1216-
1217- if ((flags & PARSE_EXPRESSIONS ) == 0 ) {
1218- Skript .error ("Functions cannot be used here (or there is a problem with your arguments)." );
1219- log .printError ();
1220- return null ;
1221- }
1222-
1223- SkriptParser skriptParser = new SkriptParser (args , flags | PARSE_LITERALS , context )
1224- .suppressMissingAndOrWarnings ();
1225- Expression <?>[] params = args .isEmpty () ? new Expression [0 ] : null ;
1226-
1227- String namespace = null ;
1228- ParserInstance parser = getParser ();
1229- if (parser .isActive ()) {
1230- namespace = parser .getCurrentScript ().getConfig ().getFileName ();
1231- }
1232-
1233- if (params == null ) { // there are arguments to parse
1234- // determine signatures that could match
1235- var signatures = FunctionRegistry .getRegistry ().getSignatures (namespace , functionName ).stream ()
1236- .filter (signature -> {
1237- if (signature .getMaxParameters () == 0 ) { // we have arguments, but this function doesn't
1238- return false ;
1239- }
1240- if (types != null ) { // filter signatures based on expected return type
1241- if (signature .getReturnType () == null ) {
1242- return false ;
1243- }
1244- Class <?> signatureType = signature .getReturnType ().getC ();
1245- for (Class <?> type : types ) {
1246- //noinspection DataFlowIssue - individual elements won't be null
1247- if (Converters .converterExists (signatureType , type )) {
1248- return true ;
1249- }
1250- }
1251- return false ;
1252- }
1253- return true ;
1254- })
1255- .toList ();
1256-
1257- // here, we map all signatures into type/plurality collections
1258- // for example, all possible types (and whether they are plural) for the first parameter
1259- // will be mapped into the 0-index of both collections
1260- record SignatureData (ClassInfo <?> classInfo , boolean plural ) { }
1261- List <List <SignatureData >> signatureDatas = new ArrayList <>();
1262- boolean trySingle = false ;
1263- boolean trySinglePlural = false ;
1264- for (var signature : signatures ) {
1265- trySingle |= signature .getMinParameters () <= 1 || signature .getMaxParameters () == 1 ;
1266- trySinglePlural |= trySingle && !signature .getParameter (0 ).isSingleValue ();
1267- for (int i = 0 ; i < signature .getMaxParameters (); i ++) {
1268- if (signatureDatas .size () <= i ) {
1269- signatureDatas .add (new ArrayList <>());
1270- }
1271- var parameter = signature .getParameter (i );
1272- signatureDatas .get (i ).add (new SignatureData (parameter .getType (), !parameter .isSingleValue ()));
1273- }
1274- }
1275- ExprInfo [] signatureInfos = new ExprInfo [signatureDatas .size ()];
1276- for (int infoIndex = 0 ; infoIndex < signatureInfos .length ; infoIndex ++) {
1277- List <SignatureData > datas = signatureDatas .get (infoIndex );
1278- ClassInfo <?>[] infos = new ClassInfo [datas .size ()];
1279- boolean [] isPlural = new boolean [infos .length ];
1280- for (int dataIndex = 0 ; dataIndex < infos .length ; dataIndex ++) {
1281- SignatureData data = datas .get (dataIndex );
1282- infos [dataIndex ] = data .classInfo ;
1283- isPlural [dataIndex ] = data .plural ;
1284- }
1285- signatureInfos [infoIndex ] = new ExprInfo (infos , isPlural );
1286- }
1287- OrderedExprInfo orderedExprInfo = new OrderedExprInfo (signatureInfos );
1288-
1289- if (trySingle ) {
1290- params = this .getFunctionArguments (
1291- () -> skriptParser .parseSingleExpr (true , null , orderedExprInfo .infos [0 ]),
1292- args );
1293- if (params == null && trySinglePlural ) {
1294- log .clear ();
1295- log .clearError ();
1296- try (ParseLogHandler listLog = SkriptLogger .startParseLogHandler ()) {
1297- params = this .getFunctionArguments (
1298- () -> skriptParser .parseExpressionList (listLog , orderedExprInfo .infos [0 ]),
1299- args );
1300- }
1301- }
1302- }
1303- if (params == null ) {
1304- log .clear ();
1305- log .clearError ();
1306- try (ParseLogHandler listLog = SkriptLogger .startParseLogHandler ()) {
1307- params = this .getFunctionArguments (
1308- () -> skriptParser .parseExpressionList (listLog , orderedExprInfo ),
1309- args );
1310- }
1311- }
1312- if (params == null ) {
1313- log .printError ();
1314- return null ;
1315- }
1316- }
1317-
1318- for (Expression <?> param : params ) {
1319- if (KeyProviderExpression .areKeysRecommended (param ))
1320- param .returnNestedStructures (true );
1321- }
1322- FunctionReference <T > functionReference = new FunctionReference <>(functionName , SkriptLogger .getNode (), namespace , types , params );
1323- if (!functionReference .validateFunction (true )) {
1324- log .printError ();
1325- return null ;
1326- }
1327- log .printLog ();
1328- return functionReference ;
1186+ public <T > org .skriptlang .skript .common .function .FunctionReference <T > parseFunctionReference () {
1187+ if (context == ParseContext .DEFAULT || context == ParseContext .EVENT ) {
1188+ return new FunctionReferenceParser (context , flags ).parseFunctionReference (expr );
13291189 }
1190+ return null ;
13301191 }
13311192
1332- private Expression <?> @ Nullable [] getFunctionArguments (Supplier <Expression <?>> parsing , String args ) {
1333- if (args .isEmpty ()) {
1334- return new Expression [0 ];
1193+ /**
1194+ * @deprecated Use {@link #parseFunctionReference()} instead.
1195+ */
1196+ @ Deprecated (forRemoval = true , since = "INSERT VERSION" )
1197+ public <T > @ Nullable FunctionReference <T > parseFunction (@ Nullable Class <? extends T >... types ) {
1198+ if (context != ParseContext .DEFAULT && context != ParseContext .EVENT ) {
1199+ return null ;
13351200 }
1336-
1337- Expression <?> parsedExpression = parsing .get ();
1338- if (parsedExpression == null ) {
1201+ var newReference = new FunctionReferenceParser (context , flags ).parseFunctionReference (expr );
1202+ if (newReference == null ) {
13391203 return null ;
13401204 }
13411205
1342- Expression <?>[] params ;
1343- if (parsedExpression instanceof ExpressionList ) {
1344- if (!parsedExpression .getAnd ()) {
1345- Skript .error ("Function arguments must be separated by commas and optionally an 'and', but not an 'or'."
1346- + " Put the 'or' into a second set of parentheses if you want to make it a single parameter, e.g. 'give(player, (sword or axe))'" );
1347- return null ;
1348- }
1349- params = ((ExpressionList <?>) parsedExpression ).getExpressions ();
1350- } else {
1351- params = new Expression [] {parsedExpression };
1352- }
1206+ var expressions = Arrays .stream (newReference .arguments ())
1207+ .map (org .skriptlang .skript .common .function .FunctionReference .Argument ::value )
1208+ .toArray (Expression []::new );
13531209
1354- return params ;
1210+ return new FunctionReference <>( newReference . name (), null , newReference . namespace (), types , expressions ) ;
13551211 }
13561212
13571213 /*
@@ -1596,22 +1452,24 @@ public static int next(String expr, int startIndex, ParseContext context) {
15961452 return startIndex + 1 ;
15971453
15981454 int index ;
1599- switch (expr .charAt (startIndex )) {
1600- case '"' :
1455+ return switch (expr .charAt (startIndex )) {
1456+ case '"' -> {
16011457 index = nextQuote (expr , startIndex + 1 );
1602- return index < 0 ? -1 : index + 1 ;
1603- case '{' :
1458+ yield index < 0 ? -1 : index + 1 ;
1459+ }
1460+ case '{' -> {
16041461 index = VariableString .nextVariableBracket (expr , startIndex + 1 );
1605- return index < 0 ? -1 : index + 1 ;
1606- case '(' :
1462+ yield index < 0 ? -1 : index + 1 ;
1463+ }
1464+ case '(' -> {
16071465 for (index = startIndex + 1 ; index >= 0 && index < exprLength ; index = next (expr , index , context )) {
16081466 if (expr .charAt (index ) == ')' )
1609- return index + 1 ;
1467+ yield index + 1 ;
16101468 }
1611- return -1 ;
1612- default :
1613- return startIndex + 1 ;
1614- }
1469+ yield -1 ;
1470+ }
1471+ default -> startIndex + 1 ;
1472+ };
16151473 }
16161474
16171475 /**
@@ -1816,18 +1674,4 @@ private static ParserInstance getParser() {
18161674 ParserInstance .registerData (DefaultValueData .class , DefaultValueData ::new );
18171675 }
18181676
1819- /**
1820- * @deprecated due to bad naming conventions,
1821- * use {@link #LIST_SPLIT_PATTERN} instead.
1822- */
1823- @ Deprecated (since = "2.7.0" , forRemoval = true )
1824- public final static Pattern listSplitPattern = LIST_SPLIT_PATTERN ;
1825-
1826- /**
1827- * @deprecated due to bad naming conventions,
1828- * use {@link #WILDCARD} instead.
1829- */
1830- @ Deprecated (since = "2.8.0" , forRemoval = true )
1831- public final static String wildcard = WILDCARD ;
1832-
18331677}
0 commit comments