Skip to content

Commit 22e131f

Browse files
committed
Begin work on background compiling.
1 parent 8c7ac55 commit 22e131f

File tree

10 files changed

+267
-41
lines changed

10 files changed

+267
-41
lines changed

src/main/java/org/byteskript/skript/compiler/Context.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ public void destroySection() {
169169
}
170170
}
171171

172+
public void destroySections() {
173+
for (final SectionMeta section : sections) {
174+
for (final Section handler : section.getHandlers()) {
175+
handler.onSectionExit(this, section);
176+
}
177+
}
178+
this.sections.clear();
179+
}
180+
172181
public abstract boolean hasFunction(String name, int arguments);
173182

174183
public Function getDefaultFunction(String name, int arguments) {
@@ -202,6 +211,10 @@ public void destroyUnit() {
202211
units.remove(0);
203212
}
204213

214+
public void destroyUnits() {
215+
units.clear();
216+
}
217+
205218
public void createUnit(final LanguageElement element) {
206219
units.add(0, new Unit(element));
207220
}

src/main/java/org/byteskript/skript/compiler/Pattern.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ public Type[] expected() {
212212
return expected;
213213
}
214214

215+
public boolean equals(String string) {
216+
return matcher.group().equals(string);
217+
}
218+
215219
}
216220

217221
}

