Skip to content

Commit f35c335

Browse files
committed
Add type converters.
1 parent 07c24c2 commit f35c335

File tree

12 files changed

+258
-49
lines changed

12 files changed

+258
-49
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>org.byteskript</groupId>
88
<artifactId>byteskript</artifactId>
9-
<version>1.0.18</version>
9+
<version>1.0.19</version>
1010
<name>ByteSkript</name>
1111
<description>A compiled JVM implementation of the Skript language.</description>
1212

src/main/java/org/byteskript/skript/api/Library.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
import mx.kenzie.foundation.compiler.State;
1212
import mx.kenzie.foundation.language.PostCompileClass;
1313
import org.byteskript.skript.compiler.Context;
14+
import org.byteskript.skript.runtime.type.Converter;
1415

15-
import java.util.ArrayList;
16-
import java.util.Collection;
17-
import java.util.List;
16+
import java.util.*;
1817

1918
@Description("""
2019
A library that provides syntax or runtime dependencies to the Skript implementation.
@@ -80,4 +79,8 @@ default Document[] generateDocumentation() {
8079
""")
8180
SyntaxElement[] getSyntax();
8281

82+
default Map<Converter.Data, Converter<?, ?>> getConverters() {
83+
return new HashMap<>();
84+
}
85+
8386
}

src/main/java/org/byteskript/skript/api/ModifiableLibrary.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.byteskript.skript.api.syntax.EventHolder;
1515
import org.byteskript.skript.compiler.CompileState;
1616
import org.byteskript.skript.compiler.Context;
17+
import org.byteskript.skript.runtime.type.Converter;
1718

