feat(engine): 执行引擎现在支持for/switch语法兼容JS;

This commit is contained in:
nexiot-studio
2026-04-01 21:14:54 +08:00
parent 4e54a9e193
commit df92bb922f
4 changed files with 349 additions and 6 deletions
@@ -59,7 +59,7 @@ public class MagicScriptCompiler implements Opcodes {
private final Stack<List<String>> vars = new Stack<>();
private final Stack<Label[]> labelStack = new Stack<>();
private final Stack<FlowControlLabel> labelStack = new Stack<>();
private final Stack<List<Node>> finallyStack = new Stack<>();
@@ -123,7 +123,7 @@ public class MagicScriptCompiler implements Opcodes {
methodVisitors.push(visitor);
vars.push(new ArrayList<>());
finallyStack.push(null);
labelStack.push(new Label[2]);
labelStack.push(new FlowControlLabel(null, null));
return this;
}
@@ -138,7 +138,13 @@ public class MagicScriptCompiler implements Opcodes {
/** 标识continue和break位置 */
public MagicScriptCompiler markLabel(Label start, Label end) {
labelStack.push(new Label[] {start, end});
labelStack.push(new FlowControlLabel(start, end));
return this;
}
/** 标识仅支持break的位置,例如 switch */
public MagicScriptCompiler markBreakLabel(Label end) {
labelStack.push(new FlowControlLabel(null, end));
return this;
}
@@ -150,12 +156,24 @@ public class MagicScriptCompiler implements Opcodes {
/** 跳转到continue位置 */
public MagicScriptCompiler start() {
return jump(GOTO, labelStack.peek()[0]);
for (int i = labelStack.size() - 1; i >= 0; i--) {
Label label = labelStack.get(i).getContinueLabel();
if (label != null) {
return jump(GOTO, label);
}
}
throw new MagicScriptCompileException("continue 只能在循环中使用");
}
/** 跳转到break位置 */
public MagicScriptCompiler end() {
return jump(GOTO, labelStack.peek()[1]);
for (int i = labelStack.size() - 1; i >= 0; i--) {
Label label = labelStack.get(i).getBreakLabel();
if (label != null) {
return jump(GOTO, label);
}
}
throw new MagicScriptCompileException("break 只能在循环或 switch 中使用");
}
/** 访问AST节点 */
@@ -315,6 +333,12 @@ public class MagicScriptCompiler implements Opcodes {
return this;
}
/** 加载本地临时变量 */
public MagicScriptCompiler loadLocal(int index) {
self().visitVarInsn(ALOAD, index);
return this;
}
/** 加载变量 */
public MagicScriptCompiler load(int index) {
return load2()
@@ -753,6 +777,26 @@ public class MagicScriptCompiler implements Opcodes {
return methodVisitors.peek();
}
private static class FlowControlLabel {
private final Label continueLabel;
private final Label breakLabel;
private FlowControlLabel(Label continueLabel, Label breakLabel) {
this.continueLabel = continueLabel;
this.breakLabel = breakLabel;
}
public Label getContinueLabel() {
return continueLabel;
}
public Label getBreakLabel() {
return breakLabel;
}
}
private static String getJvmType(Class<?> target) {
return target == null ? null : target.getName().replace(".", "/");
}
@@ -101,6 +101,7 @@ import cn.universal.core.engine.parsing.ast.literal.StringLiteral;
import cn.universal.core.engine.parsing.ast.statement.Assert;
import cn.universal.core.engine.parsing.ast.statement.AsyncCall;
import cn.universal.core.engine.parsing.ast.statement.Break;
import cn.universal.core.engine.parsing.ast.statement.ClassicForStatement;
import cn.universal.core.engine.parsing.ast.statement.ClassConverter;
import cn.universal.core.engine.parsing.ast.statement.Continue;
import cn.universal.core.engine.parsing.ast.statement.Exit;
@@ -115,6 +116,7 @@ import cn.universal.core.engine.parsing.ast.statement.MethodCall;
import cn.universal.core.engine.parsing.ast.statement.NewStatement;
import cn.universal.core.engine.parsing.ast.statement.Return;
import cn.universal.core.engine.parsing.ast.statement.Spread;
import cn.universal.core.engine.parsing.ast.statement.SwitchStatement;
import cn.universal.core.engine.parsing.ast.statement.Throw;
import cn.universal.core.engine.parsing.ast.statement.TryStatement;
import cn.universal.core.engine.parsing.ast.statement.VariableAccess;
@@ -272,6 +274,8 @@ public class Parser {
result = parseReturn();
} else if (stream.match("for", false)) {
result = parseForStatement();
} else if (isSwitchStatement()) {
result = parseSwitchStatement();
} else if (stream.match("while", false)) {
result = parseWhileStatement();
} else if (stream.match("continue", false)) {
@@ -675,9 +679,16 @@ public class Parser {
return new WhileStatement(addSpan(openingWhile, closingEnd), condition, trueBlock);
}
private ForStatement parseForStatement() {
private Node parseForStatement() {
Span openingFor = stream.expect("for").getSpan();
stream.expect("(");
if (isClassicForStatement()) {
return parseClassicForStatement(openingFor);
}
return parseIteratorForStatement(openingFor);
}
private ForStatement parseIteratorForStatement(Span openingFor) {
push();
Span index = null;
Span value = stream.expect(Identifier).getSpan();
@@ -709,6 +720,140 @@ public class Parser {
body);
}
private ClassicForStatement parseClassicForStatement(Span openingFor) {
push();
Node initializer = null;
Expression condition = null;
Expression update = null;
if (!stream.match(";", false)) {
initializer = parseForInitializer();
}
stream.expect(";");
if (!stream.match(";", false)) {
condition = parseExpression();
}
stream.expect(";");
if (!stream.match(")", false)) {
update = parseExpression();
}
stream.expect(")");
List<Node> body = parseFunctionBody();
pop();
return new ClassicForStatement(
addSpan(openingFor, stream.getPrev().getSpan()), initializer, condition, update, body);
}
private Node parseForInitializer() {
if (matchVarDefine()) {
return parseVarDefine();
}
int index = stream.makeIndex();
if (matchTypeDefine()) {
stream.resetIndex(index);
return parseVarDefine();
}
stream.resetIndex(index);
return parseExpression();
}
private boolean isClassicForStatement() {
int index = stream.makeIndex();
int depth = 0;
try {
while (stream.hasMore()) {
Token token = stream.consume();
switch (token.getType()) {
case LeftParantheses:
case LeftBracket:
case LeftCurly:
depth++;
break;
case RightParantheses:
if (depth == 0) {
return false;
}
depth--;
break;
case RightBracket:
case RightCurly:
if (depth > 0) {
depth--;
}
break;
default:
if (";".equals(token.getText()) && depth == 0) {
return true;
}
break;
}
}
return false;
} finally {
stream.resetIndex(index);
}
}
private boolean isSwitchStatement() {
if (!stream.match("switch", false)) {
return false;
}
int index = stream.makeIndex();
try {
stream.consume();
return stream.match(LeftParantheses, false);
} finally {
stream.resetIndex(index);
}
}
private SwitchStatement parseSwitchStatement() {
Span openingSwitch = stream.expect("switch").getSpan();
requiredNew = false;
Expression target = parseExpression();
requiredNew = true;
stream.expect("{");
List<SwitchStatement.SwitchCase> switchCases = new ArrayList<>();
boolean hasDefault = false;
while (stream.hasMore() && !stream.match("}", false)) {
boolean defaultCase = false;
Expression condition = null;
Span caseSpan;
if (stream.match("case", true)) {
caseSpan = stream.getPrev().getSpan();
condition = parseExpression();
} else if (stream.match("default", true)) {
caseSpan = stream.getPrev().getSpan();
defaultCase = true;
if (hasDefault) {
MagicScriptError.error("switch 中只允许一个 default", caseSpan);
}
hasDefault = true;
} else {
MagicScriptError.error("switch 中只允许 case 或 default", stream.consume().getSpan());
return null;
}
stream.expect(Colon);
List<Node> statements = new ArrayList<>();
while (stream.hasMore()
&& !stream.match("}", false)
&& !stream.match("case", false)
&& !stream.match("default", false)) {
Node node = parseStatement(true);
if (node != null) {
validateNode(node);
statements.add(node);
}
}
Span clauseEnd =
statements.isEmpty() ? stream.getPrev().getSpan() : statements.get(statements.size() - 1).getSpan();
switchCases.add(
new SwitchStatement.SwitchCase(
addSpan(caseSpan, clauseEnd), condition, statements, defaultCase));
}
Span closingEnd = expectCloseing();
return new SwitchStatement(addSpan(openingSwitch, closingEnd), target, switchCases);
}
private Span expectCloseing() {
if (!stream.hasMore()) {
MagicScriptError.error("Did not find closing }.", stream.prev().getSpan());
@@ -0,0 +1,63 @@
package cn.universal.core.engine.parsing.ast.statement;
import cn.universal.core.engine.asm.Label;
import cn.universal.core.engine.compile.MagicScriptCompiler;
import cn.universal.core.engine.parsing.Span;
import cn.universal.core.engine.parsing.ast.Expression;
import cn.universal.core.engine.parsing.ast.Node;
import cn.universal.core.engine.runtime.handle.OperatorHandle;
import java.util.List;
public class ClassicForStatement extends Node {
private final Node initializer;
private final Expression condition;
private final Expression update;
private final List<Node> body;
public ClassicForStatement(
Span span, Node initializer, Expression condition, Expression update, List<Node> body) {
super(span);
this.initializer = initializer;
this.condition = condition;
this.update = update;
this.body = body;
}
@Override
public void visitMethod(MagicScriptCompiler compiler) {
if (initializer != null) {
initializer.visitMethod(compiler);
}
if (condition != null) {
condition.visitMethod(compiler);
}
if (update != null) {
update.visitMethod(compiler);
}
body.forEach(it -> it.visitMethod(compiler));
}
@Override
public void compile(MagicScriptCompiler compiler) {
Label start = new Label();
Label updateLabel = new Label();
Label end = new Label();
Label continueLabel = update == null ? start : updateLabel;
if (initializer != null) {
compiler.compile(initializer, true);
}
compiler.markLabel(continueLabel, end).label(start);
if (condition != null) {
compiler
.visit(condition)
.invoke(INVOKESTATIC, OperatorHandle.class, "isTrue", boolean.class, Object.class)
.jump(IFEQ, end);
}
compiler.compile(body);
if (update != null) {
compiler.label(updateLabel).compile(update, true);
}
compiler.jump(GOTO, start).label(end).exitLabel();
}
}
@@ -0,0 +1,91 @@
package cn.universal.core.engine.parsing.ast.statement;
import cn.universal.core.engine.asm.Label;
import cn.universal.core.engine.compile.MagicScriptCompiler;
import cn.universal.core.engine.parsing.Span;
import cn.universal.core.engine.parsing.ast.Expression;
import cn.universal.core.engine.parsing.ast.Node;
import cn.universal.core.engine.runtime.handle.OperatorHandle;
import java.util.ArrayList;
import java.util.List;
public class SwitchStatement extends Node {
private final Expression target;
private final List<SwitchCase> switchCases;
public SwitchStatement(Span span, Expression target, List<SwitchCase> switchCases) {
super(span);
this.target = target;
this.switchCases = switchCases;
}
@Override
public void visitMethod(MagicScriptCompiler compiler) {
target.visitMethod(compiler);
switchCases.forEach(it -> it.visitMethod(compiler));
}
@Override
public void compile(MagicScriptCompiler compiler) {
int targetSlot = compiler.getTempIndex();
Label end = new Label();
List<Label> labels = new ArrayList<>(switchCases.size());
Label defaultLabel = end;
compiler.visit(target).store(targetSlot).markBreakLabel(end);
for (SwitchCase switchCase : switchCases) {
Label label = new Label();
labels.add(label);
if (switchCase.isDefaultCase()) {
defaultLabel = label;
continue;
}
compiler
.loadLocal(targetSlot)
.visit(switchCase.getCondition())
.operator("equals")
.invoke(INVOKESTATIC, OperatorHandle.class, "isTrue", boolean.class, Object.class)
.jump(IFNE, label);
}
compiler.jump(GOTO, defaultLabel);
for (int i = 0; i < switchCases.size(); i++) {
compiler.label(labels.get(i)).compile(switchCases.get(i).getStatements());
}
compiler.label(end).exitLabel();
}
public static class SwitchCase extends Node {
private final Expression condition;
private final List<Node> statements;
private final boolean defaultCase;
public SwitchCase(
Span span, Expression condition, List<Node> statements, boolean defaultCase) {
super(span);
this.condition = condition;
this.statements = statements;
this.defaultCase = defaultCase;
}
@Override
public void visitMethod(MagicScriptCompiler compiler) {
if (condition != null) {
condition.visitMethod(compiler);
}
statements.forEach(it -> it.visitMethod(compiler));
}
public Expression getCondition() {
return condition;
}
public List<Node> getStatements() {
return statements;
}
public boolean isDefaultCase() {
return defaultCase;
}
}
}