diff --git a/src/main/java/com/laytonsmith/core/functions/ArrayHandling.java b/src/main/java/com/laytonsmith/core/functions/ArrayHandling.java index 61aacacbd..7963adfec 100644 --- a/src/main/java/com/laytonsmith/core/functions/ArrayHandling.java +++ b/src/main/java/com/laytonsmith/core/functions/ArrayHandling.java @@ -16,7 +16,9 @@ import com.laytonsmith.core.ParseTree; import com.laytonsmith.core.Script; import com.laytonsmith.core.compiler.FileOptions; -import com.laytonsmith.core.compiler.analysis.StaticAnalysis; +import com.laytonsmith.core.compiler.signature.FunctionSignatures; +import com.laytonsmith.core.compiler.signature.FunctionSignatures.MatchType; +import com.laytonsmith.core.compiler.signature.SignatureBuilder; import com.laytonsmith.core.constructs.CArray; import com.laytonsmith.core.constructs.CBoolean; import com.laytonsmith.core.constructs.CClassType; @@ -100,12 +102,8 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 1) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - } - return CInt.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CInt.TYPE).param(CArray.TYPE, "array", "The array to get the size of.").build(); } @Override @@ -264,22 +262,19 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } else if(args[0].isInstanceOf(ArrayAccess.TYPE)) { com.laytonsmith.core.natives.interfaces.Iterable aa = (com.laytonsmith.core.natives.interfaces.Iterable) args[0]; - if(index instanceof CSlice) { + if(index instanceof CSlice cslice) { //It's a range - int start = (int) ((CSlice) index).getStart(); - int finish = (int) ((CSlice) index).getFinish(); - try { - //Convert negative indexes - if(start < 0) { - start = (int) aa.size() + start; - } - if(finish < 0) { - finish = (int) aa.size() + finish; - } - return aa.slice(start, finish + 1, t); - } catch (NumberFormatException e) { - throw new CRECastException("Ranges must be integer numbers, i.e., [0..5]", t); + int start = (int) cslice.getStart(); + int finish = (int) cslice.getFinish(); + + //Convert negative indexes + if(start < 0) { + start = (int) aa.size() + start; + } + if(finish < 0) { + finish = (int) aa.size() + finish; } + return aa.slice(start, finish + 1, t); } else if(index.isInstanceOf(CInt.TYPE)) { return aa.get(ArgumentValidation.getInt32(index, t), t); } else { @@ -291,17 +286,27 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 2 || argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(0), ArrayAccess.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireAnyType(argTypes.get(1), - new CClassType[] {CInt.TYPE, CSlice.TYPE, CString.TYPE}, argTargets.get(1), env, exceptions); - if(argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(2), Mixed.TYPE, argTargets.get(2), env, exceptions); - } - } - return CClassType.AUTO; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CClassType.AUTO) + .param(ArrayAccess.TYPE, "array", "The array.") + .param(CInt.TYPE, "index", "The array index.") + .param(Mixed.TYPE, "default", + "The value that is returned if no element at the given index exists.", true) + .throwsEx(CREIndexOverflowException.class, + "When no element exists at the given index, and no default value is given.") + .newSignature(CClassType.AUTO) + .param(ArrayAccess.TYPE, "array", "The array.") + .param(CString.TYPE, "key", "The array index key.") + .param(Mixed.TYPE, "default", + "The value that is returned if no element at the given index exists.", true) + .throwsEx(CREIndexOverflowException.class, + "When no element exists at the given index, and no default value is given.") + .newSignature(CArray.TYPE, "An array containing the values selected by the slice.") + .param(ArrayAccess.TYPE, "array", "The array.") + .param(CSlice.TYPE, "indexRange", "The array index range slice.") + .throwsEx(CREIndexOverflowException.class, + "When no element exists at an index within the given range.") + .build(); } @Override @@ -413,38 +418,30 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes) if(!(array.isInstanceOf(CArray.TYPE))) { throw new CRECastException("Argument 1 of " + this.getName() + " must be an array", t); } - try { - ((CArray) array).set(index, value, t); - } catch (IndexOutOfBoundsException e) { - throw new CREIndexOverflowException("The index " + new CString(index).getQuote() + " is out of bounds", t); - } + ((CArray) array).set(index, value, t); return value; } @Override public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntimeException { if(args[0].isInstanceOf(CArray.TYPE)) { - try { - ((CArray) args[0]).set(args[1], args[2], t); - } catch (IndexOutOfBoundsException e) { - throw new CREIndexOverflowException("The index " + args[1].val() + " is out of bounds", t); - } + ((CArray) args[0]).set(args[1], args[2], t); return args[2]; } throw new CRECastException("Argument 1 of " + this.getName() + " must be an array", t); } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireAnyType(argTypes.get(1), - new CClassType[] {CInt.TYPE, CSlice.TYPE, CString.TYPE}, argTargets.get(1), env, exceptions); - StaticAnalysis.requireType(argTypes.get(2), Mixed.TYPE, argTargets.get(2), env, exceptions); - return argTypes.get(2); - } - return CClassType.AUTO; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CClassType.AUTO, "The value that was set, to allow for chaining.") + .param(CArray.TYPE, "array", "The array.") + .param(CInt.TYPE, "index", "The array index.") + .param(Mixed.TYPE, "value", "The value to set.") + .newSignature(CClassType.AUTO, "The value that was set, to allow for chaining.") + .param(CArray.TYPE, "array", "The array.") + .param(CString.TYPE, "key", "The array index key.") + .param(Mixed.TYPE, "value", "The value to set.") + .build(); } @Override @@ -530,15 +527,12 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() >= 2) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - for(int i = 1; i < argTypes.size(); i++) { - StaticAnalysis.requireType(argTypes.get(i), Mixed.TYPE, argTargets.get(i), env, exceptions); - } - } - return CVoid.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CVoid.TYPE) + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "value", "The first value to push.") + .varParam(Mixed.TYPE, "values", "Additional values to push.") + .build(); } @Override @@ -638,14 +632,12 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireType(argTypes.get(1), Mixed.TYPE, argTargets.get(1), env, exceptions); - StaticAnalysis.requireType(argTypes.get(2), CInt.TYPE, argTargets.get(2), env, exceptions); - } - return CVoid.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CVoid.TYPE) + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "value", "The value to insert.") + .param(CInt.TYPE, "index", "The array index at which to insert the value.") + .build(); } @Override @@ -719,13 +711,12 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws CancelCommand } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 2) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireType(argTypes.get(1), Mixed.TYPE, argTargets.get(1), env, exceptions); - } - return CBoolean.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CBoolean.TYPE, "{@code true} if a value {@code v} is in the array for" + + " which {@code v == value}, {@code false} otherwise.") + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "value", "The value to check for.") + .build(); } @Override @@ -829,13 +820,12 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 2) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireType(argTypes.get(1), Mixed.TYPE, argTargets.get(1), env, exceptions); - } - return CBoolean.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CBoolean.TYPE, "{@code true} if a value {@code v} is in the array for" + + " which {@code equals_ic(v, value)}, {@code false} otherwise.") + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "value", "The value to check for.") + .build(); } @Override @@ -883,13 +873,12 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws CancelCommand } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 2) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireType(argTypes.get(1), Mixed.TYPE, argTargets.get(1), env, exceptions); - } - return CBoolean.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CBoolean.TYPE, "{@code true} if a value {@code v} is in the array for" + + " which {@code v === value}, {@code false} otherwise.") + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "value", "The value to check for.") + .build(); } @Override @@ -1015,15 +1004,13 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() >= 1) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - for(int i = 1; i < argTypes.size(); i++) { - StaticAnalysis.requireType(argTypes.get(i), Mixed.TYPE, argTargets.get(i), env, exceptions); - } - } - return CBoolean.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CBoolean.TYPE, + "{@code true} if the index or indices exist(s), {@code false} otherwise.") + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "index", "The array index to check for.") + .varParam(Mixed.TYPE, "index", "Additional nested array indices to check for.") + .build(); } @Override @@ -1121,16 +1108,13 @@ public CArray exec(Target t, Environment env, Mixed... args) throws ConfigRuntim } @Override - public CClassType getReturnType(Target t, List argTypes, List argTargets, - Environment env, Set exceptions) { - if(argTypes.size() == 2 || argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(0), CArray.TYPE, argTargets.get(0), env, exceptions); - StaticAnalysis.requireType(argTypes.get(1), CInt.TYPE, argTargets.get(1), env, exceptions); - if(argTypes.size() == 3) { - StaticAnalysis.requireType(argTypes.get(2), Mixed.TYPE, argTargets.get(2), env, exceptions); - } - } - return CArray.TYPE; + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, "A reference to the passed array, to allow for chaining.") + .param(CArray.TYPE, "array", "The array.") + .param(CInt.TYPE, "size", "The size to enlarge the array to.") + .param(Mixed.TYPE, "fill", + "The value to fill the new indices with. Defaults to {@code null}.", true) + .build(); } @Override @@ -1215,6 +1199,15 @@ public CArray exec(Target t, Environment env, Mixed... args) throws ConfigRuntim return ret; } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE) + .param(CInt.TYPE, "start", "The start value. Defaults to 0.", true) + .param(CInt.TYPE, "finish", "The finish value.") + .param(CInt.TYPE, "increment", "The value increment. Defaults to 1.", true) + .build(); + } + @Override public ExampleScript[] examples() throws ConfigCompileException { return new ExampleScript[]{ @@ -1286,6 +1279,13 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, "An array containing the array keys.") + .param(ArrayAccess.TYPE, "array", "The array.") + .build(); + } + @Override public ExampleScript[] examples() throws ConfigCompileException { return new ExampleScript[]{ @@ -1352,6 +1352,13 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime } } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, "The normalized array.") + .param(ArrayAccess.TYPE, "array", "The array to generate the normalized array for.") + .build(); + } + @Override public ExampleScript[] examples() throws ConfigCompileException { return new ExampleScript[]{ @@ -1436,6 +1443,15 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi return newArray; } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, "The array containing all entries from the given arrays.") + .param(ArrayAccess.TYPE, "array1", "The first array.") + .param(ArrayAccess.TYPE, "array2", "The second array.") + .varParam(ArrayAccess.TYPE, "arrays", "Additional arrays.") + .build(); + } + @Override public ExampleScript[] examples() throws ConfigCompileException { return new ExampleScript[]{ @@ -1510,6 +1526,15 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi } } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(Mixed.TYPE, "The removed value, or {@code null} if nothing was removed.") + .param(CArray.TYPE, "array", "The array.") + .param(Mixed.TYPE, "index", "The array index.") + .throwsEx(CRECastException.class, "When the array is non-associative and the index is not an int.") + .build(); + } + @Override public ExampleScript[] examples() throws ConfigCompileException { return new ExampleScript[]{ @@ -1580,6 +1605,14 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi return new CString(b.toString(), t); } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CString.TYPE, "The imploded array string.") + .param(ArrayAccess.TYPE, "array", "The array.") + .param(CString.TYPE, "glue", "The glue to place between glued values. Defaults to ' '.", true) + .build(); + } + @Override public MSVersion since() { return MSVersion.V3_3_0; @@ -1636,6 +1669,16 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi return new CString(b.toString(), t); } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CString.TYPE, "The imploded associative array string.") + .param(CArray.TYPE, "array", "The associative array.") + .param(CString.TYPE, "innerGlue", "The glue to place between the array keys and their values.") + .param(CString.TYPE, "outerGlue", "The glue to place between different key-value pairs.") + .throwsEx(CRECastException.class, "When a non-associative array is passed.") + .build(); + } + @Override public String getName() { return "map_implode"; @@ -1712,6 +1755,14 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi return new CSlice(ArgumentValidation.getInt(args[0], t), ArgumentValidation.getInt(args[1], t), t); } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CSlice.TYPE) + .param(CInt.TYPE, "start", "The start value.") + .param(CInt.TYPE, "end", "The end value.") + .build(); + } + @Override public MSVersion since() { return MSVersion.V3_3_1; @@ -1788,6 +1839,21 @@ public Mixed exec(Target t, Environment environment, Mixed... args) throws Confi return ca; } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, + "A reference to the passed array, to allow for chaining.", MatchType.MATCH_FIRST) + .param(CArray.TYPE, "array", "The array.") + .param(CClosure.TYPE, "sortClosure", "A closure to which two values v1 and v2 are passed." + + " It should return {@code true} if {@code v1 > v2}, {@code true} if {@code v1 < v2}" + + " and {@code null} if {@code v1 == v2}.") + .newSignature(CArray.TYPE, "A reference to the passed array, to allow for chaining.") + .param(CArray.TYPE, "array", "The array.") + // TODO - Make this CArray.ArraySortType once this is a valid CClassType, and remove MATCH_FIRST. + .param(Mixed.TYPE, "sortType", "The array sort type.", true) + .build(); + } + private CArray customSort(CArray ca, CClosure closure, Target t) { if(ca.size() <= 1) { return ca; @@ -1999,6 +2065,21 @@ public void run() { return CVoid.VOID; } + @Override + public FunctionSignatures getSignatures() { + return new SignatureBuilder(CArray.TYPE, + "A reference to the passed array, to allow for chaining.", MatchType.MATCH_FIRST) + .param(CArray.TYPE, "array", "The array.") + .param(CClosure.TYPE, "sortClosure", "A closure to which two values v1 and v2 are passed." + + " It should return {@code true} if {@code v1 > v2}, {@code true} if {@code v1 < v2}" + + " and {@code null} if {@code v1 == v2}.") + .newSignature(CArray.TYPE, "A reference to the passed array, to allow for chaining.") + .param(CArray.TYPE, "array", "The array.") + // TODO - Make this CArray.ArraySortType once this is a valid CClassType, and remove MATCH_FIRST. + .param(Mixed.TYPE, "sortType", "The array sort type.", true) + .build(); + } + @Override public String getName() { return "array_sort_async";