1819
import java.lang.reflect.Method;
1920
import java.util.*;
@@ -38,6 +39,8 @@ public class ModifiableLibrary implements SyntaxAnnotationUnwrapper, Library {
3839
protected final List<Type> types = new ArrayList<>();
3940
@Description("This library's name for debugging and error messages.")
4041
protected final String name;
42+
@Ignore
43+
protected final Map<Converter.Data, Converter<?, ?>> converters = new HashMap<>();
4144

4245
@Description("""
4346
Create a new library instance with the given name.
@@ -117,6 +120,11 @@ public void registerTypes(Type... types) {
117120
}
118121
}
119122

123+
public <From, To> void registerConverter(Class<From> from, Class<To> to, Converter<From, To> converter) {
124+
final Converter.Data data = new Converter.Data(from, to);
125+
this.converters.put(data, converter);
126+
}
127+
120128
@Ignore
121129
public Type registerType(String classPath) {
122130
final Type type = new Type(classPath);
@@ -175,4 +183,10 @@ public SyntaxElement[] getSyntax() {
175183
}
176184
return elements.toArray(new SyntaxElement[0]);
177185
}
186+
187+
@Override
188+
@Ignore
189+
public Map<Converter.Data, Converter<?, ?>> getConverters() {
190+
return converters;
191+
}
178192
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
import mx.kenzie.foundation.language.PostCompileClass;
1212
import org.byteskript.skript.api.Library;
1313
import org.byteskript.skript.runtime.internal.ModifiableCompiler;
14+
import org.byteskript.skript.runtime.type.Converter;
1415

1516
import java.io.InputStream;
17+
import java.util.HashMap;
18+
import java.util.Map;
1619

1720
public abstract class SkriptCompiler implements Compiler<SkriptLangSpec>, ModifiableCompiler {
1821

@@ -31,6 +34,15 @@ public SkriptLangSpec getLanguage() {
3134

3235
public abstract boolean removeLibrary(Library library);
3336

37+
@Override
38+
public Map<Converter.Data, Converter<?, ?>> getConverters() {
39+
final Map<Converter.Data, Converter<?, ?>> map = new HashMap<>();
40+
for (final Library library : this.getLibraries()) {
41+
map.putAll(library.getConverters());
42+
}
43+
return map;
44+
}
45+
3446
public abstract PostCompileClass[] compile(InputStream stream, Type name);
3547

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

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.byteskript.skript.lang.syntax.variable.GlobalVariableExpression;
7272
import org.byteskript.skript.lang.syntax.variable.ThreadVariableExpression;
7373
import org.byteskript.skript.lang.syntax.variable.VariableExpression;
74+
import org.byteskript.skript.runtime.Skript;
7475
import org.byteskript.skript.runtime.internal.IOHandlers;
7576
import org.byteskript.skript.runtime.type.DataList;
7677
import org.byteskript.skript.runtime.type.DataMap;
@@ -121,6 +122,13 @@ private SkriptLangSpec() {
121122
CommonTypes.METHOD,
122123
CommonTypes.FIELD
123124
);
125+
registerConverter(String.class, Integer.class, Integer::valueOf);
126+
registerConverter(String.class, Double.class, Double::valueOf);
127+
registerConverter(String.class, Long.class, Long::valueOf);
128+
registerConverter(String.class, Number.class, Double::valueOf);
129+
registerConverter(String.class, Error.class, Error::new);
130+
registerConverter(String.class, Class.class, Skript::findAnyClass);
131+
registerConverter(Object.class, String.class, Object::toString);
124132
registerSyntax(CompileState.ROOT,
125133
new TypeMember(),
126134
new TemplateTypeMember(),
@@ -233,6 +241,7 @@ private SkriptLangSpec() {
233241
new NewLineExpression(),
234242
new ResultOfExpression(), // must try before property
235243
new PropertyExpression(),
244+
new ConverterExpression(),
236245
new SystemInputExpression(),
237246
new SystemPropertyExpression(),
238247
new ExternalFunctionExpression(),
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.lang.syntax.generic;
8+
9+
import mx.kenzie.foundation.MethodBuilder;
10+
import mx.kenzie.foundation.Type;
11+
import mx.kenzie.foundation.WriteInstruction;
12+
import org.byteskript.skript.api.syntax.SimpleExpression;
13+
import org.byteskript.skript.compiler.CommonTypes;
14+
import org.byteskript.skript.compiler.Context;
15+
import org.byteskript.skript.compiler.Pattern;
16+
import org.byteskript.skript.compiler.SkriptLangSpec;
17+
import org.byteskript.skript.lang.element.StandardElements;
18+
import org.byteskript.skript.runtime.internal.ExtractedSyntaxCalls;
19+
20+
import java.lang.reflect.Method;
21+
22+
public class ConverterExpression extends SimpleExpression {
23+
24+
public ConverterExpression() {
25+
super(SkriptLangSpec.LIBRARY, StandardElements.EXPRESSION, "%Object% [parsed] as %Type%");
26+
}
27+
28+
@Override
29+
public Pattern.Match match(String thing, Context context) {
30+
if (!thing.contains(" as ")) return null;
31+
return super.match(thing, context);
32+
}
33+
34+
@Override
35+
public boolean allowAsInputFor(Type type) {
36+
return true;
37+
}
38+
39+
@Override
40+
public Type getReturnType() {
41+
return CommonTypes.OBJECT;
42+
}
43+
44+
@Override
45+
public void compile(Context context, Pattern.Match match) throws Throwable {
46+
final Type type = context.getCompileCurrent().nested()[1].match().meta();
47+
final MethodBuilder method = context.getMethod();
48+
assert method != null;
49+
final Method target = ExtractedSyntaxCalls.class.getMethod("convert", Object.class, Object.class);
50+
this.writeCall(method, target, context);
51+
method.writeCode(WriteInstruction.cast(type));
52+
}
53+
}

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

Lines changed: 90 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.byteskript.skript.error.ScriptRuntimeError;
2323
import org.byteskript.skript.runtime.internal.*;
2424
import org.byteskript.skript.runtime.threading.*;
25+
import org.byteskript.skript.runtime.type.Converter;
2526

2627
import java.io.*;
2728
import java.nio.charset.StandardCharsets;
@@ -80,6 +81,8 @@ public final class Skript {
8081
final WeakList<ScriptClassLoader> loaders = new WeakList<>();
8182
@Ignore
8283
final List<Script> scripts = new ArrayList<>(); // the only strong reference, be careful!
84+
@Ignore
85+
final Map<Converter.Data, Converter<?, ?>> converters;
8386

8487
@Description("""
8588
Create a Skript runtime with a custom (non-default) Skript compiler.
@@ -106,12 +109,16 @@ public Skript(SkriptThreadProvider threadProvider, ModifiableCompiler compiler,
106109
this.compiler = compiler;
107110
this.factory = threadProvider;
108111
this.factory.setSkriptInstance(this);
109-
executor = Executors.newCachedThreadPool(factory);
112+
this.executor = Executors.newCachedThreadPool(factory);
110113
this.mainThread = main;
111114
this.scheduler = new ScheduledThreadPoolExecutor(4, factory);
112115
this.processes = new ArrayList<>();
113116
this.events = new HashMap<>();
117+
this.converters = new HashMap<>();
114118
skript = this;
119+
if (compiler != null) {
120+
this.converters.putAll(compiler.getConverters());
121+
}
115122
}
116123

117124
@Description("""
@@ -186,12 +193,84 @@ public static Skript localInstance() {
186193
return thread.skript;
187194
}
188195

196+
private static String createClassName(String name, String path) {
197+
final int index = name.lastIndexOf('.');
198+
if (path.startsWith(File.separator)) path = path.substring(1);
199+
if (index == -1) return path.replace(File.separatorChar, '.');
200+
return path.substring(0, index).replace(File.separatorChar, '.');
201+
}
202+
203+
private static List<File> getFiles(List<File> files, Path root) {
204+
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
205+
for (Path path : stream) {
206+
if (path.toFile().isDirectory()) {
207+
getFiles(files, path);
208+
} else {
209+
if (!path.toFile().getName().endsWith(".bsk")) continue;
210+
files.add(path.toAbsolutePath().toFile());
211+
}
212+
}
213+
} catch (IOException e) {
214+
e.printStackTrace();
215+
}
216+
return files;
217+
}
218+
219+
@Deprecated
220+
public static Class<?> findAnyClass(String name) {
221+
{
222+
final Class<?> found = getClass(name, Skript.class);
223+
if (found != null) return found;
224+
}
225+
final Skript skript = findInstance();
226+
{
227+
final Class<?> found = skript.getClass(name);
228+
if (found != null) return found;
229+
}
230+
{
231+
for (final Script script : skript.scripts) {
232+
for (final Class<?> type : script.classes()) {
233+
if (type.getName().equals(name)) return type;
234+
}
235+
}
236+
for (final Script script : skript.scripts) {
237+
for (final Class<?> type : script.classes()) {
238+
if (type.getSimpleName().equals(name)) return type;
239+
}
240+
}
241+
}
242+
return null;
243+
}
244+
245+
private static Class<?> getClass(String name, Class<?> owner) {
246+
try {
247+
return Class.forName(name, false, owner.getClassLoader());
248+
} catch (ClassNotFoundException ex) {
249+
return null;
250+
}
251+
}
252+
189253
private static Skript findInstance() {
190254
final Thread current = Thread.currentThread();
191255
if (!(current instanceof ScriptThread thread)) return currentInstance();
192256
return thread.skript;
193257
}
194258

259+
@Description("""
260+
This searches the app class loader and all library class loaders for the given class.
261+
This does not search script class loaders.
262+
""")
263+
@GenerateExample
264+
public Class<?> getClass(String name) {
265+
final Class<?> found = getClass(name, Skript.class);
266+
if (found != null) return found;
267+
for (Library library : compiler.getLibraries()) {
268+
final Class<?> test = getClass(name, library.getClass());
269+
if (test != null) return test;
270+
}
271+
return null;
272+
}
273+
195274
@Description("""
196275
This returns the most recently-created Skript runtime.
197276
It is designed to be an entry-point for programs that attach in an unusual way, and have
@@ -205,27 +284,19 @@ public static Skript currentInstance() {
205284
return skript;
206285
}
207286

208-
private static String createClassName(String name, String path) {
209-
final int index = name.lastIndexOf('.');
210-
if (path.startsWith(File.separator)) path = path.substring(1);
211-
if (index == -1) return path.replace(File.separatorChar, '.');
212-
return path.substring(0, index).replace(File.separatorChar, '.');
287+
public <From, To> void registerConverter(Class<From> from, Class<To> to, Converter<From, To> converter) {
288+
final Converter.Data data = new Converter.Data(from, to);
289+
this.converters.put(data, converter);
213290
}
214291

215-
private static List<File> getFiles(List<File> files, Path root) {
216-
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
217-
for (Path path : stream) {
218-
if (path.toFile().isDirectory()) {
219-
getFiles(files, path);
220-
} else {
221-
if (!path.toFile().getName().endsWith(".bsk")) continue;
222-
files.add(path.toAbsolutePath().toFile());
223-
}
224-
}
225-
} catch (IOException e) {
226-
e.printStackTrace();
292+
public <From, To> Converter<From, To> getConverter(Class<From> from, Class<To> to) {
293+
final Converter.Data data = new Converter.Data(from, to);
294+
if (converters.containsKey(data)) return (Converter<From, To>) converters.get(data);
295+
for (final Converter.Data found : converters.keySet()) {
296+
if (from.isAssignableFrom(found.from()) && to.isAssignableFrom(found.to()))
297+
return (Converter<From, To>) converters.get(data);
227298
}
228-
return files;
299+
return null;
229300
}
230301

231302
@Description("""
@@ -397,29 +468,6 @@ public void registerEventHandler(final Class<? extends Event> event, final Scrip
397468
this.events.get(event).add(runner);
398469
}
399470

400-
@Description("""
401-
This searches the app class loader and all library class loaders for the given class.
402-
This does not search script class loaders.
403-
""")
404-
@GenerateExample
405-
public Class<?> getClass(String name) {
406-
final Class<?> found = getClass(name, Skript.class);
407-
if (found != null) return found;
408-
for (Library library : compiler.getLibraries()) {
409-
final Class<?> test = getClass(name, library.getClass());
410-
if (test != null) return test;
411-
}
412-
return null;
413-
}
414-
415-
private static Class<?> getClass(String name, Class<?> owner) {
416-
try {
417-
return Class.forName(name, false, owner.getClassLoader());
418-
} catch (ClassNotFoundException ex) {
419-
return null;
420-
}
421-
}
422-
423471
@Description("""
424472
Registers a single class library, typically compiled from a script to load
425473
custom syntax.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.byteskript.skript.runtime.Skript;
1414
import org.byteskript.skript.runtime.UnsafeAccessor;
1515
import org.byteskript.skript.runtime.threading.ScriptThread;
16+
import org.byteskript.skript.runtime.type.Converter;
1617
import org.byteskript.skript.runtime.type.DataList;
1718

1819
import java.io.BufferedReader;
@@ -36,6 +37,14 @@ private static Skript findInstance() {
3637
return thread.skript;
3738
}
3839

40+
public static Object convert(Object from, Object object) {
41+
if (!(object instanceof Class<?> to)) throw new ScriptRuntimeError("Object must be converted to a type.");
42+
if (from.getClass().isAssignableFrom(to)) return from;
43+
final Converter converter = findInstance().getConverter(from.getClass(), to);
44+
if (converter == null) return from;
45+
return converter.convert(from);
46+
}
47+
3948
public static void handleTestError(Throwable throwable) {
4049
if (isTest()) UNSAFE.throwException(throwable);
4150
}

0 commit comments

Comments
 (0)