From c46d61b22126eb1710fe07826203b6fee4081ab6 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 29 Dec 2025 17:02:25 +0200 Subject: [PATCH] Delegated Casting resolution to a helper class --- .../script/interpreter/ScriptDocument.java | 170 +------------- .../expression/CastExpressionResolver.java | 220 ++++++++++++++++++ 2 files changed, 231 insertions(+), 159 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/util/script/interpreter/expression/CastExpressionResolver.java 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 43e8db151..0e58f0904 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 @@ -1,6 +1,7 @@ package noppes.npcs.client.gui.util.script.interpreter; import noppes.npcs.client.ClientProxy; +import noppes.npcs.client.gui.util.script.interpreter.expression.CastExpressionResolver; import noppes.npcs.client.gui.util.script.interpreter.expression.ExpressionNode; import noppes.npcs.client.gui.util.script.interpreter.expression.ExpressionTypeResolver; import noppes.npcs.client.gui.util.script.interpreter.field.AssignmentInfo; @@ -2405,6 +2406,7 @@ public class ScriptDocument { /** * Resolve a cast or parenthesized expression. + * Delegates to CastExpressionResolver helper class. * Handles: * - Simple casts: (Type) expr * - Nested casts: ((Type) expr) @@ -2416,166 +2418,16 @@ public class ScriptDocument { * @return The resolved type */ private TypeInfo resolveCastOrParenthesizedExpression(String expr, int position) { - if (!expr.startsWith("(")) return null; - - // Find the matching closing parenthesis for the first open paren - int depth = 0; - int closeParen = -1; - boolean inString = false; - char stringChar = 0; - - for (int i = 0; i < expr.length(); i++) { - char c = expr.charAt(i); - - // Handle string literals - if ((c == '"' || c == '\'') && (i == 0 || expr.charAt(i - 1) != '\\')) { - if (!inString) { - inString = true; - stringChar = c; - } else if (c == stringChar) { - inString = false; - } - continue; - } - if (inString) continue; - - if (c == '(') { - depth++; - } else if (c == ')') { - depth--; - if (depth == 0) { - closeParen = i; - break; - } - } - } - - if (closeParen < 0) return null; - - // Extract content inside parens - String insideParens = expr.substring(1, closeParen).trim(); - String afterParens = expr.substring(closeParen + 1).trim(); - - // Check if this is a cast expression - // A cast has a type name inside the parens (starts with uppercase or is primitive) - // and is followed by an expression (not an operator or nothing) - if (looksLikeCastType(insideParens) && looksLikeExpressionStart(afterParens)) { - // This is a cast: (Type) rest - TypeInfo castType = resolveType(insideParens); - if (castType != null && castType.isResolved()) { - // If there's more after the cast target, resolve the chain - // E.g., ((EntityPlayer) entity).worldObj - if (!afterParens.isEmpty()) { - // Check if afterParens starts with '.' for a method/field chain - if (afterParens.startsWith(".")) { - // Continue resolving the chain from the cast type - String chainExpr = afterParens.substring(1).trim(); - List segments = parseExpressionChain(chainExpr); - TypeInfo currentType = castType; - for (ChainSegment segment : segments) { - currentType = resolveChainSegment(currentType, segment); - if (currentType == null) return null; - } - return currentType; - } - } - return castType; - } - } - - // Not a cast - treat as parenthesized expression - // Could be ((expr).method()) or just (expr) - - // First, resolve what's inside the parens - TypeInfo innerType = resolveExpressionType(insideParens, position); - - // If there's a chain after the parens, continue resolving - if (innerType != null && !afterParens.isEmpty() && afterParens.startsWith(".")) { - String chainExpr = afterParens.substring(1).trim(); - List segments = parseExpressionChain(chainExpr); - TypeInfo currentType = innerType; - for (ChainSegment segment : segments) { - currentType = resolveChainSegment(currentType, segment); - if (currentType == null) return null; - } - return currentType; - } - - return innerType; - } - - /** - * Check if a string looks like a valid type name for a cast. - * Valid: EntityPlayer, int, String, java.lang.String, int[] - */ - private boolean looksLikeCastType(String s) { - if (s == null || s.isEmpty()) return false; - s = s.trim(); - - // Handle array types: Type[] or Type[][] - while (s.endsWith("[]")) { - s = s.substring(0, s.length() - 2).trim(); - } - - if (s.isEmpty()) return false; - - // Primitive types - switch (s) { - case "byte": case "short": case "int": case "long": - case "float": case "double": case "char": case "boolean": - return true; - } - - // Class type: starts with uppercase or is fully qualified - if (Character.isUpperCase(s.charAt(0))) return true; - - // Fully qualified name: contains dots and ends with uppercase segment - if (s.contains(".")) { - String[] parts = s.split("\\."); - String lastPart = parts[parts.length - 1]; - return !lastPart.isEmpty() && Character.isUpperCase(lastPart.charAt(0)); - } - - return false; - } - - /** - * Check if a string looks like the start of an expression (what follows a cast). - * Valid: identifier, (, new, literals, this, etc. - * Invalid: empty, operators like +, -, etc. - */ - private boolean looksLikeExpressionStart(String s) { - if (s == null || s.isEmpty()) return false; - s = s.trim(); - if (s.isEmpty()) return false; - - char first = s.charAt(0); - - // Parenthesis (nested cast or parenthesized expr) - if (first == '(') return true; - - // Identifier or keyword - if (Character.isJavaIdentifierStart(first)) return true; - - // String or char literal - if (first == '"' || first == '\'') return true; - - // Number literal (could start with digit or dot for .5) - if (Character.isDigit(first)) return true; - if (first == '.' && s.length() > 1 && Character.isDigit(s.charAt(1))) return true; - - // Unary operators followed by expression - if (first == '!' || first == '~') return true; - if ((first == '+' || first == '-') && s.length() > 1) { - // Could be unary +/- or ++/-- - char second = s.charAt(1); - if (second == first || Character.isDigit(second) || Character.isJavaIdentifierStart(second) || second == '(') { - return true; - } - } - - return false; + return CastExpressionResolver.resolveCastOrParenthesizedExpression( + expr, + position, + this::resolveType, + this::resolveExpressionType, + this::parseExpressionChain, + this::resolveChainSegment + ); } + /** * Parse an expression string into chain segments. diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/expression/CastExpressionResolver.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/expression/CastExpressionResolver.java new file mode 100644 index 000000000..f0d691174 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/expression/CastExpressionResolver.java @@ -0,0 +1,220 @@ +package noppes.npcs.client.gui.util.script.interpreter.expression; + +import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; +import java.util.List; +import java.util.function.Function; + +/** + * Helper class for resolving cast expressions and distinguishing them from parenthesized expressions. + * Handles: + * - Simple casts: (Type) expr + * - Nested casts: ((Type) expr) + * - Cast chains: ((Type) expr).method() + * - Parenthesized expressions: (expr) + */ +public class CastExpressionResolver { + + /** + * Resolve a cast or parenthesized expression. + * + * @param expr The expression starting with '(' + * @param position The position in the source text + * @param resolveType Function to resolve type names to TypeInfo + * @param resolveExpression Function to resolve arbitrary expressions + * @param parseChain Function to parse expression chain + * @param resolveChainSegment Function to resolve chain segments + * @return The resolved type + */ + public static TypeInfo resolveCastOrParenthesizedExpression( + String expr, + int position, + Function resolveType, + TypeResolver resolveExpression, + ChainParser parseChain, + ChainSegmentResolver resolveChainSegment) { + + if (!expr.startsWith("(")) return null; + + // Find the matching closing parenthesis for the first open paren + int depth = 0; + int closeParen = -1; + boolean inString = false; + char stringChar = 0; + + for (int i = 0; i < expr.length(); i++) { + char c = expr.charAt(i); + + // Handle string literals + if ((c == '"' || c == '\'') && (i == 0 || expr.charAt(i - 1) != '\\')) { + if (!inString) { + inString = true; + stringChar = c; + } else if (c == stringChar) { + inString = false; + } + continue; + } + if (inString) continue; + + if (c == '(') { + depth++; + } else if (c == ')') { + depth--; + if (depth == 0) { + closeParen = i; + break; + } + } + } + + if (closeParen < 0) return null; + + // Extract content inside parens + String insideParens = expr.substring(1, closeParen).trim(); + String afterParens = expr.substring(closeParen + 1).trim(); + + // Check if this is a cast expression + // A cast has a type name inside the parens (starts with uppercase or is primitive) + // and is followed by an expression (not an operator or nothing) + if (looksLikeCastType(insideParens) && looksLikeExpressionStart(afterParens)) { + // This is a cast: (Type) rest + TypeInfo castType = resolveType.apply(insideParens); + if (castType != null && castType.isResolved()) { + // If there's more after the cast target, resolve the chain + // E.g., ((EntityPlayer) entity).worldObj + if (!afterParens.isEmpty()) { + // Check if afterParens starts with '.' for a method/field chain + if (afterParens.startsWith(".")) { + // Continue resolving the chain from the cast type + String chainExpr = afterParens.substring(1).trim(); + List segments = parseChain.parse(chainExpr); + TypeInfo currentType = castType; + for (T segment : segments) { + currentType = resolveChainSegment.resolve(currentType, segment); + if (currentType == null) return null; + } + return currentType; + } + } + return castType; + } + } + + // Not a cast - treat as parenthesized expression + // Could be ((expr).method()) or just (expr) + + // First, resolve what's inside the parens + TypeInfo innerType = resolveExpression.resolve(insideParens, position); + + // If there's a chain after the parens, continue resolving + if (innerType != null && !afterParens.isEmpty() && afterParens.startsWith(".")) { + String chainExpr = afterParens.substring(1).trim(); + List segments = parseChain.parse(chainExpr); + TypeInfo currentType = innerType; + for (T segment : segments) { + currentType = resolveChainSegment.resolve(currentType, segment); + if (currentType == null) return null; + } + return currentType; + } + + return innerType; + } + + /** + * Check if a string looks like a valid type name for a cast. + * Valid: EntityPlayer, int, String, java.lang.String, int[] + */ + public static boolean looksLikeCastType(String s) { + if (s == null || s.isEmpty()) return false; + s = s.trim(); + + // Handle array types: Type[] or Type[][] + while (s.endsWith("[]")) { + s = s.substring(0, s.length() - 2).trim(); + } + + if (s.isEmpty()) return false; + + // Primitive types + switch (s) { + case "byte": case "short": case "int": case "long": + case "float": case "double": case "char": case "boolean": + return true; + } + + // Class type: starts with uppercase or is fully qualified + if (Character.isUpperCase(s.charAt(0))) return true; + + // Fully qualified name: contains dots and ends with uppercase segment + if (s.contains(".")) { + String[] parts = s.split("\\."); + String lastPart = parts[parts.length - 1]; + return !lastPart.isEmpty() && Character.isUpperCase(lastPart.charAt(0)); + } + + return false; + } + + /** + * Check if a string looks like the start of an expression (what follows a cast). + * Valid: identifier, (, new, literals, this, etc. + * Invalid: empty, operators like +, -, etc. + */ + public static boolean looksLikeExpressionStart(String s) { + if (s == null || s.isEmpty()) return false; + s = s.trim(); + if (s.isEmpty()) return false; + + char first = s.charAt(0); + + // Parenthesis (nested cast or parenthesized expr) + if (first == '(') return true; + + // Identifier or keyword + if (Character.isJavaIdentifierStart(first)) return true; + + // String or char literal + if (first == '"' || first == '\'') return true; + + // Number literal (could start with digit or dot for .5) + if (Character.isDigit(first)) return true; + if (first == '.' && s.length() > 1 && Character.isDigit(s.charAt(1))) return true; + + // Unary operators followed by expression + if (first == '!' || first == '~') return true; + if ((first == '+' || first == '-') && s.length() > 1) { + // Could be unary +/- or ++/-- + char second = s.charAt(1); + if (second == first || Character.isDigit(second) || Character.isJavaIdentifierStart(second) || second == '(') { + return true; + } + } + + return false; + } + + /** + * Functional interface for resolving type expressions. + */ + @FunctionalInterface + public interface TypeResolver { + TypeInfo resolve(String expr, int position); + } + + /** + * Functional interface for parsing expression chains. + */ + @FunctionalInterface + public interface ChainParser { + List parse(String expr); + } + + /** + * Functional interface for resolving chain segments. + */ + @FunctionalInterface + public interface ChainSegmentResolver { + TypeInfo resolve(TypeInfo currentType, T segment); + } +}