For loops handling now functional

This commit is contained in:
bigguy345 committed 2026-03-07 02:37:58 +02:00
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;