mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 04:17:36 +00:00
For loops handling now functional
This commit is contained in:
1 parent
437b74b353
commit
cdae8e5e29
2 files changed
+182
-11
No files matched your search
@@ -0,0 +1,157 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.ScopeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
class LoopVariableParser {
|
||||
|
||||
private static final Pattern ENHANCED_FOR = Pattern.compile(
|
||||
"\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*:\\s*([^)]+)\\)");
|
||||
private static final Pattern CLASSIC_FOR = Pattern.compile(
|
||||
"\\bfor\\s*\\(\\s*([A-Za-z_][\\w.<>\\[\\] \\t]*?(?:\\[\\s*\\])*)\\s+(\\w+)\\s*=");
|
||||
private static final Pattern FOR_IN = Pattern.compile(
|
||||
"\\bfor\\s*\\(\\s*(?:var|let|const)\\s+(\\w+)\\s+in\\s+([^)]+)\\)");
|
||||
private static final Pattern FOR_OF = Pattern.compile(
|
||||
"\\bfor\\s*\\(\\s*(?:var|let|const)\\s+(\\w+)\\s+of\\s+([^)]+)\\)");
|
||||
private static final Pattern FOR_IN_OF_LOOKAHEAD = Pattern.compile("^\\s+(?:in|of)\\s");
|
||||
|
||||
private static final Set<String> SKIP_TYPES = new HashSet<>(Arrays.asList(
|
||||
"return", "if", "while", "for", "switch", "catch", "new", "throw"));
|
||||
|
||||
private final ScriptDocument doc;
|
||||
private final String text;
|
||||
|
||||
LoopVariableParser(ScriptDocument doc, String text) {
|
||||
this.doc = doc;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
boolean isForInOrOf(String textAfterVarName) {
|
||||
return FOR_IN_OF_LOOKAHEAD.matcher(textAfterVarName).find();
|
||||
}
|
||||
|
||||
boolean isInsideForHeader(int bodyStart, int absPos) {
|
||||
int enclosingParen = doc.findEnclosingParenStart(bodyStart, absPos);
|
||||
return enclosingParen >= 0 && "for".equals(doc.readKeywordBefore(enclosingParen));
|
||||
}
|
||||
|
||||
void parse(Map<Integer, Map<String, List<FieldInfo>>> methodLocals) {
|
||||
boolean js = doc.isJavaScript();
|
||||
|
||||
for (MethodInfo method : doc.getAllMethods()) {
|
||||
int bodyStart = method.getBodyStart();
|
||||
int bodyEnd = method.getBodyEnd();
|
||||
if (bodyStart < 0 || bodyEnd <= bodyStart) continue;
|
||||
String bodyText = text.substring(bodyStart, Math.min(bodyEnd, text.length()));
|
||||
Map<String, List<FieldInfo>> locals = methodLocals.computeIfAbsent(method.getDeclarationOffset(), k -> new HashMap<>());
|
||||
scanRange(bodyText, bodyStart, bodyEnd, method, locals, js);
|
||||
}
|
||||
|
||||
for (InnerCallableScope scope : doc.getInnerScopes()) {
|
||||
int bodyStart = scope.getBodyStart();
|
||||
int bodyEnd = scope.getBodyEnd();
|
||||
if (bodyStart < 0 || bodyEnd <= bodyStart) continue;
|
||||
String bodyText = text.substring(bodyStart, Math.min(bodyEnd, text.length()));
|
||||
MethodInfo ownerMethod = doc.findMethodAtPosition(bodyStart);
|
||||
if (ownerMethod == null) continue;
|
||||
Map<String, List<FieldInfo>> locals = methodLocals.computeIfAbsent(ownerMethod.getDeclarationOffset(), k -> new HashMap<>());
|
||||
scanRange(bodyText, bodyStart, bodyEnd, ownerMethod, locals, js);
|
||||
}
|
||||
}
|
||||
|
||||
private void scanRange(String bodyText, int bodyStart, int bodyEnd,
|
||||
MethodInfo method, Map<String, List<FieldInfo>> locals, boolean js) {
|
||||
if (!js) {
|
||||
Matcher m = ENHANCED_FOR.matcher(bodyText);
|
||||
while (m.find()) {
|
||||
int absForPos = bodyStart + m.start();
|
||||
if (doc.isExcluded(absForPos)) continue;
|
||||
String typeName = m.group(1).trim();
|
||||
String varName = m.group(2);
|
||||
int absVarPos = bodyStart + m.start(2);
|
||||
TypeInfo typeInfo = doc.resolveType(typeName);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, bodyStart, bodyEnd);
|
||||
FieldInfo fi = FieldInfo.localField(varName, typeInfo, absVarPos, method, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
locals.computeIfAbsent(varName, k -> new ArrayList<>()).add(fi);
|
||||
}
|
||||
|
||||
m = CLASSIC_FOR.matcher(bodyText);
|
||||
while (m.find()) {
|
||||
int absForPos = bodyStart + m.start();
|
||||
if (doc.isExcluded(absForPos)) continue;
|
||||
String typeName = m.group(1).trim();
|
||||
String varName = m.group(2);
|
||||
if (SKIP_TYPES.contains(typeName)) continue;
|
||||
int absVarPos = bodyStart + m.start(2);
|
||||
TypeInfo typeInfo = doc.resolveType(typeName);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, bodyStart, bodyEnd);
|
||||
FieldInfo fi = FieldInfo.localField(varName, typeInfo, absVarPos, method, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
locals.computeIfAbsent(varName, k -> new ArrayList<>()).add(fi);
|
||||
}
|
||||
} else {
|
||||
Matcher m = FOR_IN.matcher(bodyText);
|
||||
while (m.find()) {
|
||||
int absForPos = bodyStart + m.start();
|
||||
if (doc.isExcluded(absForPos)) continue;
|
||||
String varName = m.group(1);
|
||||
int absVarPos = bodyStart + m.start(1);
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, bodyStart, bodyEnd);
|
||||
FieldInfo fi = FieldInfo.localField(varName, TypeInfo.fromPrimitive("int"), absVarPos, method, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
locals.computeIfAbsent(varName, k -> new ArrayList<>()).add(fi);
|
||||
}
|
||||
|
||||
m = FOR_OF.matcher(bodyText);
|
||||
while (m.find()) {
|
||||
int absForPos = bodyStart + m.start();
|
||||
if (doc.isExcluded(absForPos)) continue;
|
||||
String varName = m.group(1);
|
||||
String iterableExpr = m.group(2).trim();
|
||||
int absVarPos = bodyStart + m.start(1);
|
||||
TypeInfo iterableType = doc.resolveExpressionType(iterableExpr, absForPos);
|
||||
TypeInfo elementType = (iterableType != null) ? iterableType.getElementType() : null;
|
||||
if (elementType == null) elementType = TypeInfo.ANY;
|
||||
ScopeInfo scope = computeForBodyScope(absForPos, bodyStart, bodyEnd);
|
||||
FieldInfo fi = FieldInfo.localField(varName, elementType, absVarPos, method, -1, -1, 0);
|
||||
fi.setScopeInfo(scope);
|
||||
locals.computeIfAbsent(varName, k -> new ArrayList<>()).add(fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ScopeInfo computeForBodyScope(int forKeywordPos, int bodyStart, int bodyEnd) {
|
||||
int max = Math.min(bodyEnd, text.length());
|
||||
int openParen = -1;
|
||||
for (int i = forKeywordPos; i < max; i++) {
|
||||
if (doc.isExcluded(i)) continue;
|
||||
char c = text.charAt(i);
|
||||
if (c == '(') { openParen = i; break; }
|
||||
if (!Character.isWhitespace(c) && c != 'f' && c != 'o' && c != 'r') break;
|
||||
}
|
||||
if (openParen < 0) return new ScopeInfo(forKeywordPos, bodyEnd, false, "block");
|
||||
|
||||
int closeParen = doc.findMatchingParen(openParen, max);
|
||||
if (closeParen < 0) return new ScopeInfo(forKeywordPos, bodyEnd, false, "block");
|
||||
|
||||
int after = doc.skipWhitespaceAndExcluded(closeParen + 1, max);
|
||||
if (after < 0 || after >= max) return new ScopeInfo(forKeywordPos, bodyEnd, false, "block");
|
||||
|
||||
if (text.charAt(after) == '{') {
|
||||
int closeBrace = doc.findMatchingBrace(after);
|
||||
if (closeBrace > 0) return new ScopeInfo(forKeywordPos, closeBrace + 1, false, "block");
|
||||
return new ScopeInfo(forKeywordPos, bodyEnd, false, "block");
|
||||
}
|
||||
|
||||
int stmtEnd = doc.findStatementEnd(after, max);
|
||||
if (stmtEnd > after) return new ScopeInfo(forKeywordPos, stmtEnd, false, "block");
|
||||
return new ScopeInfo(forKeywordPos, bodyEnd, false, "block");
|
||||
}
|
||||
}
|
||||
@@ -1312,7 +1312,7 @@ public class ScriptDocument {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int skipWhitespaceAndExcluded(int pos, int limit) {
|
||||
int skipWhitespaceAndExcluded(int pos, int limit) {
|
||||
int i = Math.max(pos, 0);
|
||||
int max = Math.min(limit, text.length());
|
||||
while (i < max) {
|
||||
@@ -1328,7 +1328,7 @@ public class ScriptDocument {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findStatementEnd(int start, int limit) {
|
||||
int findStatementEnd(int start, int limit) {
|
||||
int max = Math.min(limit, text.length());
|
||||
int parenDepth = 0;
|
||||
int bracketDepth = 0;
|
||||
@@ -1351,7 +1351,7 @@ public class ScriptDocument {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findEnclosingParenStart(int min, int position) {
|
||||
int findEnclosingParenStart(int min, int position) {
|
||||
int depth = 0;
|
||||
for (int i = Math.min(position - 1, text.length() - 1); i >= min; i--) {
|
||||
if (isExcluded(i)) {
|
||||
@@ -1370,7 +1370,7 @@ public class ScriptDocument {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findMatchingParen(int openParenIndex, int limit) {
|
||||
int findMatchingParen(int openParenIndex, int limit) {
|
||||
if (openParenIndex < 0 || openParenIndex >= text.length()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1392,7 +1392,7 @@ public class ScriptDocument {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String readKeywordBefore(int position) {
|
||||
String readKeywordBefore(int position) {
|
||||
int i = position - 1;
|
||||
while (i >= 0) {
|
||||
if (isExcluded(i)) {
|
||||
@@ -1464,6 +1464,12 @@ public class ScriptDocument {
|
||||
int absPos = bodyStart + m.start(2);
|
||||
if (isExcluded(absPos)) continue;
|
||||
|
||||
int afterVar = m.end(2);
|
||||
if (afterVar < bodyText.length()) {
|
||||
String rest = bodyText.substring(afterVar);
|
||||
if (rest.length() > 0 && java.util.regex.Pattern.compile("^\\s+(?:in|of)\\s").matcher(rest).find()) continue;
|
||||
}
|
||||
|
||||
boolean insideInner = false;
|
||||
for (InnerCallableScope scope : innerScopes) {
|
||||
if (scope.containsPosition(absPos)) {
|
||||
@@ -1583,6 +1589,9 @@ public class ScriptDocument {
|
||||
continue;
|
||||
}
|
||||
|
||||
int enclosingParen = findEnclosingParenStart(bodyStart, absPos);
|
||||
if (enclosingParen >= 0 && "for".equals(readKeywordBefore(enclosingParen))) continue;
|
||||
|
||||
int modifiers = parseModifiers(typeName);
|
||||
|
||||
TypeInfo typeInfo;
|
||||
@@ -1655,11 +1664,10 @@ public class ScriptDocument {
|
||||
for (InnerCallableScope scope : innerScopes) {
|
||||
parseLocalVariablesInScope(scope);
|
||||
}
|
||||
|
||||
new LoopVariableParser(this, text).parse(methodLocals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse local variable declarations inside an inner callable scope (lambda or JS function expression).
|
||||
*/
|
||||
|
||||
private void parseLocalVariablesInScope(InnerCallableScope scope) {
|
||||
int start = scope.getBodyStart();
|
||||
int end = scope.getBodyEnd();
|
||||
@@ -1749,6 +1757,12 @@ public class ScriptDocument {
|
||||
int declPos = start + m.start();
|
||||
if (declPos < start || declPos >= end) continue;
|
||||
if (isExcluded(declPos)) continue;
|
||||
|
||||
int afterVar = m.end(1);
|
||||
if (afterVar < rangeText.length()) {
|
||||
String rest = rangeText.substring(afterVar);
|
||||
if (rest.length() > 0 && java.util.regex.Pattern.compile("^\\s+(?:in|of)\\s").matcher(rest).find()) continue;
|
||||
}
|
||||
|
||||
String varName = m.group(1);
|
||||
String initializer = null;
|
||||
@@ -7696,7 +7710,7 @@ public class ScriptDocument {
|
||||
return pos;
|
||||
}
|
||||
|
||||
private MethodInfo findMethodAtPosition(int position) {
|
||||
MethodInfo findMethodAtPosition(int position) {
|
||||
for (MethodInfo method : getAllMethods()) {
|
||||
if (method.containsPosition(position)) {
|
||||
return method;
|
||||
@@ -7728,7 +7742,7 @@ public class ScriptDocument {
|
||||
return findMethodAtPosition(position);
|
||||
}
|
||||
|
||||
private int findMatchingBrace(int openBraceIndex) {
|
||||
int findMatchingBrace(int openBraceIndex) {
|
||||
if (openBraceIndex < 0 || openBraceIndex >= text.length())
|
||||
return -1;
|
||||
|
||||
|
||||
Reference in new issue
Block a user