Casting completely functional

This commit is contained in:
bigguy345 committed 2025-12-29 16:46:03 +02:00
1 parent bc05164209
commit efd7d96ec7
1 file changed
+236
@@ -67,6 +67,11 @@ public class ScriptDocument {
private static final Pattern FIELD_DECL_PATTERN = Pattern.compile(
"\\b([A-Za-z_][a-zA-Z0-9_<>,[ \\t]\\[\\]]*)[ \\t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \\t]*(=|;)");
private static final Pattern NEW_TYPE_PATTERN = Pattern.compile("\\bnew\\s+([A-Za-z_][a-zA-Z0-9_]*)");
// Cast expression pattern: captures type name inside cast parentheses
// Matches: (Type), (Type[]), (pkg.Type), (Type<Generic>)
private static final Pattern CAST_TYPE_PATTERN = Pattern.compile(
"\\(\\s*([A-Za-z_][a-zA-Z0-9_]*(?:\\s*\\.\\s*[A-Za-z_][a-zA-Z0-9_]*)*)\\s*(?:<[^>]*>)?\\s*(\\[\\s*\\])*\\s*\\)\\s*(?=[a-zA-Z_\"'(\\d!~+-])");
// Function parameters (for JS-style scripts)
private static final Pattern FUNC_PARAMS_PATTERN = Pattern.compile(
@@ -1452,6 +1457,9 @@ public class ScriptDocument {
// Chained field accesses (e.g., mc.player.world, this.field)
markChainedFieldAccesses(marks);
// Cast type expressions (e.g., (EntityPlayer) entity)
markCastTypes(marks);
// Imported class usages
markImportedClassUsages(marks);
@@ -2267,6 +2275,11 @@ public class ScriptDocument {
return resolveExpressionWithOperators(expr, position);
}
// Handle cast expressions: (Type)expr, ((Type)expr).method(), etc.
if (expr.startsWith("(")) {
return resolveCastOrParenthesizedExpression(expr, position);
}
// Invalid expressions starting with brackets
if (expr.startsWith("[") || expr.startsWith("]")) {
return null; // Invalid syntax
@@ -2389,6 +2402,180 @@ public class ScriptDocument {
return currentType;
}
/**
* Resolve a cast or parenthesized expression.
* Handles:
* - Simple casts: (Type) expr
* - Nested casts: ((Type) expr)
* - Cast chains: ((Type) expr).method()
* - Parenthesized expressions: (expr)
*
* @param expr The expression starting with '('
* @param position The position in the source text
* @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;
}
/**
* Parse an expression string into chain segments.
@@ -4003,6 +4190,55 @@ public class ScriptDocument {
// Attach as the declaration assignment
targetField.setDeclarationAssignment(info);
}
/**
* Mark types in cast expressions with their appropriate type color.
* Handles: (Type), (Type[]), (pkg.Type), etc.
*/
private void markCastTypes(List<ScriptLine.Mark> marks) {
Matcher m = CAST_TYPE_PATTERN.matcher(text);
while (m.find()) {
String typeName = m.group(1);
int typeStart = m.start(1);
int typeEnd = m.end(1);
// Skip if inside string or comment
if (isExcluded(typeStart)) continue;
// Skip in import/package statements
if (isInImportOrPackage(typeStart)) continue;
// Resolve the type
TypeInfo info = resolveType(typeName);
if (info != null && info.isResolved()) {
// Mark the type with its appropriate color
marks.add(new ScriptLine.Mark(typeStart, typeEnd, info.getTokenType(), info));
} else {
// Unknown type - check if it's a primitive
if (isPrimitiveType(typeName)) {
marks.add(new ScriptLine.Mark(typeStart, typeEnd, TokenType.KEYWORD));
} else {
// Mark as undefined type
marks.add(new ScriptLine.Mark(typeStart, typeEnd, TokenType.UNDEFINED_VAR));
}
}
}
}
/**
* Check if a type name is a primitive type.
*/
private boolean isPrimitiveType(String typeName) {
switch (typeName) {
case "byte": case "short": case "int": case "long":
case "float": case "double": case "char": case "boolean":
case "void":
return true;
default:
return false;
}
}
private void markImportedClassUsages(List<ScriptLine.Mark> marks) {
// Find uppercase identifiers followed by dot (static method calls, field access)