Skip to content

Commit fd3e28a

Browse files
committed
Error messages, try/catch and finish-futures.
1 parent e14b62e commit fd3e28a

22 files changed

+491
-51
lines changed

src/main/java/mx/kenzie/skript/api/SyntaxElement.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import mx.kenzie.skript.api.note.ForceBridge;
66
import mx.kenzie.skript.api.note.ForceExtract;
77
import mx.kenzie.skript.api.note.ForceInline;
8-
import mx.kenzie.skript.compiler.CommonTypes;
9-
import mx.kenzie.skript.compiler.Context;
10-
import mx.kenzie.skript.compiler.InlineController;
11-
import mx.kenzie.skript.compiler.Pattern;
8+
import mx.kenzie.skript.compiler.*;
129
import mx.kenzie.skript.compiler.structure.PreVariable;
1310

1411
import java.lang.reflect.Method;
@@ -54,6 +51,10 @@ default String[] examples() {
5451

5552
void setHandler(HandlerType type, Method method);
5653

54+
default CompileState getSubState() {
55+
return CompileState.STATEMENT;
56+
}
57+
5758
default boolean allowAsInputFor(Type type) {
5859
return type.equals(CommonTypes.OBJECT) || type.equals(getReturnType());
5960
}

src/main/java/mx/kenzie/skript/api/syntax/Effect.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ public Effect(final Library provider, final LanguageElement type, final String..
2020
super(provider, type, patterns);
2121
}
2222

23+
@Override
24+
public CompileState getSubState() {
25+
return CompileState.STATEMENT; // looking for expressions here
26+
}
27+
28+
@Override
29+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
30+
}
31+
2332
@Override
2433
public void compile(Context context, Pattern.Match match) throws Throwable {
2534
final MethodBuilder method = context.getMethod();
@@ -38,7 +47,7 @@ public boolean allowedIn(State state, Context context) {
3847

3948
@Override
4049
public boolean allowAsInputFor(Type type) {
41-
return false;
50+
return true; // support meta-effects
4251
}
4352

4453
}

src/main/java/mx/kenzie/skript/compiler/SimpleSkriptCompiler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package mx.kenzie.skript.compiler;
22

33
import mx.kenzie.foundation.Type;
4+
import mx.kenzie.foundation.WriteInstruction;
45
import mx.kenzie.foundation.language.PostCompileClass;
56
import mx.kenzie.skript.api.Library;
67
import mx.kenzie.skript.api.SyntaxElement;
@@ -89,6 +90,9 @@ public PostCompileClass[] compile(String file, Type path) {
8990
context.lineNumber++;
9091
context.line = null;
9192
if (line.isBlank()) continue;
93+
if (context.getMethod() != null) {
94+
context.getMethod().writeCode(WriteInstruction.lineNumber(context.lineNumber));
95+
}
9296
this.compileLine(line, context);
9397
}
9498
for (int i = 0; i < context.units.size(); i++) {
@@ -234,7 +238,7 @@ public ElementTree assembleStatement(final String statement, final FileContext c
234238
final Type[] types = match.expected();
235239
final String[] inputs = match.groups();
236240
if (inputs.length < types.length) continue;
237-
context.setState(CompileState.STATEMENT);
241+
context.setState(handler.getSubState()); // move state change to syntax
238242
context.currentEffect = handler;
239243
inner:
240244
for (int i = 0; i < types.length; i++) {

src/main/java/mx/kenzie/skript/compiler/SkriptLangSpec.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import mx.kenzie.skript.lang.syntax.flow.conditional.ElseIfSection;
3030
import mx.kenzie.skript.lang.syntax.flow.conditional.ElseSection;
3131
import mx.kenzie.skript.lang.syntax.flow.conditional.IfSection;
32+
import mx.kenzie.skript.lang.syntax.flow.error.CatchSection;
33+
import mx.kenzie.skript.lang.syntax.flow.error.TryEffect;
34+
import mx.kenzie.skript.lang.syntax.flow.error.TrySection;
3235
import mx.kenzie.skript.lang.syntax.flow.execute.*;
3336
import mx.kenzie.skript.lang.syntax.flow.lambda.RunnableSection;
3437
import mx.kenzie.skript.lang.syntax.flow.lambda.SupplierSection;
@@ -120,6 +123,8 @@ private SkriptLangSpec() {
120123
new WaitEffect(),
121124
new ReturnEffect(),
122125
new WhileSection(),
126+
new TrySection(),
127+
new CatchSection(),
123128
new LoopTimesSection(),
124129
new LoopInSection(),
125130
new IfSection(),
@@ -141,6 +146,7 @@ private SkriptLangSpec() {
141146
new ContinueEffect(),
142147
new BreakIfEffect(),
143148
new BreakEffect(),
149+
new TryEffect(),
144150
new ClearList(),
145151
new ClearMap()
146152
);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package mx.kenzie.skript.compiler.structure;
2+
3+
import mx.kenzie.foundation.MethodBuilder;
4+
import mx.kenzie.skript.api.SyntaxElement;
5+
import mx.kenzie.skript.compiler.CommonTypes;
6+
import mx.kenzie.skript.compiler.Context;
7+
import mx.kenzie.skript.error.ScriptCompileError;
8+
import mx.kenzie.skript.lang.syntax.flow.error.CatchSection;
9+
import org.objectweb.asm.Label;
10+
11+
public class TryCatchTree extends ProgrammaticSplitTree {
12+
13+
private final SectionMeta owner;
14+
private boolean open;
15+
private final Label startTry = new Label();
16+
private final Label startCatch = new Label();
17+
private final MultiLabel end;
18+
19+
public TryCatchTree(SectionMeta owner, MultiLabel end) {
20+
this.owner = owner;
21+
this.open = true;
22+
this.end = end;
23+
}
24+
25+
public Label getStartTry() {
26+
return startTry;
27+
}
28+
29+
public Label getStartCatch() {
30+
return startCatch;
31+
}
32+
33+
public MultiLabel getEnd() {
34+
return end;
35+
}
36+
37+
@Override
38+
public SectionMeta owner() {
39+
return owner;
40+
}
41+
42+
@Override
43+
public void start(Context context) {
44+
final MethodBuilder method = context.getMethod();
45+
if (method == null) throw new ScriptCompileError(context.lineNumber(), "Try/catch used outside method.");
46+
method.writeCode((writer, visitor) -> {
47+
visitor.visitTryCatchBlock(startTry, end.use(), startCatch, CommonTypes.THROWABLE.internalName());
48+
visitor.visitLabel(startTry);
49+
});
50+
}
51+
52+
@Override
53+
public void branch(Context context) {
54+
}
55+
56+
@Override
57+
public void close(Context context) {
58+
this.open = false;
59+
final MethodBuilder method = context.getMethod();
60+
if (method == null) throw new ScriptCompileError(context.lineNumber(), "Try/catch section left unclosed.");
61+
method.writeCode(end.instruction());
62+
context.removeTree(this);
63+
}
64+
65+
@Override
66+
public boolean permit(SyntaxElement element) {
67+
return element instanceof CatchSection;
68+
}
69+
70+
@Override
71+
public boolean isOpen() {
72+
return open;
73+
}
74+
75+
}

src/main/java/mx/kenzie/skript/lang/syntax/control/AddEffect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public AddEffect() {
1818
}
1919

2020
@Override
21-
public void preCompile(Context context, Pattern.Match match) {
21+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
2222
final ElementTree tree = context.getLine();
2323
final ElementTree[] inputs = tree.nested();
2424
assert inputs.length == 2;
@@ -30,6 +30,7 @@ public void preCompile(Context context, Pattern.Match match) {
3030
throw new ScriptParseError(context.lineNumber(), "Syntax '" + inputs[1].current()
3131
.name() + "' cannot be added to.");
3232
// inputs[1].compile = false; //todo
33+
super.preCompile(context, match);
3334
}
3435

3536
@Override

src/main/java/mx/kenzie/skript/lang/syntax/control/DeleteEffect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public Pattern.Match match(String thing, Context context) {
2525
}
2626

2727
@Override
28-
public void preCompile(Context context, Pattern.Match match) {
28+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
2929
final ElementTree tree = context.getLine();
3030
final ElementTree[] inputs = tree.nested();
3131
assert inputs.length == 1;
@@ -40,6 +40,7 @@ public void preCompile(Context context, Pattern.Match match) {
4040
throw new ScriptParseError(context.lineNumber(), "Syntax '" + inputs[0].current()
4141
.name() + "' cannot be deleted.");
4242
inputs[0].compile = false;
43+
super.preCompile(context, match);
4344
}
4445

4546
@Override

src/main/java/mx/kenzie/skript/lang/syntax/control/RemoveEffect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public RemoveEffect() {
1818
}
1919

2020
@Override
21-
public void preCompile(Context context, Pattern.Match match) {
21+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
2222
final ElementTree tree = context.getLine();
2323
final ElementTree[] inputs = tree.nested();
2424
assert inputs.length == 2;
@@ -30,6 +30,7 @@ public void preCompile(Context context, Pattern.Match match) {
3030
throw new ScriptParseError(context.lineNumber(), "Syntax '" + inputs[0].current()
3131
.name() + "' cannot be removed from.");
3232
inputs[1].compile = false;
33+
super.preCompile(context, match);
3334
}
3435

3536
@Override

src/main/java/mx/kenzie/skript/lang/syntax/control/SetEffect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public Pattern.Match match(String thing, Context context) {
2323
}
2424

2525
@Override
26-
public void preCompile(Context context, Pattern.Match match) {
26+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
2727
final ElementTree tree = context.getCompileCurrent();
2828
final ElementTree[] inputs = tree.nested();
2929
assert inputs.length == 2;
@@ -36,6 +36,7 @@ public void preCompile(Context context, Pattern.Match match) {
3636
inputs[0].emptyNest();
3737
replacement[replacement.length - 1] = inputs[0];
3838
tree.replaceNest(replacement);
39+
super.preCompile(context, match);
3940
}
4041

4142
@Override
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package mx.kenzie.skript.lang.syntax.flow.error;
2+
3+
import mx.kenzie.foundation.MethodBuilder;
4+
import mx.kenzie.foundation.WriteInstruction;
5+
import mx.kenzie.foundation.compiler.State;
6+
import mx.kenzie.skript.api.syntax.Section;
7+
import mx.kenzie.skript.compiler.*;
8+
import mx.kenzie.skript.compiler.structure.PreVariable;
9+
import mx.kenzie.skript.compiler.structure.SectionMeta;
10+
import mx.kenzie.skript.compiler.structure.TryCatchTree;
11+
import mx.kenzie.skript.error.ScriptCompileError;
12+
import mx.kenzie.skript.error.ScriptParseError;
13+
import mx.kenzie.skript.lang.element.StandardElements;
14+
import mx.kenzie.skript.lang.handler.StandardHandlers;
15+
import mx.kenzie.skript.lang.syntax.variable.VariableExpression;
16+
import org.objectweb.asm.Label;
17+
import org.objectweb.asm.Opcodes;
18+
19+
public class CatchSection extends Section {
20+
21+
public CatchSection() {
22+
super(SkriptLangSpec.LIBRARY, StandardElements.SECTION, "catch %Variable%");
23+
}
24+
25+
@Override
26+
public void preCompile(Context context, Pattern.Match match) throws Throwable {
27+
final ElementTree holder = context.getLine().nested()[0];
28+
if (!(holder.current() instanceof VariableExpression))
29+
throw new ScriptParseError(context.lineNumber(), "The error must be a variable: 'catch {varname}'");
30+
holder.type = StandardHandlers.SET;
31+
holder.compile = false;
32+
}
33+
34+
@Override
35+
public void compile(Context context, Pattern.Match match) throws Throwable {
36+
if (!(context.getTree(context.getSection(1)) instanceof TryCatchTree tree))
37+
throw new ScriptCompileError(context.lineNumber(), "Catch used without preceding try-section.");
38+
final ElementTree holder = context.getLine().nested()[0];
39+
if (!(holder.current() instanceof VariableExpression))
40+
throw new ScriptParseError(context.lineNumber(), "The error must be a variable: 'catch {varname}'");
41+
final Label label = tree.getEnd().use();
42+
final Label next = tree.getStartCatch();
43+
final MethodBuilder method = context.getMethod();
44+
if (method == null) throw new ScriptCompileError(context.lineNumber(), "Try/catch used outside method.");
45+
context.getMethod().writeCode(((writer, visitor) -> {
46+
visitor.visitJumpInsn(Opcodes.GOTO, label);
47+
visitor.visitLabel(next);
48+
}));
49+
final int slot = context.slotOf(this.getHolderVariable(context, match));
50+
method.writeCode(WriteInstruction.storeObject(slot));
51+
context.setState(CompileState.CODE_BODY);
52+
}
53+
54+
private PreVariable getHolderVariable(Context context, Pattern.Match match) {
55+
final String pattern = match.groups()[0].trim();
56+
assert pattern.startsWith("{") && pattern.endsWith("}");
57+
final String name = pattern.substring(1, pattern.length() - 1);
58+
if (name.charAt(0) == '@' || name.charAt(0) == '_' || name.charAt(0) == '!')
59+
throw new ScriptCompileError(context.lineNumber(), "Holder variable must be a normal variable: '{var}'");
60+
return context.getVariable(name);
61+
}
62+
63+
@Override
64+
public Pattern.Match match(String thing, Context context) {
65+
if (!thing.startsWith("catch ")) return null;
66+
return super.match(thing, context);
67+
}
68+
69+
@Override
70+
public boolean allowedIn(State state, Context context) {
71+
return super.allowedIn(state, context)
72+
&& context.getSection() != null
73+
&& context.getMethod() != null;
74+
}
75+
76+
@Override
77+
public void onSectionExit(Context context, SectionMeta meta) {
78+
if (!(context.getTree(context.getSection()) instanceof TryCatchTree tree))
79+
throw new ScriptCompileError(context.lineNumber(), "Unable to balance try/catch flow tree.");
80+
context.setState(CompileState.CODE_BODY);
81+
tree.close(context);
82+
}
83+
84+
@Override
85+
public void compileInline(Context context, Pattern.Match match) throws Throwable {
86+
throw new ScriptCompileError(context.lineNumber(), "'Catch' must be used as section-header.");
87+
}
88+
89+
}

0 commit comments

Comments
 (0)