mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-14 00:14:09 +00:00
Delegated Casting resolution to a helper class
This commit is contained in:
1 parent
efd7d96ec7
commit
c46d61b221
2 files changed
+231
-159
No files matched your search
@@ -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<ChainSegment> 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<ChainSegment> 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.
|
||||
|
||||
+220
@@ -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 <T> TypeInfo resolveCastOrParenthesizedExpression(
|
||||
String expr,
|
||||
int position,
|
||||
Function<String, TypeInfo> resolveType,
|
||||
TypeResolver resolveExpression,
|
||||
ChainParser<T> parseChain,
|
||||
ChainSegmentResolver<T> 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<T> 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<T> 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<T> {
|
||||
List<T> parse(String expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface for resolving chain segments.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ChainSegmentResolver<T> {
|
||||
TypeInfo resolve(TypeInfo currentType, T segment);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user