src/main/java/org/byteskript/skript/compiler/SimpleSkriptCompiler.java

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,9 @@ public ElementTree parseLine(final String line, final FileContext context) {
182182
context.destroySection();
183183
context.destroyUnit();
184184
context.indent--;
185-
}
186-
if (actual == 0) { // allow different indentation per member
187-
context.setIndentUnit(null);
188-
}
189-
} else if (actual != expected) throw new ScriptParseError(context.lineNumber(), "Wrong indent.");
185+
} // allow different indentation per member
186+
if (actual == 0) context.setIndentUnit(null);
187+
} else if (actual != expected) throw new ScriptParseError(context.lineNumber(), "Incorrect indentation.");
190188
final String statement = line.trim();
191189
if (statement.isBlank()) return null;
192190
if (line.endsWith(":")) {
@@ -239,7 +237,7 @@ public ElementTree assembleExpression(String expression, final Type expected, fi
239237
if (!handler.allowAsInputFor(expected)) continue;
240238
final Pattern.Match match = handler.match(expression, context);
241239
if (match == null) continue;
242-
if (!match.matcher().group().equals(expression)) continue;
240+
if (!match.equals(expression)) continue;
243241
final Type[] types = match.expected();
244242
final String[] inputs = match.groups();
245243
if (inputs.length < types.length) continue;
@@ -268,6 +266,18 @@ public Class<?> load(byte[] bytecode, String name) {
268266
return Skript.currentLoader().loadClass(name, bytecode);
269267
}
270268

269+
@Override
270+
public boolean addLibrary(Library library) {
271+
if (libraries.contains(library)) return false;
272+
this.libraries.add(0, library); // need to make sure it goes before skript
273+
return true;
274+
}
275+
276+
@Override
277+
public boolean removeLibrary(Library library) {
278+
return libraries.remove(library);
279+
}
280+
271281
@Override
272282
public PostCompileClass[] compile(InputStream stream, Type name) {
273283
return compile(unstream(stream), name);
@@ -282,12 +292,10 @@ public PostCompileClass[] compile(String source, Type path) {
282292
final FileContext context = new FileContext(path);
283293
context.libraries.addAll(libraries);
284294
for (final Library library : libraries) {
285-
for (final Type type : library.getTypes()) {
286-
context.registerType(type);
287-
}
295+
for (final Type type : library.getTypes()) context.registerType(type);
288296
}
289297
final List<String> lines = this.removeComments(source);
290-
for (String line : lines) {
298+
for (final String line : lines) {
291299
context.lineNumber++;
292300
context.line = null;
293301
if (line.isBlank()) continue;
@@ -302,30 +310,14 @@ public PostCompileClass[] compile(String source, Type path) {
302310
throw new ScriptCompileError(context.lineNumber, "Unknown error during compilation:", ex);
303311
}
304312
}
305-
for (int i = 0; i < context.units.size(); i++) {
306-
context.destroyUnit();
307-
}
308-
for (int i = 0; i < context.sections.size(); i++) {
309-
context.destroySection();
310-
}
313+
context.destroyUnits();
314+
context.destroySections();
311315
return context.compile();
312316
}
313317

314-
@Override
315-
public boolean addLibrary(Library library) {
316-
if (libraries.contains(library)) return false;
317-
libraries.add(0, library); // need to make sure it goes before skript
318-
return true;
319-
}
320-
321-
@Override
322-
public boolean removeLibrary(Library library) {
323-
return libraries.remove(library);
324-
}
325-
326318
int trueIndent(final String line, final String unit) {
327-
int indent = 0, offset = 0;
328319
if (unit == null) return 0;
320+
int indent = 0, offset = 0;
329321
final int length = unit.length();
330322
while (line.startsWith(unit, offset)) {
331323
indent++;

src/main/java/org/byteskript/skript/compiler/SkriptCompiler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ public SkriptLangSpec getLanguage() {
2727

2828
public abstract Class<?> load(byte[] bytecode, String name);
2929

30+
public abstract boolean addLibrary(Library library);
31+
32+
public abstract boolean removeLibrary(Library library);
33+
3034
public abstract PostCompileClass[] compile(InputStream stream, Type name);
3135

3236
public abstract PostCompileClass[] compile(InputStream file, String path);
3337

3438
public abstract PostCompileClass[] compile(String file, Type path);
3539

36-
public abstract boolean addLibrary(Library library);
37-
38-
public abstract boolean removeLibrary(Library library);
39-
4040
}

src/main/java/org/byteskript/skript/runtime/Script.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public final class Script {
5959
}
6060

6161
@Ignore
62+
@SuppressWarnings("unchecked")
6263
Script(boolean init, Skript skript, File sourceFile, Class<?>... classes) {
6364
this.skript = skript;
6465
this.sourceFile = sourceFile;

src/main/java/org/byteskript/skript/runtime/Skript.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.nio.file.Path;
3131
import java.util.*;
3232
import java.util.concurrent.*;
33+
import java.util.concurrent.atomic.AtomicInteger;
3334

3435
@Description("""
3536
This class is the entry-point for any program or library using ByteSkript.
@@ -533,7 +534,7 @@ This can compile only simple scripts (which create a single class file.)
533534
@CompilerDependent
534535
public void compileScript(final InputStream input, final String name, final OutputStream target)
535536
throws IOException {
536-
final PostCompileClass datum = compileScript(input, name);
537+
final PostCompileClass datum = this.compileScript(input, name);
537538
target.write(datum.code());
538539
}
539540

@@ -592,6 +593,44 @@ public PostCompileClass[] compileScripts(final File root) throws IOException {
592593
return scripts.toArray(new PostCompileClass[0]);
593594
}
594595

596+
@Description("""
597+
Compiles all scripts in the root file to code representations.
598+
These representations may be loaded, written to files or otherwise used.
599+
""")
600+
@GenerateExample
601+
@CompilerDependent
602+
public Promise<PostCompileClass[]> compileScriptsAsync(final File root) throws IOException {
603+
if (!root.exists()) throw new ScriptLoadError("Root folder does not exist.");
604+
if (!root.isDirectory()) throw new ScriptLoadError("Root must be a folder.");
605+
final List<File> files = getFiles(new ArrayList<>(), root.toPath());
606+
final CompletableFuture<PostCompileClass[]> future = new CompletableFuture<>();
607+
final Promise<PostCompileClass[]> promise = new Promise<>(future);
608+
final Collection<PostCompileClass> scripts = Collections.synchronizedCollection(new ArrayList<>());
609+
final AtomicInteger integer = new AtomicInteger();
610+
for (final File file : files) {
611+
if (file == null) continue;
612+
if (!file.getName().endsWith(".bsk")) continue;
613+
try (final InputStream stream = new FileInputStream(file)) {
614+
final String name = this.getClassName(file, root);
615+
this.compileComplexScriptAsync(stream, name)
616+
.whenComplete(output -> {
617+
scripts.addAll(Arrays.asList(output));
618+
integer.incrementAndGet();
619+
});
620+
}
621+
}
622+
final int expected = files.size();
623+
future.completeAsync(() -> {
624+
while (integer.get() < expected) {
625+
try {
626+
Thread.sleep(5);
627+
} catch (InterruptedException e) {}
628+
}
629+
return scripts.toArray(new PostCompileClass[0]);
630+
}, this.executor);
631+
return promise;
632+
}
633+
595634
@Description("""
596635
Compiles a single script to its class files.
597636
This method may be unavailable in some distributions.
@@ -614,6 +653,27 @@ public PostCompileClass compileScript(final String code, final String name) {
614653
return compileScript(new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8)), name);
615654
}
616655

656+
@Description("""
657+
Compiles a simple script from its string source to a memory class.
658+
This method may be unavailable in some distributions.
659+
""")
660+
@GenerateExample
661+
@CompilerDependent
662+
public Promise<PostCompileClass[]> compileScriptAsync(final String code, final String name) {
663+
return compileComplexScriptAsync(new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8)), name);
664+
}
665+
666+
@Description("""
667+
Compiles a single script to its class files.
668+
This version runs in the background and returns a promise
669+
This method may be unavailable in some distributions.
670+
""")
671+
@GenerateExample
672+
@CompilerDependent
673+
public Promise<PostCompileClass[]> compileComplexScriptAsync(final InputStream stream, final String name) {
674+
return compiler.compileAsync(stream, new Type(name), this);
675+
}
676+
617677
@Description("""
618678
Unloads a script by its main class.
619679
This is a destructive operation.

src/main/java/org/byteskript/skript/runtime/internal/ModifiableCompiler.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
import mx.kenzie.foundation.language.PostCompileClass;
1212
import org.byteskript.skript.api.Document;
1313
import org.byteskript.skript.api.Library;
14+
import org.byteskript.skript.runtime.Skript;
1415

1516
import java.io.InputStream;
1617
import java.util.ArrayList;
1718
import java.util.Arrays;
1819
import java.util.List;
20+
import java.util.concurrent.CompletableFuture;
21+
import java.util.function.Supplier;
1922

2023
@Description("""
2124
This is a template for a modifiable compiler.
@@ -27,12 +30,6 @@ public interface ModifiableCompiler {
2730

2831
Class<?> load(byte[] bytecode, String name);
2932

30-
PostCompileClass[] compile(InputStream stream, Type name);
31-
32-
PostCompileClass[] compile(InputStream file, String path);
33-
34-
PostCompileClass[] compile(String file, Type path);
35-
3633
boolean addLibrary(Library library);
3734

3835
boolean removeLibrary(Library library);
@@ -50,4 +47,26 @@ default Document[] generateDocumentation() {
5047

5148
Library[] getLibraries();
5249

50+
default Promise<PostCompileClass[]> compileAsync(InputStream stream, Type name, Skript skript) {
51+
return this.background(() -> compile(stream, name), skript);
52+
}
53+
54+
default Promise<PostCompileClass[]> background(Supplier<PostCompileClass[]> supplier, Skript skript) {
55+
return new Promise<>(CompletableFuture.supplyAsync(supplier, skript.getExecutor()));
56+
}
57+
58+
PostCompileClass[] compile(InputStream stream, Type name);
59+
60+
default Promise<PostCompileClass[]> compileAsync(InputStream file, String path, Skript skript) {
61+
return this.background(() -> compile(file, path), skript);
62+
}
63+
64+
PostCompileClass[] compile(InputStream file, String path);
65+
66+
default Promise<PostCompileClass[]> compileAsync(String file, Type path, Skript skript) {
67+
return this.background(() -> compile(file, path), skript);
68+
}
69+
70+
PostCompileClass[] compile(String file, Type path);
71+
5372
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (c) 2022 ByteSkript org (Moderocky)
3+
* View the full licence information and permissions:
4+
* https://github.com/Moderocky/ByteSkript/blob/master/LICENSE
5+
*/
6+
7+
package org.byteskript.skript.runtime.internal;
8+
9+
import org.byteskript.skript.error.ScriptCompileError;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.concurrent.*;
13+
import java.util.function.Consumer;
14+
15+
public class Promise<Type> implements Future<Type> {
16+
17+
protected final CompletableFuture<Type> future;
18+
protected volatile Type type;
19+
20+
public Promise(CompletableFuture<Type> future) {
21+
this.future = future;
22+
}
23+
24+
public boolean ready() {
25+
return future.isDone();
26+
}
27+
28+
public void cancel() {
29+
this.future.cancel(true);
30+
}
31+
32+
@Override
33+
public boolean cancel(boolean mayInterruptIfRunning) {
34+
return future.cancel(mayInterruptIfRunning);
35+
}
36+
37+
@Override
38+
public boolean isCancelled() {
39+
return future.isCancelled();
40+
}
41+
42+
@Override
43+
public boolean isDone() {
44+
return future.isDone();
45+
}
46+
47+
public synchronized Type get() {
48+
if (type == null) await();
49+
return type;
50+
}
51+
52+
public synchronized void await() {
53+
try {
54+
this.type = future.get();
55+
} catch (InterruptedException | ExecutionException ex) {
56+
throw new ScriptCompileError(-1, ex);
57+
}
58+
}
59+
60+
@Override
61+
public synchronized Type get(long timeout, @NotNull TimeUnit unit) {
62+
try {
63+
return type = future.get(timeout, unit);
64+
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
65+
throw new ScriptCompileError(-1, ex);
66+
}
67+
}
68+
69+
public void whenComplete(Consumer<Type> consumer) {
70+
this.future.thenAccept(consumer);
71+
}
72+
73+
}

src/main/java/skript.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static double to_radians(double number) {
7979
return Math.toRadians(number);
8080
}
8181

82-
public static Number abs(Object object) {
82+
public static Number abs(Object object) { // the instance-checks make sure the correct absolute is used
8383
if (object == null) return 0;
8484
if (object instanceof Byte number) return Math.abs(number);
8585
if (object instanceof Short number) return Math.abs(number);

0 commit comments

Comments
 (0)