From ad2c8c89c1201f8d0716836caedd9b99a18f9e96 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Thu, 5 Mar 2026 16:07:39 +0200 Subject: [PATCH] Array syntax is now functional and resolves i.e. 'items[i].getName()' --- .../script/interpreter/ScriptDocument.java | 65 +++++++++++++++++-- .../script/interpreter/type/TypeInfo.java | 28 ++++++-- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java index d24828942..110d9bb64 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java @@ -4954,9 +4954,15 @@ public class ScriptDocument { String fieldName = segments.get(1).name; if (globalFields.containsKey(fieldName)) { currentType = globalFields.get(fieldName).getTypeInfo(); + if (currentType != null && segments.get(1).hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } // Continue from segment 2 for (int i = 2; i < segments.size(); i++) { currentType = resolveChainSegment(currentType, segments.get(i)); + if (currentType != null && segments.get(i).hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } if (currentType == null) return null; } return currentType; @@ -5004,10 +5010,17 @@ public class ScriptDocument { } } } - + + if (currentType != null && first.hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } + // Resolve the rest of the chain for (int i = 1; i < segments.size(); i++) { currentType = resolveChainSegment(currentType, segments.get(i)); + if (currentType != null && segments.get(i).hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } if (currentType == null) { return null; } @@ -5104,13 +5117,12 @@ public class ScriptDocument { } } - // Check if followed by array brackets (array access) - // Skip array accesses like [0] or [i] - treat them as part of the current segment + boolean hasArrayAccess = false; while (i < expr.length() && Character.isWhitespace(expr.charAt(i))) { i++; } if (i < expr.length() && expr.charAt(i) == '[') { - // Skip to the matching closing bracket + hasArrayAccess = true; int depth = 1; i++; while (i < expr.length() && depth > 0) { @@ -5121,7 +5133,7 @@ public class ScriptDocument { } } - segments.add(new ChainSegment(name, start, i, isMethodCall, arguments)); + segments.add(new ChainSegment(name, start, i, isMethodCall, arguments, hasArrayAccess)); // Skip whitespace while (i < expr.length() && Character.isWhitespace(expr.charAt(i))) { @@ -5239,16 +5251,48 @@ public class ScriptDocument { final int end; final boolean isMethodCall; final String arguments; // The text between parentheses for method calls, or null for fields + final boolean hasArrayAccess; - ChainSegment(String name, int start, int end, boolean isMethodCall, String arguments) { + ChainSegment(String name, int start, int end, boolean isMethodCall, String arguments, boolean hasArrayAccess) { this.name = name; this.start = start; this.end = end; this.isMethodCall = isMethodCall; this.arguments = arguments; + this.hasArrayAccess = hasArrayAccess; } } + /** + * Extract the element type from an array type, using multiple fallback strategies. + * + * Used when a chain segment has array subscript notation (e.g., items[1]). + * Given an array type like IItemStack[], returns IItemStack so that subsequent + * chain resolution (e.g., .getName()) operates on the element type, not the array type. + * + * Strategy (in priority order): + * 1. If TypeInfo stores elementType (preferred, from arrayOf factory), use it directly + * 2. If fullName has [] suffix (legacy or JS types), strip it and re-resolve + * 3. If underlying javaClass is an array, use Class.getComponentType() + * 4. Fallback to ANY type to prevent null propagation + * + * @param arrayType The array type to unwrap + * @return The element type, or TypeInfo.ANY if unwrapping fails + */ + private TypeInfo unwrapArrayElement(TypeInfo arrayType) { + if (arrayType == null) return null; + TypeInfo el = arrayType.getElementType(); + if (el != null) return el; + String name = arrayType.getFullName(); + if (name != null && name.endsWith("[]")) { + return resolveType(name.substring(0, name.length() - 2)); + } + if (arrayType.getJavaClass() != null && arrayType.getJavaClass().isArray()) { + return TypeInfo.fromClass(arrayType.getJavaClass().getComponentType()); + } + return TypeInfo.ANY; + } + /** * Resolve a single segment of a chain given the current type context. */ @@ -5741,9 +5785,16 @@ public class ScriptDocument { } } } - + + if (currentType != null && first.hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } + for (int i = 1; i < segments.size(); i++) { currentType = resolveChainSegment(currentType, segments.get(i)); + if (currentType != null && segments.get(i).hasArrayAccess) { + currentType = unwrapArrayElement(currentType); + } if (currentType == null) { return null; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java index 924af3958..aa6ad6408 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java @@ -81,6 +81,10 @@ public class TypeInfo { // Null for non-parameterized types private final TypeInfo rawType; + // For array types, the element type (e.g., IItemStack for IItemStack[]) + // Null for non-array types + private final TypeInfo elementType; + // Documentation (script-defined types) private JSDocInfo jsDocInfo; @@ -90,18 +94,18 @@ public class TypeInfo { private TypeInfo(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType) { - this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, null, null, null); + this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, null, null, null, null); } private TypeInfo(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType, JSTypeInfo jsTypeInfo) { - this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, jsTypeInfo, null, null); + this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, jsTypeInfo, null, null, null); } private TypeInfo(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType, - JSTypeInfo jsTypeInfo, TypeInfo rawType, List appliedTypeArgs) { + JSTypeInfo jsTypeInfo, TypeInfo rawType, List appliedTypeArgs, TypeInfo elementType) { this.simpleName = simpleName; this.fullName = fullName; this.packageName = packageName; @@ -111,6 +115,7 @@ public class TypeInfo { this.enclosingType = enclosingType; this.jsTypeInfo = jsTypeInfo; this.rawType = rawType; + this.elementType = elementType; if (appliedTypeArgs != null) { this.appliedTypeArgs.addAll(appliedTypeArgs); } @@ -129,6 +134,7 @@ public class TypeInfo { this.enclosingType = enclosingType; this.jsTypeInfo = null; this.rawType = null; + this.elementType = null; } // Factory methods @@ -178,6 +184,10 @@ public class TypeInfo { enclosing = fromClass(clazz.getEnclosingClass()); } + if (clazz.isArray()) { + return arrayOf(fromClass(clazz.getComponentType())); + } + return new TypeInfo(simpleName, fullName, packageName, kind, clazz, true, enclosing); } @@ -333,7 +343,7 @@ public class TypeInfo { } } - return new TypeInfo(simpleName, fullName, pkg, Kind.CLASS, arrayClass, true, null); + return new TypeInfo(simpleName, fullName, pkg, Kind.CLASS, arrayClass, true, null, null, null, null, elementType); } /** @@ -390,6 +400,14 @@ public class TypeInfo { public TypeInfo getRawType() { return rawType != null ? rawType : this; } + + public TypeInfo getElementType() { + return elementType; + } + + public boolean isArray() { + return elementType != null || (javaClass != null && javaClass.isArray()); + } /** * Get the applied type arguments. @@ -417,7 +435,7 @@ public class TypeInfo { // Generic arguments are tracked via appliedTypeArgs and rendered via getDisplayName*(). TypeInfo raw = getRawType(); return new TypeInfo(raw.simpleName, raw.fullName, raw.packageName, raw.kind, raw.javaClass, - raw.resolved, raw.enclosingType, raw.jsTypeInfo, raw, typeArgs); + raw.resolved, raw.enclosingType, raw.jsTypeInfo, raw, typeArgs, null); } /**