mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 19:34:09 +00:00
Narrow integer literals to byte/short/char when expected i.e setByte(100) no longer throws "Expected byte, provided int"
This commit is contained in:
1 parent
edb285c90b
commit
36f7233297
3 files changed
+177
-5
No files matched your search
@@ -4616,6 +4616,17 @@ public class ScriptDocument {
|
||||
return significantDigits > maxDigits;
|
||||
}
|
||||
|
||||
private TypeInfo narrowIntLiteral(String literalText) {
|
||||
TypeInfo expectedType = ExpressionTypeResolver.CURRENT_EXPECTED_TYPE;
|
||||
if (expectedType != null) {
|
||||
TypeInfo narrowed = TypeChecker.narrowLiteralToExpectedType(literalText, expectedType);
|
||||
if (narrowed != null) {
|
||||
return narrowed;
|
||||
}
|
||||
}
|
||||
return TypeInfo.fromPrimitive("int");
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive expression type resolver that handles:
|
||||
* - Literals (strings, numbers, booleans, null)
|
||||
@@ -4692,9 +4703,17 @@ public class ScriptDocument {
|
||||
if (expr.matches("-?\\d+[lL]")) {
|
||||
return TypeInfo.fromPrimitive("long");
|
||||
}
|
||||
// Hex integer literals: 0x7F, -0x80, 0X1A
|
||||
if (expr.matches("[-+]?0[xX][0-9a-fA-F][0-9a-fA-F_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
// Binary integer literals: 0b1010, -0b1100, 0B1111
|
||||
if (expr.matches("[-+]?0[bB][01][01_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
// Int: plain integers without suffix
|
||||
if (expr.matches("-?\\d+")) {
|
||||
return TypeInfo.fromPrimitive("int");
|
||||
if (expr.matches("[-+]?\\d[\\d_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
|
||||
// "this" keyword
|
||||
@@ -5459,8 +5478,16 @@ public class ScriptDocument {
|
||||
if (expr.matches("-?\\d+[lL]")) {
|
||||
return TypeInfo.fromPrimitive("long");
|
||||
}
|
||||
if (expr.matches("-?\\d+")) {
|
||||
return TypeInfo.fromPrimitive("int");
|
||||
// Hex integer literals: 0x7F, -0x80, 0X1A
|
||||
if (expr.matches("[-+]?0[xX][0-9a-fA-F][0-9a-fA-F_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
// Binary integer literals: 0b1010, -0b1100, 0B1111
|
||||
if (expr.matches("[-+]?0[bB][01][01_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
if (expr.matches("[-+]?\\d[\\d_]*")) {
|
||||
return narrowIntLiteral(expr);
|
||||
}
|
||||
|
||||
// "this" keyword
|
||||
|
||||
+16
-1
@@ -75,7 +75,14 @@ public class ExpressionTypeResolver {
|
||||
private TypeInfo resolveNodeType(ExpressionNode node) {
|
||||
if (node == null) return null;
|
||||
|
||||
if (node instanceof ExpressionNode.IntLiteralNode) return TypeInfo.fromPrimitive("int");
|
||||
if (node instanceof ExpressionNode.IntLiteralNode) {
|
||||
if (CURRENT_EXPECTED_TYPE != null) {
|
||||
TypeInfo narrowed = TypeChecker.narrowLiteralToExpectedType(
|
||||
((ExpressionNode.IntLiteralNode) node).getValue(), CURRENT_EXPECTED_TYPE);
|
||||
if (narrowed != null) return narrowed;
|
||||
}
|
||||
return TypeInfo.fromPrimitive("int");
|
||||
}
|
||||
if (node instanceof ExpressionNode.LongLiteralNode) return TypeInfo.fromPrimitive("long");
|
||||
if (node instanceof ExpressionNode.FloatLiteralNode) return TypeInfo.fromPrimitive("float");
|
||||
if (node instanceof ExpressionNode.DoubleLiteralNode) return TypeInfo.fromPrimitive("double");
|
||||
@@ -132,6 +139,14 @@ public class ExpressionTypeResolver {
|
||||
|
||||
if (node instanceof ExpressionNode.UnaryOpNode) {
|
||||
ExpressionNode.UnaryOpNode un = (ExpressionNode.UnaryOpNode) node;
|
||||
// Special case: negation of an IntLiteralNode can narrow to byte/short
|
||||
if (un.getOperator() == OperatorType.UNARY_MINUS
|
||||
&& un.getOperand() instanceof ExpressionNode.IntLiteralNode
|
||||
&& CURRENT_EXPECTED_TYPE != null) {
|
||||
String negatedText = "-" + ((ExpressionNode.IntLiteralNode) un.getOperand()).getValue();
|
||||
TypeInfo narrowed = TypeChecker.narrowLiteralToExpectedType(negatedText, CURRENT_EXPECTED_TYPE);
|
||||
if (narrowed != null) return narrowed;
|
||||
}
|
||||
TypeInfo operandType = resolveNodeType(un.getOperand());
|
||||
return TypeRules.resolveUnaryOperatorType(un.getOperator(), operandType);
|
||||
}
|
||||
|
||||
@@ -332,6 +332,136 @@ public final class TypeChecker {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to narrow an integer literal to byte, short, or char when the
|
||||
* expected type requires it. The expression resolver normally infers integer
|
||||
* literals as {@code int}; this helper allows callers to re-type a literal
|
||||
* when {@code ExpressionTypeResolver.CURRENT_EXPECTED_TYPE} is set (e.g.
|
||||
* method-argument second pass or assignment RHS).
|
||||
*
|
||||
* <p>Supported literal formats:</p>
|
||||
* <ul>
|
||||
* <li>Decimal: {@code 42}, {@code -128}, {@code +127}</li>
|
||||
* <li>Hexadecimal: {@code 0xFF}, {@code 0XAB}</li>
|
||||
* <li>Binary: {@code 0b1010}, {@code 0B1111_0000}</li>
|
||||
* <li>Underscores as digit separators: {@code 1_000}</li>
|
||||
* <li>Optional trailing {@code l}/{@code L} suffix (stripped before parsing)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Range checks:</p>
|
||||
* <ul>
|
||||
* <li>byte: [-128 .. 127]</li>
|
||||
* <li>short: [-32768 .. 32767]</li>
|
||||
* <li>char: [0 .. 65535]</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param literalText the raw text of the integer literal (e.g. {@code "0xFF"})
|
||||
* @param expectedType the target type at the call/assignment site
|
||||
* @return a {@link TypeInfo} for the narrowed primitive type, or {@code null}
|
||||
* if the expected type is not byte/short/char, the literal is not a
|
||||
* valid integer, or the value falls outside the target range
|
||||
*/
|
||||
public static TypeInfo narrowLiteralToExpectedType(String literalText, TypeInfo expectedType) {
|
||||
if (literalText == null || literalText.isEmpty() || expectedType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine the narrowing target from the expected type
|
||||
String expectedSimple = expectedType.getSimpleName();
|
||||
if (expectedSimple == null) {
|
||||
return null;
|
||||
}
|
||||
String unboxed = getUnboxedName(expectedSimple);
|
||||
if (!"byte".equals(unboxed) && !"short".equals(unboxed) && !"char".equals(unboxed)) {
|
||||
return null; // Only narrow to byte, short, or char
|
||||
}
|
||||
|
||||
// Parse the literal value
|
||||
Long value = parseIntegerLiteral(literalText);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Range check for the target type
|
||||
long v = value;
|
||||
switch (unboxed) {
|
||||
case "byte":
|
||||
if (v < -128 || v > 127) return null;
|
||||
break;
|
||||
case "short":
|
||||
if (v < -32768 || v > 32767) return null;
|
||||
break;
|
||||
case "char":
|
||||
if (v < 0 || v > 65535) return null;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return TypeInfo.fromPrimitive(unboxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an integer literal string into a {@code Long} value.
|
||||
* Supports decimal, hexadecimal (0x/0X), and binary (0b/0B) formats,
|
||||
* optional leading sign (+/-), underscores as digit separators,
|
||||
* and an optional trailing l/L suffix.
|
||||
*
|
||||
* @param text the raw literal text
|
||||
* @return the parsed value, or {@code null} on parse failure
|
||||
*/
|
||||
private static Long parseIntegerLiteral(String text) {
|
||||
String s = text.trim();
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine sign
|
||||
boolean negative = false;
|
||||
if (s.charAt(0) == '-') {
|
||||
negative = true;
|
||||
s = s.substring(1);
|
||||
} else if (s.charAt(0) == '+') {
|
||||
s = s.substring(1);
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip optional trailing l/L suffix
|
||||
if (s.charAt(s.length() - 1) == 'l' || s.charAt(s.length() - 1) == 'L') {
|
||||
s = s.substring(0, s.length() - 1);
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove underscores (Java digit separators)
|
||||
s = s.replace("_", "");
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
long value;
|
||||
if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
|
||||
// Hexadecimal
|
||||
value = Long.parseUnsignedLong(s.substring(2), 16);
|
||||
} else if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'b' || s.charAt(1) == 'B')) {
|
||||
// Binary
|
||||
value = Long.parseUnsignedLong(s.substring(2), 2);
|
||||
} else {
|
||||
// Decimal
|
||||
value = Long.parseLong(s);
|
||||
}
|
||||
return negative ? -value : value;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getJavaKeywords() {
|
||||
String[] keywords = {
|
||||
"if", "else", "for", "while", "do", "switch", "case", "break", "continue",
|
||||
|
||||
Reference in new issue
Block a user