mirror of
https://gitee.com/NexIoT/Universal-IoT-Java.git
synced 2026-10-31 00:41:27 +08:00
feat(engine): 执行引擎现在支持for/switch语法兼容JS;
This commit is contained in:
+49
-5
@@ -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(".", "/");
|
||||
}
|
||||
|
||||
+146
-1
@@ -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());
|
||||
|
||||
+63
@@ -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();
|
||||
}
|
||||
}
|
||||
+91
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user